Exemplo n.º 1
0
    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);
    }
Exemplo n.º 3
0
    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;
        }
    }
Exemplo n.º 4
0
    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);
    }