Created
February 6, 2022 04:19
-
-
Save lamont-granquist/78bf88d0038375fd2771739a02ede2b9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Kode.Interfaces; | |
using Kode.Interfaces.PartModules; | |
using static Kode.Logging; | |
namespace Kode.AutopilotModules | |
{ | |
// TODO: hotstaging [requires deltaV analysis] | |
// TODO: early solid jettison [requires deltaV analysis] | |
// TODO: prevent prelaunch activation? [requires events from KSP] | |
// FIXME: this needs a rename to not collide with the KSP class | |
public class StagingManager : BaseAutopilot | |
{ | |
private double _lastStagingTime; | |
private double _startingStagingTime; | |
private State _state = State.Running; | |
public StagingManager(IVessel vessel) : base(vessel) | |
{ | |
} | |
public double ClampThrustPct { get; set; } = 0.99; | |
public bool AggressiveStaging { get; set; } = true; | |
public double FairingMaxDynamicPressure { get; set; } = 5000; // Pa | |
public double FairingMinAltitude { get; set; } = 50000; // m | |
public double FairingMaxAerothermalFlux { get; set; } = 1135.0; // W/m^2 | |
public double PreStagingDelay { get; set; } = 0.5; // wait before staging | |
public double PostStagingDelay { get; set; } = 1.0; // wait after last staging | |
protected override void OnDrive() | |
{ | |
switch (_state) | |
{ | |
case State.Running: | |
StateRunning(); | |
break; | |
case State.Staging: | |
StateStaging(); | |
break; | |
} | |
} | |
private void StateRunning() | |
{ | |
// TODO: need KSP event for staging so that we wait until the user manually stages | |
//if (Vessel.Situation == IVesselSituations.PRELAUNCH) | |
// return; | |
if (Vessel.CurrentStage < 0) | |
return; | |
if (WouldDecoupleActiveEngine()) return; | |
if (WouldDecoupleDrainingTank()) return; | |
if (WouldDropUnfiredParachute()) return; | |
if (ShouldDecoupleFairing()) | |
{ | |
Log("ShouldDecoupleFairing"); | |
TransitionDoStage(); | |
return; | |
} | |
if (ShouldDecoupleDeactivatedEngine()) | |
{ | |
Log("ShouldDecoupleDeactivatedEngine"); | |
TransitionRequestStage(); | |
return; | |
} | |
if (ShouldDecoupleDrainedTank()) | |
{ | |
Log("ShouldDecoupleDrainedTank"); | |
TransitionRequestStage(); | |
return; | |
} | |
if (ShouldReleaseLaunchClamps()) | |
{ | |
Log("ShouldReleaseLaunchClamps"); | |
TransitionDoStage(); | |
return; | |
} | |
if (NoActiveEngines() && AggressiveStaging) | |
{ | |
Log("NoActiveEngines"); | |
TransitionRequestStage(); | |
} | |
} | |
private void TransitionRequestStage() | |
{ | |
_state = State.Staging; | |
_startingStagingTime = Vessel.Time; | |
} | |
private void StateStaging() | |
{ | |
if (Vessel.Time > _startingStagingTime + PreStagingDelay && | |
Vessel.Time > _lastStagingTime + PostStagingDelay) | |
TransitionDoStage(); | |
} | |
private void TransitionDoStage() | |
{ | |
Stage(); | |
_state = State.Running; | |
_lastStagingTime = Vessel.Time; | |
} | |
private void Stage() | |
{ | |
Vessel.Control.Stage(); | |
} | |
private bool WouldDecoupleActiveEngine() | |
{ | |
foreach (IEngine e in Vessel.Parts.GetModulesDroppedInStage<IEngine>(Vessel.CurrentStage - 1)) | |
{ | |
if (e.IsSepratron || !e.IsEnabled) | |
continue; | |
if ((e.State == IPartStates.ACTIVE || e.State == IPartStates.IDLE) && !e.FlameoutState) return true; | |
} | |
return false; | |
} | |
private bool NoActiveEngines() | |
{ | |
foreach (IEngine e in Vessel.Parts.GetModules<IEngine>()) | |
{ | |
if (e.IsSepratron || !e.IsEnabled) | |
continue; | |
if (e.IgnitionState && !e.FlameoutState) return false; | |
} | |
return true; | |
} | |
private bool WouldDecoupleDrainingTank() | |
{ | |
foreach (IPart p in Vessel.Parts.GetPartsDroppedInStage(Vessel.CurrentStage - 1)) | |
{ | |
if (IsSepratron(p)) | |
continue; | |
for (int j = 0; j < p.Resources.Count; j++) | |
{ | |
IResource r = p.Resources[j]; | |
if (r.Electricity) | |
continue; | |
if (p.InActiveEngineCrossfeedSet && r.Amount > p.ResourceRequestRemainingThreshold) return true; | |
} | |
} | |
return false; | |
} | |
// FIXME: move to IPart base class | |
private bool IsSepratron(IPart p) | |
{ | |
foreach (IEngine e in p.Modules.GetModules<IEngine>()) | |
if (e.IsSepratron) | |
return true; | |
return false; | |
} | |
// FIXME: this is awfully similar to WouldDecompleDrainingTank | |
private bool ShouldDecoupleDrainedTank() | |
{ | |
bool hadResources = false; | |
foreach (IPart p in Vessel.Parts.GetPartsDroppedInStage(Vessel.CurrentStage - 1)) | |
{ | |
if (IsSepratron(p)) | |
continue; | |
for (int j = 0; j < p.Resources.Count; j++) | |
{ | |
IResource r = p.Resources[j]; | |
if (r.Electricity) | |
continue; | |
if (r.MaxAmount > p.ResourceRequestRemainingThreshold) hadResources = true; | |
if (r.Amount > p.ResourceRequestRemainingThreshold) return false; | |
} | |
} | |
return hadResources; | |
} | |
private bool ShouldDecoupleFairing() | |
{ | |
if (Vessel.DynamicPressure > FairingMaxDynamicPressure) return false; | |
if (Vessel.Altitude < FairingMinAltitude) return false; | |
if (Vessel.AerothermalFlux > FairingMaxAerothermalFlux) return false; | |
// stock ModuleProceduralFairing | |
foreach (IProceduralFairing f in Vessel.Parts.GetModulesInStage<IProceduralFairing>(Vessel.CurrentStage - 1)) return true; | |
// ProceduralFairingDecoupler from Proc Fairings | |
foreach (IProceduralFairingDecoupler f in Vessel.Parts.GetModulesInStage<IProceduralFairingDecoupler>(Vessel.CurrentStage - 1) | |
) return true; | |
// FIXME: RSB fairings (all they seem to have is a ModuleDecouple so I guess would need to see if the part name matched? | |
return false; | |
} | |
private bool ShouldDecoupleDeactivatedEngine() | |
{ | |
foreach (IEngine e in Vessel.Parts.GetModulesDroppedInStage<IEngine>(Vessel.CurrentStage - 1)) | |
{ | |
if (e.State == IPartStates.DEACTIVATED && !e.IsSepratron) | |
return true; | |
if (e.FlameoutState) | |
return true; | |
} | |
return false; | |
} | |
private bool WouldDropUnfiredParachute() | |
{ | |
foreach (IParachute p in Vessel.Parts.GetModulesDroppedInStage<IParachute>(Vessel.CurrentStage - 1)) | |
if (p.InverseStage != p.DecoupledInStage) | |
return true; | |
return false; | |
} | |
private bool ShouldReleaseLaunchClamps() | |
{ | |
if (Vessel.ThrustCurrent.magnitude == 0) return false; | |
if (Vessel.ThrustCurrent.magnitude / Vessel.ThrustMax.magnitude < ClampThrustPct) return false; | |
foreach (ILaunchClamp c in Vessel.Parts.GetModulesDroppedInStage<ILaunchClamp>(Vessel.CurrentStage - 1)) return true; | |
return false; | |
} | |
private enum State | |
{ | |
Running, | |
Staging | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment