void runSimulations() { if (TimeWarp.CurrentRate <= TimeWarp.MaxPhysicsRate && (nextSimulationDelayMs == 0 || nextSimulationTimer.ElapsedMilliseconds > nextSimulationDelayMs)) { Stopwatch s = Stopwatch.StartNew(); prediction = simulateReentryRK4(simulationTimeStep, Vector3d.zero); s.Stop(); nextSimulationDelayMs = 10 * s.ElapsedMilliseconds; nextSimulationTimer.Reset(); nextSimulationTimer.Start(); if (autoLandAtTarget && (landStep == LandStep.COURSE_CORRECTIONS || landStep == LandStep.ON_COURSE || landStep == LandStep.DECELERATING)) { if (!velocityCorrectionSet) simCounter = 0; if ((simCounter % 25) == 0) { if (courseCorrectionsAllowed()) { velAUnit = chooseDownrangeVelocityVector(); } else { velAUnit = vesselState.upNormalToVelSurface; } velBUnit = vesselState.leftSurface; } if ((simCounter % 5) == 0) computeVelocityCorrection(); } } }
//predict the reentry trajectory. uses the fourth order Runge-Kutta numerical integration scheme protected LandingPrediction simulateReentryRK4(Vector3d startPos, Vector3d startVel, double startTime, double dt, bool recurse) { LandingPrediction result = new LandingPrediction(); //should change this to also account for hyperbolic orbits where we have passed periapsis //should also change this to use the orbit giv en by the parameters if (part.vessel.orbit.PeA > part.vessel.mainBody.maxAtmosphereAltitude) { result.outcome = LandingPrediction.Outcome.NO_REENTRY; return result; } //use the known orbit in vacuum to find the position and velocity when we first hit the atmosphere //or start decelerating with thrust: AROrbit freefallTrajectory = new AROrbit(startPos, startVel, startTime, part.vessel.mainBody); double initialT = projectFreefallEndTime(freefallTrajectory, startTime); //a hack to detect improperly initialized stuff and not try to do the simulation if (double.IsNaN(initialT)) { result.outcome = LandingPrediction.Outcome.NO_REENTRY; return result; } Vector3d pos = freefallTrajectory.positionAtTime(initialT); Vector3d vel = freefallTrajectory.velocityAtTime(initialT); double initialAltitude = FlightGlobals.getAltitudeAtPos(pos); Vector3d initialPos = new Vector3d(pos.x, pos.y, pos.z); Vector3d initialVel = new Vector3d(vel.x, vel.y, vel.z); ; double dragCoeffOverMass = vesselState.massDrag / vesselState.mass; //now integrate the equations of motion until we hit the surface or we exit the atmosphere again: double t = initialT; result.maxGees = 0; bool beenInAtmosphere = false; while (true) { //one time step of RK4: Vector3d dv1 = dt * ARUtils.computeTotalAccel(pos, vel, dragCoeffOverMass, part.vessel.mainBody); Vector3d dx1 = dt * vel; Vector3d dv2 = dt * ARUtils.computeTotalAccel(pos + 0.5 * dx1, vel + 0.5 * dv1, dragCoeffOverMass, part.vessel.mainBody); Vector3d dx2 = dt * (vel + 0.5 * dv1); Vector3d dv3 = dt * ARUtils.computeTotalAccel(pos + 0.5 * dx2, vel + 0.5 * dv2, dragCoeffOverMass, part.vessel.mainBody); Vector3d dx3 = dt * (vel + 0.5 * dv2); Vector3d dv4 = dt * ARUtils.computeTotalAccel(pos + dx3, vel + dv3, dragCoeffOverMass, part.vessel.mainBody); Vector3d dx4 = dt * (vel + dv3); Vector3d dx = (dx1 + 2 * dx2 + 2 * dx3 + dx4) / 6.0; Vector3d dv = (dv1 + 2 * dv2 + 2 * dv3 + dv4) / 6.0; pos += dx; vel += dv; t += dt; double altitudeASL = FlightGlobals.getAltitudeAtPos(pos); if (altitudeASL < part.vessel.mainBody.maxAtmosphereAltitude) beenInAtmosphere = true; //We will thrust to reduce our speed if it is too high. However we have to //be aware that it's the *surface* frame speed that is controlled. Vector3d surfaceVel = vel - part.vessel.mainBody.getRFrmVel(pos); double maxSurfaceVel = decelerationSpeed(altitudeASL, vesselState.maxThrustAccel, part.vessel.mainBody); if (altitudeASL > decelerationEndAltitudeASL && surfaceVel.magnitude > maxSurfaceVel) { surfaceVel = maxSurfaceVel * surfaceVel.normalized; vel = surfaceVel + part.vessel.mainBody.getRFrmVel(pos); } //during reentry the gees you feel come from drag: double dragAccel = ARUtils.computeDragAccel(pos, vel, dragCoeffOverMass, part.vessel.mainBody).magnitude; result.maxGees = Math.Max(dragAccel / 9.81, result.maxGees); //detect landing if (altitudeASL < decelerationEndAltitudeASL) { result.outcome = LandingPrediction.Outcome.LANDED; result.landingLatitude = part.vessel.mainBody.GetLatitude(pos); result.landingLongitude = part.vessel.mainBody.GetLongitude(pos); result.landingLongitude -= (t - vesselState.time) * (360.0 / (part.vessel.mainBody.rotationPeriod)); //correct for planet rotation (360 degrees every 6 hours) result.landingLongitude = ARUtils.clampDegrees(result.landingLongitude); result.landingTime = t; return result; } //detect the end of an aerobraking pass if (beenInAtmosphere && part.vessel.mainBody.maxAtmosphereAltitude > 0 && altitudeASL > part.vessel.mainBody.maxAtmosphereAltitude + 1000 && Vector3d.Dot(vel, pos - part.vessel.mainBody.position) > 0) { if (part.vessel.orbit.PeA > 0 || !recurse) { result.outcome = LandingPrediction.Outcome.AEROBRAKED; result.aerobrakeApoapsis = ARUtils.computeApoapsis(pos, vel, part.vessel.mainBody); result.aerobrakePeriapsis = ARUtils.computePeriapsis(pos, vel, part.vessel.mainBody); return result; } else { //continue suborbital trajectories to a landing return simulateReentryRK4(pos, vel, t, dt, false); } } //Don't tie up the CPU by running forever. Some real reentry trajectories will time out, but that's //just too bad. It's possible to spend an arbitarily long time in the atmosphere before landing. //For example, a circular orbit just inside the edge of the atmosphere will decay //extremely slowly. So there's no reasonable timeout setting here that will simulate all valid reentry trajectories //to their conclusion. if (t > initialT + 1000) { result.outcome = LandingPrediction.Outcome.TIMED_OUT; print("MechJeb landing simulation timed out:"); print("current ship altitude ASL = " + vesselState.altitudeASL); print("freefall end altitude ASL = " + initialAltitude); print("freefall end position = " + initialPos); print("freefall end vel = " + initialVel); print("timeout position = " + pos); print("timeout altitude ASL = " + altitudeASL); return result; } } }
//programmatic interface to land at a specific target. public void landAt(double latitude, double longitude) { this.enabled = true; core.controlClaim(this); autoLandAtTarget = true; deorbitBurnFirstMessage = false; gaveUpOnCourseCorrections = false; targetLatitude = latitude; targetLongitude = longitude; initializeDecimalStrings(); initializeDMSStrings(); if (targetLatitude == BEACH_LATITUDE && targetLongitude == BEACH_LONGITUDE) dmsInput = false; core.landDeactivate(this); landStep = LandStep.COURSE_CORRECTIONS; FlightInputHandler.SetNeutralControls(); //run an initial landing simulation prediction = simulateReentryRK4(simulationTimeStep, Vector3d.zero); //initialize the state machine if (part.vessel.orbit.PeA < 0 || prediction.outcome == LandingPrediction.Outcome.LANDED) { landStep = LandStep.COURSE_CORRECTIONS; } else { landStep = LandStep.DEORBIT_BURN; deorbiting = false; } }