Пример #1
0
    // Vallado Algorithm 10, p118
    // (generic, will work for Ellipse as well, provided special cases are handled)
    // Not used yet.
    private void SetInitialPosition(NBody nbody)
    {
        // phase is in
        float   denom = 1 + ecc * Mathf.Cos(phase_nu);
        Vector3 r_pqw = new Vector3(p * Mathf.Cos(phase_nu) / denom, p * Mathf.Sin(phase_nu) / denom, 0);

        r_initial_phy = r_pqw.magnitude;
        Vector3 r = hyper_orientation * r_pqw;

        // orbit position is WRT center. Could be adding dynamically to an object in motion, so need current position.
        Vector3 centerPos = Vector3.zero;

        // used by widgets - so need to get explcitly
        centerNbody = OrbitUtils.GetCenterNbody(this.transform, centerObject);
        if (centerNbody.engineRef != null)
        {
            centerPos = GravityEngine.Instance().GetPhysicsPosition(centerNbody);
        }
        else
        {
            // setup - not yet added to GE
            centerPos = centerNbody.initialPhysPosition;
        }
        nbody.initialPhysPosition = r + centerPos;
    }
Пример #2
0
    /// <summary>
    /// Sets the initial position based on the orbit parameters. Used in the init phase to set the NBody in the
    /// correct position in the scene before handing control GE.
    /// </summary>
    public void SetInitialPosition(NBody nbody)
    {
        float phaseRad = phase * Mathf.Deg2Rad;
        // position object using true anomoly (angle from  focus)
        float r = a_scaled * (1f - ecc * ecc) / (1f + ecc * Mathf.Cos(phaseRad));

        Vector3 pos = new Vector3(r * Mathf.Cos(phaseRad), r * Mathf.Sin(phaseRad), 0);
        // move from XY plane to the orbital plane
        Vector3 new_p = ellipse_orientation * pos;
        // orbit position is WRT center. Could be adding dynamically to an object in motion, so need current position.
        Vector3 centerPos = Vector3.zero;

        // used by widgets - so need to get explcitly
        centerNbody = OrbitUtils.GetCenterNbody(transform, centerObject);
        if (centerNbody.engineRef != null)
        {
            centerPos = GravityEngine.Instance().GetPhysicsPosition(centerNbody);
        }
        else
        {
            // setup - not yet added to GE
            centerPos = centerNbody.initialPhysPosition;
        }
        nbody.initialPhysPosition = new_p + centerPos;
    }
Пример #3
0
    public override void OnInspectorGUI()
    {
        GUI.changed = false;
        OrbitEllipse orbit = (OrbitEllipse)target;

        OrbitEllipse.evolveType evolveMode = orbit.evolveMode;

        evolveMode = (OrbitEllipse.evolveType)EditorGUILayout.EnumPopup(new GUIContent("Evolve Mode", modeTip), orbit.evolveMode);

        if (GUI.changed)
        {
            Undo.RecordObject(orbit, "OrbitEllipse Change");
            orbit.evolveMode = evolveMode;
            EditorUtility.SetDirty(orbit);
        }
        base.OnInspectorGUI();

        // Display the Hill Radius as a guide for where to place moons...
        float r_hill = 0;

        if (orbit.GetCenterObject() != null)
        {
            r_hill = OrbitUtils.HillRadius(orbit.GetCenterObject(), orbit.transform.gameObject);
        }
        EditorGUILayout.LabelField(new GUIContent(string.Format("Hill Radius:  {0}", r_hill), hillTip));
        // EditorGUILayout.LabelField(new GUIContent(string.Format("Orbit Period: {0}", orbit.GetPeriod()), periodTip));

        if (axisUpdated)
        {
            orbit.ApplyScale(GravityEngine.Instance().GetLengthScale());
        }
    }
    // Use this for initialization
    void Start()
    {
        ge     = GravityEngine.Instance();
        x_axis = new Vector3d(1, 0, 0);

        // mass scaling will cancel in this ratio
        soiRadius = OrbitUtils.SoiRadius(planet, moonBody);

        // TODO: allow moon to be OrbitUniversal as well.
        OrbitUniversal moonOrbit = moonBody.gameObject.GetComponent <OrbitUniversal>();

        if (moonOrbit == null)
        {
            Debug.LogError("Moon is required to have OrbitUniversal");
        }
        moonRadius = moonOrbit.GetMajorAxis();

        shipOrbit = spaceship.GetComponent <OrbitUniversal>();
        if (shipOrbit == null)
        {
            Debug.LogError("Require that the ship have an OrbitU");
        }
        if (shipOrbit.evolveMode != OrbitUniversal.EvolveMode.KEPLERS_EQN)
        {
            Debug.LogError("Controller requires ship on-rails but spaceship is off-rails");
        }

        // assuming circular orbit for ship
        shipRadius = shipOrbit.GetApogee();

        shipOrbitPredictor = spaceship.GetComponentInChildren <OrbitPredictor>();
    }
Пример #5
0
    // Check eccentricity and inclination
    public void RVtoCOEtoRV()
    {
        GameObject star = TestSetupUtils.CreateNBody(10, new Vector3(0, 0, 0));

        TestSetupUtils.SetupGravityEngine(star, null);
        NBody starBody = star.GetComponent <NBody>();

        RVpair[] rvp =
        {
            new RVpair(10,   0, 0,  0,   1, 0),
            new RVpair(10,   0, 0,  0,  10, 0),
            new RVpair(10,   0, 0,  0,  -1, 0),
            new RVpair(10,   0, 0,  0, -10, 0),
            new RVpair(-10, 10, 0, -4,   3, 0),
            new RVpair(-10, 10, 0,  4,  -3, 0)
        };

        for (int i = 0; i < rvp.Length; i++)
        {
            OrbitUtils.OrbitElements oe = OrbitUtils.RVtoCOE(rvp[i].r, rvp[i].v, starBody, false);
            Vector3d r1 = new Vector3d();
            Vector3d v1 = new Vector3d();
            OrbitUtils.COEtoRV(oe, starBody, ref r1, ref v1, false);

            Debug.LogFormat("i={0} r_in={1} r_out={2}\n v_in={3} v_out={4}\n oe: {5}",
                            i, rvp[i].r, r1, rvp[i].v, v1, oe);
            Assert.IsTrue(GEUnit.Vec3dEqual(rvp[i].r, r1, small));
            Assert.IsTrue(GEUnit.Vec3dEqual(rvp[i].v, v1, small));
        }
    }
Пример #6
0
        // /////////////////////////////////////////////////////////////////////
        static void Main(string[] args)
        {
            // Sample obsCodeode to test the SGP4 and SDP4 implementation. The test
            // TLEs come from the NORAD document "Space Track Report No. 3".

            // Test SGP4
            string str1 = "SGP4 Test";
            string str2 = "1 88888U          80275.98708465  .00073094  13844-3  66816-4 0     8";
            string str3 = "2 88888  72.8435 115.9689 0086731  52.6988 110.5714 16.05824518   105";

            TwoLineElement tle1 = new TwoLineElement(str1, str2, str3);

            PrintPosVel(tle1);

            Console.WriteLine();

            // Test SDP4
            str1 = "SDP4 Test";
            str2 = "1 11801U          80230.29629788  .01431103  00000-0  14311-1       8";
            str3 = "2 11801  46.7916 230.4354 7318036  47.4722  10.4117  2.28537848     6";

            TwoLineElement tle2 = new TwoLineElement(str1, str2, str3);

            PrintPosVel(tle2);

            Console.WriteLine("\nExample output:");

            // Example: Define a location on the earth, then determine the look-angle
            // to the SDP4 satellite defined above.

            // Create an orbit object using the SDP4 TLE object.
            // Satellite satSDP4 = new Satellite(tle2);
            Orbit orbit = new Orbit(tle2);
            // Get the location of the satellite from the Orbit object. The
            // earth-centered inertial information is placed into eciSDP4.
            // Here we ask for the location of the satellite 90 minutes after
            // the TLE epoch.
            TimedMotionState eciSDP4 = orbit.PositionEci(90.0);

            // Now create a site object. Site objects represent a location on the
            // surface of the earth. Here we arbitrarily select a point on the
            // equator.
            GeoCoord siteEquator = new GeoCoord(0.0, -100.0, 0); // 0.00 N, 100.00 W, 0 km altitude

            // Now get the "look angle" from the site to the satellite.
            // Note that the ECI object "eciSDP4" has a time associated
            // with the coordinates it contains; this is the time at which
            // the look angle is valid.
            TopoCoord topoLook = OrbitUtils.GetSatTopoCoord(eciSDP4, siteEquator);

            // Print out the results. Note that the Azimuth and Elevation are
            // stored in the CoordTopo object as radians. Here we funcKeyToDouble
            // to degrees using Rad2Deg()
            Console.Write("AZ: {0:f3}  EL: {1:f3}\n",
                          topoLook.Azimuth,
                          topoLook.Elevation);
        }
Пример #7
0
    /// <summary>
    /// Displays the path of the elliptical orbit when the object is selected in the editor.
    /// </summary>
    void OnDrawGizmosSelected()
    {
        // need to have a center to draw gizmo.
        if (GetCenterObject() == null)
        {
            return;
        }
        // only display if this object or parent is selected
        bool selected = Selection.Contains(transform.gameObject);

        if (transform.parent != null)
        {
            selected |= Selection.Contains(transform.parent.gameObject);
        }
        if (!selected)
        {
            return;
        }

        UpdateOrbitParams();
        CalculateRotation();

        const int NUM_STEPS     = 100;
        const int STEPS_PER_RAY = 10;
        int       rayCount      = 0;

        Gizmos.color = Color.white;

        Vector3[] positions = OrbitPositions(NUM_STEPS);
        for (int i = 1; i < NUM_STEPS; i++)
        {
            Gizmos.DrawLine(positions[i], positions[i - 1]);
            rayCount = (rayCount + 1) % STEPS_PER_RAY;
            if (rayCount == 0)
            {
                Gizmos.DrawLine(centerObject.transform.position, positions[i]);
            }
        }
        // close the circle
        Gizmos.DrawLine(positions[NUM_STEPS - 1], positions[0]);

        // Draw the axes in a different color
        Gizmos.color = Color.red;
        Gizmos.DrawLine(PositionForTheta(0.5f * Mathf.PI), PositionForTheta(-0.5f * Mathf.PI));
        Gizmos.color = Color.blue;
        Gizmos.DrawLine(PositionForTheta(0f), PositionForTheta(Mathf.PI));


        // move body to location specified by parameters
        SetTransform();

        // Draw the Hill sphere
        Gizmos.color = Color.white;
        Gizmos.DrawWireSphere(transform.position, OrbitUtils.HillRadius(centerObject, transform.gameObject));
    }
Пример #8
0
    // Use this for initialization
    void Start()
    {
        soiRenderer = GetComponent <LineRenderer>();
        soiRadius   = OrbitUtils.SoiRadius(planetBody, moonBody);

        OrbitUniversal orbitU = moonBody.GetComponent <OrbitUniversal>();

        if (orbitU != null)
        {
            inclination = (float)orbitU.inclination;
        }
    }
Пример #9
0
    /// <summary>
    /// Init the hyperbola, verify a center body is present and determine orientation.
    /// </summary>
    public void Init()
    {
        CalcOrbitParams();
        centerNbody = OrbitUtils.GetCenterNbody(transform, centerObject);
        CalculateRotation();

        NBody nbody = GetComponent <NBody>();

        // particle ring would not have an NBody
        if (nbody != null)
        {
            SetInitialPosition(nbody);
        }
    }
    // Use this for initialization
    void Start()
    {
        ge = GravityEngine.Instance();

        shipAngle = shipAngleDeg * Mathf.Deg2Rad;
        soiAngle  = soiAngleDeg * Mathf.Deg2Rad;

        // disable maneuver predictor until things settle (can get Invalid local AABB otherwise)
        SetOrbitDisplays(false);

        // mass scaling will cancel in this ratio
        soiRadius = OrbitUtils.SoiRadius(planet, moonBody);
        toMoonOrbit.hyperDisplayRadius = soiRadius;

        // TODO: allow moon to be OrbitUniversal as well.
        OrbitEllipse moonEllipse = moonBody.gameObject.GetComponent <OrbitEllipse>();

        moonRadius  = moonEllipse.a_scaled;
        targetPoint = new Vector3d(moonRadius, soiRadius, 0);

        float        inclination = 0;
        OrbitEllipse shipEllipse = spaceship.gameObject.GetComponent <OrbitEllipse>();

        if (shipEllipse != null)
        {
            shipRadius  = shipEllipse.a_scaled;
            inclination = shipEllipse.inclination;
        }
        else
        {
            OrbitUniversal orbitU = spaceship.GetComponent <OrbitUniversal>();
            if (orbitU != null)
            {
                // assuming circular orbit
                shipRadius  = (float)orbitU.GetApogee();
                inclination = (float)orbitU.inclination;
            }
        }

        // check moon and ship orbit are co-planar
        if (Mathf.Abs(inclination - moonEllipse.inclination) > 1E-3)
        {
            Debug.LogWarning("Ship inclination and moon inclination are not equal.");
        }

        startPoint = new Vector3d(0, -shipRadius, 0);
        UpdateSoiPosition();
        UpdateStartPosition();
    }
 /// <summary>
 /// Calculates the mass of the NEO from the current diameter and density.
 /// If mass cannot be computed, sets <see cref="Mass"/> to -1.
 /// </summary>
 void TryCalculateMass()
 {
     if (diameter > 0.0 && density > 0.0)
     {
         Mass           = OrbitUtils.MassOfSphere(diameter, density);
         MassText.text  = Mass.ToString("E3") + " kg";
         MassText.color = normalColor;
     }
     else
     {
         Mass           = double.NegativeInfinity;
         MassText.text  = "NaN";
         MassText.color = warningColor;
     }
 }
Пример #12
0
        public void ProcessCollisionEnter(Body larger, Body smaller)
        {
            if (IsPaused || InReverse)
            {
                Debug.Log("Detected collision will not be processesed while paused or in reverse");
                return;
            }

            // Earth-Asteroid Impact
            if (larger.tag == "Earth" && smaller.tag == "Asteroid")
            {
                manager.GetComponent <MenuManager>().LoadEndScreen();
                StartCoroutine(FadeTextToFullAlpha(MessageText, 0f));
                DistanceText.enabled = true;
                ProcessCollisionStay(larger, smaller);
            }

            // Asteroid-KI Deflection
            if (larger.tag == "Asteroid" && smaller.tag == "Spacecraft")
            {
                smaller.gameObject.SetActive(false);

                larger.SetTrailColor(Color.green, new Color(0f, 155f / 255f, 0f));

                // Save the current epoch before making any changes (just in case)
                double originalEpoch = CurrentEpoch;

                // Do delta V calculation at true time of deflection
                larger.Orbit.Epoch  = DeflectionEpoch;
                smaller.Orbit.Epoch = DeflectionEpoch;

                // Calculate the delta V and apply it to the deflected asteroid
                Vector3d deltaV = OrbitUtils.CalculateDeflectionDeltaVEcliptic(larger.Orbit, smaller.Orbit, MassNeo, MassKI, Beta);
                //triggerClone.GetComponent<Body>().Orbit.Velocity += deltaV;
                larger.Orbit.Velocity += deltaV;

                // Return orbits to original epoch (position change should not be noticeable)
                larger.Orbit.Epoch  = originalEpoch;
                smaller.Orbit.Epoch = originalEpoch; // Not really necessary because not active

                Debug.LogFormat("Delta V: {0:E5} {1:E5} {2:E5}", deltaV.x, deltaV.y, deltaV.z);

                // Update list of active bodies
                Bodies = FindObjectsOfType <Body>();
                return;
            }
            Debug.Log("Detected unrecognized collision: " + larger.name + "-" + smaller.name);
        }
Пример #13
0
    /// <summary>
    /// Use orbit utils to put the dust ball in a random orbit.
    /// </summary>
    /// <param name="dustBall"></param>
    /// <param name="centerNBody"></param>
    private void SetOrbitForDustBall(DustBall dustBall, NBody centerNBody)
    {
        OrbitUtils.OrbitElements oe = new OrbitUtils.OrbitElements();
        // must use p to set orbit scale!
        oe.p    = Random.Range(minRadius, maxRadius);
        oe.incl = Random.Range(-80f, 80f);
        oe.ecc  = Random.Range(minEccentricity, maxEccentricity);
        oe.raan = Random.Range(0, 2.0f * Mathf.PI);
        oe.argp = Random.Range(0, 2.0f * Mathf.PI);
        Vector3d r = new Vector3d();
        Vector3d v = new Vector3d();

        OrbitUtils.COEtoRV(oe, centerNBody, ref r, ref v, false);
        dustBall.velocity           = v.ToVector3();
        dustBall.transform.position = r.ToVector3();
    }
Пример #14
0
    private void ExecuteTransfer()
    {
        // should always be set
        if (lambertU == null)
        {
            Debug.LogError("Internal state bug - no Lambert xfer to execute");
            return;
        }

        // If ship is Kepler, then implement the transfer as a series of conics and not manuevers.
        if (OrbitUtils.IsOnRails(spaceshipNBody))
        {
            TransferOnRails();
        }
        else
        {
            List <Maneuver> mlist = lambertU.GetManeuvers();
            // to ensure no maneuver dt error on first burn, set velocity explicitly
            ge.SetVelocity(spaceshipNBody, lambertU.GetTransferVelocity());
            if (mlist.Count == 2)
            {
                // Add second maneuver to match target orbit
                // add callback to dismiss maneuver info once done
                mlist[1].onExecuted = ManeuverDoneCallback;
                ge.AddManeuver(mlist[1]);
            }
            else
            {
                Debug.LogError("Expected two maneuvers");
            }
        }
        // start evolution if paused.
        if (!ge.GetEvolve())
        {
            ge.SetEvolve(true);
        }
        maneuverOrbitPredictor.gameObject.SetActive(false);
        maneuverSegment.gameObject.SetActive(false);
        if (maneuverRenderer != null)
        {
            maneuverRenderer.Clear();
        }
    }
Пример #15
0
    private void RVtoCOEWrapper(NBody aroundNBody, Vector3 r, Vector3 v)
    {
        OrbitUtils.OrbitElements oe = OrbitUtils.RVtoCOE(r, v, aroundNBody, false);
        ecc = (float)oe.ecc;
        a   = (float)oe.a;
        // awkward, but hyperbola expects -a (TODO clean this all out!)
        if (ecc > 1.0f)
        {
            a = -a;
        }
        perihelion  = a * (ecc - 1);
        inclination = Mathf.Rad2Deg * (float)oe.incl;
        omega_uc    = 0;
        omega_lc    = 0;
        if (oe.IsInclined())
        {
            if (oe.IsCircular())
            {
                omega_uc = Mathf.Rad2Deg * (float)oe.raan;
            }
            else
            {
                omega_uc = Mathf.Rad2Deg * (float)oe.raan;
                omega_lc = Mathf.Rad2Deg * (float)oe.argp;
            }
        }
        else
        {
            if (!oe.IsCircular())
            {
                omega_lc = Mathf.Rad2Deg * (float)oe.lonper;
            }
            // if not inclined and circular, no omega_uc or omega_lc, just phase.
        }
        phase   = (float)OrbitUtils.GetPhaseFromOE(oe) * Mathf.Rad2Deg;
        ecc_vec = oe.ecc_vec;

        if (ecc < 1.0f)
        {
            CalcPeriod();
            CalcTau(r, v);
        }
    }
Пример #16
0
    /// <summary>
    /// Recompute and update the kepler depth of a fixed body.
    /// </summary>
    /// <param name="nbody"></param>
    public void UpdateKeplerDepth(NBody nbody, OrbitUniversal orbitU)
    {
        if (nbody.engineRef == null)
        {
            return;
        }
        GravityEngine.FixedBody fixedBody = nbody.engineRef.fixedBody;
        if (fixedBody == null)
        {
            return;
        }
        int depth    = nbody.GetOrbitDepth();
        int newDepth = OrbitUtils.CalcKeplerDepth(orbitU);

        if (newDepth != depth)
        {
            fixedBody.kepler_depth = newDepth;
            keplerDepthChanged.Add(fixedBody);
        }
    }
Пример #17
0
    /// <summary>
    /// Init the ellipse, verify a center body is present, determine orientation and update transform.
    /// </summary>
    public void Init()
    {
        // mode determines if user wants to define wrt to A or P. Determine other quantity
        if (paramBy == ParamBy.AXIS_A)
        {
            p = a * (1 - ecc);
        }
        else if (paramBy == ParamBy.CLOSEST_P)
        {
            a = p / (1 - ecc);
        }
        centerNbody = OrbitUtils.GetCenterNbody(transform, centerObject);
        CalculateRotation();
        NBody nbody = GetComponent <NBody>();

        // particle ring would not have an NBody
        if (nbody != null)
        {
            SetInitialPosition(nbody);
        }
    }
Пример #18
0
        private void button_radarCaculate_Click(object sender, EventArgs e)
        {
            bool isGeoCoord = this.radioButton_geoCoord.Checked;

            GeoCoord siteCoord = null;

            if (isGeoCoord)
            {
                siteCoord      = GeoCoord.Parse(this.textBox_coord.Text);
                siteCoord.Unit = AngleUnit.Degree;
            }
            else
            {
                XYZ xyz = XYZ.Parse(this.textBox_coord.Text);
                siteCoord = CoordTransformer.XyzToGeoCoord(xyz);
            }

            TwoLineElement tle = GetTwoLineElement();

            Gnsser.Orbits.Orbit orbit = new Gnsser.Orbits.Orbit(tle);

            double intervalMin = Double.Parse(this.textBox_intervalMin.Text);
            double count       = Int32.Parse(this.textBox_count.Text);

            lonlats = new List <AnyInfo.Geometries.Point>();
            lonlats.Add(new AnyInfo.Geometries.Point(siteCoord, "SitePoint"));
            for (int i = 0; i < count; i++)
            {
                double           time     = i * intervalMin;
                TimedMotionState eciSDP4  = orbit.PositionEci(time);
                TopoCoord        topoLook = OrbitUtils.GetSatTopoCoord(eciSDP4, siteCoord);

                if (topoLook.Elevation > 0)
                {
                    GeoCoord geoCoord = CoordTransformer.XyzToGeoCoord(eciSDP4.Position * 1000, AngleUnit.Degree);
                    lonlats.Add(new AnyInfo.Geometries.Point(geoCoord, i + ""));
                }
            }
        }
Пример #19
0
    /// <summary>
    /// Displays the path of the elliptical orbit when the object is selected in the editor.
    /// </summary>
    void OnDrawGizmosSelected()
    {
        // need to have a center to draw gizmo.
        if (GetCenterObject() == null)
        {
            return;
        }
        centerNbody = centerObject.GetComponent <NBody>();
        if (centerNbody == null)
        {
            return;
        }
        // only display if this object or parent is selected
        bool selected = Selection.Contains(transform.gameObject);

        if (transform.parent != null)
        {
            selected |= Selection.Contains(transform.parent.gameObject);
        }
        if (!selected)
        {
            return;
        }

        UpdateOrbitParams();
        CalculateRotation();

        const int NUM_STEPS     = 100;
        const int STEPS_PER_RAY = 10;
        int       rayCount      = 0;

        Gizmos.color = Color.white;

        // Center object may need to determine it's position in an orbit
        // and update it's intialPhyPosition
        GravityEngine ge = GravityEngine.Instance();

        centerNbody.InitPosition(ge);
        centerNbody.EditorUpdate(ge);
        Vector3 centerPos = centerNbody.transform.position;

        Init();

        Vector3[] positions = OrbitPositions(NUM_STEPS, centerPos, false); // do not apply mapToScene
        for (int i = 1; i < NUM_STEPS; i++)
        {
            Gizmos.DrawLine(positions[i], positions[i - 1]);
            rayCount = (rayCount + 1) % STEPS_PER_RAY;
            if (rayCount == 0)
            {
                Gizmos.DrawLine(centerObject.transform.position, positions[i]);
            }
        }
        // close the circle
        Gizmos.DrawLine(positions[NUM_STEPS - 1], positions[0]);

        // Draw the axes in a different color
        Gizmos.color = Color.red;
        Gizmos.DrawLine(PositionForTheta(0.5f * Mathf.PI, centerPos), PositionForTheta(-0.5f * Mathf.PI, centerPos));
        Gizmos.color = Color.blue;
        Gizmos.DrawLine(PositionForTheta(0f, centerPos), PositionForTheta(Mathf.PI, centerPos));

        // move body to location specified by parameters but only if GE not running
        if (!Application.isPlaying)
        {
            NBody nbody = GetComponent <NBody>();
            if (nbody != null)
            {
                nbody.EditorUpdate(ge);
            }
        }
        // Draw the Hill sphere
        Gizmos.color = Color.white;
        Gizmos.DrawWireSphere(transform.position, OrbitUtils.HillRadius(centerObject, transform.gameObject));
    }
Пример #20
0
 void SetEpochText()
 {
     // alert 1
     if (OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "7/27/2017" && alert1 == 0)
     {
         alert1 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("July 27, 2017");
     }
     // alert 2
     else if (OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "12/4/2017" && alert2 == 0)
     {
         alert2 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("December 4, 2017");
     }
     // alert 3
     else if (OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "4/17/2018" && alert3 == 0)
     {
         alert3 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("April 7, 2018");
     }
     // alert 4
     else if (OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "11/29/2018" && alert4 == 0)
     {
         alert4 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("November 29, 2018");
     }
     // alert 5
     else if (OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "6/7/2019" && alert5 == 0)
     {
         alert5 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("June 7, 2019");
     }
     // alert 6
     else if (OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "1/2/2020" && alert6 == 0)
     {
         alert6 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("January 2, 2020");
     }
     // alert 7
     else if (OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "7/23/2020" && alert7 == 0)
     {
         alert7 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("July 23, 2020");
     }
     // alert 8
     else if (OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "3/14/2021" && alert8 == 0)
     {
         alert8 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("March 14, 2021");
     }
     // alert 9
     else if (OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "10/13/2022" && alert9 == 0)
     {
         alert9 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("October 13, 2022");
     }
     // alert 10
     else if (OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "5/9/2023" && alert10 == 0)
     {
         alert10 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("May 9, 2023");
     }
     // alert 11
     else if ((OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "12/11/2024" || OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "12/12/2024" || OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "12/13/2024") && alert11 == 0)
     {
         alert11 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("December 11, 2024");
     }
     // alert 12
     else if (OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "2/16/2025" && alert12 == 0)
     {
         alert12 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("February 16, 2025");
     }
     // alert 13
     else if (OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString() == "8/1/2026" && alert13 == 0)
     {
         alert13 = 1;
         manager2.GetComponent <AlertsManager>().AddAlert("August 1, 2026");
     }
     EpochText.text = OrbitUtils.JDN2DateTime(CurrentEpoch).ToShortDateString();
 }
Пример #21
0
    /// <summary>
    ///
    /// </summary>
    /// <param name="reverse">direction of motion, true for reverse path (long way around)</param>
    /// <param name="df">Controls intial guess, undocumented in Vallado. False seems to work.</param>
    /// <param name="nrev">Number of revolutions until transfer (typically 0)</param>
    /// <param name="dtsec">Required time of flight</param>
    /// <returns>error ()</returns>
    ///     error = 1;   // g not converged
    ///     error = 2;   // y negative
    ///     error = 3;   // impossible 180 transfer
    public int ComputeXfer(
        bool reverse,
        bool df,
        int nrev,
        double dtsec)
    {
        const double small   = 0.0000001;
        const int    numiter = 40;
        // const double mu = 398600.4418;  // m3s2
        const double pi = System.Math.PI;

        v1 = new double[] { 0, 0, 0 };
        v2 = new double[] { 0, 0, 0 };

        int    loops, ynegktr;
        double vara, y, upper, lower, cosdeltanu, f, g, gdot, xold, xoldcubed, magr1, magr2,
               psiold, psinew, c2new, c3new, dtnew, c2dot, c3dot, dtdpsi, psiold2;

        y = 0.0;

        /* --------------------  initialize values   -------------------- */
        int error = 0;

        psinew = 0.0;

        magr1 = r1.magnitude;
        magr2 = r2.magnitude;

        cosdeltanu = Vector3d.Dot(r1, r2) / (magr1 * magr2);
        if (reverse)
        {
            vara = -System.Math.Sqrt(magr1 * magr2 * (1.0 + cosdeltanu));
        }
        else
        {
            vara = System.Math.Sqrt(magr1 * magr2 * (1.0 + cosdeltanu));
        }

        /* -------- set up initial bounds for the bissection ------------ */
        if (nrev == 0)
        {
            upper = 4.0 * pi * pi;  // could be negative infinity for all cases
            lower = -4.0 * pi * pi; // allow hyperbolic and parabolic solutions
        }
        else
        {
            lower = 4.0 * nrev * nrev * pi * pi;
            upper = 4.0 * (nrev + 1.0) * (nrev + 1.0) * pi * pi;
        }

        /* ----------------  form initial guesses   --------------------- */
        psinew = 0.0;
        xold   = 0.0;
        if (nrev == 0)
        {
            // use log to get initial guess
            // empirical relation here from 10000 random draws
            // 10000 cases up to 85000 dtsec  0.11604050x + 9.69546575
            psiold = (System.Math.Log(dtsec) - 9.61202327) / 0.10918231;
            if (psiold > upper)
            {
                psiold = upper - pi;
            }
        }
        else
        {
            if (df)
            {
                psiold = lower + (upper - lower) * 0.3;
            }
            else
            {
                psiold = lower + (upper - lower) * 0.6;
            }
        }
        OrbitUtils.FindC2C3(psiold, out c2new, out c3new);


        /* --------  determine if the orbit is possible at all ---------- */
        if (System.Math.Abs(vara) > small)  // 0.2??
        {
            loops   = 0;
            ynegktr = 1; // y neg ktr
            dtnew   = -10.0;
            while ((System.Math.Abs(dtnew - dtsec) >= small) && (loops < numiter) && (ynegktr <= 10))
            {
                loops = loops + 1;
                if (System.Math.Abs(c2new) > small)
                {
                    y = magr1 + magr2 - (vara * (1.0 - psiold * c3new) / System.Math.Sqrt(c2new));
                }
                else
                {
                    y = magr1 + magr2;
                }
                /* ------- check for negative values of y ------- */
                if ((vara > 0.0) && (y < 0.0))
                {
                    ynegktr = 1;
                    while ((y < 0.0) && (ynegktr < 10))
                    {
                        psinew = 0.8 * (1.0 / c3new) *
                                 (1.0 - (magr1 + magr2) * System.Math.Sqrt(c2new) / vara);

                        /* ------ find c2 and c3 functions ------ */
                        OrbitUtils.FindC2C3(psinew, out c2new, out c3new);
                        psiold = psinew;
                        lower  = psiold;
                        if (System.Math.Abs(c2new) > small)
                        {
                            y = magr1 + magr2 -
                                (vara * (1.0 - psiold * c3new) / System.Math.Sqrt(c2new));
                        }
                        else
                        {
                            y = magr1 + magr2;
                        }
                        ynegktr++;
                    }
                }

                if (ynegktr < 10)
                {
                    if (System.Math.Abs(c2new) > small)
                    {
                        xold = System.Math.Sqrt(y / c2new);
                    }
                    else
                    {
                        xold = 0.0;
                    }
                    xoldcubed = xold * xold * xold;
                    dtnew     = (xoldcubed * c3new + vara * System.Math.Sqrt(y)) / System.Math.Sqrt(mu);

                    // try newton rhapson iteration to update psi
                    if (System.Math.Abs(psiold) > 1e-5)
                    {
                        c2dot = 0.5 / psiold * (1.0 - psiold * c3new - 2.0 * c2new);
                        c3dot = 0.5 / psiold * (c2new - 3.0 * c3new);
                    }
                    else
                    {
                        psiold2 = psiold * psiold;
                        c2dot   = -1.0 / Factorial(4) + 2.0 * psiold / Factorial(6) - 3.0 * psiold2 / Factorial(8) +
                                  4.0 * psiold2 * psiold / Factorial(10) - 5.0 * psiold2 * psiold2 / Factorial(12);
                        c3dot = -1.0 / Factorial(5) + 2.0 * psiold / Factorial(7) - 3.0 * psiold2 / Factorial(9) +
                                4.0 * psiold2 * psiold / Factorial(11) - 5.0 * psiold2 * psiold2 / Factorial(13);
                    }
                    dtdpsi = (xoldcubed * (c3dot - 3.0 * c3new * c2dot / (2.0 * c2new)) + vara / 8.0 * (3.0 * c3new * System.Math.Sqrt(y) / c2new + vara / xold)) / System.Math.Sqrt(mu);
                    psinew = psiold - (dtnew - dtsec) / dtdpsi;

                    // check if newton guess for psi is outside bounds(too steep a slope)
                    if (System.Math.Abs(psinew) > upper || psinew < lower)
                    {
                        // --------readjust upper and lower bounds------ -
                        if (dtnew < dtsec)
                        {
                            if (psiold > lower)
                            {
                                lower = psiold;
                            }
                        }
                        if (dtnew > dtsec)
                        {
                            if (psiold < upper)
                            {
                                upper = psiold;
                            }
                        }
                        psinew = (upper + lower) * 0.5;
                    }
                    /* -------------- find c2 and c3 functions ---------- */
                    OrbitUtils.FindC2C3(psinew, out c2new, out c3new);
                    psiold = psinew;

                    /* ---- make sure the first guess isn't too close --- */
                    if ((System.Math.Abs(dtnew - dtsec) < small) && (loops == 1))
                    {
                        dtnew = dtsec - 1.0;
                    }
                }
            }

            if ((loops >= numiter) || (ynegktr >= 10))
            {
                error = 1; // g not converged

                if (ynegktr >= 10)
                {
                    error = 2;  // y negative
                }
            }
            else
            {
                /* ---- use f and g series to find velocity vectors ----- */
                f    = 1.0 - y / magr1;
                gdot = 1.0 - y / magr2;
                g    = 1.0 / (vara * System.Math.Sqrt(y / mu)); // 1 over g
                //	fdot = sqrt(y) * (-magr2 - magr1 + y) / (magr2 * magr1 * vara);
                //for (int i = 0; i < 3; i++) {
                //    v1[i] = ((r2[i] - f * r1[i]) * g);
                //    v2[i] = ((gdot * r2[i] - r1[i]) * g);
                //}
                v1[0] = ((r2.x - f * r1.x) * g);
                v2[0] = ((gdot * r2.x - r1.x) * g);
                v1[1] = ((r2.y - f * r1.y) * g);
                v2[1] = ((gdot * r2.y - r1.y) * g);
                v1[2] = ((r2.z - f * r1.z) * g);
                v2[2] = ((gdot * r2.z - r1.z) * g);
            }
        }
        else
        {
            error = 3;   // impossible 180 transfer
        }
        // Determine maneuvers needed for ship (fromOrbit)
        if (error == 0)
        {
            maneuvers.Clear();
            Vector3 v1_vec = new Vector3((float)v1[0], (float)v1[1], (float)v1[2]);
            Vector3 v2_vec = new Vector3((float)v2[0], (float)v2[1], (float)v2[2]);
            // Departure
            Maneuver departure = new Maneuver();
            departure.mtype        = Maneuver.Mtype.vector;
            departure.nbody        = fromOrbit.nbody;
            departure.physPosition = r1 + center3d;
            if (innerToOuter)
            {
                departure.velChange = v1_vec - GravityEngine.Instance().GetVelocity(innerOrbit.nbody);
            }
            else
            {
                // Need to establish arrival velocity
                departure.velChange = v2_vec - GravityEngine.Instance().GetVelocity(outerOrbit.nbody);
            }
            departure.worldTime = (float)GravityEngine.Instance().GetGETime();
            maneuvers.Add(departure);
            deltaV = departure.velChange.magnitude;

            // Arrival (will not be required if intercept)
            if (toOrbit != null)
            {
                Maneuver arrival = new Maneuver();
                arrival.nbody        = fromOrbit.nbody;
                arrival.physPosition = r2 + center3d;
                arrival.worldTime    = departure.worldTime + (float)dtsec;
                arrival.mtype        = Maneuver.Mtype.vector;
                arrival.velChange    = toOrbit.GetPhysicsVelocityForEllipse(toOrbit.phase) - v2_vec;
                maneuvers.Add(arrival);
                deltaV += arrival.velChange.magnitude;
            }
        }
        return(error);
    }
    /// <summary>
    /// Computes the transfer and updates all the ghost bodies.
    /// </summary>
    /// <returns></returns>
    private void ComputeTransfer()
    {
        double timeNow = ge.GetPhysicalTimeDouble();

        // First using the transfer time, move the ghost Moon to position at SOI arrival.
        // Call evolve via LockAtTime on the ghostMoon to move it. Set position based on this.
        double t_flight  = tflightFactor * timeHohmann;
        double timeatSoi = timeNow + t_flight;

        ghostMoonOrbit[MOON_SOI_ENTER].LockAtTime(timeatSoi);
        // Determine the moon phase angle
        double moonPhase = ghostMoonSoiEnterOrbitPredictor.GetOrbitUniversal().phase;

        // Place the TLI ship at the user-requested angle wrt planet-moon line
        // Put ghost ship in same orbit geometry as the moon, assuming it is circular. Then
        // can use same phase value.
        // (Ship needs to reach this departure point, it may not even be on the ship orbit
        //  in general).
        ghostShipOrbit[TLI].phase       = shipTLIAngleDeg + (moonPhase + 180f);
        ghostShipOrbit[TLI].inclination = ghostMoonOrbit[MOON_SOI_ENTER].inclination;
        ghostShipOrbit[TLI].omega_lc    = ghostMoonOrbit[MOON_SOI_ENTER].omega_lc;
        ghostShipOrbit[TLI].omega_uc    = ghostMoonOrbit[MOON_SOI_ENTER].omega_uc;
        ghostShipOrbit[TLI].p_inspector = shipOrbit.p;
        ghostShipOrbit[TLI].Init();
        ghostShipOrbit[TLI].LockAtTime(0);

        // Place the SOI enter ship at the user-requested angle in an SOI orbit. Lock at time 0 so the phase
        // is held per the user input.
        ghostShipOrbit[ENTER_SOI].phase       = soiAngleDeg + moonPhase;
        ghostShipOrbit[ENTER_SOI].inclination = soiInclination + shipOrbit.inclination;
        ghostShipOrbit[ENTER_SOI].omega_lc    = ghostMoonOrbit[MOON_SOI_ENTER].omega_lc;
        ghostShipOrbit[ENTER_SOI].omega_uc    = ghostMoonOrbit[MOON_SOI_ENTER].omega_uc;
        ghostShipOrbit[ENTER_SOI].Init();
        ghostShipOrbit[ENTER_SOI].LockAtTime(0);

        // Find the line to the ENTER_SOI position. Ship departs from that line continued through planet
        // at the shipRadius distance (assumes circular ship orbit)
        // TODO: Handle planet not at (0,0,0)
        Vector3d soiEntryPos    = ge.GetPositionDoubleV3(ghostShip[ENTER_SOI]);
        Vector3d planetPos      = ge.GetPositionDoubleV3(planet);
        Vector3d departurePoint = ge.GetPositionDoubleV3(ghostShip[TLI]);

        // Use Lambert to find the departure velocity to get from departure to soiEntry
        // Since we need 180 degrees from departure to arrival, use LambertBattin
        lambertB = new LambertBattin(ghostShip[TO_MOON], planet, departurePoint, soiEntryPos, shipOrbit.GetAxis());

        // apply any time of flight change
        bool reverse = !shortPath;

        const bool df    = false;
        const int  nrev  = 0;
        int        error = lambertB.ComputeXfer(reverse, df, nrev, t_flight);

        if (error != 0)
        {
            Debug.LogWarning("Lambert failed to find solution. error=" + error);
            return;
        }
        // Check Lambert is going in the correct direction
        //Vector3 shipOrbitAxis = Vector3.Cross(ge.GetPhysicsPosition(spaceship), ge.GetVelocity(spaceship) ).normalized;
        //Vector3 tliOrbitAxis = Vector3.Cross(departurePoint.ToVector3(), lambertB.GetTransferVelocity().ToVector3()).normalized;
        Vector3 shipOrbitAxis = Vector3.Cross(ge.GetVelocity(spaceship), ge.GetPhysicsPosition(spaceship)).normalized;
        Vector3 tliOrbitAxis  = Vector3.Cross(lambertB.GetTransferVelocity().ToVector3(), departurePoint.ToVector3()).normalized;

        if (Vector3.Dot(shipOrbitAxis, tliOrbitAxis) < 0)
        {
            error = lambertB.ComputeXfer(!reverse, df, nrev, t_flight);
            if (error != 0)
            {
                Debug.LogWarning("Lambert failed to find solution for reverse path. error=" + error);
                return;
            }
        }
        Debug.LogFormat("tli_vel={0}", lambertB.GetTransferVelocity());

        ghostShipOrbit[TO_MOON].InitFromRVT(departurePoint, lambertB.GetTransferVelocity(), timeNow, planet, false);

        // Set velocity for orbit around moon. Will be updated every frame
        ghostShipOrbit[SOI_HYPER].InitFromRVT(soiEntryPos, lambertB.GetFinalVelocity(), timeNow, ghostMoon[MOON_SOI_ENTER], false);

        // Find the exit point of the hyperbola in the SOI
        OrbitUtils.OrbitElements oe = OrbitUtils.RVtoCOE(soiEntryPos, lambertB.GetFinalVelocity(), ghostMoon[MOON_SOI_ENTER], false);
        Vector3d soiExitR           = new Vector3d();
        Vector3d soiExitV           = new Vector3d();

        OrbitUtils.COEtoRVMirror(oe, ghostMoon[MOON_SOI_ENTER], ref soiExitR, ref soiExitV, false);

        // Find time to go around the moon. TOF requires relative positions!!
        Vector3d ghostSoiEnterPos   = ge.GetPositionDoubleV3(ghostMoon[MOON_SOI_ENTER]);
        Vector3d soiEnterRelative   = soiEntryPos - ghostSoiEnterPos;
        Vector3d soiExitRelative    = soiExitR - ghostSoiEnterPos;
        Vector3d soiExitVelRelative = soiExitV - ge.GetVelocityDoubleV3(ghostMoon[MOON_SOI_ENTER]);
        double   hyperTOF           = ghostShipOrbit[SOI_HYPER].TimeOfFlight(soiEnterRelative, soiExitRelative);

        // Position the ghost moon for SOI exit (timeAtSoi includes timeNow)
        t_soiExit = timeatSoi + hyperTOF;
        ghostMoonOrbit[MOON_SOI_EXIT].LockAtTime(t_soiExit);

        // Set position and vel for exit ship, so exit orbit predictor can run.
        Vector3d ghostMoonSoiExitPos = ge.GetPositionDoubleV3(ghostMoon[MOON_SOI_EXIT]);
        Vector3d ghostMoonSoiExitVel = ge.GetVelocityDoubleV3(ghostMoon[MOON_SOI_EXIT]);

        ghostShipOrbit[EXIT_SOI].InitFromRVT(soiExitRelative + ghostMoonSoiExitPos,
                                             soiExitVelRelative + ghostMoonSoiExitVel,
                                             timeNow, planet, false);
    }
    /// <summary>
    /// Computes the transfer with the moon on the +X axis without accounting for the moon motion during
    /// transit. (That is accounted for in the ExecuteTransfer routine).
    ///
    /// This allows a co-rotating visualization of the orbit.
    /// </summary>
    /// <returns></returns>
    private Vector3 ComputeTransfer()
    {
        OrbitData shipOrbit = new OrbitData();

        shipOrbit.SetOrbitForVelocity(spaceship, planet);

        // compute the min energy path (this will be in the short path direction)
        lambertU = new LambertUniversal(shipOrbit, startPoint, targetPoint, shortPath);

        // apply any time of flight change
        double t_flight = tflightFactor * lambertU.GetTMin();
        bool   reverse  = !shortPath;

        const bool df    = false;
        const int  nrev  = 0;
        int        error = lambertU.ComputeXfer(reverse, df, nrev, t_flight);

        if (error != 0)
        {
            Debug.LogWarning("Lambert failed to find solution.");
            aroundMoonSegment.gameObject.SetActive(false);
            return(Vector3.zero);
        }
        // Check Lambert is going in the correct direction
        Vector3 shipOrbitAxis = Vector3.Cross(ge.GetVelocity(spaceship), ge.GetPhysicsPosition(spaceship)).normalized;
        Vector3 tliOrbitAxis  = Vector3.Cross(lambertU.GetTransferVelocity(), startPoint.ToVector3());

        if (Vector3.Dot(shipOrbitAxis, tliOrbitAxis) < 0)
        {
            error = lambertU.ComputeXfer(!reverse, df, nrev, t_flight);
            if (error != 0)
            {
                Debug.LogWarning("Lambert failed to find solution for reverse path. error=" + error);
                return(Vector3.zero);
            }
        }

        Vector3 tliVelocity = lambertU.GetTransferVelocity();

        toMoonOrbit.SetVelocity(tliVelocity);
        toMoonSegment.SetVelocity(tliVelocity);
        aroundMoonSegment.gameObject.SetActive(true);

        // Set velocity for orbit around moon
        Vector3 soiEnterVel = lambertU.GetFinalVelocity();

        aroundMoonSegment.SetVelocity(soiEnterVel);

        // update shipEnterSOI object
        ge.UpdatePositionAndVelocity(shipEnterSOI, targetPoint.ToVector3(), soiEnterVel);

        // Find the orbit around the moon. By using the mirror position we're assuming it's
        // a hyperbola (since there is no course correction at SOI this is true).
        // (Moon is in correct position for these calcs so can use world positions, relativePos=false)
        Vector3d soiEnterV = new Vector3d(lambertU.GetFinalVelocity());

        OrbitUtils.OrbitElements oe = OrbitUtils.RVtoCOE(targetPoint, soiEnterV, moonBody, false);
        Vector3d soiExitR           = new Vector3d();
        Vector3d soiExitV           = new Vector3d();

        OrbitUtils.COEtoRVMirror(oe, moonBody, ref soiExitR, ref soiExitV, false);
        // Set position and vel for exit ship, so exit orbit predictor can run. Moon offset/vel already added.
        ge.SetPositionDoubleV3(shipExitSOI, soiExitR);
        ge.SetVelocityDoubleV3(shipExitSOI, soiExitV);
        aroundMoonSegment.UpdateOrbit();
        return(tliVelocity);
    }
    /// <summary>
    /// Set up a KeplerSeqeunce to do the three phases of the transfer as Kepler mode conics.
    ///
    /// Leave the existing ship orbit as the first
    /// </summary>
    /// <param name="transferTime"></param>
    private void TransferOnRails(double transferTime, Vector3 shipPos, Vector3 shipVel, float moonOmega)
    {
        // the ship needs to have a KeplerSequence
        KeplerSequence kseq = spaceship.GetComponent <KeplerSequence>();

        if (kseq == null)
        {
            Debug.LogError("Could not find a KeplerSequence on " + spaceship.name);
            return;
        }
        float      moonPhaseDeg = moonOmega * (float)transferTime * Mathf.Rad2Deg;
        Quaternion moonPhaseRot = Quaternion.AngleAxis(moonPhaseDeg, Vector3.forward);

        // Ellipse 1: shipPos/shipvel already phased by the caller.
        double t = ge.GetPhysicalTime();

        KeplerSequence.ElementStarted noCallback = null;
        kseq.AppendElementRVT(new Vector3d(shipPos), new Vector3d(shipVel), t, false, spaceship, planet, noCallback);

        // Hyperbola: start at t + transferTime
        // have targetPoint and final velocity from LambertTransfer. Need to make these wrt moon at this time
        // targetPoint is w.r.t current moon position, but need to rotate around SOI by amount moon will shift
        // as ship transits to moon
        Vector3 targetPos    = targetPoint.ToVector3();
        Vector3 moonPosAtSoi = moonPhaseRot * ge.GetPhysicsPosition(moonBody);
        Vector3 moonVelAtSoi = moonPhaseRot * ge.GetVelocity(moonBody);
        // get the relative positions (i.e. as if moon at the origin with v=0)
        Vector3 adjustedTarget = moonPhaseRot * targetPos - moonPosAtSoi;
        Vector3 adjustedVel    = moonPhaseRot * lambertU.GetFinalVelocity() - moonVelAtSoi;

        // Create moon hyperbola at the moon position after flight to moon. This means the init cannot make reference
        // to the CURRENT moon position.
        Vector3d       soiEnterR  = new Vector3d(adjustedTarget);
        Vector3d       soiEnterV  = new Vector3d(adjustedVel);
        OrbitUniversal hyperOrbit = kseq.AppendElementRVT(soiEnterR,
                                                          soiEnterV,
                                                          t + transferTime,
                                                          true,
                                                          spaceship,
                                                          moonBody,
                                                          EnterMoonSoi);

        // Find the hyperbola exit SOI position/vel
        OrbitUtils.OrbitElements oe = OrbitUtils.RVtoCOE(soiEnterR, soiEnterV, moonBody, true);
        Vector3d soiExitR           = new Vector3d();
        Vector3d soiExitV           = new Vector3d();

        Debug.Log("oe=" + oe);
        // Gives position and velocity in relative position
        OrbitUtils.COEtoRVMirror(oe, moonBody, ref soiExitR, ref soiExitV, true);

        // Determine hyperbola transit time to the soiExit position
        double hyperTOF = hyperOrbit.TimeOfFlight(soiEnterR, soiExitR);

        //Debug.LogFormat("Hyper TOF={0} r0={1} r1={2} p={3}", hyperTOF, adjustedTarget, soiExitR,
        //    hyperOrbit.p);

        // Ellipse 2:
        // Adjust phase to allow for moon travel during hyperbola transit
        // Need to set position and vel relative to the planet using position relative to moon at 0
        moonPhaseDeg = moonOmega * (float)hyperTOF * Mathf.Rad2Deg;
        Quaternion moonHyperRot     = Quaternion.AngleAxis(moonPhaseDeg, Vector3.forward);
        Vector3    moonAtExit       = moonHyperRot * moonPosAtSoi;
        Vector3    moonVelAtExit    = moonHyperRot * moonVelAtSoi;
        Vector3    soiExitwrtPlanet = soiExitR.ToVector3() + moonAtExit;
        // soiexitV is relative to moon at (0,0,0) BUT frame of hyperbola does not rotate
        Vector3 soiExitVelwrtPlanet = moonHyperRot * soiExitV.ToVector3() + moonVelAtExit;

        Debug.LogFormat("Ellipse2: soiExitV={0} moonV={1} net={2}", soiExitV, moonVelAtExit, soiExitVelwrtPlanet);

        kseq.AppendElementRVT(new Vector3d(soiExitwrtPlanet),
                              new Vector3d(soiExitVelwrtPlanet),
                              t + transferTime + hyperTOF,
                              true,
                              spaceship,
                              planet,
                              ExitMoonSoi);
        running = true;
    }