private void TestRV(OrbitData od, GameObject planet, GameObject star) { GameObject testPlanet = TestSetupUtils.CreatePlanetInHyper(star, 1f); testPlanet.name = "TestPlanet"; OrbitHyper testHyper = testPlanet.GetComponent <OrbitHyper>(); testHyper.InitFromOrbitData(od); planet.name = "Planet"; // Awkward but cannot add a new object to GE when it is stopped, so re-add all three GravityEngine ge = GravityEngine.Instance(); ge.Clear(); ge.AddBody(star); ge.AddBody(planet); ge.AddBody(testPlanet); ge.Setup(); ge.LogDump(); Vector3 r_od = ge.GetPhysicsPosition(testPlanet.GetComponent <NBody>()); Vector3 v_od = ge.GetVelocity(testPlanet); Vector3 r_i = ge.GetPhysicsPosition(planet.GetComponent <NBody>()); Vector3 v_i = ge.GetVelocity(planet); Debug.Log(" r_i=" + r_i + " r_od=" + r_od + " delta=" + Vector3.Distance(r_i, r_od)); Debug.Log(" v_i=" + v_i + " v_od=" + v_od + " delta=" + Vector3.Distance(v_i, v_od)); Assert.IsTrue(FloatEqual(Vector3.Distance(r_i, r_od), 0f, 5E-2)); Assert.IsTrue(FloatEqual(Vector3.Distance(v_i, v_od), 0f, 5E-2)); }
private void TestRV(OrbitData od, GameObject planet, NBody starNbody, float orbitRadius) { GameObject testPlanet = TestSetupUtils.CreatePlanetInOrbitUniversal(starNbody, 1f, orbitRadius); OrbitUniversal orbitU = testPlanet.GetComponent <OrbitUniversal>(); // Run init explicitly to update transform details orbitU.InitFromOrbitData(od, 0); // Awkward but previously could not add a new object to GE when it is stopped, so re-add all three // Leave as is, since it works! GravityEngine ge = GravityEngine.Instance(); ge.Clear(); ge.AddBody(starNbody.gameObject); ge.AddBody(planet); ge.AddBody(testPlanet); ge.Setup(); ge.LogDump(); Vector3 r_od = ge.GetPhysicsPosition(testPlanet.GetComponent <NBody>()); Vector3 v_od = ge.GetVelocity(testPlanet); Vector3 r_i = ge.GetPhysicsPosition(planet.GetComponent <NBody>()); Vector3 v_i = ge.GetVelocity(planet); Debug.Log(" r_i=" + r_i + " r_od=" + r_od + " delta=" + Vector3.Distance(r_i, r_od)); Debug.Log(" v_i=" + v_i + " v_od=" + v_od + " delta=" + Vector3.Distance(v_i, v_od)); Assert.IsTrue(GEUnit.FloatEqual(Vector3.Distance(r_i, r_od), 0f, 1E-2)); Assert.IsTrue(GEUnit.FloatEqual(Vector3.Distance(v_i, v_od), 0f, 1E-2)); }
private void TestRV(OrbitData od, GameObject planet, GameObject star, float orbitRadius) { GameObject testPlanet = TestSetupUtils.CreatePlanetInOrbit(star, 1f, orbitRadius); OrbitEllipse testEllipse = testPlanet.GetComponent <OrbitEllipse>(); // Run init explicitly to update transform details testEllipse.InitFromOrbitData(od); // Awkward but cannot add a new object to GE when it is stopped, so re-add all three GravityEngine ge = GravityEngine.Instance(); ge.Clear(); ge.AddBody(star); ge.AddBody(planet); ge.AddBody(testPlanet); ge.Setup(); ge.LogDump(); Vector3 r_od = ge.GetPhysicsPosition(testPlanet.GetComponent <NBody>()); Vector3 v_od = ge.GetVelocity(testPlanet); Vector3 r_i = ge.GetPhysicsPosition(planet.GetComponent <NBody>()); Vector3 v_i = ge.GetVelocity(planet); Debug.Log(" r_i=" + r_i + " r_od=" + r_od + " delta=" + Vector3.Distance(r_i, r_od)); Debug.Log(" v_i=" + v_i + " v_od=" + v_od + " delta=" + Vector3.Distance(v_i, v_od)); Assert.IsTrue(FloatEqual(Vector3.Distance(r_i, r_od), 0f, 1E-2)); Assert.IsTrue(FloatEqual(Vector3.Distance(v_i, v_od), 0f, 1E-2)); }
public string DumpAll(NBody[] gameNBodies, GravityEngine ge) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.Append("Massive Bodies:\n"); for (int i = 0; i < numBodies; i++) { Vector3 vel = ge.GetVelocity(gameNBodies[i]); int engineIndex = gameNBodies[i].engineRef.index; sb.Append(string.Format(" n={0} {1} m={2} r={3} {4} {5} v={6} {7} {8} info={9} engineRef.index={10} extAcc={11}\n", i, gameNBodies[i].name, m[i], r[i, 0], r[i, 1], r[i, 2], vel.x, vel.y, vel.z, info[i], engineIndex, integrator.GetExternalAccelForIndex(engineIndex) )); } // Title printed by MBE if (masslessEngine != null) { sb.Append(masslessEngine.DumpString()); } sb.Append("Fixed Bodies:\n"); for (int i = 0; i < fixedBodies.Count; i++) { int fb_index = fixedBodies[i].nbody.engineRef.index; sb.Append(string.Format(" n={0} {1} kepler_depth={2}\n", i, gameNBodies[fb_index].name, fixedBodies[i].kepler_depth)); // add extra info if OrbitU or KeplerSeq sb.Append(fixedBodies[i].nbody.engineRef.fixedBody.fixedOrbit.DumpInfo()); } sb.Append(" fixedBodiesInIntegrator=" + fixedBodiesInIntegrator); // Any pending maneuvers - TODO // sb.Append(maneuverMgr.DumpAll()); return(sb.ToString()); }
/// <summary> /// Execute the maneuver. Called automatically by Gravity Engine for maneuvers that /// have been added to the GE via AddManeuver(). /// /// Unusual to call this method directly. /// </summary> /// <param name="ge"></param> public void Execute(GravityEngine ge) { Vector3 vel = ge.GetVelocity(nbody); switch (mtype) { case Mtype.vector: vel += velChange; break; case Mtype.scalar: // scalar: adjust existing velocity by dV Vector3 change = Vector3.Normalize(vel) * dV; vel += change; break; case Mtype.circularize: // find velocity vector perpendicular to r for circular orbit // Since we could be mid-integration need to get exact position from GE double[] r_ship = new double[3]; double[] v_ship = new double[3]; ge.GetPositionVelocityScaled(nbody, ref r_ship, ref v_ship); double[] r_center = new double[3]; double[] v_center = new double[3]; ge.GetPositionVelocityScaled(centerBody, ref r_center, ref v_center); Vector3 pos_ship = new Vector3((float)r_ship[0], (float)r_ship[1], (float)r_ship[2]); Vector3 vel_ship = new Vector3((float)v_ship[0], (float)v_ship[1], (float)v_ship[2]); Vector3 pos_center = new Vector3((float)r_center[0], (float)r_center[1], (float)r_center[2]); Vector3 r = pos_ship - pos_center; // to get axis of orbit, can take v x r Vector3 axis = Vector3.Normalize(Vector3.Cross(vel_ship, pos_ship)); // vis visa for circular orbit float mu = nbody.mass * ge.massScale; float v_mag = Mathf.Sqrt(mu / Vector3.Magnitude(r)); // positive v is counter-clockwise Vector3 v_dir = Vector3.Normalize(Vector3.Cross(axis, r)); Vector3 v_circular = v_mag * v_dir; ge.SetVelocity(nbody, v_circular); break; } #pragma warning disable 162 // disable unreachable code warning if (GravityEngine.DEBUG) { Debug.Log("Applied manuever: " + LogString() + " engineRef.index=" + nbody.engineRef.index + " engineRef.bodyType=" + nbody.engineRef.bodyType + " timeError=" + (worldTime - ge.GetPhysicalTime())); Debug.Log("r= " + Vector3.Magnitude(nbody.transform.position)); } #pragma warning restore 162 // enable unreachable code warning ge.SetVelocity(nbody, vel); }
// If there is no init delegate use the initial particle position (scaled to physics space) and the // initial particle velocity to seed the particle physics data. private void NoInitDelegateSetup(int fromP, int toP) { float scale = gravityEngine.physToWorldFactor; Vector3 sourceVelocity = Vector3.zero; if (addNBodyVelocity && nbodySource != null) { sourceVelocity = gravityEngine.GetVelocity(nbodySource); } sourceVelocity += initialVelocity; for (int i = fromP; i < toP; i++) { // particles are in world space - just use their position r[i, 0] = (particles[i].position.x) / scale; r[i, 1] = (particles[i].position.y) / scale; r[i, 2] = (particles[i].position.z) / scale; v[i, 0] = sourceVelocity.x + particles[i].velocity.x; v[i, 1] = sourceVelocity.y + particles[i].velocity.y; v[i, 2] = sourceVelocity.z + particles[i].velocity.z; inactive[i] = false; particles[i].velocity = Vector3.zero; } }
/// <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); }
// Update is called once per frame void Update() { Vector3 v = ge.GetVelocity(nbody.gameObject); transform.rotation = Quaternion.FromToRotation(axis.normalized, v.normalized); }