/// <summary>
    /// Compute the transfer time for the minimum energy transfer.
    /// </summary>
    /// <returns></returns>
    private double ComputeMinTime(ref double[] r0, ref double[] r, NBody centerBody, bool shortPath)
    {
        // Fundamentals of Astrodynamics and Applications, Vallado, 4th Ed., Algorithm 56 p475
        // Take r0, r => a_min, e_min, t_min
        // This approach assumes r > r0.
        // If we have r0 > r then need to flip them and then reverse the velocities when doing the
        // maneuver determination

        double[] center = new double[] { 0, 0, 0 };

        GravityEngine ge = GravityEngine.Instance();

        ge.GetPositionDouble(centerBody, ref center);
        Vector3d center3d = new Vector3d(ref center);

        Vector3d r0_vec = new Vector3d(ref r0) - center3d;
        Vector3d r_vec  = new Vector3d(ref r) - center3d;

        double r0_mag       = Vector3d.Magnitude(r0_vec);
        double r_mag        = Vector3d.Magnitude(r_vec);
        double cos_delta_nu = Vector3d.Dot(r0_vec, r_vec) / (r0_mag * r_mag);

        double c = System.Math.Sqrt(r0_mag * r0_mag + r_mag * r_mag
                                    - 2 * r0_mag * r_mag * cos_delta_nu);
        double s = (r0_mag + r_mag + c) / 2;

        a_min = s / 2;
        // double p_min = r0_mag * r_mag * (1 - cos_delta_nu) / c;
        // e_min = System.Math.Sqrt(1 - 2 * p_min / s);

        // alpha for elliptical xfer, min energy
        double alpha_e = System.Math.PI;

        // p471: set beta_e negative if delta_nu > 18
        double beta_e = 2.0 * System.Math.Asin(System.Math.Sqrt((s - c) / s));
        // t_min for a_min
        // +/- in front of (beta_e...). Which sign? Negative for shorter path.
        double sign = -1;

        if (!shortPath)
        {
            sign = 1;
        }
        t_min = System.Math.Sqrt(a_min * a_min * a_min / mu) * (alpha_e + sign * (beta_e - System.Math.Sin(beta_e)));
        return(t_min);
    }
    /// <summary>
    /// Setup for Lambert Universal construction. Calculation is done via ComputeXfer and may be
    /// done more than once for different transit times.
    ///
    /// Initial conditions are passed in when created.
    ///
    /// The transfer assumes there is an active NBody at the fromOrbit to be maneuvered to the
    /// orbit specified by the toOrbit. One of the from/to orbit data elements must have a
    /// centerNBody.
    ///
    /// </summary>
    /// <param name="fromOrbit"></param>
    /// <param name="toOrbit"></param>
    /// <param name="shortPath">Take shortest path along the ellipse (if xfer is an ellipse)</param>

    public LambertUniversal(OrbitData _fromOrbit, OrbitData _toOrbit, bool shortPath) : base(_fromOrbit, _toOrbit)
    {
        name = "LambertUniversal";

        // Fundamentals of Astrodynamics and Applications, Vallado, 4th Ed., Algorithm 56 p475
        // Take r0, r => a_min, e_min, t_min, v0

        double[] r1_array = new double[] { 0, 0, 0 };
        double[] r2_array = new double[] { 0, 0, 0 };
        double[] center   = new double[] { 0, 0, 0 };

        GravityEngine ge = GravityEngine.Instance();

        // If Nbody is available get position directly. If not (target marker) then
        // use orbit phase to compute position
        ge.GetPositionDouble(centerBody, ref center);
        center3d = new Vector3d(ref center);

        if (fromOrbit.nbody != null)
        {
            ge.GetPositionDouble(fromOrbit.nbody, ref r1_array);
        }
        else
        {
            Vector3 pos = toOrbit.GetPhysicsPositionforEllipse(fromOrbit.phase);
            r1_array = new double[] { pos.x, pos.y, pos.z };
        }

        if (toOrbit.nbody != null)
        {
            ge.GetPositionDouble(toOrbit.nbody, ref r2_array);
        }
        else
        {
            Vector3 pos = toOrbit.GetPhysicsPositionforEllipse(toOrbit.phase);
            r2_array = new double[] { pos.x, pos.y, pos.z };
        }

        r1 = new Vector3d(ref r1_array) - center3d;
        r2 = new Vector3d(ref r2_array) - center3d;

        if (fromOrbit.a < toOrbit.a)
        {
            innerOrbit   = fromOrbit;
            outerOrbit   = toOrbit;
            innerToOuter = true;
        }
        else
        {
            innerOrbit   = toOrbit;
            outerOrbit   = fromOrbit;
            innerToOuter = false;
        }
        mu = ge.GetPhysicsMass(fromOrbit.centralMass);
        // determine t_min.
        // If Nbody is available get position directly. If not (target marker) then
        // use orbit phase to compute position
        double[] r0 = new double[] { 0, 0, 0 };
        double[] r  = new double[] { 0, 0, 0 };
        if (innerOrbit.nbody != null)
        {
            ge.GetPositionDouble(innerOrbit.nbody, ref r0);
        }
        else
        {
            Vector3 pos = toOrbit.GetPhysicsPositionforEllipse(innerOrbit.phase);
            r0 = new double[] { pos.x, pos.y, pos.z };
        }

        if (outerOrbit.nbody != null)
        {
            ge.GetPositionDouble(outerOrbit.nbody, ref r);
        }
        else
        {
            Vector3 pos = toOrbit.GetPhysicsPositionforEllipse(outerOrbit.phase);
            r = new double[] { pos.x, pos.y, pos.z };
        }

        ComputeMinTime(ref r0, ref r, fromOrbit.centralMass, shortPath);
    }