private static void CreateTrackingInfo(Trolley trolley) { var newTrackingInfo = new TrolleyTrackingInfo(trolley); if (activeRoutes.Count == 1) { // For a single active route, all trolleys follow that route newTrackingInfo.CurrentRoute = activeRoutes.First().Value; } trolleyTrackingInfo.Add(trolley.ID, newTrackingInfo); }
private static void UpdateStopTime(TrolleyTrackingInfo trackingInfo) { // See if trolley has moved far enough to justify a full stop distance check var currentLocation = new Coordinate((double)trackingInfo.CurrentTrolley.CurrentLat, (double)trackingInfo.CurrentTrolley.CurrentLon); if (currentLocation.Distance(trackingInfo.LastReportedLocation) > MinMoveDistance) { // Heading will be integer 0..359 trackingInfo.CurrentHeading = ((int)(trackingInfo.LastReportedLocation.DirectionToward(currentLocation) + 0.5)) % 360; CheckForStopInRange(trackingInfo, currentLocation); trackingInfo.LastReportedLocation = currentLocation; } }
/// <summary> /// Find stop index in the ordered stop list /// </summary> /// <param name="trackingInfo"></param> /// <param name="stopID"></param> /// <returns>Stop index or -1 if not found</returns> private static int FindRouteStopIndex(TrolleyTrackingInfo trackingInfo, int stopID) { var stopIndex = -1; for (int i = 0; i < trackingInfo.CurrentRoute.RouteStops.Count; i++) { if (trackingInfo.CurrentRoute.RouteStops[i].StopID == stopID) { stopIndex = i; break; } } return(stopIndex); }
/// <summary> /// Test that route path next to this stop is heading in the same direction as trolley, /// and not the return path /// </summary> /// <param name="trackingInfo">Tracking info - CurrentRouteStop and PreviousRouteStop will be assigned here</param> /// <param name="currentLocation"></param> /// <param name="stopSummary"></param> /// <returns></returns> private static bool StopIsOnRight(TrolleyTrackingInfo trackingInfo, Coordinate currentLocation, StopSummary stopSummary) { // Find route direction at our current location (stop) var routeStopIndex = FindRouteStopIndex(trackingInfo, stopSummary.ID); if (routeStopIndex < 0) { Debug.WriteLine($"Trolley {trackingInfo.CurrentTrolley.Number} Route Stop Index not found"); return(false); } var routeStop = trackingInfo.CurrentRoute.RouteStops[routeStopIndex]; var numStops = trackingInfo.CurrentRoute.RouteStops.Count; trackingInfo.PreviousRouteStop = trackingInfo.CurrentRoute.RouteStops[(routeStopIndex + numStops - 1) % numStops]; trackingInfo.CurrentRouteStop = routeStop; trackingInfo.CurrentRouteStopIndex = routeStopIndex; var routeSegmentIndex = routeStop.RouteSegmentIndex; if (routeSegmentIndex < 0) { Debug.WriteLine($"Trolley {trackingInfo.CurrentTrolley.Number} Route Stop Index not set"); return(false); } var segmentCount = trackingInfo.CurrentRoute.Shapes.Count; var segmentEnd = trackingInfo.CurrentRoute.Shapes[routeSegmentIndex]; var segmentStart = trackingInfo.CurrentRoute.Shapes[(routeSegmentIndex + segmentCount - 1) % segmentCount]; var routeSegmentStart = new Coordinate(segmentStart.Lat, segmentStart.Lon); var routeSegmentEnd = new Coordinate(segmentEnd.Lat, segmentEnd.Lon); var routeDirection = routeSegmentStart.DirectionToward(routeSegmentEnd); var trolleyDirection = trackingInfo.CurrentHeading; // Convert directions from 0..360 to +/-180 for comparison if (routeDirection > 180) { routeDirection = routeDirection - 360; } if (trolleyDirection > 180) { trolleyDirection = trolleyDirection - 360; } Debug.WriteLine($"Trolley {trackingInfo.CurrentTrolley.Number} Route direction: {routeDirection}, trolley heading: {trolleyDirection}"); if (Math.Abs((int)routeDirection - trolleyDirection) < 130) { return(true); } return(false); }
/// <summary> /// Calculate and update travel time from previous to current stop /// </summary> /// <param name="trackingInfo"></param> private static void UpdateTravelTime(TrolleyTrackingInfo trackingInfo) { var routeStop = trackingInfo.CurrentRouteStop; if (routeStop == null) { return; } var lastRouteStop = trackingInfo.PreviousRouteStop; if (!lastRouteStop.LastTimeAtStop.HasValue) { return; } var elapsedSeconds = (routeStop.LastTimeAtStop - lastRouteStop.LastTimeAtStop).Value.TotalSeconds; if (elapsedSeconds > MaxTimeBetweenStops) { // Invalid time - likely from a long break or first stop of the day return; } var newTravelTime = (int)elapsedSeconds; if (routeStop.AverageTravelTimeToNextStop != 0) { // Average is approximted by a weighted moving average instead of true moving average int percentWeight = 50; var adjustment = (newTravelTime - routeStop.AverageTravelTimeToNextStop) * percentWeight / 100; // Avoid having a single anomaly create a large swing if (Math.Abs(adjustment) > (routeStop.AverageTravelTimeToNextStop / 2)) { adjustment = (routeStop.AverageTravelTimeToNextStop / 2) * Math.Sign(adjustment); } newTravelTime = routeStop.AverageTravelTimeToNextStop + adjustment; if (newTravelTime < MinTimeBetweenStops) { newTravelTime = MinTimeBetweenStops; } } routeStop.AverageTravelTimeToNextStop = newTravelTime; UpdateTravelTimeInDB(routeStop); }
/// <summary> /// Check for stop proximity - try to minimize number of polled stops /// - First preference is the previous stop /// Then loop through all stops /// </summary> /// <param name="trackingInfo"></param> /// <param name="currentLocation"></param> private static void CheckForStopInRange(TrolleyTrackingInfo trackingInfo, Coordinate currentLocation) { if (stopSummaries.ContainsKey(trackingInfo.LastStopID)) { // Test if Still within stop zone var stopSummary = stopSummaries[trackingInfo.LastStopID]; if (currentLocation.Distance(new Coordinate(stopSummary.Lat, stopSummary.Lon)) < StopRadiusCheck) { return; } Debug.WriteLine($"Trolley {trackingInfo.CurrentTrolley.Number} Left stop zone {stopSummary.Name}"); } if (trackingInfo.CurrentRoute == null) { return; // No route found to assign to trolley } foreach (var routeStop in trackingInfo.CurrentRoute.RouteStops) { var stopID = routeStop.StopID; var stopSummary = stopSummaries[stopID]; if (currentLocation.Distance(new Coordinate(stopSummary.Lat, stopSummary.Lon)) < StopRadiusCheck) { Debug.WriteLine($"Trolley {trackingInfo.CurrentTrolley.Number} Entered stop zone {stopSummary.Name}"); // Found new stop zone; validate that it's an upcoming stop on right side and not on the left side // (which would be on the return trip) if (StopIsOnRight(trackingInfo, currentLocation, stopSummary)) { UpdateStopArrivalTime(trackingInfo.CurrentRouteStop); UpdateTravelTime(trackingInfo); PredictStopArrivalTimes(trackingInfo); trackingInfo.LastStopID = stopSummary.ID; Debug.WriteLine($"Trolley {trackingInfo.CurrentTrolley.Number} Stop was on right {stopSummary.Name}"); return; } else { Debug.WriteLine($"Trolley {trackingInfo.CurrentTrolley.Number} Stop not on right {stopSummary.Name}"); } } } // Not currently in any stop zone trackingInfo.LastStopID = -1; }
/// <summary> /// Set or refresh stop arrival times for all stops on this route based on just arriving at current stop /// </summary> /// <param name="trackingInfo"></param> /// <param name="currentLocation"></param> /// <param name="stopSummary"></param> private static void PredictStopArrivalTimes(TrolleyTrackingInfo trackingInfo) { var trolleyNumber = trackingInfo.CurrentTrolley.Number; var now = UTCToLocalTime.LocalTimeFromUTC(DateTime.UtcNow); var stopsCount = trackingInfo.CurrentRoute.RouteStops.Count; int travelSeconds = 0; // Total travel time from current stop for (var i = 0; i <= stopsCount; i++) { var stopIndex = (trackingInfo.CurrentRouteStopIndex + i) % stopsCount; var routeStop = trackingInfo.CurrentRoute.RouteStops[stopIndex]; if (routeStop.LastTimeAtStop.HasValue) { var timeAtStop = now.AddSeconds(travelSeconds); var previousRouteStop = trackingInfo.CurrentRoute.RouteStops[(stopIndex + stopsCount - 1) % stopsCount]; var previousSummary = stopSummaries[previousRouteStop.StopID]; SetArrivalTime(trolleyNumber, previousSummary, timeAtStop); travelSeconds += routeStop.AverageTravelTimeToNextStop; } } }