Skip to content

Commit

Permalink
Fix autostage limit bug that broke coasts
Browse files Browse the repository at this point in the history
- fix in the StagingController to update the limit every time in
  case it changes (which it does in the guidance controller).
- fixes UI bug to show KSP stage in the PVG settings menu
- fixes UI bug to show KSP stage next to the coast in the PVG solution
  display.

Signed-off-by: Lamont Granquist <lamont@scriptkiddie.org>
  • Loading branch information
lamont-granquist committed Jul 26, 2023
1 parent 6514738 commit 705c6d5
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 131 deletions.
47 changes: 10 additions & 37 deletions MechJeb2/MechJebLib/PVG/Solution.cs
Expand Up @@ -178,10 +178,7 @@ public double TgoBar(double tbar, int n)
return _tmax[n] - _tmin[n];
}

public double Vgo(double t)
{
return DV(Tf) - DV(t);
}
public double Vgo(double t) => DV(Tf) - DV(t);

public bool Coast(double t)
{
Expand All @@ -202,7 +199,7 @@ public bool WillCoast(double t)
return false;
}

public int CoastStage()
public int CoastKSPStage()
{
for (int i = 0; i < Phases.Count; i++)
{
Expand All @@ -212,42 +209,27 @@ public int CoastStage()
return -1;
}

public int TerminalStage()
{
return Phases[Phases.Count - 1].KSPStage;
}
public int TerminalStage() => Phases[Phases.Count - 1].KSPStage;

public bool Unguided(double t)
{
double tbar = (t - T0) / _timeScale;
return Phases[IndexForTbar(tbar)].Unguided;
}

public bool CoastPhase(int phase)
{
return Phases[phase].Coast;
}
public bool CoastPhase(int phase) => Phases[phase].Coast;

public bool OptimizeTime(int phase)
{
return Phases[phase].OptimizeTime;
}
public bool OptimizeTime(int phase) => Phases[phase].OptimizeTime;

public V3 U0(double t)
{
double tbar = (t - T0) / _timeScale;
return Phases[IndexForTbar(tbar)].u0;
}

public int KSPStage(int phase)
{
return Phases[phase].KSPStage;
}
public int KSPStage(int phase) => Phases[phase].KSPStage;

public int MJPhase(int phase)
{
return Phases[phase].MJPhase;
}
public int MJPhase(int phase) => Phases[phase].MJPhase;

public double StageTimeLeft(double t)
{
Expand Down Expand Up @@ -284,10 +266,7 @@ public double StageTimeLeft(double t)
return (pitch, heading);
}

public (V3 r, V3 v) TerminalStateVectors()
{
return StateVectors(tmax);
}
public (V3 r, V3 v) TerminalStateVectors() => StateVectors(tmax);

public (V3 r, V3 v) StateVectors(double tbar)
{
Expand Down Expand Up @@ -344,15 +323,9 @@ public int IndexForKSPStage(int kspStage, bool coasting)
return -1;
}

private Vn Interpolate(double tbar)
{
return _interpolants[IndexForTbar(tbar)].Evaluate(tbar);
}
private Vn Interpolate(double tbar) => _interpolants[IndexForTbar(tbar)].Evaluate(tbar);

private Vn Interpolate(int segment, double tbar)
{
return _interpolants[segment].Evaluate(tbar);
}
private Vn Interpolate(int segment, double tbar) => _interpolants[segment].Evaluate(tbar);

// this is for terminal guidance.
public bool TerminalGuidanceSatisfied(V3 pos, V3 vel, int index)
Expand Down
23 changes: 7 additions & 16 deletions MechJeb2/MechJebModuleAscentMenu.cs
Expand Up @@ -499,14 +499,14 @@ private static double TimeToPhaseAngle(double phaseAngle, CelestialBody launchBo

private string PhaseString(Solution solution, double t, int pvgPhase)
{
int mjPhase = solution.MJPhase(pvgPhase);
int kspStage = solution.KSPStage(pvgPhase);

if (solution.CoastPhase(pvgPhase))
return $"coast: {solution.Tgo(t, pvgPhase):F1}s";
return $"coast: {kspStage} {solution.Tgo(t, pvgPhase):F1}s";

double stageDeltaV = 0;

int mjPhase = solution.MJPhase(pvgPhase);
int kspStage = solution.KSPStage(pvgPhase);

if (mjPhase < Core.StageStats.VacStats.Count)
stageDeltaV = Core.StageStats.VacStats[mjPhase].DeltaV;

Expand All @@ -518,19 +518,10 @@ private string PhaseString(Solution solution, double t, int pvgPhase)
return $"burn: {kspStage} {solution.Tgo(t, pvgPhase):F1}s {solution.DV(t, pvgPhase):F1}m/s ({excessDV:F1}m/s)";
}

public override GUILayoutOption[] WindowOptions()
{
return new[] { GUILayout.Width(275), GUILayout.Height(30) };
}
public override GUILayoutOption[] WindowOptions() => new[] { GUILayout.Width(275), GUILayout.Height(30) };

public override string GetName()
{
return CachedLocalizer.Instance.MechJeb_Ascent_title; //"Ascent Guidance"
}
public override string GetName() => CachedLocalizer.Instance.MechJeb_Ascent_title; //"Ascent Guidance"

public override string IconName()
{
return "Ascent Guidance";
}
public override string IconName() => "Ascent Guidance";
}
}
24 changes: 8 additions & 16 deletions MechJeb2/MechJebModuleAscentPVGSettingsMenu.cs
@@ -1,4 +1,5 @@
using JetBrains.Annotations;
using System.Collections.Generic;
using JetBrains.Annotations;
using MechJebLib.Simulations;
using UnityEngine;
using static MechJebLib.Utils.Statics;
Expand All @@ -18,10 +19,7 @@ public MechJebModuleAscentPVGSettingsMenu(MechJebCore core) : base(core)
private static GUIStyle _btNormal;
private static GUIStyle _btActive;

public override GUILayoutOption[] WindowOptions()
{
return new[] { GUILayout.Width(300), GUILayout.Height(100) };
}
public override GUILayoutOption[] WindowOptions() => new[] { GUILayout.Width(300), GUILayout.Height(100) };

private void SetupButtonStyles()
{
Expand All @@ -48,9 +46,9 @@ protected override void WindowGUI(int windowID)

int topstage = -1;

var vacStats = Core.StageStats.VacStats;
List<FuelStats> vacStats = Core.StageStats.VacStats;

if (vacStats.Count > 0 && _ascentSettings.LastStage < vacStats[vacStats.Count-1].KSPStage)
if (vacStats.Count > 0 && _ascentSettings.LastStage < vacStats[vacStats.Count - 1].KSPStage)
{
GUILayout.BeginVertical(GUI.skin.box);

Expand All @@ -66,7 +64,7 @@ protected override void WindowGUI(int windowID)
topstage = stats.KSPStage;

GUILayout.BeginHorizontal();
GUILayout.Label($"{mjPhase,3} {stats.DeltaV:##,###0} m/s");
GUILayout.Label($"{stats.KSPStage,3} {stats.DeltaV:##,###0} m/s");
if (_ascentSettings.UnguidedStages.Contains(mjPhase))
GUILayout.Label(" (unguided)");
if (_ascentSettings.OptimizeStage == mjPhase)
Expand Down Expand Up @@ -134,14 +132,8 @@ protected override void WindowGUI(int windowID)
base.WindowGUI(windowID);
}

public override string GetName()
{
return "PVG Settings";
}
public override string GetName() => "PVG Settings";

public override string IconName()
{
return "PVGSettings";
}
public override string IconName() => "PVGSettings";
}
}
86 changes: 26 additions & 60 deletions MechJeb2/MechJebModuleGuidanceController.cs
Expand Up @@ -75,10 +75,7 @@ protected override void OnModuleDisabled()
private bool _allowExecution;

// we wait until we get a signal to allow execution to start
public void AssertStart(bool allow_execution = true)
{
_allowExecution = allow_execution;
}
public void AssertStart(bool allow_execution = true) => _allowExecution = allow_execution;

public override void OnFixedUpdate()
{
Expand Down Expand Up @@ -226,49 +223,25 @@ private void HandleSpinup()
Core.Spinup.RollAngularVelocity = _ascentSettings.SpinupAngularVelocity;
}

public bool IsTerminal()
{
return Status == PVGStatus.TERMINAL_RCS || Status == PVGStatus.TERMINAL_STAGING || Status == PVGStatus.TERMINAL;
}
public bool IsTerminal() => Status == PVGStatus.TERMINAL_RCS || Status == PVGStatus.TERMINAL_STAGING || Status == PVGStatus.TERMINAL;

/* is guidance usable? */
public bool IsStable()
{
return IsNormal() || IsTerminal();
}
public bool IsStable() => IsNormal() || IsTerminal();

// either ENABLED and waiting for a Solution, or executing a solution "normally" (not terminal, not failed)
public bool IsReady()
{
return Status == PVGStatus.ENABLED || IsNormal();
}
public bool IsReady() => Status == PVGStatus.ENABLED || IsNormal();

// not TERMINAL guidance or TERMINAL_RCS -- when we should be running the optimizer
public bool IsNormal()
{
return Status == PVGStatus.INITIALIZED || Status == PVGStatus.BURNING || Status == PVGStatus.COASTING;
}
public bool IsNormal() => Status == PVGStatus.INITIALIZED || Status == PVGStatus.BURNING || Status == PVGStatus.COASTING;

public bool IsCoasting()
{
return Status == PVGStatus.COASTING;
}
public bool IsCoasting() => Status == PVGStatus.COASTING;

private bool IsThrustOn()
{
return IsBurning() || IsTerminal();
}
private bool IsThrustOn() => IsBurning() || IsTerminal();

private bool IsBurning()
{
return Status == PVGStatus.BURNING;
}
private bool IsBurning() => Status == PVGStatus.BURNING;

/* normal pre-states but not usefully converged */
public bool IsInitializing()
{
return Status == PVGStatus.ENABLED || Status == PVGStatus.INITIALIZED;
}
public bool IsInitializing() => Status == PVGStatus.ENABLED || Status == PVGStatus.INITIALIZED;

private void HandleThrottle()
{
Expand All @@ -290,17 +263,19 @@ private void HandleThrottle()
return;
}

int coastStage = Solution.CoastStage();
int coastStage = Solution.CoastKSPStage();

//Debug.Log($"coast stage: {coastStage} current stage: {vessel.currentStage} will coast: {Solution.WillCoast(vesselState.time)}");
//Debug.Log($"coast stage: {coastStage} current stage: {Vessel.currentStage} will coast: {Solution.WillCoast(VesselState.time)}");


// Stop autostaging if we need to do a coast. Also, we need to affirmatively set the termination
// of autostaging to the top of the rocket. If we don't, then when we cut the engines and do the
// RCS trim, autostaging will stage off the spent engine if there's no relights. This is unwanted
// since the insertion stage may still have RCS which is necessary to complete the mission.
if (coastStage >= 0 && Vessel.currentStage == coastStage && Solution.WillCoast(VesselState.time))
if (coastStage >= 0 && Vessel.currentStage >= coastStage && Solution.WillCoast(VesselState.time))
Core.Staging.AutoStageLimitRequest(coastStage, this);
else
Core.Staging.AutoStageLimitRequest(Solution.TerminalStage(), this);

if (Solution.Coast(VesselState.time))
{
Expand All @@ -318,22 +293,19 @@ private void HandleThrottle()
RCSOn();

ThrustOff();
return;
}
else
{
ThrottleOn();

Core.Staging.AutoStageLimitRequest(Solution.TerminalStage(), this);

ThrottleOn();

Status = PVGStatus.BURNING;
Status = PVGStatus.BURNING;
}
}

private bool IsGrounded()
{
return Vessel.situation == Vessel.Situations.LANDED ||
Vessel.situation == Vessel.Situations.PRELAUNCH ||
Vessel.situation == Vessel.Situations.SPLASHED;
}
private bool IsGrounded() =>
Vessel.situation == Vessel.Situations.LANDED ||
Vessel.situation == Vessel.Situations.PRELAUNCH ||
Vessel.situation == Vessel.Situations.SPLASHED;

private void UpdatePitchAndHeading()
{
Expand Down Expand Up @@ -394,21 +366,15 @@ private void DrawTrajetory()
GLUtils.DrawOrbit(_finalOrbit, Color.yellow);
}

private void ThrottleOn()
{
Core.Thrust.TargetThrottle = 1.0F;
}
private void ThrottleOn() => Core.Thrust.TargetThrottle = 1.0F;

private void RCSOn()
{
Core.Thrust.ThrustOff();
Vessel.ctrlState.Z = -1.0F;
}

private void ThrustOff()
{
Core.Thrust.ThrustOff();
}
private void ThrustOff() => Core.Thrust.ThrustOff();

private void TerminalDone()
{
Expand All @@ -419,7 +385,7 @@ private void TerminalDone()
}

// if we still have a coast to do in this stage, start the coast
if (Vessel.currentStage == Solution.CoastStage() && Solution.WillCoast(VesselState.time))
if (Vessel.currentStage == Solution.CoastKSPStage() && Solution.WillCoast(VesselState.time))
{
ThrustOff();
Status = PVGStatus.COASTING;
Expand Down
11 changes: 9 additions & 2 deletions MechJeb2/MechJebModuleStagingController.cs
Expand Up @@ -57,7 +57,7 @@ public MechJebModuleStagingController(MechJebCore core)

private readonly Dictionary<object, int> _autoStageModuleLimit = new Dictionary<object, int>();

public void AutoStageLimitRequest(int stage, object user) => _autoStageModuleLimit.TryAdd(user, stage);
public void AutoStageLimitRequest(int stage, object user) => _autoStageModuleLimit[user] = stage;

public void AutoStageLimitRemove(object user)
{
Expand All @@ -69,8 +69,11 @@ private int ActiveAutoStageModuleLimit()
{
int limit = 0;
foreach (int value in _autoStageModuleLimit.Values)
if (value > limit)
{
if (value > limit)
limit = value;
}

return limit;
}

Expand Down Expand Up @@ -304,7 +307,9 @@ public override void OnUpdate()

// always stage if we have no active engines
if (!InverseStageHasActiveEngines(Vessel.currentStage))
{
Stage();
}

// prevent staging when the current stage has active engines and the next stage has any engines (but not decouplers or clamps)
if (HotStaging && InverseStageHasEngines(Vessel.currentStage - 1) &&
Expand All @@ -318,7 +323,9 @@ public override void OnUpdate()

// Always drop deactivated engines or tanks
if (InverseStageDecouplesDeactivatedEngineOrTank(Vessel.currentStage - 1))
{
Stage();
}

// only decouple fairings if the dynamic pressure, altitude, and aerothermal flux conditions are respected
if (WaitingForFairing())
Expand Down

0 comments on commit 705c6d5

Please sign in to comment.