public Option <DeltaVStageInfoAdapter> StageDeltaV(long stage) { DeltaVStageInfo stageInfo = vessel.VesselDeltaV.GetStage((int)stage); return(stageInfo != null ? new Option <DeltaVStageInfoAdapter>(new DeltaVStageInfoAdapter(this, stageInfo)) : new Option <DeltaVStageInfoAdapter>()); }
/// <summary> /// Construct a one-stage deltaV calculator /// </summary> /// <param name="shared"></param> /// <param name="dv"></param> public DeltaVCalc(SharedObjects shared, DeltaVStageInfo dv) { this.shared = shared; // It is possible for the KSP API to give us a DeltaVStageInfo of null for // stages that currently don't have any dV (if they are just a stage with // a decoupler alone, for example). If this value is null, then we should // behave as if the dV values are all zero: stageDV = dv; RegisterInitializer(InitializeSuffixes); }
public void ManeuverProvider() { if (FlightGlobals.ActiveVessel == null) { return; } myManeuver.timeToNextManeuver = 0.0f; myManeuver.deltaVNextManeuver = 0.0f; myManeuver.durationNextManeuver = 0.0f; myManeuver.deltaVTotal = 0.0f; myManeuver.headingNextManeuver = 0.0f; myManeuver.pitchNextManeuver = 0.0f; if (FlightGlobals.ActiveVessel.patchedConicSolver != null) { if (FlightGlobals.ActiveVessel.patchedConicSolver.maneuverNodes != null) { System.Collections.Generic.List <ManeuverNode> maneuvers = FlightGlobals.ActiveVessel.patchedConicSolver.maneuverNodes; if (maneuvers.Count > 0) { myManeuver.timeToNextManeuver = (float)(maneuvers[0].UT - Planetarium.GetUniversalTime()); myManeuver.deltaVNextManeuver = (float)maneuvers[0].DeltaV.magnitude; WorldVecToNavHeading(FlightGlobals.ActiveVessel, maneuvers[0].GetBurnVector(maneuvers[0].patch), out myManeuver.headingNextManeuver, out myManeuver.pitchNextManeuver); DeltaVStageInfo currentStageInfo = getCurrentStageDeltaV(); if (currentStageInfo != null) { //For now, use a simple crossmultiplication to compute the estimated burn time based on the current stage only myManeuver.durationNextManeuver = (float)(maneuvers[0].DeltaV.magnitude * currentStageInfo.stageBurnTime) / currentStageInfo.deltaVActual; } foreach (ManeuverNode maneuver in maneuvers) { myManeuver.deltaVTotal += (float)maneuver.DeltaV.magnitude; } } } } if (maneuverChannel != null) { maneuverChannel.Fire(OutboundPackets.ManeuverData, myManeuver); } }
/// <summary> /// Calculate the burn time for a given vessel and delta V amount /// </summary> /// <param name="dvCalc">Stock delta V object from a vessel</param> /// <returns> /// null if not ready yet; /// PositiveInfinity if not enough fuel to do the burn; /// NaN if we can't burn at all; /// otherwise number of seconds required for the burn /// </returns> public double?Duration(VesselDeltaV dvCalc) { if (dvCalc != null && totalDeltaV > 0) { if (!dvCalc.IsReady) { return(null); } else if (totalDeltaV > dvCalc.TotalDeltaVActual) { return(double.PositiveInfinity); } else { double remaining = totalDeltaV; double t = 0; for (int i = 0; i < dvCalc.OperatingStageInfo.Count; ++i) { DeltaVStageInfo stg = dvCalc.OperatingStageInfo[i]; double exhVel = stg.ispActual * PhysicsGlobals.GravitationalAcceleration; if (remaining >= stg.deltaVActual) { // We need to expend this whole stage, so just add its complete time remaining -= stg.deltaVActual; t += stg.stageBurnTime; } else { // We only need part of this stage, so appeal to Tsiolkovsky t += exhVel * stg.startMass * (1.0 - Math.Exp(-remaining / exhVel)) / stg.thrustActual; break; } } return(t); } } else { // No such burn return(double.NaN); } }
public void BurnTimeProvider() { DeltaVStageInfo currentStageInfo = getCurrentStageDeltaV(); if (currentStageInfo != null) { myBurnTimeStruct.stageBurnTime = (float)currentStageInfo.stageBurnTime; myBurnTimeStruct.totalBurnTime = (float)FlightGlobals.ActiveVessel.VesselDeltaV.TotalBurnTime; } else { myBurnTimeStruct.stageBurnTime = 0; myBurnTimeStruct.totalBurnTime = 0; } if (burnTimeChannel != null) { burnTimeChannel.Fire(OutboundPackets.BurnTime, myBurnTimeStruct); } }
public void DeltaVProvider() { DeltaVStageInfo currentStageInfo = getCurrentStageDeltaV(); if (currentStageInfo != null) { myDeltaVStruct.stageDeltaV = (float)currentStageInfo.deltaVActual; myDeltaVStruct.totalDeltaV = (float)FlightGlobals.ActiveVessel.VesselDeltaV.TotalDeltaVActual; } else { myDeltaVStruct.stageDeltaV = 0; myDeltaVStruct.totalDeltaV = 0; } if (deltaVChannel != null) { deltaVChannel.Fire(OutboundPackets.DeltaV, myDeltaVStruct); } }
public void DeltaVEnvProvider() { DeltaVStageInfo currentStageInfo = getCurrentStageDeltaV(); if (currentStageInfo != null) { myDeltaVEnvStruct.stageDeltaVASL = (float)currentStageInfo.deltaVatASL; myDeltaVEnvStruct.stageDeltaVVac = (float)currentStageInfo.deltaVinVac; myDeltaVEnvStruct.totalDeltaVASL = (float)FlightGlobals.ActiveVessel.VesselDeltaV.TotalDeltaVASL; myDeltaVEnvStruct.totalDeltaVVac = (float)FlightGlobals.ActiveVessel.VesselDeltaV.TotalDeltaVVac; } else { myDeltaVEnvStruct.stageDeltaVASL = 0; myDeltaVEnvStruct.stageDeltaVVac = 0; myDeltaVEnvStruct.totalDeltaVASL = 0; myDeltaVEnvStruct.totalDeltaVVac = 0; } if (deltaVEnvChannel != null) { deltaVEnvChannel.Fire(OutboundPackets.DeltaVEnv, myDeltaVEnvStruct); } }
//Return the DeltaVStageInfo of the first stage to consider for deltaV and burn time computation //Can return null when no deltaV is available (for instance in EVA). private DeltaVStageInfo getCurrentStageDeltaV() { if (FlightGlobals.ActiveVessel.VesselDeltaV == null) { return(null); //This happen in EVA for instance. } DeltaVStageInfo currentStageInfo = null; try { if (FlightGlobals.ActiveVessel.currentStage == FlightGlobals.ActiveVessel.VesselDeltaV.OperatingStageInfo.Count) { // Rocket has not taken off, use first stage with deltaV (to avoid stage of only stabilizer) for (int i = FlightGlobals.ActiveVessel.VesselDeltaV.OperatingStageInfo.Count - 1; i >= 0; i--) { currentStageInfo = FlightGlobals.ActiveVessel.VesselDeltaV.GetStage(i); if (currentStageInfo.deltaVActual > 0) { break; } } } else { currentStageInfo = FlightGlobals.ActiveVessel.VesselDeltaV.GetStage(FlightGlobals.ActiveVessel.currentStage); } } catch (NullReferenceException) { // This happens when reverting a flight. // FlightGlobals.ActiveVessel.VesselDeltaV.OperatingStageInfo is not null but using it produce a // NullReferenceException in KSP code. This is probably due to the fact that the rocket is not fully initialized. } return(currentStageInfo); }
internal DeltaVStageInfoAdapter(VesselAdapter vesselAdapter, DeltaVStageInfo deltaVStageInfo) { this.vesselAdapter = vesselAdapter; this.deltaVStageInfo = deltaVStageInfo; }
private void UpdateStockDeltaV() { Profiler.BeginSample("UpdateStockDV"); if (!_settings.DisableStockDeltaV) { return; } _updateDeltaV = false; if (HighLogic.LoadedSceneIsFlight) { if (FlightGlobals.ActiveVessel == null) { return; } } if (HighLogic.LoadedSceneIsEditor) { if (EditorLogic.fetch == null || EditorLogic.fetch.ship == null) { return; } } if (_vesselDeltaV == null) { return; } if (stages == null) { return; } if (_vesselDeltaV.enabled) { _vesselDeltaV.enabled = false; } List <DeltaVStageInfo> stageInfo = _vesselDeltaV.stageInfo; stageInfo.Clear(); for (int i = stages.Length - 1; i >= 0; i--) { if (HighLogic.LoadedSceneIsFlight) { stageInfo.Add(new DeltaVStageInfo((ShipConstruct)null, stages[i].number, _vesselDeltaV)); } else if (HighLogic.LoadedSceneIsEditor) { stageInfo.Add(new DeltaVStageInfo((Vessel)null, stages[i].number, _vesselDeltaV)); } } if (_vesselDeltaVTotalDVActual != null && _vesselDeltaVTotalDVASL != null && _vesselDeltaVTotalDVVac != null) { _vesselDeltaVTotalDVActual.SetValue(_vesselDeltaV, _lastStage.totalDeltaV); _vesselDeltaVTotalDVASL.SetValue(_vesselDeltaV, _lastStage.totalDeltaV); _vesselDeltaVTotalDVVac.SetValue(_vesselDeltaV, _lastStage.totalDeltaV); } _vesselDeltaV.lowestStageWithDeltaV = int.MaxValue; for (int i = _vesselDeltaV.stageInfo.Count - 1; i >= 0; i--) { DeltaVStageInfo info = _vesselDeltaV.stageInfo[i]; Stage stage = GetStage(info.stage); if (stage == null) { continue; } info.deltaVActual = (float)stage.deltaV; info.deltaVatASL = info.deltaVActual; info.deltaVinVac = info.deltaVActual; double gee = HighLogic.LoadedSceneIsEditor ? _currentBody.GeeASL : FlightGlobals.currentMainBody.GeeASL; info.TWRActual = (float)(stage.actualThrust / (stage.startMass * gee * GRAVITY)); info.TWRASL = (float)(stage.thrust / (stage.startMass * gee * GRAVITY)); info.TWRVac = info.TWRASL; info.stageMass = (float)stage.mass; info.startMass = (float)stage.startMass; info.endMass = (float)stage.endMass; info.fuelMass = (float)stage.resourceMass; info.thrustActual = (float)stage.actualSimpleThrust; info.thrustASL = (float)stage.simpleThrust; info.thrustVac = info.thrustASL; info.vectoredThrustActual = (float)stage.actualThrust; info.vectoredThrustASL = (float)stage.thrust; info.vectoredThrustVac = info.vectoredThrustASL; info.ispActual = (float)stage.isp; info.ispASL = (float)stage.isp; info.ispVac = info.ispASL; info.totalExhaustVelocityActual = (float)stage.totalActualExhaustVelocity; info.totalExhaustVelocityASL = (float)stage.totalExhaustVelocity; info.totalExhaustVelocityVAC = info.totalExhaustVelocityASL; info.vectoredExhaustVelocityActual = stage.totalVectoredActualExhaustVelocity; info.vectoredExhaustVelocityASL = stage.totalVectoredExhaustVelocity; info.vectoredExhaustVelocityVAC = info.vectoredExhaustVelocityASL; info.stageBurnTime = (float)stage.time; if (stage.deltaV > 0) { if (stage.number < _vesselDeltaV.lowestStageWithDeltaV) { _vesselDeltaV.lowestStageWithDeltaV = stage.number; } } } Profiler.EndSample(); }
/// <summary> /// Fires when the simulator is updated /// Populates the KSPFields for PP tanks so their PartModules can do the scaling /// </summary> private void OnSimUpdate(VesselDeltaV dvCalc) { if (dvCalc == null) { return; } if (Paused) { pausedDeltaV = dvCalc; return; } string nodesErr = ""; getNodeStructureError(ref nodesErr); double totalMassChange = 0; for (int st = dvCalc.OperatingStageInfo.Count - 1; st >= 0; --st) { DeltaVStageInfo stage = dvCalc.OperatingStageInfo[st]; List <SmartTankPart> drained = new List <SmartTankPart>(drainedTanks(dvCalc, stage.stage)); int numTanks = drained.Count; if (stage != null && numTanks > 0) { if (stage.thrustVac <= 0) { // No thrust on this stage, so fuel doesn't make sense either. // Note that IdealTotalMass effectively is optional for the parts // to obey; if AutoScale is false, they can ignore it. for (int t = 0; t < numTanks; ++t) { // Reset all the tanks to minimum size with no thrust drained[t].nodesError = nodesErr; drained[t].IdealTotalMass = 0; if (drained[0].AutoScale) { totalMassChange -= partTotalMass(drained[t].part); } } } else { // This stage has thrust that we can balance against the fuel. // Add up the current procedural tank mass double currentProcTankMass = 0; for (int t = 0; t < numTanks; ++t) { currentProcTankMass += partTotalMass(drained[t].part); } // Determine the mass that the procedural parts can't change double nonProcMass = stage.startMass - currentProcTankMass + totalMassChange; if (nonProcMass < 0) { // Sanity check, this is negative a lot continue; } // Get the thrust this stage is configured to use double thrust = drained[0].Atmospheric ? stage.thrustASL : stage.thrustVac; // Calculate the mass to distribute among this stage's procedural tanks // This includes their wet AND dry mass! double targetProcTankMass = optimalTankMass( thrust, drained[0].bodyGravAccel, drained[0].targetTWR, nonProcMass ); // Assume we'll have our way if auto scaling, // otherwise use the existing mass if (drained[0].AutoScale) { double massChange = targetProcTankMass > 0 ? targetProcTankMass - currentProcTankMass : 0; totalMassChange += massChange; } // Distribute the mass evenly double massPerTank = targetProcTankMass / numTanks; for (int t = 0; t < numTanks; ++t) { drained[t].nodesError = nodesErr; drained[t].IdealTotalMass = massPerTank; } } } } }