public override void Draw() { #if DEBUG if (ToOrbit != null) { Utils.GLVec(Body.position, ToOrbit.Target.xzy, Color.green); Utils.GLVec(Body.position, VesselOrbit.getRelativePositionAtUT(VSL.Physics.UT + VesselOrbit.timeToAp).xzy, Color.magenta); Utils.GLVec(Body.position, VesselOrbit.GetOrbitNormal().normalized.xzy *Body.Radius * 1.1, Color.cyan); Utils.GLVec(Body.position, Vector3d.Cross(VesselOrbit.pos, ToOrbit.Target).normalized.xzy *Body.Radius * 1.1, Color.red); } #endif if (stage == Stage.None) { if (Utils.ButtonSwitch("ToOrbit", ShowOptions, "Achieve a circular orbit with desired radius and inclination", GUILayout.ExpandWidth(true))) { toggle_orbit_editor(); } } else if (GUILayout.Button(new GUIContent("ToOrbit", "Change target orbit or abort"), Styles.danger_button, GUILayout.ExpandWidth(true))) { toggle_orbit_editor(); } }
public bool GravityTurn(double ApA_offset, double gturn_curve, double dist2vel, double Dtol) { UpdateTargetPosition(); VSL.Engines.ActivateNextStageOnFlameout(); dApA = TargetR-VesselOrbit.ApR; var vel = Vector3d.zero; var cApV = VesselOrbit.getRelativePositionAtUT(VSL.Physics.UT+VesselOrbit.timeToAp); var hv = Vector3d.Exclude(VesselOrbit.pos, target-VesselOrbit.pos).normalized; var arc = Utils.ProjectionAngle(cApV, target, hv)*Mathf.Deg2Rad*cApV.magnitude; ErrorThreshold.Value = CorrectOnlyAltitude? dApA : dApA+arc; ApoapsisReached |= dApA < Dtol; THR.CorrectThrottle = ApoapsisReached; if(!ErrorThreshold) { var startF = Utils.Clamp((VSL.Altitude.Absolute-GravityTurnStart)/GLB.ORB.GTurnOffset, 0, 1); if(dApA > Dtol) vel += CorrectOnlyAltitude && ApoapsisReached? VesselOrbit.vel.normalized.xzy*dApA : VSL.Physics.Up.xzy*dApA*gturn_curve; if(arc > Dtol && (!ApoapsisReached || !CorrectOnlyAltitude)) { var hvel = Utils.ClampL(arc-dApA*gturn_curve, 0)*startF; if(Body.atmosphere) hvel *= Math.Sqrt(Utils.Clamp(VSL.Altitude.Absolute/Body.atmosphereDepth, 0, 1)); vel += hv*hvel; } vel *= VSL.Physics.StG/Utils.G0*dist2vel; if(!vel.IsZero()) { var norm = VesselOrbit.GetOrbitNormal(); var dFi = (90-Vector3d.Angle(norm, target))*Mathf.Deg2Rad; vel += norm*Math.Sin(dFi)*vel.magnitude*startF *Utils.Clamp(VSL.VerticalSpeed.Absolute/VSL.Physics.G-MinClimbTime, 0, 100) *Utils.ClampL(Vector3d.Dot(hv, VesselOrbit.vel.normalized), 0); } vel = vel.xzy; CircularizationOffset = -1; if(Executor.Execute(vel, Utils.Clamp(1-VSL.Torque.MaxCurrent.AA_rad, 0.1f, 1))) { if(CFG.AT.Not(Attitude.KillRotation)) { CFG.BR.OnIfNot(BearingMode.Auto); BRC.ForwardDirection = hv.xzy; } Status("Gravity turn..."); return true; } } Status("Coasting..."); CFG.BR.OffIfOn(BearingMode.Auto); CFG.AT.OnIfNot(Attitude.KillRotation); THR.Throttle = 0; if(CircularizationOffset < 0) { ApAUT = VSL.Physics.UT+VesselOrbit.timeToAp; CircularizationOffset = VSL.Engines.TTB((float)TrajectoryCalculator.dV4C(VesselOrbit, hV(ApAUT), ApAUT).magnitude)/2; } return VesselOrbit.timeToAp > ApA_offset+CircularizationOffset && Body.atmosphere && VesselOrbit.radius < Body.Radius+Body.atmosphereDepth; }
protected Vector3d Node2OrbitDeltaV(Vector3d NodeDeltaV, double StartUT) { var norm = VesselOrbit.GetOrbitNormal().normalized; var prograde = hV(StartUT).normalized; var radial = Vector3d.Cross(prograde, norm).normalized; return(radial * NodeDeltaV.x + norm * NodeDeltaV.y + prograde * NodeDeltaV.z); }
Vector3d correct_dV(Vector3d dV, double UT) { var v = VesselOrbit.getOrbitalVelocityAtUT(UT); var nV = dV + v; return(QuaternionD.AngleAxis(-inclination_error(VesselOrbit.inclination), VesselOrbit.getRelativePositionAtUT(UT)) * nV - v); }
protected override void fine_tune_approach() { update_landing_trajecotry(); var V = VesselOrbit.getOrbitalVelocityAtUT(VSL.Physics.UT + CorrectionOffset).magnitude; ComputeTrajectory(new LandingSiteCorrector(this, V, LTRJ.Dtol / 2)); stage = Stage.CorrectTrajectory; trajectory = null; }
protected override void fine_tune_approach() { trajectory = null; stage = Stage.CorrectTrajectory; double angle = 0; double V = VesselOrbit.getOrbitalVelocityAtUT(VSL.Physics.UT + LTRJ.CorrectionOffset).magnitude; setup_calculation((o, b) => orbit_correction(o, b, ref angle, ref V, LTRJ.CorrectionOffset)); }
//Node: radial, normal, prograde protected Vector3d Orbit2NodeDeltaV(Vector3d OrbitDeltaV, double StartUT) { var norm = VesselOrbit.GetOrbitNormal().normalized; var prograde = hV(StartUT).normalized; var radial = Vector3d.Cross(prograde, norm).normalized; return(new Vector3d(Vector3d.Dot(OrbitDeltaV, radial), Vector3d.Dot(OrbitDeltaV, norm), Vector3d.Dot(OrbitDeltaV, prograde))); }
protected void update_state(float Dtol) { hvdir = Vector3d.Exclude(VesselOrbit.pos, VesselOrbit.vel).normalized; htdir = Vector3d.Exclude(VesselOrbit.pos, target - VesselOrbit.pos).normalized; ApV = VesselOrbit.getRelativePositionAtUT(VSL.Physics.UT + VesselOrbit.timeToAp); dApA = Utils.ClampL(TargetR - VesselOrbit.ApR, 0); dArc = Utils.ClampL(Utils.ProjectionAngle(ApV, target, htdir) * ToOrbitAutopilot.C.Dtol, 0); ErrorThreshold.Value = CorrectOnlyAltitude ? dApA : dApA + dArc; ApoapsisReached |= dApA < Dtol; CourseOnTarget = Vector3d.Dot(htdir, hvdir) > 0.1; }
void start_orbit() { ToOrbit = null; var dV = Vector3d.zero; var old = VesselOrbit; var StartUT = VSL.Physics.UT + CorrectionOffset; CFG.BR.OffIfOn(BearingMode.Auto); update_trajectory(); if (VesselOrbit.PeR < MinPeR) { StartUT = Math.Min(trajectory.AtTargetUT, VSL.Physics.UT + (VesselOrbit.ApAhead()? VesselOrbit.timeToAp : CorrectionOffset)); if (trajectory.DistanceToTarget < REN.ApproachThreshold * 2 && StartUT.Equals(trajectory.AtTargetUT)) { //approach is close enough to directly match orbits match_orbits(); return; } var transfer_time = Utils.ClampL(TargetOrbit.period * (0.25 - AngleDelta(VesselOrbit, TargetOrbit, StartUT) / 360), 1); var solver = new LambertSolver(VesselOrbit, TargetOrbit.getRelativePositionAtUT(StartUT + transfer_time), StartUT); dV = solver.dV4Transfer(transfer_time); var trj = new RendezvousTrajectory(VSL, dV, StartUT, CFG.Target, MinPeR, transfer_time); if (!dV.IsZero() && !trj.KillerOrbit) { //approach orbit is possible compute_start_orbit(StartUT); return; } //starting from circular orbit and proceeding to TTR fitting... StartUT = VesselOrbit.ApAhead()? VSL.Physics.UT + VesselOrbit.timeToAp : VSL.Physics.UT + CorrectionOffset; dV = dV4C(old, hV(StartUT), StartUT); old = NewOrbit(old, dV, StartUT); } else if (trajectory.RelDistanceToTarget < REN.CorrectionStart || TargetLoaded) { if (trajectory.RelDistanceToTarget > REN.CorrectionStart / 4 && !TargetLoaded) { fine_tune_approach(); } else { match_orbits(); } return; } //compute orbit with desired TTR and activate maneuver autopilot dV += dV4TTR(old, TargetOrbit, REN.MaxTTR, REN.MaxDeltaV, MinPeR, StartUT); if (!dV.IsZero()) { add_node(dV, StartUT); CFG.AP1.On(Autopilot1.Maneuver); } stage = Stage.StartOrbit; }
protected LandingTrajectory fixed_inclination_orbit(LandingTrajectory old, LandingTrajectory best, ref Vector3d dir, ref double V, float start_offset) { var StartUT = VSL.Physics.UT + start_offset; if (old != null) { V += old.DeltaR * (1 - CFG.Target.AngleTo(VSL) / Math.PI * 0.9) * Body.GeeASL; dir = Quaternion.AngleAxis((float)old.DeltaFi, VSL.Physics.Up.xzy) * dir; } return(new LandingTrajectory(VSL, dir * V - VesselOrbit.getOrbitalVelocityAtUT(StartUT), StartUT, CFG.Target, old == null? TargetAltitude : old.TargetAltitude)); }
protected LandingTrajectory orbit_correction(LandingTrajectory old, LandingTrajectory best, ref double angle, ref double V, float start_offset) { var StartUT = VSL.Physics.UT + start_offset; if (old != null) { V += old.DeltaR * (1 - CFG.Target.AngleTo(VSL) / Math.PI * 0.9) * Body.GeeASL; angle += old.DeltaFi; } var vel = VesselOrbit.getOrbitalVelocityAtUT(StartUT); return(new LandingTrajectory(VSL, QuaternionD.AngleAxis(angle, VSL.Physics.Up.xzy) * vel.normalized * V - vel, StartUT, CFG.Target, old == null? TargetAltitude : old.TargetAltitude)); }
protected Vector3d tune_needed_vel(Vector3d needed_vel, Vector3d pg_vel, double startF) { if (CourseOnTarget) { norm_correction.Update((float)(90 - Utils.Angle2(VesselOrbit.GetOrbitNormal(), target))); needed_vel = QuaternionD.AngleAxis(norm_correction.Action, VesselOrbit.pos) * needed_vel; } else { norm_correction.Update(0); } return(startF < 1 ? needed_vel.magnitude * Vector3d.Lerp(pg_vel.normalized, needed_vel.normalized, startF) : needed_vel); }
IEnumerator<YieldInstruction> compute_initial_eccentricity() { // Log("Calculating initial orbit eccentricity...");//debug var tPos = CFG.Target.RelOrbPos(Body); var UT = VSL.Physics.UT + AngleDelta(VesselOrbit, tPos, VSL.Physics.UT)/360*VesselOrbit.period; var vPos = VesselOrbit.getRelativePositionAtUT(UT); var vVel = VesselOrbit.getOrbitalVelocityAtUT(UT); var dir = Vector3d.Exclude(vPos, vVel); var ini_dV = dV4Pe(VesselOrbit, (Body.Radius+TargetAltitude)*0.999, UT); var trj = new LandingTrajectory(VSL, ini_dV, UT, CFG.Target, TargetAltitude); var atmo_curve = trj.GetAtmosphericCurve(5); var maxV = vVel.magnitude; var minV = 0.0; var dV = 0.0; dir = -dir.normalized; var in_plane = Math.Abs(90-Vector3.Angle(tPos, VesselOrbit.GetOrbitNormal())) < 5; // Log("in plane {}, ini dV {}\nini trj:\n{}", in_plane, ini_dV, trj);//debug yield return null; while(maxV-minV > 1) { dV = (maxV+minV)/2; trj = new LandingTrajectory(VSL, ini_dV+dir*dV, UT, CFG.Target, trj.TargetAltitude); atmo_curve = trj.GetAtmosphericCurve(5); // Log("dV: {} : {} : {} m/s\ntrj:\n{}", minV, dV, maxV, trj);//debug if(!trj.NotEnoughFuel && (in_plane || trj.DeltaR < 0) && (atmo_curve != null && (will_overheat(atmo_curve) || atmo_curve[atmo_curve.Count-1].DynamicPressure > DEO.MaxDynPressure) || !Body.atmosphere && trj.LandingAngle < DEO.MinLandingAngle)) minV = dV; else maxV = dV; yield return null; } currentEcc = trj.Orbit.eccentricity; dEcc = currentEcc/DEO.EccSteps; // Log("currentEcc: {}, dEcc {}", currentEcc, dEcc);//debug }
protected Vector3d hV(double UT) { return(VesselOrbit.hV(UT)); }
public void DeorbitCallback(Multiplexer.Command cmd) { switch (cmd) { case Multiplexer.Command.Resume: // Utils.Log("Resuming: stage {}, landing_stage {}, landing {}", stage, landing_stage, landing);//debug if (!check_patched_conics()) { return; } NeedRadarWhenMooving(); if (stage == Stage.None && !landing) { goto case Multiplexer.Command.On; } else if (VSL.HasManeuverNode) { CFG.AP1.OnIfNot(Autopilot1.Maneuver); } break; case Multiplexer.Command.On: reset(); if (!check_patched_conics()) { return; } if (!setup()) { CFG.AP2.Off(); return; } if (VesselOrbit.PeR < Body.Radius) { Status("red", "Already deorbiting. Trying to correct course and land."); fine_tune_approach(); } else { // Log("Calculating initial orbit eccentricity...");//debug var tPos = CFG.Target.RelOrbPos(Body); var UT = VSL.Physics.UT + AngleDelta(VesselOrbit, tPos, VSL.Physics.UT) / 360 * VesselOrbit.period; var vPos = VesselOrbit.getRelativePositionAtUT(UT); var dir = Vector3d.Exclude(vPos, tPos - vPos); var ini_orb = CircularOrbit(dir, UT); var ini_dV = ini_orb.getOrbitalVelocityAtUT(UT) - VesselOrbit.getOrbitalVelocityAtUT(UT) + dV4Pe(ini_orb, Body.Radius * 0.9, UT); var trj = new LandingTrajectory(VSL, ini_dV, UT, CFG.Target, TargetAltitude); var dV = 10.0; dir = -dir.normalized; while (trj.Orbit.eccentricity < DEO.StartEcc) { // Log("\ndV: {}m/s\nini trj:\n{}", dV, trj);//debug if (trj.DeltaR > 0) { break; } trj = new LandingTrajectory(VSL, ini_dV + dir * dV, UT, CFG.Target, trj.TargetAltitude); dV += 10; } if (trj.Orbit.eccentricity > DEO.StartEcc) { currentEcc = DEO.StartEcc; if (Body.atmosphere) { currentEcc = Utils.ClampH(currentEcc * (2.1 - Utils.ClampH(VSL.Torque.MaxPossible.AngularDragResistance / Body.atmDensityASL * DEO.AngularDragF, 1)), DEO.MaxEcc); } } else { currentEcc = Utils.ClampL(trj.Orbit.eccentricity - DEO.dEcc, DEO.dEcc); } // Log("currentEcc: {}, dEcc {}", currentEcc, DEO.dEcc);//debug if (Globals.Instance.AutosaveBeforeLanding) { Utils.SaveGame(VSL.vessel.vesselName.Replace(" ", "_") + "-before_landing"); } compute_landing_trajectory(); } goto case Multiplexer.Command.Resume; case Multiplexer.Command.Off: UnregisterFrom <Radar>(); reset(); break; } }
protected override void Update() { switch (stage) { case Stage.Start: if (VSL.LandedOrSplashed) { stage = Stage.Liftoff; } else { ToOrbit.StartGravityTurn(); stage = Stage.GravityTurn; } break; case Stage.Liftoff: if (ToOrbit.Liftoff()) { break; } stage = Stage.GravityTurn; break; case Stage.GravityTurn: update_inclination_limits(); var norm = VesselOrbit.GetOrbitNormal(); var ApV = VesselOrbit.getRelativePositionAtUT(VSL.Physics.UT + VesselOrbit.timeToAp); var arcT = AngleDelta(VesselOrbit, ToOrbit.Target); if (arcT > 0 && arcT < AngleDelta(VesselOrbit, ApV)) { ApV.Normalize(); var chord = ApV * VesselOrbit.radius - VesselOrbit.pos; var alpha = inclination_correction(VesselOrbit.inclination, chord.magnitude); ToOrbit.Target = QuaternionD.AngleAxis(alpha, Vector3d.Cross(norm, ApV)) * ApV * ToOrbit.TargetR; } else { var n = Vector3d.Cross(VesselOrbit.pos, ToOrbit.Target.normalized); var inclination = Math.Acos(n.z / n.magnitude) * Mathf.Rad2Deg; var chord = Vector3d.Exclude(norm, ToOrbit.Target).normalized *VesselOrbit.radius - VesselOrbit.pos; var alpha = inclination_correction(inclination, chord.magnitude); ToOrbit.Target = QuaternionD.AngleAxis(alpha, Vector3d.Cross(norm, VesselOrbit.pos)) * ToOrbit.Target; } if (ToOrbit.GravityTurn(ManeuverOffset, ORB.GTurnCurve, ORB.Dist2VelF, ORB.Dtol)) { break; } CFG.BR.OffIfOn(BearingMode.Auto); var ApAUT = VSL.Physics.UT + VesselOrbit.timeToAp; if (ApR > MinPeR + ORB.RadiusOffset) { change_ApR(ApAUT); } else { circularize(ApAUT); } break; case Stage.ChangeApA: TmpStatus("Achieving target apoapsis..."); if (CFG.AP1[Autopilot1.Maneuver]) { break; } circularize(VSL.Physics.UT + VesselOrbit.timeToAp); stage = Stage.Circularize; break; case Stage.Circularize: TmpStatus("Circularization..."); if (CFG.AP1[Autopilot1.Maneuver]) { break; } Disable(); ClearStatus(); break; } }
protected override void Update() { switch (stage) { case Stage.Start: if (VSL.LandedOrSplashed || VSL.VerticalSpeed.Absolute < 5) { stage = Stage.Liftoff; } else { ToOrbit.StartGravityTurn(); stage = Stage.GravityTurn; } break; case Stage.Liftoff: if (ToOrbit.Liftoff()) { break; } stage = Stage.GravityTurn; break; case Stage.GravityTurn: update_inclination_limits(); var norm = VesselOrbit.GetOrbitNormal(); var needed_norm = Vector3d.Cross(VesselOrbit.pos, ToOrbit.Target.normalized); var norm2norm = Math.Abs(Utils.Angle2(norm, needed_norm) - 90); if (norm2norm > 60) { var ApV = VesselOrbit.getRelativePositionAtUT(VSL.Physics.UT + VesselOrbit.timeToAp); var arcApA = AngleDelta(VesselOrbit, ApV); var arcT = AngleDelta(VesselOrbit, ToOrbit.Target); if (arcT > 0 && arcT < arcApA) { ApV.Normalize(); var chord = ApV * VesselOrbit.radius - VesselOrbit.pos; var alpha = inclination_correction(VesselOrbit.inclination, chord.magnitude); var axis = Vector3d.Cross(norm, ApV); ToOrbit.Target = QuaternionD.AngleAxis(alpha, axis) * ApV * ToOrbit.TargetR; } else { var inclination = Math.Acos(needed_norm.z / needed_norm.magnitude) * Mathf.Rad2Deg; var chord = Vector3d.Exclude(norm, ToOrbit.Target).normalized *VesselOrbit.radius - VesselOrbit.pos; var alpha = inclination_correction(inclination, chord.magnitude); var axis = Vector3d.Cross(norm, VesselOrbit.pos.normalized); if (arcT < 0) { alpha = -alpha; } ToOrbit.Target = QuaternionD.AngleAxis(alpha, axis) * ToOrbit.Target; } } if (ToOrbit.GravityTurn(C.Dtol)) { break; } CFG.BR.OffIfOn(BearingMode.Auto); var ApAUT = VSL.Physics.UT + VesselOrbit.timeToAp; if (ApR > ToOrbit.MaxApR) { change_ApR(ApAUT); } else { circularize(ApAUT); } break; case Stage.ChangeApA: TmpStatus("Achieving target apoapsis..."); if (CFG.AP1[Autopilot1.Maneuver]) { break; } circularize(VSL.Physics.UT + VesselOrbit.timeToAp); stage = Stage.Circularize; break; case Stage.Circularize: TmpStatus("Circularization..."); if (CFG.AP1[Autopilot1.Maneuver]) { break; } Disable(); ClearStatus(); break; } }
protected override void Update() { switch (stage) { case Stage.Start: if (VSL.LandedOrSplashed || VSL.VerticalSpeed.Absolute < 5) { stage = Stage.Liftoff; } else { ToOrbit.StartGravityTurn(); inclinationPID.Reset(); stage = Stage.GravityTurn; } break; case Stage.Liftoff: if (ToOrbit.Liftoff()) { break; } inclinationPID.Reset(); stage = Stage.GravityTurn; break; case Stage.GravityTurn: update_inclination_limits(); var orbitNormal = VesselOrbit.GetOrbitNormal(); var targetNormalized = ToOrbit.Target.normalized; var vsl2TargetNormal = Vector3d.Cross(VesselOrbit.pos, targetNormalized); var norm2norm = Math.Abs(Utils.Angle2(orbitNormal, vsl2TargetNormal) - 90); if (norm2norm > 60) { var ApV = VesselOrbit.getRelativePositionAtUT(VSL.Physics.UT + VesselOrbit.timeToAp); var arcApA = Utils.Angle2(VesselOrbit.pos, ApV); var arcApAT = Utils.Angle2(ApV, ToOrbit.Target); if (arcApAT < arcApA) { ToOrbit.Target = QuaternionD.AngleAxis(arcApA - arcApAT, vsl2TargetNormal) * ToOrbit.Target; } var inclination = Math.Acos(vsl2TargetNormal.z / vsl2TargetNormal.magnitude) * Mathf.Rad2Deg; inclinationPID.Update((float)inclination_error(inclination)); var axis = Vector3d.Cross(vsl2TargetNormal, targetNormalized); ToOrbit.Target = QuaternionD.AngleAxis(inclinationPID.Action, axis) * ToOrbit.Target; } if (ToOrbit.GravityTurn(C.Dtol)) { break; } CFG.BR.OffIfOn(BearingMode.Auto); var ApAUT = VSL.Physics.UT + VesselOrbit.timeToAp; if (ApR > ToOrbit.MaxApR) { change_ApR(ApAUT); } else { circularize(ApAUT); } break; case Stage.ChangeApA: TmpStatus("Achieving target apoapsis..."); if (CFG.AP1[Autopilot1.Maneuver]) { break; } circularize(VSL.Physics.UT + VesselOrbit.timeToAp); stage = Stage.Circularize; break; case Stage.Circularize: TmpStatus("Circularization..."); if (CFG.AP1[Autopilot1.Maneuver]) { break; } Disable(); ClearStatus(); break; } }
protected bool do_land() { if (VSL.LandedOrSplashed) { THR.Throttle = 0; SetTarget(); ClearStatus(); CFG.AP2.Off(); return(true); } VSL.Engines.ActivateEngines(); if (VSL.Engines.MaxThrustM.Equals(0) && !VSL.Engines.HaveNextStageEngines) { landing_stage = LandingStage.HardLanding; } landing_deadzone = VSL.Geometry.D + CFG.Target.AbsRadius; if (VSL.vessel.dynamicPressurekPa > 0) { if (!dP_up_timer.RunIf(VSL.Controls.AttitudeError > last_Err || Mathf.Abs(VSL.Controls.AttitudeError - last_Err) < 0.01f)) { dP_down_timer.RunIf(VSL.Controls.AttitudeError < last_Err && VSL.vessel.dynamicPressurekPa < last_dP); } } else { dP_threshold = LTRJ.MaxDPressure; } rel_dP = VSL.vessel.dynamicPressurekPa / dP_threshold; last_Err = VSL.Controls.AttitudeError; float rel_Ve; double terminal_velocity; Vector3d brake_pos, brake_vel, obt_vel; switch (landing_stage) { case LandingStage.Wait: Status("Preparing for deceleration..."); THR.Throttle = 0; update_trajectory(); nose_to_target(); rel_altitude_if_needed(); obt_vel = VesselOrbit.getOrbitalVelocityAtUT(trajectory.BrakeStartUT); brake_pos = VesselOrbit.getRelativePositionAtUT(trajectory.BrakeStartUT); brake_vel = corrected_brake_velocity(obt_vel, brake_pos); brake_vel = corrected_brake_direction(brake_vel, brake_pos.xzy); CFG.AT.OnIfNot(Attitude.Custom); ATC.SetThrustDirW(brake_vel); VSL.Info.Countdown = trajectory.BrakeEndUT - VSL.Physics.UT - 1 - Math.Max(MatchVelocityAutopilot.BrakingOffset((float)obt_vel.magnitude, VSL, out VSL.Info.TTB), LTRJ.MinBrakeOffset * (1 - Utils.ClampH(Body.atmDensityASL, 1))); correct_attitude_with_thrusters(VSL.Torque.MaxPossible.MinRotationTime(VSL.Controls.AttitudeError)); if (obstacle_ahead(trajectory) > 0) { decelerate(true); break; } if (VSL.Info.Countdown <= rel_dP) { decelerate(false); break; } if (VSL.Controls.CanWarp) { VSL.Controls.WarpToTime = VSL.Physics.UT + VSL.Info.Countdown; } else { VSL.Controls.StopWarp(); } break; case LandingStage.Decelerate: rel_altitude_if_needed(); update_trajectory(); nose_to_target(); if (Working) { Status("red", "Possible collision detected."); correct_attitude_with_thrusters(VSL.Torque.MaxPossible.MinRotationTime(VSL.Controls.AttitudeError)); Executor.Execute(VSL.Physics.Up * 10); if (obstacle_ahead(trajectory) > 0) { CollisionTimer.Reset(); break; } if (!CollisionTimer.TimePassed) { break; } start_landing(); break; } else { Status("white", "Decelerating. Landing site error: {0}", Utils.formatBigValue((float)trajectory.DistanceToTarget, "m")); compute_terminal_velocity(); if (VSL.Controls.HaveControlAuthority) { DecelerationTimer.Reset(); } if (Vector3d.Dot(VSL.HorizontalSpeed.Vector, CFG.Target.WorldPos(Body) - VSL.Physics.wCoM) < 0) { if (Executor.Execute(-VSL.vessel.srf_velocity, LTRJ.BrakeEndSpeed)) { break; } } else if (!DecelerationTimer.TimePassed && trajectory.DistanceToTarget > landing_deadzone && Vector3d.Dot(CFG.Target.VectorTo(trajectory.SurfacePoint, Body), VSL.HorizontalSpeed.Vector) > 0) { brake_vel = corrected_brake_velocity(VesselOrbit.vel, VesselOrbit.pos); brake_vel = corrected_brake_direction(brake_vel, VesselOrbit.pos.xzy); //this is nice smoothing, but is dangerous on a low decending trajectory VSL.Info.TTB = VSL.Engines.TTB((float)VSL.vessel.srfSpeed); if (VSL.Info.Countdown - VSL.Torque.MaxCurrent.TurnTime - VSL.vessel.dynamicPressurekPa > VSL.Info.TTB) { brake_vel = brake_vel.normalized * VSL.HorizontalSpeed.Absolute * Utils.ClampH(trajectory.DistanceToTarget / CFG.Target.DistanceTo(VSL.vessel), 1); } if (Executor.Execute(-brake_vel, (float)Utils.ClampL(LTRJ.BrakeEndSpeed * Body.GeeASL, GLB.THR.MinDeltaV))) { break; } } } landing_stage = LandingStage.Coast; break; case LandingStage.Coast: Status("white", "Coasting. Landing site error: {0}", Utils.formatBigValue((float)trajectory.DistanceToTarget, "m")); THR.Throttle = 0; update_trajectory(); nose_to_target(); setup_for_deceleration(); terminal_velocity = compute_terminal_velocity(); correct_attitude_with_thrusters(VSL.Torque.MaxPossible.MinRotationTime(VSL.Controls.AttitudeError)); correct_landing_site(); VSL.Info.TTB = VSL.Engines.TTB((float)VSL.vessel.srfSpeed); VSL.Info.Countdown -= Math.Max(VSL.Info.TTB + VSL.Torque.NoEngines.TurnTime + VSL.vessel.dynamicPressurekPa, TRJ.ManeuverOffset); if (VSL.Info.Countdown <= 0) { Working = false; rel_Ve = VSL.Engines.RelVeASL; if (rel_Ve <= 0) { Message(10, "Not enough thrust to land properly.\nPerforming emergency landing..."); landing_stage = LandingStage.HardLanding; break; } if (!(VSL.Controls.HaveControlAuthority || VSL.Torque.HavePotentialControlAuthority)) { Message(10, "Lacking control authority to land properly.\nPerforming emergency landing..."); landing_stage = LandingStage.HardLanding; break; } var fuel_left = VSL.Engines.GetAvailableFuelMass(); var fuel_needed = VSL.Engines.FuelNeeded((float)terminal_velocity, rel_Ve); if (!CheatOptions.InfinitePropellant && (fuel_needed >= fuel_left || VSL.Engines.MaxHoverTimeASL(fuel_left - fuel_needed) < LTRJ.HoverTimeThreshold)) { Message(10, "Not enough fuel to land properly.\nPerforming emergency landing..."); landing_stage = LandingStage.HardLanding; break; } landing_stage = LandingStage.SoftLanding; } break; case LandingStage.HardLanding: Status("yellow", "Emergency Landing..."); set_destination_vector(); update_trajectory(); VSL.BrakesOn(); CFG.BR.Off(); setup_for_deceleration(); terminal_velocity = compute_terminal_velocity(); if (VSL.Engines.MaxThrustM > 0 && (VSL.Controls.HaveControlAuthority || VSL.Torque.HavePotentialControlAuthority)) { rel_Ve = VSL.Engines.RelVeASL; var fuel_left = VSL.Engines.GetAvailableFuelMass(); var fuel_needed = rel_Ve > 0? VSL.Engines.FuelNeeded((float)terminal_velocity, rel_Ve) : fuel_left * 2; VSL.Info.Countdown -= fuel_left > fuel_needed? VSL.Engines.TTB((float)terminal_velocity) : VSL.Engines.TTB(VSL.Engines.DeltaV(fuel_left)); if ((VSL.Info.Countdown < 0 && (!VSL.OnPlanetParams.HaveParachutes || VSL.OnPlanetParams.ParachutesActive && VSL.OnPlanetParams.ParachutesDeployed))) { THR.Throttle = VSL.VerticalSpeed.Absolute < -5? 1 : VSL.OnPlanetParams.GeeVSF; } else { THR.Throttle = 0; } Status("yellow", "Not enough fuel to land properly.\nWill deceletate as much as possible before impact..."); } if (Body.atmosphere && VSL.OnPlanetParams.HaveUsableParachutes) { VSL.OnPlanetParams.ActivateParachutes(); if (!VSL.OnPlanetParams.ParachutesActive) { ATC.SetCustomRotationW(VSL.Geometry.MaxAreaDirection, VSL.Physics.Up); StageTimer.RunIf(Body.atmosphere && //!VSL.Controls.HaveControlAuthority && VSL.vessel.currentStage - 1 > VSL.OnPlanetParams.NearestParachuteStage && VSL.vessel.dynamicPressurekPa > LTRJ.DropBallastThreshold * PressureASL && VSL.vessel.mach > LTRJ.MachThreshold); if (CFG.AutoParachutes) { Status("yellow", "Waiting for safe speed to deploy parachutes.\n" + "Trying to decelerate using drag..."); } else { Status("red", "Automatic parachute deployment is disabled.\nActivate parachutes manually when needed."); } } } if (!VSL.OnPlanetParams.HaveParachutes && !VSL.Engines.HaveNextStageEngines && (VSL.Engines.MaxThrustM.Equals(0) || !VSL.Controls.HaveControlAuthority)) { if (Body.atmosphere) { ATC.SetCustomRotationW(VSL.Geometry.MaxAreaDirection, VSL.Physics.Up); } Status("red", "Crash is imminent.\nImpact speed: {0}", Utils.formatBigValue((float)terminal_velocity, "m/s")); } break; case LandingStage.SoftLanding: THR.Throttle = 0; set_destination_vector(); update_trajectory(); setup_for_deceleration(); compute_terminal_velocity(); nose_to_target(); if (Working) { Status("white", "Final deceleration. Landing site error: {0}", Utils.formatBigValue((float)trajectory.DistanceToTarget, "m")); ATC.SetThrustDirW(correction_direction()); if (VSL.Altitude.Relative > GLB.LND.WideCheckAltitude) { var brake_spd = Mathf.Max(VSL.HorizontalSpeed.Absolute, -VSL.VerticalSpeed.Absolute); var min_thrust = Utils.Clamp(brake_spd / (VSL.Engines.MaxAccel - VSL.Physics.G) / Utils.ClampL((float)VSL.Info.Countdown, 0.01f), VSL.OnPlanetParams.GeeVSF, 1); THR.Throttle = Utils.Clamp(brake_spd / LTRJ.BrakeThrustThreshod, min_thrust, 1); } else { THR.Throttle = 1; } if (VSL.vessel.srfSpeed > LTRJ.BrakeEndSpeed) { Working = THR.Throttle.Equals(1) || VSL.Info.Countdown < 10; break; } } else { var turn_time = VSL.Torque.MaxPossible.MinRotationTime(VSL.Controls.AttitudeError); correct_attitude_with_thrusters(turn_time); correct_landing_site(); VSL.Info.TTB = VSL.Engines.TTB((float)VSL.vessel.srfSpeed); VSL.Info.Countdown -= VSL.Info.TTB + turn_time + LTRJ.FinalBrakeOffset; if (VSL.Controls.InvAlignmentFactor > 0.5) { Status("white", "Final deceleration: correcting attitude.\nLanding site error: {0}", Utils.formatBigValue((float)trajectory.DistanceToTarget, "m")); } else { Status("white", "Final deceleration: waiting for the burn.\nLanding site error: {0}", Utils.formatBigValue((float)trajectory.DistanceToTarget, "m")); } Working = VSL.Info.Countdown <= 0 || VSL.vessel.srfSpeed < LTRJ.BrakeEndSpeed; break; } THR.Throttle = 0; if (CFG.Target.DistanceTo(VSL.vessel) - VSL.Geometry.R > LTRJ.Dtol) { approach(); } else { land(); } break; case LandingStage.Approach: Status("Approaching the target..."); set_destination_vector(); if (!CFG.Nav[Navigation.GoToTarget]) { land(); } break; case LandingStage.Land: set_destination_vector(); break; } return(false); }
protected override void Update() { if (!IsActive) { CFG.AP2.OffIfOn(Autopilot2.BallisticJump); return; } if (landing) { do_land(); return; } switch (stage) { case Stage.Start: CFG.HF.OnIfNot(HFlight.Stop); CFG.AltitudeAboveTerrain = true; CFG.VF.OnIfNot(VFlight.AltitudeControl); if (VSL.LandedOrSplashed || VSL.Altitude.Relative < StartAltitude) { CFG.DesiredAltitude = StartAltitude; } else { CFG.DesiredAltitude = VSL.Altitude.Relative; } if (VSL.Altitude.Relative > StartAltitude - 5) { compute_initial_trajectory(); } else { Status("Gaining initial altitude..."); } break; case Stage.Compute: if (!trajectory_computed()) { break; } var obst = obstacle_ahead(trajectory); if (obst > 0) { StartAltitude += (float)obst + BJ.ObstacleOffset; stage = Stage.Start; } else if (check_initial_trajectory()) { accelerate(); } else { stage = Stage.Wait; } break; case Stage.Wait: if (!string.IsNullOrEmpty(TCAGui.StatusMessage)) { break; } accelerate(); break; case Stage.Accelerate: if (!VSL.HasManeuverNode) { CFG.AP2.Off(); break; } if (VSL.Controls.AlignmentFactor > 0.9 || !CFG.VSCIsActive) { CFG.DisableVSC(); if (!Executor.Execute(VSL.FirstManeuverNode.GetBurnVector(VesselOrbit), 10)) { fine_tune_approach(); } Status("white", "Accelerating..."); } break; case Stage.CorrectTrajectory: if (!trajectory_computed()) { break; } add_correction_node_if_needed(); stage = Stage.Coast; break; case Stage.Coast: var ap_ahead = VesselOrbit.ApAhead(); if (CFG.AP1[Autopilot1.Maneuver]) { if (ap_ahead) { Status("Correcting trajectory..."); break; } } Status("Coasting..."); if (ap_ahead && !correct_trajectory()) { break; } stage = Stage.None; start_landing(); break; } }
protected bool do_land() { if (VSL.LandedOrSplashed) { stop_aerobraking(); THR.Throttle = 0; SetTarget(); ClearStatus(); CFG.AP2.Off(); return(true); } update_trajectory(); VSL.Engines.ActivateEngines(); NoEnginesTimer.RunIf(VSL.Engines.MaxThrustM.Equals(0) && !VSL.Engines.HaveNextStageEngines); landing_deadzone = VSL.Geometry.D + CFG.Target.AbsRadius; if (VSL.vessel.dynamicPressurekPa > 0) { if (!dP_up_timer.RunIf(VSL.Controls.AttitudeError > last_Err || Mathf.Abs(VSL.Controls.AttitudeError - last_Err) < 0.01f)) { dP_down_timer.RunIf(VSL.Controls.AttitudeError < last_Err && VSL.vessel.dynamicPressurekPa < last_dP); } } else { dP_threshold = LTRJ.MaxDPressure; } rel_dP = VSL.vessel.dynamicPressurekPa / dP_threshold; last_Err = VSL.Controls.AttitudeError; float rel_Ve; Vector3d brake_pos, brake_vel, obt_vel; var vessel_within_range = CFG.Target.DistanceTo(VSL.vessel) < LTRJ.Dtol; var vessel_after_target = Vector3.Dot(VSL.HorizontalSpeed.Vector, CFG.Target.VectorTo(VSL.vessel)) >= 0; var target_within_range = trajectory.DistanceToTarget < LTRJ.Dtol; var landing_before_target = trajectory.DeltaR > 0; var terminal_velocity = compute_terminal_velocity();; switch (landing_stage) { case LandingStage.Wait: Status("Preparing for deceleration..."); THR.Throttle = 0; nose_to_target(); rel_altitude_if_needed(); obt_vel = VesselOrbit.getOrbitalVelocityAtUT(trajectory.BrakeStartUT); brake_pos = VesselOrbit.getRelativePositionAtUT(trajectory.BrakeStartUT); brake_vel = corrected_brake_velocity(obt_vel, brake_pos); brake_vel = corrected_brake_direction(brake_vel, brake_pos.xzy); CFG.AT.OnIfNot(Attitude.Custom); ATC.SetThrustDirW(brake_vel); var offset = MatchVelocityAutopilot.BrakingOffset((float)obt_vel.magnitude, VSL, out VSL.Info.TTB); offset = Mathf.Lerp(VSL.Info.TTB, offset, Utils.Clamp(VSL.Engines.TMR - 0.1f, 0, 1)); VSL.Info.Countdown = trajectory.BrakeEndUT - VSL.Physics.UT - 1 - Math.Max(offset, LTRJ.MinBrakeOffset * (1 - Utils.ClampH(Body.atmDensityASL, 1))); correct_attitude_with_thrusters(VSL.Torque.MaxPossible.MinRotationTime(VSL.Controls.AttitudeError)); if (obstacle_ahead(trajectory) > 0) { decelerate(true); break; } if (VSL.Info.Countdown <= rel_dP || will_overheat(trajectory.GetAtmosphericCurve(5, VSL.Physics.UT + TRJ.ManeuverOffset))) { decelerate(false); break; } if (VSL.Controls.CanWarp) { VSL.Controls.WarpToTime = VSL.Physics.UT + VSL.Info.Countdown; } else { VSL.Controls.StopWarp(); } if (CorrectTarget && VSL.Info.Countdown < CorrectionOffset) { scan_for_landing_site(); } break; case LandingStage.Decelerate: rel_altitude_if_needed(); CFG.BR.Off(); if (Working) { Status("red", "Possible collision detected."); correct_attitude_with_thrusters(VSL.Torque.MaxPossible.MinRotationTime(VSL.Controls.AttitudeError)); Executor.Execute(VSL.Physics.Up * 10); if (obstacle_ahead(trajectory) > 0) { CollisionTimer.Reset(); break; } if (!CollisionTimer.TimePassed) { break; } start_landing(); break; } else { Status("white", "Decelerating. Landing site error: {0}", Utils.formatBigValue((float)trajectory.DistanceToTarget, "m")); if (CorrectTarget) { scan_for_landing_site(); } do_aerobraking_if_requested(); if (VSL.Controls.HaveControlAuthority) { DecelerationTimer.Reset(); } if (Vector3d.Dot(VSL.HorizontalSpeed.Vector, CFG.Target.WorldPos(Body) - VSL.Physics.wCoM) < 0) { if (Executor.Execute(-VSL.vessel.srf_velocity, LTRJ.BrakeEndSpeed)) { break; } } else if (!DecelerationTimer.TimePassed && trajectory.DistanceToTarget > landing_deadzone && Vector3d.Dot(CFG.Target.VectorTo(trajectory.SurfacePoint, Body), VSL.HorizontalSpeed.Vector) > 0) { THR.Throttle = 0; brake_vel = corrected_brake_velocity(VesselOrbit.vel, VesselOrbit.pos); brake_vel = corrected_brake_direction(brake_vel, VesselOrbit.pos.xzy); VSL.Info.TTB = VSL.Engines.TTB((float)VSL.vessel.srfSpeed); var aerobraking = rel_dP > 0 && VSL.OnPlanetParams.ParachutesActive; if (!vessel_after_target) { var overheating = rel_dP > 0 && VSL.vessel.Parts.Any(p => p.temperature / p.maxTemp > PhysicsGlobals.TemperatureGaugeThreshold || p.skinTemperature / p.skinMaxTemp > PhysicsGlobals.TemperatureGaugeThreshold); if (!overheating) { ATC.SetThrustDirW(brake_vel); THR.Throttle = CFG.Target.DistanceTo(VSL.vessel) > trajectory.DistanceToTarget? (float)Utils.ClampH(trajectory.DistanceToTarget / LTRJ.Dtol / 2, 1) : 1; } else { THR.Throttle = 1; } } if (THR.Throttle > 0 || aerobraking) { break; } } } if (landing_before_target || target_within_range && !vessel_within_range) { stop_aerobraking(); } landing_stage = LandingStage.Coast; break; case LandingStage.Coast: Status("white", "Coasting. Landing site error: {0}", Utils.formatBigValue((float)trajectory.DistanceToTarget, "m")); THR.Throttle = 0; nose_to_target(); setup_for_deceleration(); if (correct_landing_site()) { correct_attitude_with_thrusters(VSL.Torque.MaxPossible.MinRotationTime(VSL.Controls.AttitudeError)); } if (landing_before_target || target_within_range && !vessel_within_range) { stop_aerobraking(); } VSL.Info.TTB = VSL.Engines.TTB((float)VSL.vessel.srfSpeed); VSL.Info.Countdown -= Math.Max(VSL.Info.TTB + VSL.Torque.NoEngines.TurnTime + VSL.vessel.dynamicPressurekPa, ManeuverOffset); if (VSL.Info.Countdown > 0) { if (THR.Throttle.Equals(0)) { warp_to_coundown(); } } else { Working = false; rel_Ve = VSL.Engines.RelVeASL; if (rel_Ve <= 0) { Message(10, "Not enough thrust for powered landing.\nPerforming emergency landing..."); landing_stage = LandingStage.HardLanding; break; } if (!(VSL.Controls.HaveControlAuthority || VSL.Torque.HavePotentialControlAuthority)) { Message(10, "Lacking control authority to land properly.\nPerforming emergency landing..."); landing_stage = LandingStage.HardLanding; break; } var fuel_left = VSL.Engines.GetAvailableFuelMass(); var fuel_needed = VSL.Engines.FuelNeeded((float)terminal_velocity, rel_Ve); var needed_hover_time = LandASAP? LTRJ.HoverTimeThreshold / 5 : LTRJ.HoverTimeThreshold; if (!CheatOptions.InfinitePropellant && (fuel_needed >= fuel_left || VSL.Engines.MaxHoverTimeASL(fuel_left - fuel_needed) < needed_hover_time)) { Message(10, "Not enough fuel for powered landing.\nPerforming emergency landing..."); landing_stage = LandingStage.HardLanding; // Log("Hard Landing. Trajectory:\n{}", trajectory);//debug break; } landing_stage = LandingStage.SoftLanding; // Log("Soft Landing. Trajectory:\n{}", trajectory);//debug } break; case LandingStage.HardLanding: Status("yellow", VSL.OnPlanetParams.ParachutesActive? "Landing on parachutes..." : "Emergency Landing..."); set_destination_vector(); CFG.BR.Off(); var not_too_hot = VSL.vessel.externalTemperature < VSL.Physics.MinMaxTemperature; if (not_too_hot) { setup_for_deceleration(); } if (VSL.Engines.MaxThrustM > 0 && (VSL.Controls.HaveControlAuthority || VSL.Torque.HavePotentialControlAuthority)) { rel_Ve = VSL.Engines.RelVeASL; var fuel_left = VSL.Engines.GetAvailableFuelMass(); var fuel_needed = rel_Ve > 0? VSL.Engines.FuelNeeded((float)terminal_velocity, rel_Ve) : fuel_left * 2; VSL.Info.Countdown -= fuel_left > fuel_needed? VSL.Engines.TTB((float)terminal_velocity) : VSL.Engines.TTB(VSL.Engines.DeltaV(fuel_left)); if ((VSL.Info.Countdown < 0 && (!VSL.OnPlanetParams.HaveParachutes || VSL.OnPlanetParams.ParachutesActive && VSL.OnPlanetParams.ParachutesDeployed))) { THR.Throttle = VSL.VerticalSpeed.Absolute < -5? 1 : VSL.OnPlanetParams.GeeVSF; } else if (VSL.Info.Countdown > 0.5f) { THR.Throttle = 0; } Status("yellow", "Not enough fuel for powered landing.\nWill deceletate as much as possible before impact."); } if (Body.atmosphere && VSL.OnPlanetParams.HaveUsableParachutes) { if (vessel_within_range || vessel_after_target || trajectory.BrakeEndUT - VSL.Physics.UT < LTRJ.ParachutesDeployOffset) { VSL.OnPlanetParams.ActivateParachutesASAP(); } else { VSL.OnPlanetParams.ActivateParachutesBeforeUnsafe(); } if (!VSL.OnPlanetParams.ParachutesActive) { //don't push our luck when it's too hot outside if (not_too_hot) { brake_with_drag(); } else { CFG.AT.Off(); CFG.StabilizeFlight = false; VSL.vessel.ActionGroups.SetGroup(KSPActionGroup.SAS, false); } StageTimer.RunIf(Body.atmosphere && //!VSL.Controls.HaveControlAuthority && VSL.vessel.currentStage - 1 > VSL.OnPlanetParams.NearestParachuteStage && VSL.vessel.dynamicPressurekPa > LTRJ.DropBallastThreshold * PressureASL && VSL.vessel.mach > LTRJ.MachThreshold); if (CFG.AutoParachutes) { Status("yellow", "Waiting for the right moment to deploy parachutes..."); } else { Status("red", "Automatic parachute deployment is disabled.\nActivate parachutes manually when needed."); } } } if (Body.atmosphere) { VSL.BrakesOn(); } if (!VSL.OnPlanetParams.HaveParachutes && !VSL.Engines.HaveNextStageEngines && (VSL.Engines.MaxThrustM.Equals(0) || !VSL.Controls.HaveControlAuthority)) { if (Body.atmosphere && not_too_hot) { brake_with_drag(); } Status("red", "Crash is imminent.\nVertical impact speed: {0}", Utils.formatBigValue((float)terminal_velocity, "m/s")); } break; case LandingStage.SoftLanding: CFG.BR.Off(); THR.Throttle = 0; set_destination_vector(); setup_for_deceleration(); if (vessel_within_range || vessel_after_target || trajectory.BrakeEndUT - VSL.Physics.UT < LTRJ.ParachutesDeployOffset) { do_aerobraking_if_requested(true); } var turn_time = VSL.Torque.MaxPossible.MinRotationTime(VSL.Controls.AttitudeError); if (!Working) { correct_landing_site(); correct_attitude_with_thrusters(turn_time); VSL.Info.TTB = VSL.Engines.TTB((float)VSL.vessel.srfSpeed); //VSL.Engines.OnPlanetTTB(VSL.vessel.srf_velocity, VSL.Physics.Up); VSL.Info.Countdown -= VSL.Info.TTB + turn_time; Working = VSL.Info.Countdown <= 0 || VSL.vessel.srfSpeed < LTRJ.BrakeEndSpeed; //TODO: is this correct? if (!Working) { if (VSL.Controls.InvAlignmentFactor > 0.5) { Status("white", "Final deceleration: correcting attitude.\nLanding site error: {0}", Utils.formatBigValue((float)trajectory.DistanceToTarget, "m")); } else { Status("white", "Final deceleration: waiting for the burn.\nLanding site error: {0}", Utils.formatBigValue((float)trajectory.DistanceToTarget, "m")); } break; } } if (Working) { ATC.SetThrustDirW(correction_direction()); if (!VSL.Controls.HaveControlAuthority) { correct_attitude_with_thrusters(turn_time); if (!VSL.Torque.HavePotentialControlAuthority) { landing_stage = LandingStage.HardLanding; } break; } if (vessel_within_range || VSL.Altitude.Relative > GLB.LND.WideCheckAltitude) { var brake_spd = -VSL.VerticalSpeed.Absolute; var min_thrust = Utils.Clamp(brake_spd / (VSL.Engines.MaxAccel - VSL.Physics.G) / Utils.ClampL((float)VSL.Info.Countdown, 0.01f), VSL.OnPlanetParams.GeeVSF, 1); THR.Throttle = Utils.Clamp(brake_spd / LTRJ.BrakeThrustThreshod, min_thrust, 1); } else { THR.Throttle = 1; } if (vessel_within_range && VSL.Altitude.Relative > GLB.LND.StopAtH * VSL.Geometry.D || VSL.Altitude.Relative > GLB.LND.WideCheckAltitude) { VSL.Info.TTB = VSL.Engines.TTB((float)VSL.vessel.srfSpeed); VSL.Info.Countdown -= VSL.Info.TTB + turn_time; Working = THR.Throttle > 0.7 || VSL.Info.Countdown < 10; Status("white", "Final deceleration. Landing site error: {0}", Utils.formatBigValue((float)trajectory.DistanceToTarget, "m")); break; } } THR.Throttle = 0; if (LandASAP) { landing_stage = LandingStage.LandHere; } else { stop_aerobraking(); if (CFG.Target.DistanceTo(VSL.vessel) - VSL.Geometry.R > LTRJ.Dtol) { approach(); } else { land(); } } break; case LandingStage.LandHere: Status("lime", "Landing..."); CFG.BR.Off(); CFG.BlockThrottle = true; CFG.AltitudeAboveTerrain = true; CFG.VF.On(VFlight.AltitudeControl); CFG.HF.OnIfNot(HFlight.Stop); if (CFG.DesiredAltitude >= 0 && !VSL.HorizontalSpeed.MoovingFast) { CFG.DesiredAltitude = 0; } else { CFG.DesiredAltitude = Utils.ClampL(VSL.Altitude.Relative / 2, VSL.Geometry.H * 2); } break; case LandingStage.Approach: Status("Approaching the target..."); set_destination_vector(); if (!CFG.Nav[Navigation.GoToTarget]) { land(); } break; case LandingStage.Land: set_destination_vector(); break; } return(false); }