// 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)); } }
/// <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(); }
} // coe2rvMirror public static double GetPhaseFromOE(OrbitUtils.OrbitElements oe) { if (oe.ecc < small) { // ---------------- circular equatorial ------------------ if ((oe.incl < small) | (Mathd.Abs(oe.incl - Mathd.PI) < small)) { return(oe.truelon); } else { // -------------- circular inclined ------------------ return(oe.arglat); } } return(oe.nu); }
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> /// 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> /// 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; }
/// <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); }