示例#1
0
        internal static void Departing(this FlightContext context)
        {
            /*
             * First check plausible scenarios. The easiest to track is an aerotow.
             *
             * If not, wait until the launch is completed.
             */

            if (context.Flight.LaunchMethod == LaunchMethods.None)
            {
                var departure = context.Flight.PositionUpdates
                                .Where(q => q.Heading != 0 && !double.IsNaN(q.Heading))
                                .OrderBy(q => q.TimeStamp)
                                .Take(5)
                                .ToList();

                if (departure.Count > 4)
                {
                    context.Flight.DepartureHeading = Convert.ToInt16(departure.Average(q => q.Heading));

                    if (context.Flight.DepartureHeading == 0)
                    {
                        context.Flight.DepartureHeading = 360;
                    }

                    // Only start method recognition after the heading has been determined
                    context.Flight.LaunchMethod = LaunchMethods.Unknown | LaunchMethods.Aerotow | LaunchMethods.Winch | LaunchMethods.Self;
                }
                else
                {
                    return;
                }
            }

            if (context.Flight.DepartureTime != null &&
                (context.CurrentPosition.TimeStamp - (context.Flight.PositionUpdates.FirstOrDefault(q => q.Speed > 30)?.TimeStamp ?? context.CurrentPosition.TimeStamp)).TotalSeconds < 10)
            {
                return;
            }

            // We can safely try to extract the correct path

            if (context.Flight.LaunchMethod.HasFlag(LaunchMethods.Unknown | LaunchMethods.Aerotow))
            {
                var encounters = context.TowEncounter().ToList();

                if (encounters.Count(q => q?.Type == EncounterType.Tug || q?.Type == EncounterType.Tow) > 1)
                {
                    return;
                }

                var encounter = encounters.SingleOrDefault(q => q?.Type == EncounterType.Tug || q?.Type == EncounterType.Tow);

                if (encounter != null)
                {
                    context.Flight.LaunchMethod = LaunchMethods.Aerotow
                                                  | (encounter.Type == EncounterType.Tug
                            ? LaunchMethods.OnTow
                            : LaunchMethods.TowPlane
                                                     );

                    context.Flight.Encounters.Add(encounter);

                    context.StateMachine.Fire(FlightContext.Trigger.TrackAerotow);

                    return;
                }
                else if (encounters.Any(q => q == null))
                {
                    return;
                }

                context.Flight.LaunchMethod &= ~LaunchMethods.Aerotow;
            }

            if (context.Flight.LaunchMethod.HasFlag(LaunchMethods.Unknown))
            {
                var x = new DenseVector(context.Flight.PositionUpdates.Select(w => (w.TimeStamp - context.Flight.DepartureTime.Value).TotalSeconds).ToArray());
                var y = new DenseVector(context.Flight.PositionUpdates.Select(w => w.Altitude).ToArray());

                var interpolation = CubicSpline.InterpolateNatural(x, y);

                var r  = new List <double>();
                var r2 = new List <double>();

                for (var i = 0; i < (context.CurrentPosition.TimeStamp - context.Flight.DepartureTime.Value).TotalSeconds; i++)
                {
                    r.Add(interpolation.Differentiate(i));
                    r2.Add(interpolation.Differentiate2(i));
                }

                // When the initial climb has completed
                if (interpolation.Differentiate((context.CurrentPosition.TimeStamp - context.Flight.DepartureTime.Value).TotalSeconds) < 0)
                {
                    // Skip the first element because heading is 0 when in rest
                    var averageHeading = context.Flight.PositionUpdates.Skip(1).Average(q => q.Heading);

                    // ToDo: Add check to see whether there is another aircraft nearby
                    if (context.Flight.PositionUpdates
                        .Skip(1)
                        .Where(q => interpolation.Differentiate((context.CurrentPosition.TimeStamp - context.Flight.DepartureTime.Value).TotalSeconds) > 0)
                        .Select(q => Geo.GetHeadingError(averageHeading, q.Heading))
                        .Any(q => q > 20) ||
                        Geo.DistanceTo(
                            context.Flight.PositionUpdates.First().Location,
                            context.CurrentPosition.Location) > 3000)
                    {
                        context.Flight.LaunchMethod = LaunchMethods.Self;
                    }
                    else
                    {
                        context.Flight.LaunchMethod = LaunchMethods.Winch;
                    }

                    context.Flight.LaunchFinished = context.CurrentPosition.TimeStamp;
                    context.InvokeOnLaunchCompletedEvent();
                    context.StateMachine.Fire(FlightContext.Trigger.LaunchCompleted);
                }
            }
        }
示例#2
0
        internal static void Stationary(this FlightContext context)
        {
            if (context.CurrentPosition == null)
            {
                return;
            }

            if (context.CurrentPosition.Speed > 30)
            {
                double groundElevation = 0;
                if (context.Options.NearbyRunwayAccessor != null)
                {
                    groundElevation = context.Options.NearbyRunwayAccessor(
                        context.CurrentPosition.Location,
                        Constants.RunwayQueryRadius)?
                                      .OrderBy(q => q.Sides
                                               .Min(w => Geo.DistanceTo(w, context.CurrentPosition.Location))
                                               ).FirstOrDefault()
                                      ?.Sides
                                      .Average(q => q.Z)
                                      ?? 0;
                }

                // Walk back to when the speed was 0
                var start = context.Flight.PositionUpdates
                            .Where(q => (q.Speed == 0 || double.IsNaN(q.Speed)) &&
                                   (context.CurrentPosition.TimeStamp - q.TimeStamp).TotalSeconds < 30)
                            .OrderByDescending(q => q.TimeStamp)
                            .FirstOrDefault();

                if (start == null && context.CurrentPosition.Altitude > (groundElevation + Constants.ArrivalHeight))
                {
                    // The flight was already in progress, or we could not find the starting point (trees in line of sight?)

                    // Create an estimation about the departure time. Unless contact happens high in the sky
                    context.Flight.DepartureInfoFound = false;

                    context.InvokeOnRadarContactEvent();

                    context.StateMachine.Fire(FlightContext.Trigger.TrackMovements);
                    return;
                }
                else if (start == null && context.CurrentPosition.Altitude <= (groundElevation + Constants.ArrivalHeight))
                {
                    // ToDo: Try to estimate the departure time
                    context.Flight.DepartureTime     = context.CurrentPosition.TimeStamp;
                    context.Flight.DepartureLocation = context.CurrentPosition.Location;

                    context.Flight.PositionUpdates
                    .Where(q => q.TimeStamp < context.Flight.DepartureTime.Value)
                    .ToList()
                    .ForEach(q => context.Flight.PositionUpdates.Remove(q));

                    context.Flight.DepartureInfoFound = false;
                }
                else if (start != null)
                {
                    context.Flight.DepartureTime     = start.TimeStamp;
                    context.Flight.DepartureLocation = context.CurrentPosition.Location;

                    // Remove points not related to this flight
                    context.Flight.PositionUpdates
                    .Where(q => q.TimeStamp < context.Flight.DepartureTime.Value)
                    .ToList()
                    .ForEach(q => context.Flight.PositionUpdates.Remove(q));

                    context.Flight.DepartureInfoFound = false;
                }

                context.Flight.DepartureHeading = (short)context.CurrentPosition.Heading;
                context.InvokeOnTakeoffEvent();

                context.StateMachine.Fire(FlightContext.Trigger.Depart);
            }
        }
示例#3
0
        internal static void Arriving(this FlightContext context)
        {
            /*
             * - Create an estimate for the arrival time
             * - When data shows a landing, use that data
             * - When no data is received anymore, use the estimation
             */
            double groundElevation = 0;

            if (context.Options.NearbyRunwayAccessor != null)
            {
                groundElevation = context.Options.NearbyRunwayAccessor(
                    context.CurrentPosition.Location,
                    Constants.RunwayQueryRadius)?
                                  .OrderBy(q => q.Sides
                                           .Min(w => Geo.DistanceTo(w, context.CurrentPosition.Location))
                                           ).FirstOrDefault()
                                  ?.Sides
                                  .Average(q => q.Z)
                                  ?? 0;
            }

            if (context.CurrentPosition.Altitude > (groundElevation + Constants.ArrivalHeight))
            {
                context.Flight.ArrivalTime      = null;
                context.Flight.ArrivalInfoFound = null;
                context.Flight.ArrivalHeading   = 0;
                context.StateMachine.Fire(FlightContext.Trigger.LandingAborted);
                return;
            }

            var arrival = context.Flight.PositionUpdates
                          .Where(q => q.Heading != 0 && !double.IsNaN(q.Heading))
                          .OrderByDescending(q => q.TimeStamp)
                          .Take(5)
                          .ToList();

            if (!arrival.Any())
            {
                return;
            }

            if (context.CurrentPosition.Speed == 0)
            {
                /*
                 * If a flight has been in progress, end the flight.
                 *
                 * When the aircraft has been registered mid flight the departure
                 * location is unknown, and so is the time. Therefore look at the
                 * flag which is set to indicate whether the departure location has
                 * been found.
                 *
                 * ToDo: Also check the vertical speed as it might be an indication
                 * that the flight is still in progress! (Aerobatic stuff and so)
                 */

                context.Flight.ArrivalTime      = context.CurrentPosition.TimeStamp;
                context.Flight.ArrivalInfoFound = true;
                context.Flight.ArrivalHeading   = Convert.ToInt16(arrival.Average(q => q.Heading));
                context.Flight.ArrivalLocation  = arrival.First().Location;

                if (context.Flight.ArrivalHeading == 0)
                {
                    context.Flight.ArrivalHeading = 360;
                }

                context.InvokeOnLandingEvent();

                context.StateMachine.Fire(FlightContext.Trigger.Arrived);
            }
            else if (!(context.Flight.ArrivalInfoFound ?? true) &&
                     context.CurrentPosition.TimeStamp > context.Flight.ArrivalTime.Value.AddSeconds(Constants.ArrivalTimeout))
            {
                // Our theory needs to be finalized
                context.InvokeOnLandingEvent();

                context.StateMachine.Fire(FlightContext.Trigger.Arrived);
            }
            else
            {
                var previousPoint = context.Flight.PositionUpdates.LastOrDefault();

                if (previousPoint == null)
                {
                    return;
                }

                // Take the average climbrate over the last few points

                var climbrates = new List <double>();
                var speeds     = new List <double>();

                for (var i = context.Flight.PositionUpdates.Count - 1; i > Math.Max(context.Flight.PositionUpdates.Count - 15, 0); i--)
                {
                    var p1 = context.Flight.PositionUpdates[i];
                    var p2 = context.Flight.PositionUpdates[i - 1];

                    var deltaAltitude = p1.Altitude - p2.Altitude;
                    var deltaTime     = p1.TimeStamp - p2.TimeStamp;

                    speeds.Add(p1.Speed);
                    climbrates.Add(deltaAltitude / deltaTime.TotalSeconds);
                }

                if (!climbrates.Any())
                {
                    context.Flight.ArrivalTime      = null;
                    context.Flight.ArrivalInfoFound = null;
                    context.Flight.ArrivalHeading   = 0;
                    context.Flight.ArrivalLocation  = null;
                    return;
                }

                var average = climbrates.Average();

                double ETUA = context.CurrentPosition.Altitude / -average;

                if (double.IsInfinity(ETUA) || ETUA > (60 * 10) || ETUA < 0)
                {
                    context.Flight.ArrivalTime      = null;
                    context.Flight.ArrivalInfoFound = null;
                    context.Flight.ArrivalHeading   = 0;
                    context.Flight.ArrivalLocation  = null;
                    return;
                }
                var averageHeading = arrival.Average(q => q.Heading);
                context.Flight.ArrivalTime      = context.CurrentPosition.TimeStamp.AddSeconds(ETUA);
                context.Flight.ArrivalInfoFound = false;
                context.Flight.ArrivalHeading   = Convert.ToInt16(averageHeading);
                context.Flight.ArrivalLocation  = context.CurrentPosition.Location.HaversineExtrapolation(
                    averageHeading,
                    speeds.Average() * 0.514444444 * ETUA);  // Knots to m/s times the estimated time until arrival
            }
        }