private void SetMarkers() { if (shipOrbitData == null) { // Cannot rely on controller start after GE start, so instead of forcing // start order, do a lazy init here shipOrbitData = new OrbitData(); shipOrbitData.SetOrbit(spaceshipNBody, shipOrbit.centerNbody); } // skip scaling since we are in dimensionless units Vector3 pos = shipOrbitData.GetPhysicsPositionforEllipse(fromPhase); fromMarker.transform.position = pos; pos = shipOrbitData.GetPhysicsPositionforEllipse(toPhase); toMarker.transform.position = pos; }
public CircularInclinationAndAN(OrbitData fromOrbit, OrbitData toOrbit) : base(fromOrbit, toOrbit) { name = "Circular Change Inclination and Ascending Node"; // check the orbits are circular and have the same radius if (fromOrbit.ecc > GEConst.small) { Debug.LogWarning("fromOrbit is not circular. ecc=" + fromOrbit.ecc); return; } if (toOrbit.ecc > GEConst.small) { Debug.LogWarning("toOrbit is not circular. ecc=" + toOrbit.ecc); return; } if (Mathf.Abs(fromOrbit.a - toOrbit.a) > GEConst.small) { Debug.LogWarning("Orbits do not have the same radius delta=" + Mathf.Abs(fromOrbit.a - toOrbit.a)); return; } double dOmega = (toOrbit.omega_uc - fromOrbit.omega_uc) * Mathd.Deg2Rad; double i_initial = fromOrbit.inclination * Mathd.Deg2Rad; double i_final = toOrbit.inclination * Mathd.Deg2Rad; // u_initial = omega_lc + nu (i.e. phase of circular orbit) // eqn (6-25) double cos_theta = Mathd.Cos(i_initial) * Mathd.Cos(i_final) + Mathd.Sin(i_initial) * Mathd.Sin(i_final) * Mathd.Cos(dOmega); // Quadrant check double theta = Mathd.Acos(Mathd.Clamp(cos_theta, -1.0, 1.0)); if (dOmega < 0) { theta = -theta; } // u_initial: phase of intersection in the initial orbit double numer = Mathd.Sin(i_final) * Mathd.Cos(dOmega) - cos_theta * Mathd.Sin(i_initial); double denom = Mathd.Sin(theta) * Mathd.Cos(i_initial); if (Mathd.Abs(denom) < 1E-6) { Debug.LogError("u_initial: about to divide by zero (small theta)"); // return; } double u_initial = Mathd.Acos(Mathd.Clamp(numer / denom, -1.0, 1.0)); // u_final: phase of intersection in the final orbit numer = Mathd.Cos(i_initial) * Mathd.Sin(i_final) - Mathd.Sin(i_initial) * Mathd.Cos(i_final) * Mathd.Cos(dOmega); if (Mathd.Abs(Mathd.Sin(theta)) < 1E-6) { Debug.LogError("u_final: about to divide by zero (small theta)"); return; } double u_final = Mathd.Acos(Mathd.Clamp(numer / Mathd.Sin(theta), -1.0, 1.0)); double u_initialDeg = u_initial * Mathd.Rad2Deg; double u_finalDeg = u_final * Mathd.Rad2Deg; // Orbits cross at two places, pick the location closest to the current position of the fromOrbit double time_to_crossing = fromOrbit.period * (u_initialDeg - fromOrbit.phase) / 360f; if (time_to_crossing < 0) { u_initialDeg += 180f; u_finalDeg += 180f; time_to_crossing += 0.5f * fromOrbit.period; } // Determine velocity change required Vector3 dV = toOrbit.GetPhysicsVelocityForEllipse((float)u_finalDeg) - fromOrbit.GetPhysicsVelocityForEllipse((float)u_initialDeg); // Create a maneuver object Maneuver m = new Maneuver(); m.physPosition = new Vector3d(fromOrbit.GetPhysicsPositionforEllipse((float)(u_initialDeg))); m.mtype = Maneuver.Mtype.vector; m.dV = dV.magnitude; m.velChange = dV; m.worldTime = GravityEngine.instance.GetPhysicalTime() + (float)time_to_crossing; m.nbody = fromOrbit.nbody; maneuvers.Add(m); //Debug.LogFormat("u_initial = {0} u_final={1} (deg) dOmega={2} (deg) timeToCrossing={3} fromPhase={4} cos_theta={5} theta={6}", // u_initialDeg, // u_finalDeg, // dOmega * Mathd.Rad2Deg, // time_to_crossing, // fromOrbit.phase, // cos_theta, // theta); }
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; } }
public CircNonPlanarRendezvous(OrbitData fromOrbit, OrbitData toOrbit) : base(fromOrbit, toOrbit) { name = "Circular Non-Planar Rendezvous"; double w_target = toOrbit.GetOmegaAngular(); double w_int = fromOrbit.GetOmegaAngular(); bool innerToOuter = (fromOrbit.a < toOrbit.a); if (!innerToOuter) { Debug.LogError("Fix me: algorithm does not support outer to inner yet!"); } float a_transfer = 0.5f * (fromOrbit.a + toOrbit.a); float t_transfer = Mathf.PI * Mathf.Sqrt(a_transfer * a_transfer * a_transfer / fromOrbit.mu); Debug.LogFormat("int: a={0} w={1} target: a={2} w={3}", fromOrbit.a, w_int, toOrbit.a, w_target); // lead angle required by target double alpha_L = w_target * t_transfer; // find the phase of the nearest node in the interceptor orbit // (C&P from CircularInclinationAndAN, messy to extract) double dOmega = (toOrbit.omega_uc - fromOrbit.omega_uc) * Mathd.Deg2Rad; double i_initial = fromOrbit.inclination * Mathd.Deg2Rad; double i_final = toOrbit.inclination * Mathd.Deg2Rad; // u_initial = omega_lc + nu (i.e. phase of circular orbit) // eqn (6-25) double cos_theta = Mathd.Cos(i_initial) * Mathd.Cos(i_final) + Mathd.Sin(i_initial) * Mathd.Sin(i_final) * Mathd.Cos(dOmega); // Quadrant check double theta = Mathd.Acos(Mathd.Clamp(cos_theta, -1.0, 1.0)); if (dOmega < 0) { theta = -theta; } // u_initial: phase of intersection in the initial orbit double numer = Mathd.Sin(i_final) * Mathd.Cos(dOmega) - cos_theta * Mathd.Sin(i_initial); double denom = Mathd.Sin(theta) * Mathd.Cos(i_initial); if (Mathd.Abs(denom) < 1E-6) { Debug.LogError("u_initial: about to divide by zero (small theta)"); return; } double u_initial = Mathd.Acos(Mathd.Clamp(numer / denom, -1.0, 1.0)); // u_final: phase of intersection in the final orbit numer = Mathd.Cos(i_initial) * Mathd.Sin(i_final) - Mathd.Sin(i_initial) * Mathd.Cos(i_final) * Mathd.Cos(dOmega); if (Mathd.Abs(Mathd.Sin(theta)) < 1E-6) { Debug.LogError("u_final: about to divide by zero (small theta)"); return; } double u_final = Mathd.Acos(Mathd.Clamp(numer / Mathd.Sin(theta), -1.0, 1.0)); // how far is interceptor from a node? double delta_theta_int = u_initial - fromOrbit.phase * Mathd.Deg2Rad; if (delta_theta_int < -Mathd.PI) { delta_theta_int += 2.0 * Mathd.PI; u_initial += 2.0 * Mathd.PI; u_final += 2.00 * Mathd.PI; } else if (delta_theta_int < 0) { delta_theta_int += Mathd.PI; u_initial += Mathd.PI; u_final += Mathd.PI; } double deltat_node = delta_theta_int / w_int; Debug.LogFormat("Node at: {0} (deg), distance to node (deg)={1}", u_initial * Mathd.Rad2Deg, delta_theta_int * Mathd.Rad2Deg); // Algorithm uses lambda_true. See the definition in Vallada (2-92). defined for circular equitorial // orbits as angle from I-axis (x-axis) to the satellite. // This is not the same as u = argument of latitude, which is measured from the ascending node. // Target moves to lambda_tgt1 as interceptor moves to node double lambda_tgt1 = toOrbit.phase * Mathd.Deg2Rad + w_target * deltat_node; // phase lag from interceptor to target (target is 1/2 revolution from u_initial) // Vallado uses the fact that node is at omega, which assumes destination orbit is equitorial? double lambda_int = u_final + Mathd.PI; // Mathd.PI + fromOrbit.omega_uc; double theta_new = lambda_int - lambda_tgt1; if (theta_new < 0) { theta_new += 2.0 * Mathd.PI; } // This is not working. Why?? // double alpha_new = Mathd.PI + theta_new; // Keep in 0..2 Pi (my addition) double alpha_new = theta_new % (2.0 * Mathd.PI); Debug.LogFormat("lambda_tgt1={0} theta_new={1} toOrbit.phase={2} t_node={3} w_target={4}", lambda_tgt1 * Mathd.Rad2Deg, theta_new * Mathd.Rad2Deg, toOrbit.phase, deltat_node, w_target); // k_target: number of revolutions in transfer orbit. Provided as input // k_int: number of revs in phasing orbit. Want to ensure a_phase < a_target to not // waste deltaV. double mu = fromOrbit.mu; double k_target = 0.0; double two_pi_k_target = k_target * 2.0 * Mathd.PI; double P_phase = (alpha_new - alpha_L + two_pi_k_target) / w_target; while (P_phase < 0) { Debug.Log("Pphase < 0. Bumping k_target"); k_target += 1.0; two_pi_k_target = k_target * 2.0 * Mathd.PI; P_phase = (alpha_new - alpha_L + two_pi_k_target) / w_target; } double k_int = 1.0; double two_pi_k_int = k_int * 2.0 * Mathd.PI; double a_phase = Mathd.Pow(mu * (P_phase * P_phase / (two_pi_k_int * two_pi_k_int)), 1.0 / 3.0); Debug.LogFormat("alpha_new={0} alpha_L={1} Pphase={2}", alpha_new * Mathd.Rad2Deg, alpha_L * Mathd.Rad2Deg, P_phase); // if need a long time to phase do multiple phasing orbits int loopCnt = 0; while (innerToOuter && (a_phase > toOrbit.a)) { Debug.Log("a_phase > toOrbit - add a lap"); k_int += 1.0; two_pi_k_int = k_int * 2.0 * Mathd.PI; a_phase = Mathd.Pow(mu * (P_phase * P_phase / (two_pi_k_int * two_pi_k_int)), 1.0 / 3.0); if (loopCnt++ > 10) { break; } } double t_phase = 2.0 * Mathd.PI * Mathd.Sqrt(a_phase * a_phase * a_phase / fromOrbit.mu); // Book has Abs(). Why? Need to remove this otherwise some phase differences do not work // Only take Cos of delta_incl (no sign issues) double deltaV_phase, deltaV_trans1, deltaV_trans2; double delta_incl = (toOrbit.inclination - fromOrbit.inclination) * Mathd.Deg2Rad; if (innerToOuter) { deltaV_phase = Mathd.Sqrt(2.0 * mu / fromOrbit.a - mu / a_phase) - Mathd.Sqrt(mu / fromOrbit.a); deltaV_trans1 = Mathd.Sqrt(2.0 * mu / fromOrbit.a - mu / a_transfer) - Mathd.Sqrt(2.0 * mu / fromOrbit.a - mu / a_phase); deltaV_trans2 = Mathd.Sqrt(2.0 * mu / toOrbit.a - mu / a_transfer + mu / toOrbit.a - 2.0 * Mathd.Sqrt(2.0 * mu / toOrbit.a - mu / a_transfer) * Mathd.Sqrt(mu / toOrbit.a) * Mathd.Cos(delta_incl)); } else { // FIX ME!! Get NaN, so need to review from first principles. deltaV_phase = Mathd.Sqrt(mu / a_phase - 2.0 * mu / fromOrbit.a) - Mathd.Sqrt(mu / fromOrbit.a); deltaV_trans1 = Mathd.Sqrt(mu / a_transfer - 2.0 * mu / fromOrbit.a) - Mathd.Sqrt(2.0 * mu / fromOrbit.a - mu / a_phase); deltaV_trans2 = Mathd.Sqrt(mu / a_transfer - 2.0 * mu / toOrbit.a + mu / toOrbit.a - 2.0 * Mathd.Sqrt(mu / a_transfer - 2.0 * mu / toOrbit.a) * Mathd.Sqrt(mu / toOrbit.a) * Mathd.Cos(delta_incl)); } Debug.LogFormat("T1: a_int={0} a_phase={1} a_tgt={2} dt={3} dV_phase={4} dv1={5} dv2={6}", fromOrbit.a, a_phase, toOrbit.a, deltat_node, deltaV_phase, deltaV_trans1, deltaV_trans2); // phasing burn: in same plane as the orbit at the node, use scalar maneuver double time_start = GravityEngine.Instance().GetPhysicalTimeDouble(); Maneuver m_phase = new Maneuver(); m_phase.mtype = Maneuver.Mtype.scalar; m_phase.dV = (float)deltaV_phase; m_phase.worldTime = (float)(time_start + deltat_node); m_phase.nbody = fromOrbit.nbody; m_phase.physPosition = new Vector3d(fromOrbit.GetPhysicsPositionforEllipse((float)(u_initial * Mathd.Rad2Deg))); maneuvers.Add(m_phase); // transfer burn - stay in initial orbit plane Maneuver m_trans1 = new Maneuver(); m_trans1.mtype = Maneuver.Mtype.scalar; m_trans1.dV = (float)deltaV_trans1; m_trans1.worldTime = (float)(time_start + deltat_node + t_phase); m_trans1.nbody = fromOrbit.nbody; m_trans1.physPosition = m_phase.physPosition; maneuvers.Add(m_trans1); // Arrival burn - do plane change here (just assign the correct velocity) // TODO: Need to reverse this when from outer to inner...do plane change at start float finalPhase = (float)(u_final * Mathd.Rad2Deg + 180f); Vector3 finalV = toOrbit.GetPhysicsVelocityForEllipse(finalPhase); Vector3 finalPos = toOrbit.GetPhysicsPositionforEllipse(finalPhase); Maneuver m_trans2 = new Maneuver(); m_trans2.mtype = Maneuver.Mtype.setv; m_trans2.dV = (float)deltaV_trans2; m_trans2.velChange = finalV; m_trans2.worldTime = (float)(time_start + deltat_node + t_phase + t_transfer); m_trans2.nbody = fromOrbit.nbody; m_trans2.physPosition = new Vector3d(finalPos); maneuvers.Add(m_trans2); }