/// <summary> /// Master constructor for the "top level" of our application /// TODO: create a factory method if initialization becomes complicated. Not needed now /// </summary> public FlightProcessor() { airports = AirportCollection.LoadFromFile(AirportsFilePath); ProcessingType = "file"; EventsQueue = new ConcurrentQueue <AdsbEvent>(); FlightsQueue = new ConcurrentQueue <Flight>(); MainEventProcessor = new EventProcessor(FlightsQueue, airports); }
/// <summary> /// Master constructor is used for unit tests. Clean it up later /// </summary> public FlightProcessor(string airportFile, string eventFile, string debugAirplane) { airports = AirportCollection.LoadFromFile(airportFile); ProcessingType = "file"; EventsQueue = new ConcurrentQueue <AdsbEvent>(); FlightsQueue = new ConcurrentQueue <Flight>(); MainEventProcessor = new EventProcessor(FlightsQueue, airports); AdsbEventsFilePath = eventFile; DEBUG_FLIGHT = debugAirplane; }
private static void Execute() { // Load the input data AirportCollection airports = AirportCollection.LoadFromFile(AirportsFilePath); AdsbEventCollection events = AdsbEventCollection.LoadFromFile(AdsbEventsFilePath); // Create collection of identifiable flights FlightCollection flights = CalculateFlights(airports, events); // Write the output data flights.WriteToFile(OutputFilePath); }
private static void Execute() { // Load the airports AirportCollection airports = AirportCollection.LoadFromFile(AirportsFilePath); }
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(); }
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); }