public void GetEllipse(ref Vector3[] positions, int resolution)
        {
            if (resolution < 2)
            {
                positions = new Vector3[0];
                return;
            }

            if (positions == null || positions.Length != resolution)
            {
                positions = new Vector3[resolution];
            }

            period = GetPeriod();
            double slice = period / resolution;

            OrbitData clone = new OrbitData(this);

            positions[0] = (Vector3)(clone.Position * OrbitUtils.KM2AU);
            for (int j = 1; j < resolution; j++)
            {
                clone.Epoch += slice;
                positions[j] = (Vector3)(clone.Position * OrbitUtils.KM2AU);
            }
        }
        // TODO: This function does not behave as expected and/or has not been successfully tested/implemented.
        public static void GetPositionsAlongEllipse(ref Vector3[] positions, OrbitData orbit, int resolution)
        {
            if (resolution < 2)
            {
                positions = new Vector3[0];
                return;
            }

            double a   = orbit.SemiMajorAxis;
            double ecc = orbit.Eccentricity;
            double w   = orbit.ArgumentOfPerifocus;
            double nu  = orbit.TrueAnomaly;

            // Trigometrics for any values used more than once in calculations
            double cosI  = Math.Cos(orbit.Inclination);
            double cosOm = Math.Cos(orbit.LongitudeOfAscendingNode);
            double sinOm = Math.Sin(orbit.LongitudeOfAscendingNode);

            double angle = TwoPI / resolution;

            if (ecc < 1.0 && ecc >= 0.0)
            {
                if (positions == null || positions.Length != resolution)
                {
                    positions = new Vector3[resolution];
                }

                for (int j = 0; j < resolution; j++)
                {
                    // Radial distance [km]
                    double r = orbit.SemiMajorAxis * (1.0 - ecc * ecc) / (1.0 + ecc * Math.Cos(j * angle));

                    // Flight path angle [rad]
                    //double fpa = Math.Atan2(1.0 + ecc * Math.Cos(nu), ecc * Math.Sin(nu));
                    double fpa = Math.Atan(ecc * Math.Sin(nu) / (1.0 + ecc * Math.Cos(nu)));

                    double cosNuPlusW = Math.Cos(nu + w);
                    double sinNuPlusW = Math.Sin(nu + w);

                    float x = (float)(r * (cosNuPlusW * cosOm - sinNuPlusW * cosI * sinOm));
                    float y = (float)(r * (cosNuPlusW * sinOm + sinNuPlusW * cosI * cosOm));
                    float z = (float)(r * (sinNuPlusW * Math.Sin(orbit.Inclination)));

                    positions[j] = new Vector3(x, y, z);
                }
            }
            else
            {
                Debug.LogWarning("Cannot draw orbital ellipse: orbit is parabolic/hyperbolic");
            }
        }
 public LambertSolution(int nRevs, double TA, double pTA, double Vinf,
                        double C3, double Vimp, Vector3d arrDV,
                        double VimpDotProduct, OrbitData transferOrbit)
 {
     this.nRevs          = nRevs;
     this.TA             = TA;
     this.pTA            = pTA;
     this.Vinf           = Vinf;
     this.C3             = C3;
     this.arrDV          = arrDV;
     this.Vimp           = Vimp;
     this.VimpDotProduct = VimpDotProduct;
     this.TransferOrbit  = transferOrbit;
 }
        /// <summary>
        /// Calculates the change in velocity of an object from an impact/deflection
        /// with a smaller body (e.g. kinetic impactor spacecraft) in the ecliptic plane.
        /// </summary>
        /// <returns>The delta V of <paramref name="deflected"/> in the ecliptic.</returns>
        /// <param name="deflected">Orbit of the object being deflected at time of impact [km-s].</param>
        /// <param name="impactor">Orbit of the impacting object at time of impact [km-s].</param>
        /// <param name="massDeflected">Mass of the object being deflected [kg].</param>
        /// <param name="massImpactor">Mass of the impacting object [kg].</param>
        /// <param name="beta">The momentum enhancement factor from ejecta (1 = no enhacement).</param>
        public static Vector3d CalculateDeflectionDeltaVEcliptic(OrbitData deflected, OrbitData impactor,
                                                                 double massDeflected, double massImpactor, double beta = 1.0)
        {
            // Velocity of the impactor relative to the object being deflected.
            Vector3d vrel = impactor.Velocity - deflected.Velocity;

            //Vector3d vhat = deflected.Velocity.normalized;
            //Vector3d vrelhat = vrel.normalized;

            //double Vimp = vrel.magnitude;
            //double VimpDotP = Vector3d.Dot(vhat, vrelhat);

            // Delta V from deflection, in the ecliptic plane.
            return(vrel * beta * massImpactor / massDeflected);
        }
 /// <summary>
 /// Copy constructor.
 /// </summary>
 /// <param name="other">Orbit to copy.</param>
 public OrbitData(OrbitData other)
 {
     epoch  = other.epoch;
     mu     = other.mu;
     a      = other.a;
     ecc    = other.ecc;
     i      = other.i;
     Om     = other.Om;
     w      = other.w;
     M      = other.M;
     nu     = other.nu;
     E      = other.E;
     pos    = other.pos;
     vel    = other.vel;
     N      = other.N;
     period = other.period;
 }
        /// <summary>
        /// Calculates the change in velocity of an object from an impact/deflection
        /// with a smaller body (e.g. kinetic impactor spacecraft) in the as (ΔVA, ΔVC, ΔVN).
        /// </summary>
        /// <remarks>
        /// The ACN coordinate system is a coordinate system set up at the center of mass
        /// of the asteroid at the time of deflection and used to express the magnitude
        /// and direction of the velocity change imparted to the asteroid. Its pricipal
        /// directions are:
        ///     Along-track direction (A): in the asteroid's orbital plane and along the
        ///         direction of the asteroid's current velocity vector about the Sun,
        ///     Normal direction (N): perpendicular to the asteroid's orbit plane and is
        ///         aligned with the positive orbital angular momentum vector (<c>r x v</c>)
        ///         where r is the vector from the Sun to the asteroid's current position,
        ///     Cross-track direction (C): in the orbital plane, perpendicular to the
        ///         along-track direction (<c>C = N x A</c>), and positive in the general
        ///         direction of the Sun.
        /// References: NDA Handbook v6.3
        ///             Transformation from ecliptic to ACN coordinate system
        ///             provided by Jason Anderson of Aerospace 07-2018.
        /// </remarks>
        /// <returns>The delta V of <paramref name="deflected"/> in the ACN coordinate system.</returns>
        /// <param name="deflected">Orbit of the object being deflected at time of impact [km-s].</param>
        /// <param name="impactor">Orbit of the impacting object at time of impact [km-s].</param>
        /// <param name="massDeflected">Mass of the object being deflected [kg].</param>
        /// <param name="massImpactor">Mass of the impacting object [kg].</param>
        /// <param name="beta">The momentum enhacement factor from ejecta (1 = no enhancement).</param>
        public static Vector3d CalculateDeflectionDeltaVInAcn(OrbitData deflected, OrbitData impactor,
                                                              double massDeflected, double massImpactor, double beta = 1.0)
        {
            Vector3d deltaVEcliptic = CalculateDeflectionDeltaVEcliptic(deflected, impactor, massDeflected, massImpactor, beta);

            // Velocity unit vector of object to deflect.
            Vector3d vhat = deflected.Velocity.normalized;

            // Angular momentum vector unit vector: hhat = pos x vel
            Vector3d hhat = Vector3d.Cross(deflected.Position, deflected.Velocity).normalized;

            Vector3d along  = vhat;
            Vector3d normal = hhat.normalized;
            Vector3d cross  = Vector3d.Cross(normal, along);

            double va = Vector3d.Dot(deltaVEcliptic, along);
            double vc = Vector3d.Dot(deltaVEcliptic, cross);
            double vn = Vector3d.Dot(deltaVEcliptic, normal);

            return(new Vector3d(va, vc, vn));
        }
        /// <summary>
        /// Gets a new Lambert solution.
        /// </summary>
        /// <param name="from">Departure body orbit at time of impact.</param>
        /// <param name="to">Arrival body orbit at time of impact.</param>
        /// <param name="D">Number of days before impact the deflection is to occur.</param>
        /// <param name="TOF_days">Time of flight [days]</param>
        /// <param name="nRevs">Number of revolutions or -1 to search for optimal</param>
        public static LambertSolution GetTransferOrbit(OrbitData from, OrbitData to,
                                                       double D, double TOF_days, int nRevs = -1)
        {
            Debug.Assert(Math.Abs(from.Epoch - to.Epoch) <= double.Epsilon);

            // Make local copies of orbit parameters (passed by reference).
            OrbitData dep = new OrbitData(from);
            OrbitData arr = new OrbitData(to);

            dep.Step(-(D + TOF_days));
            arr.Step(-D);

            Vector3d pos1 = dep.Position;
            Vector3d vel1 = dep.Velocity;
            Vector3d pos2 = arr.Position;
            Vector3d vel2 = arr.Velocity;

            Vector3d[] depDV = new Vector3d[2];
            Vector3d[] arrDV = new Vector3d[2];
            double[]   TA    = new double[2]; // Transfer angle
            double[]   Vinf  = new double[2]; // Earth departure V-infinity
            double[]   C3    = new double[2];

            int i;

            if (nRevs < 0)
            {
                // Search for the best number of revolutions...
                // Start the search with 0 and 1 revolutions.
                for (i = 0; i < 2; i++)
                {
                    nRevs = i;

                    // Calculate the Lambert arc from state1 to state2 in TOF days;
                    // Return the departure delta V and the arrival delta V.
                    CalcLambert(nRevs, pos1, vel1, pos2, vel2, TOF_days,
                                ref depDV[i], ref arrDV[i], out TA[i]);

                    // Earth departure V-infinity.
                    Vinf[i] = depDV[i].magnitude;
                    C3[i]   = Vinf[i] * Vinf[i];
                    Debug.Log("Rev #" + nRevs + ": C3 = " + C3[i]);
                }

                // Storage index.
                i = 1;

                // Run until C3 does not improve.
                while (C3[i == 1 ? 1 : 0] < C3[i == 1 ? 0 : 1])
                {
                    // Alternate storing index.
                    i = (i == 0 ? 1 : 0);

                    // Next revolution.
                    nRevs++;

                    // Calculate the Lambert arc from state1 to state2 in TOF days;
                    // Return the departure delta V and the arrival delta V.
                    CalcLambert(nRevs, pos1, vel1, pos2, vel2, TOF_days,
                                ref depDV[i], ref arrDV[i], out TA[i]);

                    // Earth departure V-infinity.
                    Vinf[i] = depDV[i].magnitude;
                    C3[i]   = Vinf[i] * Vinf[i];
                    Debug.Log("Rev #" + nRevs + ": C3 = " + C3[i]);
                }

                // Best solution is the previous iteration.
                nRevs--;
                i = (i == 0 ? 1: 0);
            }
            else
            {
                // Storage index.
                i = 0;

                // Calculate the Lambert arc from state1 to state2 in TOF days;
                // Return the departure delta V and the arrival delta V.
                CalcLambert(nRevs, pos1, vel1, pos2, vel2, TOF_days,
                            ref depDV[i], ref arrDV[i], out TA[i]);

                // Earth departure V-infinity.
                Vinf[i] = depDV[i].magnitude;
                C3[i]   = Vinf[i] * Vinf[i];
            }

            if (depDV[i] == Vector3d.zero)
            {
                return(null);
            }

            // Object impact velocity.
            double Vimp = arrDV[i].magnitude;

            // Planar transfer angle.
            double pTA = Math.Atan2(pos2.y, pos2.x) - Math.Atan2(pos1.y, pos1.x);

            if (pTA < 0.0)
            {
                pTA += OrbitUtils.TwoPI;
            }

            double VimpDotProduct = -arrDV[i].x * vel2.x - arrDV[i].y * vel2.y - arrDV[i].z * vel2.z / arrDV[i].magnitude / vel2.magnitude;

            vel1 += depDV[i]; // Transfer orbit velocity at departure
            OrbitData transferOrbit = new OrbitData(dep.Epoch, pos1, vel1, OrbitUtils.GM_SUN);

            return(new LambertSolution(nRevs, TA[i], pTA, Vinf[i], C3[i], Vimp,
                                       -arrDV[i], VimpDotProduct, transferOrbit));
        }