private static void Execute() { // Process data files var airports = AirportCollection.LoadFromFile(AirportsFilePath); var adsbEvents = LoadFromFile(AdsbEventsFilePath); Console.WriteLine($"Loaded {airports.Count} airports and {adsbEvents.Count} events"); // Sort events by aircraft var allAircraft = new Dictionary <string, Aircraft>(); adsbEvents.ForEach((adsbEvent) => { if (allAircraft.TryGetValue(adsbEvent.Identifier, out Aircraft aircraft)) { aircraft.AdsbEvents.Add(adsbEvent); } else { allAircraft.Add(adsbEvent.Identifier, new Aircraft(adsbEvent.Identifier, new List <AdsbEvent>() { adsbEvent })); } }); Console.WriteLine($"Identified {allAircraft.Count} aircraft"); // Find potential flights var flights = new List <Flight>(); allAircraft.Select(kv => kv.Value).ToList().ForEach((aircraft) => { var flight = new Flight() { AircraftIdentifier = aircraft.AircraftIdentifier }; // Filter out events without coordinates var aircraftEvents = aircraft.AdsbEvents .Where(adsbEvent => adsbEvent.Latitude != null && adsbEvent.Longitude != null) .OrderBy(adsbEvent => adsbEvent.Timestamp); // Implement a sliding window by analyzing the altitude data // This could also be implemented by analyzing the speed data, but the altitude data is // more consistent var events = aircraftEvents.Where(adsbEvent => adsbEvent.Altitude != null); var step = 10; var sampleSize = 25; var previousTrend = Trend.Unknown; // Check the start events for departure var startSample = events.Take(sampleSize); if (startSample.First().Altitude < startSample.Last().Altitude) { flight.DepartureTime = events.First().Timestamp; flight.DepartureAirport = airports.GetClosestAirport(events.First().GeoCoordinate).Identifier; } // Check the middle events for additional flights for (var i = 0; i < events.Count() - sampleSize; i += step) { var sample = events.Skip(i).Take(sampleSize); var currentTrend = GuessTrend(sample.Select(adsbEvent => adsbEvent.Altitude.Value)); // If the aircraft's altitude trend changes from decreasing to increasing below the altitude threshold, // assume that it's started a new flight if (previousTrend == Trend.Decrease && currentTrend == Trend.Increase && sample.First().Altitude < MAX_ALTITUDE) { var airport = airports.GetClosestAirport(sample.First().GeoCoordinate); // If there isn't an airport nearby, the airplane may have made an unexpected descent and ascent if (airport == null) { continue; } // Check if the aircraft is flying over the airport if (Math.Abs(airport.Elevation - sample.First().Altitude.Value) >= GROUND_TOLERANCE) { continue; } flight.ArrivalTime = sample.First().Timestamp; flight.ArrivalAirport = airport.Identifier; flights.Add(flight); flight = new Flight { AircraftIdentifier = aircraft.AircraftIdentifier, DepartureTime = sample.Last().Timestamp, DepartureAirport = airports.GetClosestAirport(sample.Last().GeoCoordinate).Identifier }; } previousTrend = currentTrend; } // Check the end events for arrival var endSample = events.TakeLast(sampleSize); if (endSample.First().Altitude > endSample.Last().Altitude) { flight.ArrivalTime = events.Last().Timestamp; flight.ArrivalAirport = airports.GetClosestAirport(events.Last().GeoCoordinate).Identifier; } if (flight.DepartureAirport != null || flight.ArrivalAirport != null) { flights.Add(flight); } }); Console.WriteLine($"Identified {flights.Count} potential flights"); WriteResults(flights); }