// 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; }
/// <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; }
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>(); }
// 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)); } }
// ///////////////////////////////////////////////////////////////////// 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); }
/// <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)); }
// 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; } }
/// <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; } }
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); }
/// <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(); }
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(); } }
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); } }
/// <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); } }
/// <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); } }
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 + "")); } } }
/// <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)); }
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(); }
/// <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; }