public HohmannXfer(OrbitData fromOrbit, OrbitData toOrbit, bool rendezvous) : base(fromOrbit, toOrbit) { name = "Hohmann"; if (rendezvous) { name += " Rendezvous"; } else { name += " Transfer"; } // Check both objects are orbiting in the same direction if (Vector3d.Dot(fromOrbit.GetAxis(), toOrbit.GetAxis()) < 0) { Debug.LogWarning("Objects orbiting in different directions. Will not proceed."); return; } // Hohmann xfer is via an ellipse from one circle to another. The ellipse is uniquely // defined by the radius of from and to. // Equations from Chobotov Ch 5.4 float r_inner = 0f; float r_outer = 0f; // From orbit result is in physics quantities if (fromOrbit.a < toOrbit.a) { r_inner = fromOrbit.a; r_outer = toOrbit.a; } else { r_inner = toOrbit.a; r_outer = fromOrbit.a; } // (mass scale was applied in Orbit data) float v_inner = Mathf.Sqrt(fromOrbit.mu / r_inner); float rf_ri = r_outer / r_inner; float dV_inner = v_inner * (Mathf.Sqrt(2f * rf_ri / (1f + rf_ri)) - 1f); // Debug.LogFormat("dv_iner={0} v_inner={1} r_i={2} r_o={3}", dV_inner, v_inner, r_inner, r_outer); float v_outer = Mathf.Sqrt(fromOrbit.mu / r_outer); //simplify per Roy (12.22) float dV_outer = v_outer * (1f - Mathf.Sqrt(2 / (1 + rf_ri))); // Debug.LogFormat("r_in={0} r_out={1} v_inner={2} v_outer={3}", r_inner, r_outer, v_inner, v_outer); // transfer time // Need to flip rf_ri for inner orbits to get the correct transfer_time // (should re-derive for this case sometime to see why) transfer_time = 0f; // time to wait for first maneuver (rendezvous case) float tWait = 0f; // Build the manuevers required deltaV = 0f; float worldTime = GravityEngine.Instance().GetPhysicalTime(); Maneuver m1; m1 = new Maneuver(); m1.nbody = fromOrbit.nbody; m1.mtype = Maneuver.Mtype.scalar; m1.worldTime = worldTime; Maneuver m2; m2 = new Maneuver(); m2.nbody = fromOrbit.nbody; m2.mtype = Maneuver.Mtype.scalar; // If the orbit is almost circular, then can be some omega which will impact rendezvous phasing float fromPhase = fromOrbit.phase + fromOrbit.omega_lc; float toPhase = toOrbit.phase + toOrbit.omega_lc; if (fromOrbit.a < toOrbit.a) { // inner to outer float subexpr = 1f + rf_ri; transfer_time = fromOrbit.period / Mathf.Sqrt(32) * Mathf.Sqrt(subexpr * subexpr * subexpr); if (rendezvous) { // need to determine wait time for first maneuver to phase the arrival // find angle by which outer body must lead (radians) // Chobotov 7.3 float subexpr2 = 0.5f * (1f + r_inner / r_outer); float theta_h = Mathf.PI * (1f - Mathf.Sqrt(subexpr2 * subexpr2 * subexpr2)); // find current angular seperation float phase_gap = toPhase - fromPhase; if (phase_gap < 0) { phase_gap += 360f; } // need seperation to be theta_h float dTheta = Mathf.Deg2Rad * phase_gap - theta_h; if (dTheta < 0) { dTheta += TWO_PI; } // need to wait for phase_gap to reduce to this value. It reduces at a speed based on the difference // in the angular velocities. float dOmega = TWO_PI / fromOrbit.period - TWO_PI / toOrbit.period; tWait = dTheta / dOmega; //Debug.LogFormat("inner_phase= {0} out_phase={1} phase_gap(deg)={2} thetaH={3} dTheta(rad)={4} dOmega={5} tWait={6}", // fromOrbit.phase, toOrbit.phase, phase_gap, theta_h, dTheta, dOmega, tWait); } // from inner to outer // first maneuver is to a higher orbit m1.dV = dV_inner; deltaV += dV_inner; maneuvers.Add(m1); // second manuever is opposite to velocity m2.dV = dV_outer; deltaV += dV_outer; maneuvers.Add(m2); } else { // outer to inner float subexpr_in = 1f + r_inner / r_outer; transfer_time = fromOrbit.period / Mathf.Sqrt(32) * Mathf.Sqrt(subexpr_in * subexpr_in * subexpr_in); if (rendezvous) { // Chobotov 7.2/7.3 (modified for outer to inner, use (Pi+Theta) and Pf not Pi float subexpr2 = 0.5f * (1f + r_outer / r_inner); float theta_h = Mathf.PI * (1f + Mathf.Sqrt(subexpr2 * subexpr2 * subexpr2)); // find current angular seperation float phase_gap = fromPhase - toPhase; if (phase_gap < 0) { phase_gap += 360f; } // need seperation to be -theta_h float dTheta = Mathf.Deg2Rad * phase_gap - theta_h; // Can need inner body to go around more than once... while (dTheta < 0) { dTheta += TWO_PI; } // larger (inner) omega first float dOmega = TWO_PI / toOrbit.period - TWO_PI / fromOrbit.period; tWait = dTheta / dOmega; //Debug.LogFormat("inner_phase= {0} out_phase={1} phase_gap(deg)={2} thetaH(deg)={3} dTheta(rad)={4} dOmega={5} tWait={6}", // toOrbit.phase, fromOrbit.phase, phase_gap, Mathf.Rad2Deg*theta_h, dTheta, dOmega, tWait); } // from outer to inner // first maneuver is to a lower orbit m1.dV = -dV_outer; deltaV += dV_outer; maneuvers.Add(m1); // second manuever is opposite to velocity m2.dV = -dV_inner; deltaV += dV_outer; maneuvers.Add(m2); } m1.worldTime = worldTime + tWait; m2.worldTime = worldTime + tWait + transfer_time; deltaT = tWait + transfer_time; // maneuver positions and info for KeplerSeq conversion and velocity directions Vector3d h_unit = fromOrbit.GetAxis(); float phaseAtXfer = fromOrbit.phase + (TWO_PI / fromOrbit.period) * tWait * Mathf.Rad2Deg; m1.physPosition = new Vector3d(fromOrbit.GetPhysicsPositionforEllipse(phaseAtXfer)); m1.relativePos = new Vector3d(fromOrbit.GetPhysicsPositionforEllipseRelative(phaseAtXfer)); m1.relativeVel = Vector3d.Cross(h_unit, m1.relativePos).normalized; m1.relativeTo = fromOrbit.centralMass; m2.physPosition = new Vector3d(toOrbit.GetPhysicsPositionforEllipse(phaseAtXfer + 180f)); m2.relativePos = new Vector3d(toOrbit.GetPhysicsPositionforEllipseRelative(phaseAtXfer + 180f)); m2.relativeVel = Vector3d.Cross(h_unit, m2.relativePos).normalized; m2.relativeTo = fromOrbit.centralMass; // Determine the relative velocities if (fromOrbit.a < toOrbit.a) { // inner to outer m1.relativeVel *= v_inner + m1.dV; m2.relativeVel *= v_outer; } else { // outer to inner m1.relativeVel *= v_outer + m1.dV; m2.relativeVel *= v_inner; } }