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); }