diff --git a/MechJeb2/MechJebLib/Simulations/FuelFlowSimulation.cs b/MechJeb2/MechJebLib/Simulations/FuelFlowSimulation.cs index 4a9840b8..c1226ce0 100644 --- a/MechJeb2/MechJebLib/Simulations/FuelFlowSimulation.cs +++ b/MechJeb2/MechJebLib/Simulations/FuelFlowSimulation.cs @@ -19,6 +19,7 @@ public class FuelFlowSimulation : BackgroundJob public bool DVLinearThrust = true; // include cos losses private readonly HashSet _partsWithResourceDrains = new HashSet(); private readonly HashSet _partsWithRCSDrains = new HashSet(); + private readonly HashSet _partsWithRCSDrains2 = new HashSet(); private bool _allocatedFirstSegment; protected override bool Run(object? o) @@ -34,9 +35,8 @@ protected override bool Run(object? o) while (vessel.CurrentStage >= 0) // FIXME: should stop mutating vessel.CurrentStage { - ComputeRCSMinValues(vessel); SimulateStage(vessel); - ComputeRCSMaxValues(vessel); + ComputeRcsMaxValues(vessel); FinishSegment(vessel); vessel.Stage(); } @@ -48,22 +48,55 @@ protected override bool Run(object? o) return true; // we pull results off the object not off the return value } - private void ComputeRCSMinValues(SimVessel vessel) + private void SimulateRCS(SimVessel vessel, bool max) { - vessel.UpdateRCSStats(); - vessel.UpdateActiveRCS(); - UpdateRCSDrains(vessel); - double dt = MinimumRCSTimeStep(); + vessel.SaveRcsStatus(); + _partsWithRCSDrains2.Clear(); + + double lastmass = vessel.Mass; + + int steps = MAXSTEPS; + + while (true) + { + if (steps-- == 0) + throw new Exception("FuelFlowSimulation hit max steps of " + MAXSTEPS + " steps in rcs calculations"); + + vessel.UpdateRcsStats(); + vessel.UpdateActiveRcs(); + if (vessel.ActiveRcs.Count == 0) + break; + + if (max) + Log("we've found some active engines in max"); + + UpdateRcsDrains(vessel); + double dt = MinimumRcsTimeStep(); + + if (max) + Log($"dt is {dt} in max"); + + ApplyRcsDrains(dt); + vessel.UpdateMass(); + FinishRcsSegment(max, dt, lastmass, vessel.Mass, vessel.RcsThrust); + lastmass = vessel.Mass; + } + + UnapplyRcsDrains(); + vessel.ResetRcsStatus(); + vessel.UpdateMass(); } - private void ComputeRCSMaxValues(SimVessel vessel) + private void UnapplyRcsDrains() { - vessel.UpdateRCSStats(); - vessel.UpdateActiveRCS(); - UpdateRCSDrains(vessel); - double dt = MinimumRCSTimeStep(); + foreach (SimPart p in _partsWithRCSDrains2) + p.UnapplyRCSDrains(); } + private void ComputeRcsMinValues(SimVessel vessel) => SimulateRCS(vessel, false); + + private void ComputeRcsMaxValues(SimVessel vessel) => SimulateRCS(vessel, true); + private void SimulateStage(SimVessel vessel) { vessel.UpdateMass(); @@ -72,6 +105,7 @@ private void SimulateStage(SimVessel vessel) UpdateResourceDrainsAndResiduals(vessel); GetNextSegment(vessel); + ComputeRcsMinValues(vessel); double currentThrust = vessel.ThrustMagnitude; for (int steps = MAXSTEPS; steps > 0; steps--) @@ -85,6 +119,7 @@ private void SimulateStage(SimVessel vessel) // prior > 0dV segment in the same kspStage we should add those together to reduce clutter. if (Math.Abs(vessel.ThrustMagnitude - currentThrust) > 1e-12) { + ComputeRcsMaxValues(vessel); FinishSegment(vessel); GetNextSegment(vessel); currentThrust = vessel.ThrustMagnitude; @@ -102,6 +137,13 @@ private void SimulateStage(SimVessel vessel) throw new Exception("FuelFlowSimulation hit max steps of " + MAXSTEPS + " steps"); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ApplyRcsDrains(double dt) + { + foreach (SimPart part in _partsWithRCSDrains) + part.ApplyRCSDrains(dt); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ApplyResourceDrains(double dt) { @@ -109,16 +151,16 @@ private void ApplyResourceDrains(double dt) part.ApplyResourceDrains(dt); } - private void UpdateRCSDrains(SimVessel vessel) + private void UpdateRcsDrains(SimVessel vessel) { foreach (SimPart part in _partsWithRCSDrains) part.ClearRCSDrains(); _partsWithRCSDrains.Clear(); - for (int i = 0; i < vessel.ActiveRCS.Count; i++) + for (int i = 0; i < vessel.ActiveRcs.Count; i++) { - SimModuleRCS e = vessel.ActiveRCS[i]; + SimModuleRCS e = vessel.ActiveRcs[i]; foreach (int resourceId in e.PropellantFlowModes.Keys) { switch (e.PropellantFlowModes[resourceId]) @@ -194,6 +236,7 @@ private void UpdateRCSDrainsInParts(IList parts, double resourceConsump private void UpdateRCSDrainsInPart(SimPart p, double resourceConsumption, int resourceId) { _partsWithRCSDrains.Add(p); + _partsWithRCSDrains2.Add(p); p.AddRCSDrain(resourceId, resourceConsumption); } @@ -295,7 +338,7 @@ private static void UpdateResourceResidualsInParts(IList parts, double } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private double MinimumRCSTimeStep() + private double MinimumRcsTimeStep() { double maxTime = RCSMaxTime(); @@ -332,16 +375,54 @@ private double ResourceMaxTime() return maxTime; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void FinishRcsSegment(bool max, double deltaTime, double startMass, double endMass, double rcsThrust) + { + Log($"deltatime: {deltaTime} startmass: {startMass} endmass: {endMass} rcsthrust: {rcsThrust}"); + + double rcsDeltaV = rcsThrust * deltaTime / (startMass - endMass) * Math.Log(startMass / endMass); + double rcsISP = rcsDeltaV / (G0 * Math.Log(startMass / endMass)); + + Log($"deltav: {rcsDeltaV} isp: {rcsISP}"); + + if (_currentSegment.RcsISP == 0) + _currentSegment.RcsISP = rcsISP; + if (_currentSegment.RcsThrust == 0) + _currentSegment.RcsThrust = rcsThrust; + + if (max) + { + _currentSegment.MaxRcsDeltaV += rcsDeltaV; + if (_currentSegment.RcsStartTMR == 0) + _currentSegment.RcsStartTMR = rcsThrust / startMass; + } + else + { + _currentSegment.MinRcsDeltaV += rcsDeltaV; + _currentSegment.RcsEndTMR = _currentSegment.RcsThrust / endMass; + } + + _currentSegment.RcsMass += startMass - endMass; + _currentSegment.RcsDeltaTime += deltaTime; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void FinishSegment(SimVessel vessel) { if (!_allocatedFirstSegment) return; - _currentSegment.DeltaTime = _time - _currentSegment.StartTime; - _currentSegment.EndMass = vessel.Mass; - - _currentSegment.ComputeStats(); + double startMass = _currentSegment.StartMass; + double thrust = _currentSegment.Thrust; + double endMass = vessel.Mass; + double deltaTime = _time - _currentSegment.StartTime; + double deltaV = startMass > endMass ? thrust * deltaTime / (startMass - endMass) * Math.Log(startMass / endMass) : 0; + double isp = startMass > endMass ? deltaV / (G0 * Math.Log(startMass / endMass)) : 0; + + _currentSegment.DeltaTime = deltaTime; + _currentSegment.EndMass = endMass; + _currentSegment.DeltaV = deltaV; + _currentSegment.Isp = isp; Segments.Add(_currentSegment); } diff --git a/MechJeb2/MechJebLib/Simulations/FuelStats.cs b/MechJeb2/MechJebLib/Simulations/FuelStats.cs index c85c5cfc..04bc9beb 100644 --- a/MechJeb2/MechJebLib/Simulations/FuelStats.cs +++ b/MechJeb2/MechJebLib/Simulations/FuelStats.cs @@ -1,6 +1,3 @@ -using System; -using static MechJebLib.Utils.Statics; - #nullable enable namespace MechJebLib.Simulations @@ -17,29 +14,26 @@ public struct FuelStats public double StartTime; public double Thrust; public double SpoolUpTime; + public double MaxRcsDeltaV; + public double MinRcsDeltaV; + public double RcsISP; + public double RcsDeltaTime; + public double RcsThrust; + public double RcsMass; + public double RcsStartTMR; + public double RcsEndTMR; + public double MaxAccel => EndMass > 0 ? Thrust / EndMass : 0; public double ResourceMass => StartMass - EndMass; - public double StartTWR(double geeASL) - { - return StartMass > 0 ? Thrust / (9.80665 * geeASL * StartMass) : 0; - } + public double RcsStartTWR(double geeASL) => RcsStartTMR / (9.80665 * geeASL); + public double RcsMaxTWR(double geeASL) => RcsEndTMR / (9.80665 * geeASL); - public double MaxTWR(double geeASL) - { - return MaxAccel / (9.80665 * geeASL); - } + public double StartTWR(double geeASL) => StartMass > 0 ? Thrust / (9.80665 * geeASL * StartMass) : 0; - public void ComputeStats() - { - DeltaV = StartMass > EndMass ? Thrust * DeltaTime / (StartMass - EndMass) * Math.Log(StartMass / EndMass) : 0; - Isp = StartMass > EndMass ? DeltaV / (G0 * Math.Log(StartMass / EndMass)) : 0; - } + public double MaxTWR(double geeASL) => MaxAccel / (9.80665 * geeASL); - public override string ToString() - { - return - $"KSP Stage: {KSPStage.ToString()} Thrust: {Thrust.ToString()} Time: {DeltaTime.ToString()} StartMass: {StartMass.ToString()} EndMass: {EndMass.ToString()} DeltaV: {DeltaV.ToString()} ISP: {Isp.ToString()}"; - } + public override string ToString() => + $"KSP Stage: {KSPStage.ToString()} Thrust: {Thrust.ToString()} Time: {DeltaTime.ToString()} StartMass: {StartMass.ToString()} EndMass: {EndMass.ToString()} DeltaV: {DeltaV.ToString()} ISP: {Isp.ToString()}"; } } diff --git a/MechJeb2/MechJebLib/Simulations/PartModules/SimModuleRCS.cs b/MechJeb2/MechJebLib/Simulations/PartModules/SimModuleRCS.cs index 8d191c8d..a43d0e47 100644 --- a/MechJeb2/MechJebLib/Simulations/PartModules/SimModuleRCS.cs +++ b/MechJeb2/MechJebLib/Simulations/PartModules/SimModuleRCS.cs @@ -4,7 +4,6 @@ using System.Runtime.CompilerServices; using MechJebLib.Primitives; using MechJebLib.Utils; -using static MechJebLib.Utils.Statics; namespace MechJebLib.Simulations.PartModules { @@ -17,14 +16,15 @@ public class SimModuleRCS : SimPartModule public readonly H1 AtmosphereCurve = H1.Get(true); - public double G; - public double Isp; - public double Thrust; - public bool RcsEnabled; - public double ISPMult; - public double ThrustPercentage; - public double MaxFuelFlow; - public double MassFlowRate; + public double G; + public double Isp; + public double Thrust; + public bool RcsEnabled; + private bool _savedRcsEnabled; + public double ISPMult; + public double ThrustPercentage; + public double MaxFuelFlow; + public double MassFlowRate; private double _atmPressure => Part.Vessel.ATMPressure; @@ -48,6 +48,10 @@ public void UpdateRCSStatus() RcsEnabled = false; } + public void SaveStatus() => _savedRcsEnabled = RcsEnabled; + + public void ResetStatus() => RcsEnabled = _savedRcsEnabled; + // FIXME: aggressively duplicates ModuleEngines code and should be moved to SimPart [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool CanDrawResources() @@ -110,10 +114,7 @@ public void Update() SetConsumptionRates(); } - public void Activate() - { - RcsEnabled = true; - } + public void Activate() => RcsEnabled = true; private static void Clear(SimModuleRCS m) { diff --git a/MechJeb2/MechJebLib/Simulations/SimPart.cs b/MechJeb2/MechJebLib/Simulations/SimPart.cs index 7d7a1888..d877d3f6 100644 --- a/MechJeb2/MechJebLib/Simulations/SimPart.cs +++ b/MechJeb2/MechJebLib/Simulations/SimPart.cs @@ -104,6 +104,24 @@ public void ApplyResourceDrains(double dt) Resources[id] = Resources[id].Drain(dt * _resourceDrains[id]); } + public void ApplyRCSDrains(double dt) + { + foreach (int id in _rcsDrains.Keys) + Resources[id] = Resources[id].RCSDrain(dt * _rcsDrains[id]); + } + + private readonly List _resourceKeys = new List(); + + public void UnapplyRCSDrains() + { + _resourceKeys.Clear(); + foreach (int id in Resources.Keys) + _resourceKeys.Add(id); + + foreach (int id in _resourceKeys) + Resources[id] = Resources[id].ResetRCS(); + } + public void UpdateResourceResidual(double residual, int resourceId) { if (!Resources.TryGetValue(resourceId, out SimResource resource)) diff --git a/MechJeb2/MechJebLib/Simulations/SimResource.cs b/MechJeb2/MechJebLib/Simulations/SimResource.cs index 46203725..bb6b1b75 100644 --- a/MechJeb2/MechJebLib/Simulations/SimResource.cs +++ b/MechJeb2/MechJebLib/Simulations/SimResource.cs @@ -1,22 +1,47 @@ +using static MechJebLib.Utils.Statics; + #nullable enable namespace MechJebLib.Simulations { public struct SimResource { - public bool Free; - public double MaxAmount; - public double Amount; - public int Id; - public double Density; - public double Residual; + public bool Free; + public double MaxAmount; + private double _amount; + + public double Amount + { + get => _amount + _rcsAmount; + set => _amount = value; + } + + private double _rcsAmount; + public int Id; + public double Density; + public double Residual; public double ResidualThreshold => Residual * MaxAmount; public SimResource Drain(double resourceDrain) { - Amount -= resourceDrain; - if (Amount < 0) Amount = 0; + _amount -= resourceDrain; + if (_amount < 0) _amount = 0; + return this; + } + + public SimResource RCSDrain(double rcsDrain) + { + _rcsAmount -= rcsDrain; + if (Amount < 0) _rcsAmount = _amount; + Log($"RCSDrain: {rcsDrain} _amount: {_amount} _rcsAmount: {_rcsAmount}"); + + return this; + } + + public SimResource ResetRCS() + { + _rcsAmount = 0; return this; } } diff --git a/MechJeb2/MechJebLib/Simulations/SimVessel.cs b/MechJeb2/MechJebLib/Simulations/SimVessel.cs index 39faf58d..81e04b94 100644 --- a/MechJeb2/MechJebLib/Simulations/SimVessel.cs +++ b/MechJeb2/MechJebLib/Simulations/SimVessel.cs @@ -7,7 +7,6 @@ using MechJebLib.Primitives; using MechJebLib.Simulations.PartModules; using MechJebLib.Utils; -using static MechJebLib.Utils.Statics; namespace MechJebLib.Simulations { @@ -22,12 +21,13 @@ public class SimVessel : IDisposable public readonly DictOfLists RCSActivatedInStage = new DictOfLists(10); public readonly DictOfLists RCSDroppedInStage = new DictOfLists(10); public readonly List ActiveEngines = new List(10); - public readonly List ActiveRCS = new List(10); + public readonly List ActiveRcs = new List(10); public int CurrentStage; public double MainThrottle = 1.0; public double Mass; public V3 ThrustCurrent; + public double RcsThrust; public double ThrustMagnitude; public double ThrustNoCosLoss; public double SpoolupCurrent; @@ -104,9 +104,9 @@ public void UpdateActiveEngines() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void UpdateActiveRCS() + public void UpdateActiveRcs() { - ActiveRCS.Clear(); + ActiveRcs.Clear(); for (int i = -1; i < CurrentStage; i++) { @@ -119,11 +119,25 @@ public void UpdateActiveRCS() if (!r.RcsEnabled) continue; - ActiveRCS.Add(r); + ActiveRcs.Add(r); } } - ComputeThrustAndSpoolup(); + ComputeRcsThrust(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ComputeRcsThrust() + { + RcsThrust = 0; + + for (int i = 0; i < ActiveRcs.Count; i++) + { + SimModuleRCS r = ActiveRcs[i]; + + r.Update(); + RcsThrust += r.Thrust; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -177,7 +191,7 @@ private static void Clear(SimVessel v) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void UpdateRCSStats() + public void UpdateRcsStats() { for (int i = -1; i < CurrentStage; i++) foreach (SimModuleRCS e in RCSDroppedInStage[i]) @@ -192,6 +206,22 @@ public void UpdateEngineStats() e.Update(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SaveRcsStatus() + { + for (int i = -1; i < CurrentStage; i++) + foreach (SimModuleRCS r in RCSDroppedInStage[i]) + r.SaveStatus(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ResetRcsStatus() + { + for (int i = -1; i < CurrentStage; i++) + foreach (SimModuleRCS r in RCSDroppedInStage[i]) + r.ResetStatus(); + } + public override string ToString() { var sb = new StringBuilder(); diff --git a/MechJeb2/MechJebLib/Utils/BackgroundJob.cs b/MechJeb2/MechJebLib/Utils/BackgroundJob.cs index 72c86646..079a8eaa 100644 --- a/MechJeb2/MechJebLib/Utils/BackgroundJob.cs +++ b/MechJeb2/MechJebLib/Utils/BackgroundJob.cs @@ -3,6 +3,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using static MechJebLib.Utils.Statics; namespace MechJebLib.Utils { @@ -25,10 +26,7 @@ public BackgroundJob() _onTaskCancelledDelegate = OnTaskCancelled; } - public void Cancel() - { - throw new NotImplementedException(); - } + public void Cancel() => throw new NotImplementedException(); public bool IsRunning() { @@ -47,13 +45,13 @@ protected virtual void OnTaskCompleted(Task task) protected virtual void OnTaskFaulted(Task task) { - _task = null; + Result = default!; + ResultReady = false; + _task = null; + Log($"Exception in {GetType()}: {task.Exception}"); } - protected virtual void OnTaskCancelled(Task task) - { - _task = null; - } + protected virtual void OnTaskCancelled(Task task) => _task = null; private readonly Func _executeDelegate; private readonly Action> _onTaskCompletedDelegate; diff --git a/MechJeb2/MechJebModuleInfoItems.cs b/MechJeb2/MechJebModuleInfoItems.cs index 3bd201ab..2b284165 100644 --- a/MechJeb2/MechJebModuleInfoItems.cs +++ b/MechJeb2/MechJebModuleInfoItems.cs @@ -52,64 +52,35 @@ public string NextManeuverNodeDeltaV() } [ValueInfoItem("#MechJeb_SurfaceTWR", InfoItem.Category.Vessel, format = "F2", showInEditor = true)] //Surface TWR - public double SurfaceTWR() - { - return HighLogic.LoadedSceneIsEditor + public double SurfaceTWR() => + HighLogic.LoadedSceneIsEditor ? MaxAcceleration() / 9.81 : VesselState.thrustAvailable / (VesselState.mass * MainBody.GeeASL * 9.81); - } [ValueInfoItem("#MechJeb_LocalTWR", InfoItem.Category.Vessel, format = "F2", showInEditor = false)] //Local TWR - public double LocalTWR() - { - return VesselState.thrustAvailable / (VesselState.mass * VesselState.gravityForce.magnitude); - } + public double LocalTWR() => VesselState.thrustAvailable / (VesselState.mass * VesselState.gravityForce.magnitude); [ValueInfoItem("#MechJeb_ThrottleTWR", InfoItem.Category.Vessel, format = "F2", showInEditor = false)] //Throttle TWR - public double ThrottleTWR() - { - return VesselState.thrustCurrent / (VesselState.mass * VesselState.gravityForce.magnitude); - } + public double ThrottleTWR() => VesselState.thrustCurrent / (VesselState.mass * VesselState.gravityForce.magnitude); [ValueInfoItem("#MechJeb_AtmosphericPressurePa", InfoItem.Category.Misc, format = ValueInfoItem.SI, units = "Pa")] //Atmospheric pressure (Pa) - public double AtmosphericPressurekPA() - { - return FlightGlobals.getStaticPressure(VesselState.CoM) * 1000; - } + public double AtmosphericPressurekPA() => FlightGlobals.getStaticPressure(VesselState.CoM) * 1000; [ValueInfoItem("#MechJeb_AtmosphericPressure", InfoItem.Category.Misc, format = "F3", units = "atm")] //Atmospheric pressure - public double AtmosphericPressure() - { - return FlightGlobals.getStaticPressure(VesselState.CoM) * PhysicsGlobals.KpaToAtmospheres; - } + public double AtmosphericPressure() => FlightGlobals.getStaticPressure(VesselState.CoM) * PhysicsGlobals.KpaToAtmospheres; [ValueInfoItem("#MechJeb_Coordinates", InfoItem.Category.Surface)] //Coordinates - public string GetCoordinateString() - { - return Coordinates.ToStringDMS(VesselState.latitude, VesselState.longitude, true); - } + public string GetCoordinateString() => Coordinates.ToStringDMS(VesselState.latitude, VesselState.longitude, true); - public string OrbitSummary(Orbit o) - { - return o.eccentricity > 1 ? $"hyperbolic, Pe = {o.PeA.ToSI()}m" : $"{o.PeA.ToSI()}m x {o.ApA.ToSI()}m"; - } + public string OrbitSummary(Orbit o) => o.eccentricity > 1 ? $"hyperbolic, Pe = {o.PeA.ToSI()}m" : $"{o.PeA.ToSI()}m x {o.ApA.ToSI()}m"; - public string OrbitSummaryWithInclination(Orbit o) - { - return OrbitSummary(o) + ", inc. " + o.inclination.ToString("F1") + "º"; - } + public string OrbitSummaryWithInclination(Orbit o) => OrbitSummary(o) + ", inc. " + o.inclination.ToString("F1") + "º"; [ValueInfoItem("#MechJeb_MeanAnomaly", InfoItem.Category.Orbit, format = ValueInfoItem.ANGLE)] //Mean Anomaly - public double MeanAnomaly() - { - return Orbit.meanAnomaly * UtilMath.Rad2Deg; - } + public double MeanAnomaly() => Orbit.meanAnomaly * UtilMath.Rad2Deg; [ValueInfoItem("#MechJeb_Orbit", InfoItem.Category.Orbit)] //Orbit - public string CurrentOrbitSummary() - { - return OrbitSummary(Orbit); - } + public string CurrentOrbitSummary() => OrbitSummary(Orbit); [ValueInfoItem("#MechJeb_TargetOrbit", InfoItem.Category.Target)] //Target orbit public string TargetOrbitSummary() @@ -119,10 +90,7 @@ public string TargetOrbitSummary() } [ValueInfoItem("#MechJeb_OrbitWithInc", InfoItem.Category.Orbit, description = "#MechJeb_OrbitWithInc_desc")] //Orbit||Orbit shape w/ inc. - public string CurrentOrbitSummaryWithInclination() - { - return OrbitSummaryWithInclination(Orbit); - } + public string CurrentOrbitSummaryWithInclination() => OrbitSummaryWithInclination(Orbit); [ValueInfoItem("#MechJeb_TargetOrbitWithInc", InfoItem.Category.Target, description = "#MechJeb_TargetOrbitWithInc_desc")] //Target orbit|Target orbit shape w/ inc. @@ -134,24 +102,15 @@ public string TargetOrbitSummaryWithInclination() [ValueInfoItem("#MechJeb_OrbitalEnergy", InfoItem.Category.Orbit, description = "#MechJeb_OrbitalEnergy_desc", format = ValueInfoItem.SI, units = "J/kg")] //Orbital energy||Specific orbital energy - public double OrbitalEnergy() - { - return Orbit.orbitalEnergy; - } + public double OrbitalEnergy() => Orbit.orbitalEnergy; [ValueInfoItem("#MechJeb_PotentialEnergy", InfoItem.Category.Orbit, description = "#MechJeb_PotentialEnergy_desc", format = ValueInfoItem.SI, units = "J/kg")] //Potential energy||Specific potential energy - public double PotentialEnergy() - { - return -Orbit.referenceBody.gravParameter / Orbit.radius; - } + public double PotentialEnergy() => -Orbit.referenceBody.gravParameter / Orbit.radius; [ValueInfoItem("#MechJeb_KineticEnergy", InfoItem.Category.Orbit, description = "#MechJeb_KineticEnergy_desc", format = ValueInfoItem.SI, units = "J/kg")] //Kinetic energy||Specific kinetic energy - public double KineticEnergy() - { - return Orbit.orbitalEnergy + Orbit.referenceBody.gravParameter / Orbit.radius; - } + public double KineticEnergy() => Orbit.orbitalEnergy + Orbit.referenceBody.gravParameter / Orbit.radius; //TODO: consider turning this into a binary search [ValueInfoItem("#MechJeb_TimeToImpact", InfoItem.Category.Misc)] //Time to impact @@ -306,22 +265,13 @@ public double RCSDeltaVVacuum() } [ValueInfoItem("#MechJeb_AngularVelocity", InfoItem.Category.Vessel, showInEditor = false, showInFlight = true)] //Angular Velocity - public string angularVelocity() - { - return MuUtils.PrettyPrint(VesselState.angularVelocityAvg.value.xzy * UtilMath.Rad2Deg) + "°/s"; - } + public string angularVelocity() => MuUtils.PrettyPrint(VesselState.angularVelocityAvg.value.xzy * UtilMath.Rad2Deg) + "°/s"; [ValueInfoItem("#MechJeb_CurrentAcceleration", InfoItem.Category.Vessel, format = ValueInfoItem.SI, units = "m/s²")] //Current acceleration - public double CurrentAcceleration() - { - return CurrentThrust() / (1000 * VesselMass()); - } + public double CurrentAcceleration() => CurrentThrust() / (1000 * VesselMass()); [ValueInfoItem("#MechJeb_CurrentThrust", InfoItem.Category.Vessel, format = ValueInfoItem.SI, units = "N")] //Current thrust - public double CurrentThrust() - { - return VesselState.thrustCurrent * 1000; - } + public double CurrentThrust() => VesselState.thrustCurrent * 1000; [ValueInfoItem("#MechJeb_TimeToSoIWwitch", InfoItem.Category.Orbit)] //Time to SoI switch public string TimeToSOITransition() @@ -332,28 +282,16 @@ public string TimeToSOITransition() } [ValueInfoItem("#MechJeb_SurfaceGravity", InfoItem.Category.Surface, format = ValueInfoItem.SI, units = "m/s²")] //Surface gravity - public double SurfaceGravity() - { - return MainBody.GeeASL * 9.81; - } + public double SurfaceGravity() => MainBody.GeeASL * 9.81; [ValueInfoItem("#MechJeb_EscapeVelocity", InfoItem.Category.Orbit, format = ValueInfoItem.SI, siSigFigs = 3, units = "m/s")] //Escape velocity - public double EscapeVelocity() - { - return Math.Sqrt(2 * MainBody.gravParameter / VesselState.radius); - } + public double EscapeVelocity() => Math.Sqrt(2 * MainBody.gravParameter / VesselState.radius); [ValueInfoItem("#MechJeb_VesselName", InfoItem.Category.Vessel, showInEditor = false)] //Vessel name - public string VesselName() - { - return Vessel.vesselName; - } + public string VesselName() => Vessel.vesselName; [ValueInfoItem("#MechJeb_VesselType", InfoItem.Category.Vessel, showInEditor = false)] //Vessel type - public string VesselType() - { - return Vessel != null ? Vessel.vesselType.displayDescription() : "-"; - } + public string VesselType() => Vessel != null ? Vessel.vesselType.displayDescription() : "-"; [ValueInfoItem("#MechJeb_VesselMass", InfoItem.Category.Vessel, format = "F3", units = "t", showInEditor = true)] //Vessel mass public double VesselMass() @@ -375,31 +313,19 @@ public string MaximumVesselMass() } [ValueInfoItem("#MechJeb_DryMass", InfoItem.Category.Vessel, showInEditor = true, format = "F3", units = "t")] //Dry mass - public double DryMass() - { - return parts.Where(p => p.IsPhysicallySignificant()).Sum(p => p.mass + p.GetPhysicslessChildMass()); - } + public double DryMass() => parts.Where(p => p.IsPhysicallySignificant()).Sum(p => p.mass + p.GetPhysicslessChildMass()); [ValueInfoItem("#MechJeb_LiquidFuelandOxidizerMass", InfoItem.Category.Vessel, showInEditor = true, format = "F2", units = "t")] //Liquid fuel & oxidizer mass - public double LiquidFuelAndOxidizerMass() - { - return Vessel.TotalResourceMass("LiquidFuel") + Vessel.TotalResourceMass("Oxidizer"); - } + public double LiquidFuelAndOxidizerMass() => Vessel.TotalResourceMass("LiquidFuel") + Vessel.TotalResourceMass("Oxidizer"); [ValueInfoItem("#MechJeb_MonopropellantMass", InfoItem.Category.Vessel, showInEditor = true, format = "F2", units = "kg")] //Monopropellant mass - public double MonoPropellantMass() - { - return Vessel.TotalResourceMass("MonoPropellant"); - } + public double MonoPropellantMass() => Vessel.TotalResourceMass("MonoPropellant"); [ValueInfoItem("#MechJeb_TotalElectricCharge", InfoItem.Category.Vessel, showInEditor = true, format = ValueInfoItem.SI, units = "Ah")] //Total electric charge - public double TotalElectricCharge() - { - return Vessel.TotalResourceAmount(PartResourceLibrary.ElectricityHashcode); - } + public double TotalElectricCharge() => Vessel.TotalResourceAmount(PartResourceLibrary.ElectricityHashcode); [ValueInfoItem("#MechJeb_MaxThrust", InfoItem.Category.Vessel, format = ValueInfoItem.SI, units = "N", showInEditor = true)] //Max thrust public double MaxThrust() @@ -434,23 +360,14 @@ public double MinThrust() [ValueInfoItem("#MechJeb_MaxAcceleration", InfoItem.Category.Vessel, format = ValueInfoItem.SI, units = "m/s²", showInEditor = true)] //Max acceleration - public double MaxAcceleration() - { - return MaxThrust() / (1000 * VesselMass()); - } + public double MaxAcceleration() => MaxThrust() / (1000 * VesselMass()); [ValueInfoItem("#MechJeb_MinAcceleration", InfoItem.Category.Vessel, format = ValueInfoItem.SI, units = "m/s²", showInEditor = true)] //Min acceleration - public double MinAcceleration() - { - return MinThrust() / (1000 * VesselMass()); - } + public double MinAcceleration() => MinThrust() / (1000 * VesselMass()); [ValueInfoItem("#MechJeb_Gforce", InfoItem.Category.Vessel, format = "F4", units = "g", showInEditor = true)] //G force - public double Acceleration() - { - return Vessel != null ? Vessel.geeForce : 0; - } + public double Acceleration() => Vessel != null ? Vessel.geeForce : 0; [ValueInfoItem("#MechJeb_DragCoefficient", InfoItem.Category.Vessel, format = "F3", showInEditor = true)] //Drag Coefficient public double DragCoefficient() @@ -483,10 +400,7 @@ public double DragCoefficient() } [ValueInfoItem("#MechJeb_PartCount", InfoItem.Category.Vessel, showInEditor = true)] //Part count - public int PartCount() - { - return parts.Count; - } + public int PartCount() => parts.Count; [ValueInfoItem("#MechJeb_MaxPartCount", InfoItem.Category.Vessel, showInEditor = true)] //Max part count public string MaxPartCount() @@ -499,40 +413,22 @@ public string MaxPartCount() } [ValueInfoItem("#MechJeb_PartCountDivideMaxParts", InfoItem.Category.Vessel, showInEditor = true)] //Part count / Max parts - public string PartCountAndMaxPartCount() - { - return string.Format("{0} / {1}", PartCount().ToString(), MaxPartCount()); - } + public string PartCountAndMaxPartCount() => string.Format("{0} / {1}", PartCount().ToString(), MaxPartCount()); [ValueInfoItem("#MechJeb_StrutCount", InfoItem.Category.Vessel, showInEditor = true)] //Strut count - public int StrutCount() - { - return parts.Count(p => p is CompoundPart && p.Modules.GetModule()); - } + public int StrutCount() => parts.Count(p => p is CompoundPart && p.Modules.GetModule()); [ValueInfoItem("#MechJeb_FuelLinesCount", InfoItem.Category.Vessel, showInEditor = true)] //Fuel Lines count - public int FuelLinesCount() - { - return parts.Count(p => p is CompoundPart && p.Modules.GetModule()); - } + public int FuelLinesCount() => parts.Count(p => p is CompoundPart && p.Modules.GetModule()); [ValueInfoItem("#MechJeb_VesselCost", InfoItem.Category.Vessel, showInEditor = true, format = ValueInfoItem.SI, units = "$")] //Vessel cost - public double VesselCost() - { - return parts.Sum(p => p.partInfo.cost) * 1000; - } + public double VesselCost() => parts.Sum(p => p.partInfo.cost) * 1000; [ValueInfoItem("#MechJeb_CrewCount", InfoItem.Category.Vessel)] //Crew count - public int CrewCount() - { - return Vessel.GetCrewCount(); - } + public int CrewCount() => Vessel.GetCrewCount(); [ValueInfoItem("#MechJeb_CrewCapacity", InfoItem.Category.Vessel, showInEditor = true)] //Crew capacity - public int CrewCapacity() - { - return parts.Sum(p => p.CrewCapacity); - } + public int CrewCapacity() => parts.Sum(p => p.CrewCapacity); [ValueInfoItem("#MechJeb_DistanceToTarget", InfoItem.Category.Target)] //Distance to target public string TargetDistance() @@ -780,10 +676,7 @@ public string TargetTrueLongitude() } [ValueInfoItem("#MechJeb_AtmosphericDrag", InfoItem.Category.Vessel, format = ValueInfoItem.SI, units = "m/s²")] //Atmospheric drag - public double AtmosphericDrag() - { - return VesselState.drag; - } + public double AtmosphericDrag() => VesselState.drag; [ValueInfoItem("#MechJeb_SynodicPeriod", InfoItem.Category.Target)] //Synodic period public string SynodicPeriod() @@ -858,10 +751,7 @@ public string TimeToEquatorialDescendingNode() } [ValueInfoItem("#MechJeb_CircularOrbitSpeed", InfoItem.Category.Orbit, format = ValueInfoItem.SI, units = "m/s")] //Circular orbit speed - public double CircularOrbitSpeed() - { - return OrbitalManeuverCalculator.CircularOrbitSpeed(MainBody, VesselState.radius); - } + public double CircularOrbitSpeed() => OrbitalManeuverCalculator.CircularOrbitSpeed(MainBody, VesselState.radius); [Persistent(pass = (int)Pass.GLOBAL)] public bool showStagedMass = false; @@ -878,6 +768,9 @@ public double CircularOrbitSpeed() [Persistent(pass = (int)Pass.GLOBAL)] public bool showThrust = false; + // don't persist this so it defaults to off on scene-change + public bool showRcs = false; + [Persistent(pass = (int)Pass.GLOBAL)] public bool showVacInitialTWR = true; @@ -934,10 +827,7 @@ public void AllStageStats() stageStatsHelper.AllStageStats(); } - public void UpdateItems() - { - stageStatsHelper?.UpdateStageStats(); - } + public void UpdateItems() => stageStatsHelper?.UpdateStageStats(); [ValueInfoItem("#MechJeb_StageDv_vac", InfoItem.Category.Vessel, format = "F0", units = "m/s", showInEditor = true)] //Stage ΔV (vac) public double StageDeltaVVacuum() @@ -1273,9 +1163,6 @@ private static GUIStyle separatorStyle } [GeneralInfoItem("#MechJeb_Separator", InfoItem.Category.Misc, showInEditor = true)] //Separator - public void HorizontalSeparator() - { - GUILayout.Label("", separatorStyle, GUILayout.ExpandWidth(true), GUILayout.Height(2)); - } + public void HorizontalSeparator() => GUILayout.Label("", separatorStyle, GUILayout.ExpandWidth(true), GUILayout.Height(2)); } } diff --git a/MechJeb2/MechJebStageStatsHelper.cs b/MechJeb2/MechJebStageStatsHelper.cs index 82e5aeff..1486b026 100644 --- a/MechJeb2/MechJebStageStatsHelper.cs +++ b/MechJeb2/MechJebStageStatsHelper.cs @@ -15,12 +15,12 @@ namespace MuMech // Eventually, we should figure out how to not need that store at all. public class MechJebStageStatsHelper { - private bool showStagedMass, showBurnedMass, showInitialMass, showFinalMass, showThrust, showVacInitialTWR, showAtmoInitialTWR; - private bool showAtmoMaxTWR, showVacMaxTWR, showAtmoDeltaV, showVacDeltaV, showTime, showISP, showEmpty, timeSeconds, liveSLT; - private bool oldFFS; - private int TWRBody; - private float altSLTScale, machScale; - private int StageDisplayState { get => infoItems.StageDisplayState; set => infoItems.StageDisplayState = value; } + private bool showStagedMass, showBurnedMass, showInitialMass, showFinalMass, showThrust, showVacInitialTWR, showAtmoInitialTWR; + private bool showAtmoMaxTWR, showVacMaxTWR, showAtmoDeltaV, showVacDeltaV, showTime, showISP, showEmpty, showRcs, timeSeconds, liveSLT; + private bool oldFFS; + private int TWRBody; + private float altSLTScale, machScale; + private int StageDisplayState { get => infoItems.StageDisplayState; set => infoItems.StageDisplayState = value; } private readonly MechJebModuleInfoItems infoItems; private readonly MechJebCore core; private readonly MechJebModuleStageStats stats; @@ -43,6 +43,7 @@ public MechJebStageStatsHelper(MechJebModuleInfoItems items) showTime = items.showTime; showISP = items.showISP; showThrust = items.showThrust; + showRcs = items.showRcs; showEmpty = items.showEmpty; timeSeconds = items.timeSeconds; liveSLT = items.liveSLT; @@ -59,8 +60,8 @@ public MechJebStageStatsHelper(MechJebModuleInfoItems items) private enum StageData { - KSPStage, InitialMass, FinalMass, StagedMass, BurnedMass, Thrust, VacInitialTWR, VacMaxTWR, AtmoInitialTWR, AtmoMaxTWR, Isp, AtmoDeltaV, - VacDeltaV, Time + KSPStage, InitialMass, FinalMass, StagedMass, BurnedMass, Thrust, VacInitialTWR, VacMaxTWR, AtmoInitialTWR, AtmoMaxTWR, + Isp, AtmoDeltaV, VacDeltaV, Time } private static readonly List AllStages = new List @@ -99,7 +100,6 @@ private enum StageData private void InitializeStageInfo() { - const string SPACING = " "; stageVisibility.Clear(); stageDisplayInfo.Clear(); foreach (StageData ident in AllStages) @@ -108,6 +108,13 @@ private void InitializeStageInfo() stageDisplayInfo.Add(ident, new List(16)); } + InitalizeStageHeaderData(); + } + + private void InitalizeStageHeaderData() + { + const string SPACING = " "; + stageHeaderData.Clear(); stageHeaderData.Add(StageData.KSPStage, "Stage" + SPACING); stageHeaderData.Add(StageData.InitialMass, CachedLocalizer.Instance.MechJeb_InfoItems_StatsColumn1 + SPACING); @@ -120,8 +127,8 @@ private void InitializeStageInfo() stageHeaderData.Add(StageData.AtmoInitialTWR, CachedLocalizer.Instance.MechJeb_InfoItems_StatsColumn7 + SPACING); stageHeaderData.Add(StageData.AtmoMaxTWR, CachedLocalizer.Instance.MechJeb_InfoItems_StatsColumn8 + SPACING); stageHeaderData.Add(StageData.Isp, CachedLocalizer.Instance.MechJeb_InfoItems_StatsColumn9 + SPACING); - stageHeaderData.Add(StageData.AtmoDeltaV, CachedLocalizer.Instance.MechJeb_InfoItems_StatsColumn10 + SPACING); - stageHeaderData.Add(StageData.VacDeltaV, CachedLocalizer.Instance.MechJeb_InfoItems_StatsColumn11 + SPACING); + stageHeaderData.Add(StageData.AtmoDeltaV, (showRcs ? "RCS ∆Vmin" : CachedLocalizer.Instance.MechJeb_InfoItems_StatsColumn10) + SPACING); + stageHeaderData.Add(StageData.VacDeltaV, (showRcs ? "RCS ∆Vmax" : CachedLocalizer.Instance.MechJeb_InfoItems_StatsColumn11) + SPACING); stageHeaderData.Add(StageData.Time, CachedLocalizer.Instance.MechJeb_InfoItems_StatsColumn12 + SPACING); } @@ -129,10 +136,33 @@ private void GatherStages(List stages) { stages.Clear(); for (int i = 0; i < stats.AtmoStats.Count; i++) - if (infoItems.showEmpty || stats.AtmoStats[i].DeltaV > 0) + if (infoItems.showEmpty) + stages.Add(i); + else if (infoItems.showRcs && stats.AtmoStats[i].MaxRcsDeltaV > 0) + stages.Add(i); + else if (!infoItems.showRcs && stats.AtmoStats[i].DeltaV > 0) stages.Add(i); } + private double _isp(int index) => showRcs ? stats.VacStats[index].RcsISP : stats.VacStats[index].Isp; + private double _thrust(int index) => showRcs ? stats.VacStats[index].RcsThrust : stats.VacStats[index].Thrust; + private double _burnedMass(int index) => showRcs ? stats.VacStats[index].RcsMass : stats.VacStats[index].ResourceMass; + private double _deltaTime(int index) => showRcs ? stats.VacStats[index].RcsDeltaTime : stats.VacStats[index].DeltaTime; + private double _atmoDv(int index) => showRcs ? stats.VacStats[index].MinRcsDeltaV : stats.AtmoStats[index].DeltaV; + private double _vacDv(int index) => showRcs ? stats.VacStats[index].MaxRcsDeltaV : stats.VacStats[index].DeltaV; + + private double _vacStartTWR(int index, double geeASL) => + showRcs ? stats.VacStats[index].RcsStartTWR(geeASL) : stats.VacStats[index].StartTWR(geeASL); + + private double _vacEndTWR(int index, double geeASL) => + showRcs ? stats.VacStats[index].RcsMaxTWR(geeASL) : stats.VacStats[index].MaxTWR(geeASL); + + private double _atmoStartTWR(int index, double geeASL) => + showRcs ? stats.AtmoStats[index].RcsStartTWR(geeASL) : stats.AtmoStats[index].StartTWR(geeASL); + + private double _atmoEndTWR(int index, double geeASL) => + showRcs ? stats.AtmoStats[index].RcsMaxTWR(geeASL) : stats.AtmoStats[index].MaxTWR(geeASL); + private void UpdateStageDisplayInfo(List stages, double geeASL) { foreach (KeyValuePair> kvp in stageDisplayInfo) @@ -145,22 +175,22 @@ private void UpdateStageDisplayInfo(List stages, double geeASL) if (stageVisibility[StageData.FinalMass]) stageDisplayInfo[StageData.FinalMass].Add($"{stats.AtmoStats[index].EndMass:F3} t "); if (stageVisibility[StageData.StagedMass]) stageDisplayInfo[StageData.StagedMass].Add($"{stats.AtmoStats[index].StagedMass:F3} t "); if (stageVisibility[StageData.BurnedMass]) - stageDisplayInfo[StageData.BurnedMass].Add($"{stats.AtmoStats[index].ResourceMass:F3} t "); - if (stageVisibility[StageData.Thrust]) stageDisplayInfo[StageData.Thrust].Add($"{stats.AtmoStats[index].Thrust:F3} kN "); + stageDisplayInfo[StageData.BurnedMass].Add($"{_burnedMass(index):F3} t "); + if (stageVisibility[StageData.Thrust]) stageDisplayInfo[StageData.Thrust].Add($"{_thrust(index):F3} kN "); if (stageVisibility[StageData.VacInitialTWR]) - stageDisplayInfo[StageData.VacInitialTWR].Add($"{stats.VacStats[index].StartTWR(geeASL):F2} "); - if (stageVisibility[StageData.VacMaxTWR]) stageDisplayInfo[StageData.VacMaxTWR].Add($"{stats.VacStats[index].MaxTWR(geeASL):F2} "); + stageDisplayInfo[StageData.VacInitialTWR].Add($"{_vacStartTWR(index, geeASL):F2} "); + if (stageVisibility[StageData.VacMaxTWR]) stageDisplayInfo[StageData.VacMaxTWR].Add($"{_vacEndTWR(index, geeASL):F2} "); if (stageVisibility[StageData.AtmoInitialTWR]) - stageDisplayInfo[StageData.AtmoInitialTWR].Add($"{stats.AtmoStats[index].StartTWR(geeASL):F2} "); + stageDisplayInfo[StageData.AtmoInitialTWR].Add($"{_atmoStartTWR(index, geeASL):F2} "); if (stageVisibility[StageData.AtmoMaxTWR]) - stageDisplayInfo[StageData.AtmoMaxTWR].Add($"{stats.AtmoStats[index].MaxTWR(geeASL):F2} "); - if (stageVisibility[StageData.Isp]) stageDisplayInfo[StageData.Isp].Add($"{stats.AtmoStats[index].Isp:F2} "); - if (stageVisibility[StageData.AtmoDeltaV]) stageDisplayInfo[StageData.AtmoDeltaV].Add($"{stats.AtmoStats[index].DeltaV:F0} m/s "); - if (stageVisibility[StageData.VacDeltaV]) stageDisplayInfo[StageData.VacDeltaV].Add($"{stats.VacStats[index].DeltaV:F0} m/s "); + stageDisplayInfo[StageData.AtmoMaxTWR].Add($"{_atmoEndTWR(index, geeASL):F2} "); + if (stageVisibility[StageData.Isp]) stageDisplayInfo[StageData.Isp].Add($"{_isp(index):F2} "); + if (stageVisibility[StageData.AtmoDeltaV]) stageDisplayInfo[StageData.AtmoDeltaV].Add($"{_atmoDv(index):F0} m/s "); + if (stageVisibility[StageData.VacDeltaV]) stageDisplayInfo[StageData.VacDeltaV].Add($"{_vacDv(index):F0} m/s "); if (stageVisibility[StageData.Time]) stageDisplayInfo[StageData.Time].Add(timeSeconds - ? $"{stats.AtmoStats[index].DeltaTime:F2}s " - : $"{GuiUtils.TimeToDHMS(stats.AtmoStats[index].DeltaTime, 1)} "); + ? $"{_deltaTime(index):F2}s " + : $"{GuiUtils.TimeToDHMS(_deltaTime(index), 1)} "); } } @@ -190,9 +220,7 @@ public void AllStageStats() GUILayout.BeginHorizontal(); GUILayout.Label(CachedLocalizer.Instance.MechJeb_InfoItems_label1); //"Stage stats" - if (GUILayout.Button( - oldFFS ? "Old" : "New", - GUILayout.ExpandWidth(false))) + if (GUILayout.Button(oldFFS ? "Old" : "New", GUILayout.ExpandWidth(false))) { oldFFS = !oldFFS; stats.OldFFS = oldFFS; @@ -218,6 +246,13 @@ public void AllStageStats() SetVisibility(StageDisplayState); } + if (GUILayout.Button(showRcs ? "RCS" : "Engine", GUILayout.ExpandWidth(false))) + { + showRcs = !showRcs; + infoItems.showRcs = showRcs; + InitalizeStageHeaderData(); + } + if (!HighLogic.LoadedSceneIsEditor) { if (GUILayout.Button( @@ -357,6 +392,7 @@ private void SetVisibility(int state) break; case 1: SetAllStageVisibility(true); + stageVisibility[StageData.AtmoMaxTWR] = false; stageVisibility[StageData.Thrust] = false; stageVisibility[StageData.StagedMass] = false; stageVisibility[StageData.BurnedMass] = false;