Пример #1
0
        public List <ManeuverParameters> OptimizeEjection(double UT_transfer, Orbit initial_orbit, Orbit target, CelestialBody target_body, double UT_arrival, double earliest_UT, double target_PeR, bool includeCaptureBurn)
        {
            int N = 0;

            List <ManeuverParameters> NodeList = new List <ManeuverParameters>();

            while (true)
            {
                const double DIFFSTEP = 1e-6;
                const double EPSX     = 1e-9;
                const int    MAXITS   = 100;

                alglib.minlmstate  state;
                alglib.minlmreport rep;

                double[] x     = new double[3];
                double[] scale = new double[3];

                Vector3d exitDV, captureDV;
                CalcLambertDVs(UT_transfer, UT_arrival - UT_transfer, out exitDV, out captureDV);

                Orbit source = initial_orbit.referenceBody.orbit; // helicentric orbit of the source planet

                // helicentric transfer orbit
                Orbit transfer_orbit = new Orbit();
                transfer_orbit.UpdateFromStateVectors(source.getRelativePositionAtUT(UT_transfer), source.getOrbitalVelocityAtUT(UT_transfer) + exitDV, source.referenceBody, UT_transfer);

                double UT_SOI_exit;
                OrbitalManeuverCalculator.SOI_intercept(transfer_orbit, initial_orbit.referenceBody, UT_transfer, UT_arrival, out UT_SOI_exit);

                // convert from heliocentric to body centered velocity
                Vector3d Vsoi = transfer_orbit.getOrbitalVelocityAtUT(UT_SOI_exit) - initial_orbit.referenceBody.orbit.getOrbitalVelocityAtUT(UT_SOI_exit);

                // find the magnitude of Vinf from energy
                double Vsoi_mag = Vsoi.magnitude;
                double E_h      = Vsoi_mag * Vsoi_mag / 2 - initial_orbit.referenceBody.gravParameter / initial_orbit.referenceBody.sphereOfInfluence;
                double Vinf_mag = Math.Sqrt(2 * E_h);

                // scale Vsoi by the Vinf magnitude (this is now the Vinf target that will yield Vsoi at the SOI interface, but in the Vsoi direction)
                Vector3d Vinf = Vsoi / Vsoi.magnitude * Vinf_mag;

                // using Vsoi seems to work slightly better here than the Vinf from the heliocentric computation at UT_Transfer
                //ManeuverParameters maneuver = ComputeEjectionManeuver(Vsoi, initial_orbit, UT_transfer, true);
                ManeuverParameters maneuver = ComputeEjectionManeuver(Vinf, initial_orbit, UT_transfer, true);

                //
                // common setup for the optimization problems
                //

                x[0]        = maneuver.dV.x;
                x[1]        = maneuver.dV.y;
                x[2]        = maneuver.dV.z;
                UT_transfer = maneuver.UT;

                scale[0] = scale[1] = scale[2] = 1000.0f;

                //
                // initial patched conic shooting to precisely hit the target
                //

                const int ZEROMISSCONS = 3;
                double[]  fi           = new double[ZEROMISSCONS];

                zeroMissObjectiveFunction(x, fi, null, initial_orbit, target_body, UT_transfer, UT_arrival);
                Debug.Log("zero miss phase before optimization = " + new Vector3d(fi[0], fi[1], fi[2]).magnitude + " m (" + new Vector3d(x[0], x[1], x[2]).magnitude + " m/s)");

                alglib.minlmcreatev(3, ZEROMISSCONS, x, DIFFSTEP, out state);
                alglib.minlmsetcond(state, EPSX, MAXITS);
                alglib.minlmsetscale(state, scale);
                alglib.minlmoptimize(state, (x, fi, obj) => zeroMissObjectiveFunction(x, fi, obj, initial_orbit, target_body, UT_transfer, UT_arrival), null, null);
                alglib.minlmresults(state, out x, out rep);

                zeroMissObjectiveFunction(x, fi, null, initial_orbit, target_body, UT_transfer, UT_arrival);
                Debug.Log("zero miss phase after optimization = " + new Vector3d(fi[0], fi[1], fi[2]).magnitude + " m (" + new Vector3d(x[0], x[1], x[2]).magnitude + " m/s)");

                Debug.Log("Transfer calculator: termination type=" + rep.terminationtype);
                Debug.Log("Transfer calculator: iteration count=" + rep.iterationscount);

                //
                // Fine tuning of the periapsis
                //

                bool failed = false;

                if (target_PeR > 0)
                {
                    const int PERIAPSISCONS = 1;
                    double[]  fi2           = new double[PERIAPSISCONS];

                    periapsisObjectiveFunction(x, fi2, null, initial_orbit, target_body, UT_transfer, UT_arrival, target_PeR, ref failed);
                    Debug.Log("periapsis phase before optimization = " + fi2[0] + " m (" + new Vector3d(x[0], x[1], x[2]).magnitude + " m/s)");

                    alglib.minlmcreatev(3, PERIAPSISCONS, x, DIFFSTEP, out state);
                    alglib.minlmsetcond(state, EPSX, MAXITS);
                    alglib.minlmsetscale(state, scale);
                    alglib.minlmoptimize(state, (x, fi, obj) => periapsisObjectiveFunction(x, fi, obj, initial_orbit, target_body, UT_transfer, UT_arrival, target_PeR, ref failed), null, null);
                    alglib.minlmresults(state, out x, out rep);

                    periapsisObjectiveFunction(x, fi2, null, initial_orbit, target_body, UT_transfer, UT_arrival, target_PeR, ref failed);
                    Debug.Log("periapsis phase after optimization = " + fi2[0] + " m (" + new Vector3d(x[0], x[1], x[2]).magnitude + " m/s)");

                    Debug.Log("Transfer calculator: termination type=" + rep.terminationtype);
                    Debug.Log("Transfer calculator: iteration count=" + rep.iterationscount);
                }

                maneuver.dV.x = x[0];
                maneuver.dV.y = x[1];
                maneuver.dV.z = x[2];

                //
                // exit conditions and error handling
                //

                // try again if we failed to intersect the target orbit
                if (failed)
                {
                    Debug.Log("Failed to intersect target orbit");
                }
                // try again in one orbit if the maneuver node is in the past
                else if (maneuver.UT < earliest_UT || failed)
                {
                    Debug.Log("Transfer calculator: maneuver is " + (earliest_UT - maneuver.UT) + " s too early, trying again in " + initial_orbit.period + " s");
                    UT_transfer += initial_orbit.period;
                }
                else
                {
                    Debug.Log("from optimizer DV = " + maneuver.dV + " t = " + maneuver.UT + " original arrival = " + UT_arrival);
                    NodeList.Add(maneuver);
                    break;
                }
                if (N++ > 10)
                {
                    throw new OperationException("Ejection Optimization failed; try manual selection");
                }
            }
            if (NodeList.Count > 0 && target_PeR > 0 && includeCaptureBurn)
            {
                // calculate the incoming orbit
                Orbit incoming_orbit;
                OrbitalManeuverCalculator.PatchedConicInterceptBody(initial_orbit, target_body, NodeList[0].dV, NodeList[0].UT, UT_arrival, out incoming_orbit);
                double burnUT = incoming_orbit.NextPeriapsisTime(incoming_orbit.StartUT);
                NodeList.Add(new ManeuverParameters(OrbitalManeuverCalculator.DeltaVToCircularize(incoming_orbit, burnUT), burnUT));
            }
            return(NodeList);
        }