void stop_aerobraking() { if (UseBrakes) { VSL.BrakesOn(false); } if (UseChutes && VSL.OnPlanetParams.ParachutesActive) { VSL.OnPlanetParams.CutActiveParachutes(); } }
void do_aerobraking_if_requested(bool full = false) { if (VSL.vessel.staticPressurekPa > 0) { if (UseBrakes) { VSL.BrakesOn(); } if (UseChutes && VSL.OnPlanetParams.HaveUsableParachutes && (full || !VSL.OnPlanetParams.ParachutesActive)) { VSL.OnPlanetParams.ActivateParachutesASAP(); } } }
IEnumerator <YieldInstruction> measure_area_with_brakes_and_run(Callback action) { var brakes = VSL.vessel.ActionGroups[KSPActionGroup.Brakes]; VSL.BrakesOn(); brakes_measured_timer.Reset(); AreaWithBrakes = BoundsSideAreas.MinComponentF(); while (!brakes_measured_timer.TimePassed) { TCAGui.Status(0.1, "Testing aero-brakes..."); var min_area = BoundsSideAreas.MinComponentF(); if (min_area > AreaWithBrakes) { AreaWithBrakes = min_area; brakes_measured_timer.Reset(); } yield return(null); } if (!brakes) { VSL.BrakesOn(false); } action(); }
protected override void Update() { //update state if (was_landed && !VSL.LandedOrSplashed) { stage = Stage.JustTookoff; GearTimer.Reset(); StopAction.Reset(); } else if (VSL.LandedOrSplashed && !was_landed) { stage = Stage.JustLanded1; } was_landed = VSL.LandedOrSplashed; switch (stage) { case Stage.JustLanded1: working(); srf_normal = Vector3.zero; CFG.HF.OnIfNot(HFlight.Level); VSL.BrakesOn(); LandedTimer.RunIf(() => stage = Stage.JustLanded2, VSL.HorizontalSpeed < C.MinHSpeed); break; case Stage.JustLanded2: if (ATC != null) { if (srf_normal.IsZero()) { RaycastHit hit; if (Physics.Raycast(VSL.Physics.wCoM, -VSL.Physics.Up, out hit, VSL.Geometry.D, Radar.RadarMask)) { if (hit.collider != null && !hit.normal.IsZero()) { srf_normal = -hit.normal; break; } } stage = Stage.Landed; break; } working(); CFG.HF.Off(); CFG.AT.OnIfNot(Attitude.Custom); ATC.SetThrustDirW(srf_normal); LandedTimer.RunIf(() => { stage = Stage.Landed; CFG.AT.Off(); }, VSL.Physics.NoRotation || VSL.Controls.AttitudeError < 1); break; } stage = Stage.Landed; break; case Stage.JustTookoff: working(); StopAction.Run(); GearTimer.RunIf(() => { VSL.BrakesOn(false); VSL.GearOn(false); stage = Stage.Flying; }, VSL.Altitude.Relative > 2 * VSL.Geometry.H); break; case Stage.Landed: if (CFG.VerticalCutoff <= 0) { working(false); } else { var avSqr = VSL.vessel.angularVelocity.sqrMagnitude; if (VSL.HorizontalSpeed < C.MaxHSpeed && avSqr > C.MinAngularVelocity) { working(); CFG.HF.OnIfNot(HFlight.Level); if (avSqr > C.GearOffAngularVelocity && VSL.OnPlanetParams.DTWR > C.MinDTWR) { VSL.GearOn(false); } } else { working(false); } } break; case Stage.Flying: working(false); if (!VSL.vessel.ActionGroups[KSPActionGroup.Gear]) { if (VSL.VerticalSpeed.Relative < 0 && VSL.HorizontalSpeed < C.GearOnMaxHSpeed && (!CFG.AT || !VSL.Altitude.AboveGround || VSL.Engines.Thrust.IsZero()) && VSL.Altitude.Relative + VSL.VerticalSpeed.Relative * (VSL.OnPlanetParams.GearDeployTime + C.GearOnTime) < C.GearOnAtH * VSL.Geometry.H) { VSL.GearOn(); VSL.BrakesOn(); } } else if (VSL.OnPlanetParams.GearDeploying) { if (VSC != null) { VSC.SetpointOverride = Utils.ClampH((C.GearOnAtH * VSL.Geometry.H - VSL.Altitude.Relative) / (VSL.OnPlanetParams.GearDeployTime + C.GearOnTime), 0); } } else { GearTimer.RunIf(() => { VSL.GearOn(false); VSL.BrakesOn(false); }, VSL.VerticalSpeed.Relative > 5 || VSL.HorizontalSpeed > C.GearOnMaxHSpeed || VSL.VerticalSpeed.Relative > 0 && VSL.Altitude.Relative > VSL.Geometry.H * 5); } 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) { return; } //update state if (last_state && !VSL.LandedOrSplashed) { tookoff = true; landed = false; GearTimer.Reset(); StopAction.Reset(); } else if (VSL.LandedOrSplashed && !last_state) { landed = true; tookoff = false; } last_state = VSL.LandedOrSplashed; //just landed if (landed) { working(); CFG.HF.OnIfNot(HFlight.Level); VSL.BrakesOn(); LandedTimer.RunIf(() => landed = false, VSL.HorizontalSpeed < TLA.MinHSpeed); } //just took off else if (tookoff) { working(); StopAction.Run(); GearTimer.RunIf(() => { VSL.BrakesOn(false); VSL.GearOn(false); tookoff = false; }, VSL.Altitude.Relative > TLA.GearOnAtH + VSL.Geometry.H); } //moving on the ground else if (VSL.LandedOrSplashed) { var avSqr = VSL.vessel.angularVelocity.sqrMagnitude; if (VSL.HorizontalSpeed < TLA.MaxHSpeed && avSqr > TLA.MinAngularVelocity) { working(); CFG.HF.OnIfNot(HFlight.Level); if (avSqr > TLA.GearOffAngularVelocity && VSL.OnPlanetParams.DTWR > TLA.MinDTWR) { VSL.GearOn(false); } } else { working(false); } } //if flying, check if trying to land and deploy the gear else { working(false); //if the gear is on, nothing to do; and autopilot takes precedence if (!VSL.vessel.ActionGroups[KSPActionGroup.Gear]) { //check boundary conditions GearTimer.RunIf(() => { VSL.GearOn(); VSL.BrakesOn(); }, VSL.VerticalSpeed.Relative < 0 && VSL.HorizontalSpeed < TLA.GearOnMaxHSpeed && VSL.Altitude.Relative + VSL.VerticalSpeed.Relative * (TLA.GearOnTime + TLA.GearTimer) < TLA.GearOnAtH * VSL.Geometry.H); } else { GearTimer.RunIf(() => { VSL.GearOn(false); VSL.BrakesOn(false); }, VSL.VerticalSpeed.Relative > 5 || VSL.HorizontalSpeed > TLA.GearOnMaxHSpeed || VSL.VerticalSpeed.Relative > 0 && VSL.Altitude.Relative > VSL.Geometry.H * 5); } } }
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); }