/// <summary> /// Processing a grounded flight /// We can adjust its position and look for takeoffs /// </summary> private void ProcessGrounded(AdsbEvent currentEvent) { //Set the last auport first if it hasn been set yet // This happens if we enter GROUNDED from Unknown if (LastAirport == null && LastKnownPosition != null) { LastAirport = Airports.GetClosestAirport(LastKnownPosition); } // This is where we can process flights as they slow down. We can assume a reasonable distance here // The goal is to get coser and closer with the airport until we hit taxi speed // Inspired by A44D3F Delta - and the multitude of CLos Angeles area municipal airports if (LastGroundedFlight != null) { if (currentEvent.Speed != null && currentEvent.Speed > REASONABLE_TAXI_SPEED) { Airport tempAirport = Airports.GetClosestAirport(LastKnownPosition); if (tempAirport.Identifier != LastAirport.Identifier) { UpdateLastGroundedFlight(tempAirport); } } } //The goal here is to look for takeoffs // This means we have a Reasonable grounding delay of 15 minutes before searching for takeoff speed bool takeoffEligible = true; if (LastGrounded != null) { // we will ignore events to let the plane land TimeSpan landSpan = currentEvent.Timestamp.Subtract((DateTime)LastGrounded); double landSeconds = landSpan.TotalSeconds; if (landSeconds < REASONABLE_GROUNDING_DELAY) { takeoffEligible = false; } } if ((currentEvent.Speed) != null && takeoffEligible) { LastDeviceSpeed = currentEvent.Speed; LastDeviceSpeedTime = currentEvent.Timestamp; // We have hit takeoff speed, change status if (currentEvent.Speed > AirplaneProcessed.Model.TakeoffSpeed) { // We are taking off //Console.WriteLine("Taking off at " + currentEvent.Timestamp); CurrentStatus = AirplaneStatus.FLYING; LastTakeoff = currentEvent.Timestamp; } } }
private static FlightCollection CalculateFlights(AirportCollection airports, AdsbEventCollection events) { // Organize all ADS-B events by aircraft ID FlightCollection flights = new FlightCollection(); Dictionary <string, List <AdsbEvent> > eventsByID = new Dictionary <string, List <AdsbEvent> >(); foreach (AdsbEvent adsbEvent in events.Events) { if (!eventsByID.ContainsKey(adsbEvent.Identifier)) { eventsByID.Add(adsbEvent.Identifier, new List <AdsbEvent>()); eventsByID[adsbEvent.Identifier].Add(adsbEvent); } else { // Remove events that are soon after the previous one to improve performance int eventCount = eventsByID[adsbEvent.Identifier].Count; AdsbEvent lastEvent = eventsByID[adsbEvent.Identifier][eventCount - 1]; TimeSpan timeDiff = adsbEvent.Timestamp - lastEvent.Timestamp; if (timeDiff.TotalSeconds >= TimeThreshold) { eventsByID[adsbEvent.Identifier].Add(adsbEvent); } } } // For each aircraft identifier, step through the logged events in sequence to identify flights foreach (string identifier in eventsByID.Keys) { List <AdsbEvent> eventLog = eventsByID[identifier]; FlightStatus flightStatus = FlightStatus.Unknown; Airport lastAirport = new Airport(); DateTime lastGroundTime = DateTime.MinValue; foreach (AdsbEvent adsbEvent in eventLog) { // Find closest airport to the logged coordinates, as long as the event has coordinate values GeoCoordinate eventLoc = new GeoCoordinate(adsbEvent.Latitude ?? double.NaN, adsbEvent.Longitude ?? double.NaN); if (eventLoc.HasLocation()) { Airport closestAirport = airports.GetClosestAirport(eventLoc); GeoCoordinate airportLoc = new GeoCoordinate(closestAirport.Latitude, closestAirport.Longitude); double airportDistance = eventLoc.GetDistanceTo(airportLoc); double altitudeDiff = adsbEvent.Altitude.HasValue ? Math.Abs(adsbEvent.Altitude.Value - closestAirport.Elevation) : 0f; double speed = adsbEvent.Speed ?? 0f; // If the event was logged close to an airport, assume the aircraft has landed at that airport if (airportDistance <= DistanceThreshold && altitudeDiff <= AltitudeThreshold && speed <= SpeedThreshold) { // If this is not the first event (status unknown) and the new closest airport is different, create a new completed flight record if (flightStatus != FlightStatus.Unknown && closestAirport.Identifier != lastAirport.Identifier) { flights.Flights.Add(new Flight { AircraftIdentifier = identifier, DepartureTime = lastGroundTime, DepartureAirport = lastAirport.Identifier, ArrivalTime = adsbEvent.Timestamp, ArrivalAirport = closestAirport.Identifier }); } // In any case, update the status for the new airport flightStatus = FlightStatus.Ground; lastAirport = closestAirport; lastGroundTime = adsbEvent.Timestamp; } // If the aircraft is not close to an airport and was not previously flying, set the status to airborne else if ((airportDistance > DistanceThreshold || altitudeDiff > AltitudeThreshold) && flightStatus != FlightStatus.Air) { flightStatus = FlightStatus.Air; } // Otherwise, assume no status change } } // If all events have been read and the aircraft was last known to be flying, record a final flight with no arrival if (flightStatus == FlightStatus.Air) { flights.Flights.Add(new Flight { AircraftIdentifier = identifier, DepartureTime = lastGroundTime, DepartureAirport = lastAirport.Identifier, ArrivalTime = DateTime.MaxValue, ArrivalAirport = null }); } } return(flights); }
private static void Execute() { // Load the airports AirportCollection airports = AirportCollection.LoadFromFile(AirportsFilePath); var fileLines = System.IO.File.ReadAllLines(AdsbEventsFilePath); Dictionary <string, List <AdsbEvent> > airplanes = new Dictionary <string, List <AdsbEvent> >(); foreach (var line in fileLines) { string airplaneId = Events.AdsbEvent.FromJson(line).Identifier; if (airplanes.ContainsKey(airplaneId)) { airplanes[airplaneId].Add(Events.AdsbEvent.FromJson(line)); } else { List <AdsbEvent> tmpEventList = new List <AdsbEvent>() { Events.AdsbEvent.FromJson(line) }; airplanes.Add(Events.AdsbEvent.FromJson(line).Identifier, tmpEventList); } } System.IO.StreamWriter outFile = new System.IO.StreamWriter(OutputFilePath); foreach (var key in airplanes.Keys) { // debug output // System.Console.WriteLine(key); // setting defaults DateTime prevTime = DateTime.Parse("1/01/2020 12:00:00 AM"); GeoCoordinate prevAirportLocation = null; Airport prevAirport = null; Flight flight = new Flight(); flight.AircraftIdentifier = key; foreach (var adsbEventEntry in airplanes[key]) { // create a geolocation based on airplane adsb event coords string latLong = adsbEventEntry.Latitude.ToString() + "," + adsbEventEntry.Longitude.ToString(); GeoCoordinate airplaneLocation = GeoCoordinate.FromLatitudeAndLongitudeString(latLong); // get closest airport based on current airplane location Airport tempAirport = airports.GetClosestAirport(airplaneLocation); // create airport geolocation string airportLatLong = tempAirport.Latitude.ToString() + "," + tempAirport.Longitude.ToString(); GeoCoordinate airportLocation = GeoCoordinate.FromLatitudeAndLongitudeString(airportLatLong); // In order for an event to be a landing/takeoff: // 1. geolocation within 5 miles of an airport (Flight safety, no unregistered aircraft within 5 miles of an airport) // 2. altitude diff within 500 feet (safety altitude) // 3. take off and landing speed < 200 mph // In order for an airport to have a takeoff for this airplane: // 1. The timestamp reads must have a difference > 30 mins // Airplane heading and speed is not always present in adsbevent data, which means we can't rely on it to determine landing vs takeoff double tempDist = airplaneLocation.GetDistanceTo(airportLocation); if (tempDist < 5) { if (adsbEventEntry.Altitude != null && (adsbEventEntry.Altitude - tempAirport.Elevation <= 500) && adsbEventEntry.Speed != null && adsbEventEntry.Speed <= 200) { //did some reasearch, it looks like it takes the crew around 30-60 mins to get the plane checked again for next flight if (adsbEventEntry.Timestamp - prevTime > TimeSpan.Parse("00:30:00")) { // logic for arrivals and departures if (prevAirportLocation != null && prevAirportLocation != airportLocation) { //only departure is added w/ previous timestamp flight.DepartureTime = prevTime; flight.DepartureAirport = prevAirport.Identifier; } else if (prevAirportLocation == airportLocation) { //arrival is added w/ previous timestamp flight.ArrivalTime = prevTime; flight.ArrivalAirport = prevAirport.Identifier; } } prevAirportLocation = airportLocation; prevTime = adsbEventEntry.Timestamp; prevAirport = tempAirport; } } //record flights if arrival time is populated if (flight.ArrivalAirport != null) { outFile.WriteLine(flight.ToString()); flight = new Flight(); flight.AircraftIdentifier = key; } } } outFile.Close(); }