public ManeuverParameters ComputeEjectionManeuver(Vector3d exit_velocity, Orbit initial_orbit, double UT_0)
        {
            double GM  = initial_orbit.referenceBody.gravParameter;
            double C3  = exit_velocity.sqrMagnitude;
            double Mh  = initial_orbit.referenceBody.sphereOfInfluence;
            double Rpe = initial_orbit.semiMajorAxis;

            double isma = 2 / Mh - C3 / GM; //inverted Semi-major Axis, will work for parabolic orbit
            double ecc  = 1.0 - Rpe * isma;

            //double vstart = Math.Sqrt(GM * (2 / Rpe - isma)); //{ total start boost}
            //double slat = Rpe * Rpe * vstart * vstart / GM;

            //the problem here should be R for circular orbit instead of Rpe
            double slat = 2 * Rpe - isma * Rpe * Rpe; //but we don't know start point (yet) in elliptic orbit

            double theta = Math.Acos((slat / Mh - 1) / ecc);


            /*
             * //old way infinity hyperbolic:
             * //problems: it's not necessary hyperbolic (in case of low speed exit_velocity),
             * //and exit_velocity appears not infinite far from celestial body, but only sphereOfInfluence far
             * //i.e. Mh in previous statements(theta, isma) is not infinity!
             *
             * double sma = -GM / C3;
             *
             * double ecc = 1 - Rpe / sma;
             * double theta = Math.Acos(-1 / ecc);
             *
             */
            Vector3d intersect_1;
            Vector3d intersect_2;

            // intersect_1 is the optimal position on the orbit assuming the orbit is circular and the sphere of influence is infinite
            IntersectConePlane(exit_velocity, theta, initial_orbit.h, out intersect_1, out intersect_2);

            Vector3d eccvec = initial_orbit.GetEccVector();

            double true_anomaly = AngleAboutAxis(eccvec, intersect_1, initial_orbit.h);
            // GetMeanAnomalyAtTrueAnomaly expects degrees and returns radians
            double mean_anomaly = initial_orbit.GetMeanAnomalyAtTrueAnomaly(true_anomaly * UtilMath.Rad2Deg);

            double delta_mean_anomaly = MuUtils.ClampRadiansPi(mean_anomaly - initial_orbit.MeanAnomalyAtUT(UT_0));

            double UT = UT_0 + delta_mean_anomaly / initial_orbit.MeanMotion();

            Vector3d V_0 = initial_orbit.getOrbitalVelocityAtUT(UT);
            Vector3d pos = initial_orbit.getRelativePositionAtUT(UT);

            double final_vel = Math.Sqrt(C3 + 2 * GM / pos.magnitude);

            Vector3d x = pos.normalized;
            Vector3d z = Vector3d.Cross(x, exit_velocity).normalized;
            Vector3d y = Vector3d.Cross(z, x);

            theta = Math.PI / 2;
            double dtheta = 0.001;
            Orbit  sample = new Orbit();

            double theta_err = double.MaxValue;

            for (int iteration = 0; iteration < 50; ++iteration)
            {
                Vector3d V_1 = final_vel * (Math.Cos(theta) * x + Math.Sin(theta) * y);
                sample.UpdateFromStateVectors(pos, V_1, initial_orbit.referenceBody, UT);
                theta_err = AngleAboutAxis(exit_velocity, sample.getOrbitalVelocityAtUT(OrbitExtensions.NextTimeOfRadius(sample, UT, sample.referenceBody.sphereOfInfluence)), z);
                if (double.IsNaN(theta_err))
                {
                    return(null);
                }
                if (Math.Abs(theta_err) <= 0.1 * UtilMath.Deg2Rad)
                {
                    return(new ManeuverParameters((V_1 - V_0).xzy, UT));
                }

                V_1 = final_vel * (Math.Cos(theta + dtheta) * x + Math.Sin(theta + dtheta) * y);
                sample.UpdateFromStateVectors(pos, V_1, initial_orbit.referenceBody, UT);
                double theta_err_2 = AngleAboutAxis(exit_velocity, sample.getOrbitalVelocityAtUT(OrbitExtensions.NextTimeOfRadius(sample, UT, sample.referenceBody.sphereOfInfluence)), z);

                V_1 = final_vel * (Math.Cos(theta - dtheta) * x + Math.Sin(theta - dtheta) * y);
                sample.UpdateFromStateVectors(pos, V_1, initial_orbit.referenceBody, UT);
                double theta_err_3 = AngleAboutAxis(exit_velocity, sample.getOrbitalVelocityAtUT(OrbitExtensions.NextTimeOfRadius(sample, UT, sample.referenceBody.sphereOfInfluence)), z);

                double derr = MuUtils.ClampRadiansPi(theta_err_2 - theta_err_3) / (2 * dtheta);

                theta = MuUtils.ClampRadiansTwoPi(theta - theta_err / derr);

                // if theta > pi, replace with 2pi - theta, the function ejection_angle=f(theta) is symmetrc wrt theta=pi
                if (theta > Math.PI)
                {
                    theta = 2 * Math.PI - theta;
                }
            }

            return(null);
        }
Esempio n. 2
0
        static ManeuverParameters ComputeEjectionManeuver(Vector3d exit_velocity, Orbit initial_orbit, double UT_0)
        {
            double GM    = initial_orbit.referenceBody.gravParameter;
            double C3    = exit_velocity.sqrMagnitude;
            double sma   = -GM / C3;
            double Rpe   = initial_orbit.semiMajorAxis;
            double ecc   = 1 - Rpe / sma;
            double theta = Math.Acos(-1 / ecc);

            Vector3d intersect_1;
            Vector3d intersect_2;

            // intersect_1 is the optimal position on the orbit assuming the orbit is circular and the sphere of influence is infinite
            IntersectConePlane(exit_velocity, theta, initial_orbit.h, out intersect_1, out intersect_2);

            Vector3d eccvec = initial_orbit.GetEccVector();

            double true_anomaly = AngleAboutAxis(eccvec, intersect_1, initial_orbit.h);
            // GetMeanAnomalyAtTrueAnomaly expects degrees and returns radians
            double mean_anomaly = initial_orbit.GetMeanAnomalyAtTrueAnomaly(true_anomaly * 180 / Math.PI);

            double delta_mean_anomaly = MuUtils.ClampRadiansPi(mean_anomaly - initial_orbit.MeanAnomalyAtUT(UT_0));

            double UT = UT_0 + delta_mean_anomaly / initial_orbit.MeanMotion();

            Vector3d V_0 = initial_orbit.getOrbitalVelocityAtUT(UT);
            Vector3d pos = initial_orbit.getRelativePositionAtUT(UT);

            double final_vel = Math.Sqrt(C3 + 2 * GM / pos.magnitude);

            Vector3d x = pos.normalized;
            Vector3d z = Vector3d.Cross(x, exit_velocity).normalized;
            Vector3d y = Vector3d.Cross(z, x);

            theta = Math.PI / 2;
            double dtheta = 0.001;
            Orbit  sample = new Orbit();

            double theta_err = Double.MaxValue;

            for (int iteration = 0; iteration < 50; ++iteration)
            {
                Vector3d V_1 = final_vel * (Math.Cos(theta) * x + Math.Sin(theta) * y);
                sample.UpdateFromStateVectors(pos, V_1, initial_orbit.referenceBody, UT);
                theta_err = AngleAboutAxis(exit_velocity, sample.getOrbitalVelocityAtUT(OrbitExtensions.NextTimeOfRadius(sample, UT, sample.referenceBody.sphereOfInfluence)), z);
                if (double.IsNaN(theta_err))
                {
                    return(null);
                }
                if (Math.Abs(theta_err) <= Math.PI / 1800)
                {
                    return(new ManeuverParameters((V_1 - V_0).xzy, UT));
                }

                V_1 = final_vel * (Math.Cos(theta + dtheta) * x + Math.Sin(theta + dtheta) * y);
                sample.UpdateFromStateVectors(pos, V_1, initial_orbit.referenceBody, UT);
                double theta_err_2 = AngleAboutAxis(exit_velocity, sample.getOrbitalVelocityAtUT(OrbitExtensions.NextTimeOfRadius(sample, UT, sample.referenceBody.sphereOfInfluence)), z);

                V_1 = final_vel * (Math.Cos(theta - dtheta) * x + Math.Sin(theta - dtheta) * y);
                sample.UpdateFromStateVectors(pos, V_1, initial_orbit.referenceBody, UT);
                double theta_err_3 = AngleAboutAxis(exit_velocity, sample.getOrbitalVelocityAtUT(OrbitExtensions.NextTimeOfRadius(sample, UT, sample.referenceBody.sphereOfInfluence)), z);

                double derr = MuUtils.ClampRadiansPi(theta_err_2 - theta_err_3) / (2 * dtheta);

                theta = MuUtils.ClampRadiansTwoPi(theta - theta_err / derr);

                // if theta > pi, replace with 2pi - theta, the function ejection_angle=f(theta) is symmetrc wrt theta=pi
                if (theta > Math.PI)
                {
                    theta = 2 * Math.PI - theta;
                }
            }

            return(null);
        }