//Allows for automatic initiation and termination of burns private void automation(ManeuverNode myNode) { //Auto shutdown if (autoShutdown) { if ((myNode.GetBurnVector(FlightGlobals.ActiveVessel.orbit).magnitude < 0.5 || offTarget(myNode.GetBurnVector(FlightGlobals.ActiveVessel.orbit), 7.6f)) && timeToBurn < 1) { autoShutdown = false; FlightInputHandler.state.mainThrottle = 0; Log.Info("(SG) Burn complete, throttle back."); } } //if (offTarget(myNode.GetBurnVector(FlightGlobals.ActiveVessel.orbit),7.2f)) Log.Info("Off target!"); //Auto burn if (autoBurn) { if (timeToBurn <= 5) { TimeWarp.SetRate(0, true); } if (timeToBurn <= 50 && TimeWarp.CurrentRateIndex > 2) { TimeWarp.SetRate(2, true); } if (timeToBurn <= 500 && TimeWarp.CurrentRateIndex > 3) { TimeWarp.SetRate(3, true); } if (timeToBurn <= 5000 && TimeWarp.CurrentRateIndex > 4) { TimeWarp.SetRate(4, true); } if (timeToBurn < 1 && FlightInputHandler.state.mainThrottle == 0 && !offTarget(myNode.GetBurnVector(FlightGlobals.ActiveVessel.orbit), 2f)) { //Add check for w/in 2 degrees autoBurn = false; FlightInputHandler.state.mainThrottle = 1; Log.Info("(SG) Node reached, commencing burn!"); } } //update min delta V every .2 seconds if (Planetarium.GetUniversalTime() > lastTime + 0.2) { lastTime = Planetarium.GetUniversalTime(); if (myNode.GetBurnVector(FlightGlobals.ActiveVessel.orbit).magnitude < minDv) { minDv = myNode.GetBurnVector(FlightGlobals.ActiveVessel.orbit).magnitude; } if (timeToBurn > 0) { minDv = myNode.DeltaV.magnitude; //suck it blue! } } }
public override AutopilotStep Drive(FlightCtrlState s) { if (node != null) { double dVLeft = node.GetBurnVector(orbit).magnitude; core.thrust.targetThrottle = 0; if (burnTriggered && alignedForBurn) { if (core.attitude.attitudeAngleFromTarget() < 90) { double timeConstant = (dVLeft > 10 || vesselState.minThrustAccel > 0.25 * vesselState.maxThrustAccel ? 0.5 : 2); core.thrust.ThrustForDV(dVLeft + core.node.tolerance, timeConstant); } else { alignedForBurn = false; } } else { //aim along the node core.attitude.attitudeTo(Vector3d.forward, AttitudeReference.MANEUVER_NODE, this); if (core.attitude.attitudeAngleFromTarget() < 2) { alignedForBurn = true; } } } return(this); }
void Update() { if (!HighLogic.LoadedSceneIsFlight) { return; } Vessel _vessel = FlightGlobals.ActiveVessel; string[] _keys = Enum.GetNames(typeof(QKey.Key)); int _length = _keys.Length; for (int _key = 1; _key < _length; _key++) { QKey.Key _getKey = (QKey.Key)_key; if (_getKey == QKey.Key.WarpToNode) { continue; } if (QKey.isKeyDown(_getKey)) { StartCoroutine(startSAS(QKey.GetAutoPilot(_getKey))); ScreenMessages.PostScreenMessage(string.Format("[{0}] {1}", MOD, QKey.GetText(_getKey)), 5, ScreenMessageStyle.UPPER_CENTER); Log(QKey.GetText(_getKey), "QSAS"); } } if (QKey.isKeyDown(QKey.Key.WarpToNode)) { if (_vessel.patchedConicSolver.maneuverNodes.Count != 0) { double _UT; ManeuverNode _manNode = _vessel.patchedConicSolver.maneuverNodes[0]; if (!QSettings.Instance.WarpToEnhanced) { _UT = _manNode.UT - 60; } else { double _estimatedBurnTime = _manNode.GetBurnVector(_vessel.orbit).magnitude / _vessel.specificAcceleration; _UT = _manNode.UT - (_estimatedBurnTime / 2) - 15; } if (Planetarium.GetUniversalTime() > _UT) { ScreenMessages.PostScreenMessage(string.Format("[{0}] No need to time warp!", MOD), 5, ScreenMessageStyle.UPPER_CENTER); Log("No need to time warp!", "QSAS"); return; } TimeWarp.fetch.WarpTo(_UT); Log(QKey.GetText(QKey.Key.WarpToNode), "QSAS"); } else { ScreenMessages.PostScreenMessage(string.Format("[{0}] No maneuver node!", MOD), 5, ScreenMessageStyle.UPPER_CENTER); Log("No maneuver node!", "QSAS"); } } }
public string NextNodeCountdown() { if (!vessel.patchedConicsUnlocked() || !vessel.patchedConicSolver.maneuverNodes.Any()) { return("-"); } ManeuverNode node = vessel.patchedConicSolver.maneuverNodes.First(); double dV = node.GetBurnVector(orbit).magnitude; return(GuiUtils.TimeToDHMS(node.UT - BurnTime(dV) * leadFraction - vesselState.time)); }
public string NextNodeBurnTime() { if (!vessel.patchedConicsUnlocked() || !vessel.patchedConicSolver.maneuverNodes.Any()) { return("-"); } ManeuverNode node = vessel.patchedConicSolver.maneuverNodes.First(); double dV = node.GetBurnVector(orbit).magnitude; return(GuiUtils.TimeToDHMS(BurnTime(dV))); }
void update_maneuver_node() { Node = Solver.maneuverNodes[0]; InitialDeltaV = Node.DeltaV.magnitude; ThresholdDeltaV = Math.Min(InitialDeltaV, 10); NodeDeltaV = Node.GetBurnVector(VSL.orbit); if (VSL.Engines.MaxDeltaV < InitialDeltaV) { Status("yellow", "WARNING: there may be not enough propellant for the maneuver"); } }
void update_maneuver_node() { Node = Solver.maneuverNodes[0]; NodeDeltaV = Node.GetBurnVector(VSL.orbit); NodeCB = Node.patch.referenceBody; TargetOrbit = Node.nextPatch; if (VSL.Engines.MaxDeltaV < Node.DeltaV.magnitude) { Status("yellow", "WARNING: there may be not enough propellant for the maneuver"); } }
public string NextManeuverNodeBurnTime() { if (!vessel.patchedConicsUnlocked() || !vessel.patchedConicSolver.maneuverNodes.Any()) { return("N/A"); } ManeuverNode node = vessel.patchedConicSolver.maneuverNodes.First(); double burnTime = node.GetBurnVector(node.patch).magnitude / vesselState.limitedMaxThrustAccel; return(GuiUtils.TimeToDHMS(burnTime)); }
public string NextNodeBurnTime() { if (!vessel.patchedConicsUnlocked() || vessel.patchedConicSolver.maneuverNodes.Count == 0) { return("-"); } ManeuverNode node = vessel.patchedConicSolver.maneuverNodes[0]; double dV = node.GetBurnVector(orbit).magnitude; double halfBurnTIme; return(GuiUtils.TimeToDHMS(BurnTime(dV, out halfBurnTIme))); }
public override AutopilotStep OnFixedUpdate() { if (!vessel.patchedConicsUnlocked() || vessel.patchedConicSolver.maneuverNodes.Count == 0) { return(doAfterExecution); } node = vessel.patchedConicSolver.maneuverNodes[0]; double dVLeft = node.GetBurnVector(orbit).magnitude; if (dVLeft < core.node.tolerance && core.attitude.attitudeAngleFromTarget() > 5) { burnTriggered = false; node.RemoveSelf(); return(this); // we are done for this frame, continue in next } double halfBurnTime; double burnTime = core.node.BurnTime(dVLeft, out halfBurnTime); double timeToNode = node.UT - vesselState.time; status = "Moving to node"; if ((!double.IsInfinity(halfBurnTime) && halfBurnTime > 0 && timeToNode < halfBurnTime) || timeToNode < 0) { burnTriggered = true; status = "Executing node"; if (!MuUtils.PhysicsRunning()) { core.warp.MinimumWarp(); } } //autowarp, but only if we're already aligned with the node if (core.node.autowarp && !burnTriggered) { if ((core.attitude.attitudeAngleFromTarget() < 1 && core.vessel.angularVelocity.magnitude < 0.01) || (core.attitude.attitudeAngleFromTarget() < 10 && !MuUtils.PhysicsRunning())) { core.warp.WarpToUT(node.UT - halfBurnTime - core.node.leadTime); } else if (!MuUtils.PhysicsRunning() && core.attitude.attitudeAngleFromTarget() > 10 && timeToNode < 600) { //realign core.warp.MinimumWarp(); } } return(this); }
//Draws the digital readouts to the gauge private void drawNumbers(ManeuverNode myNode) { //dv = Isp * ln (m0 / m1) //e^(dv/ISP) = m0/m1 //m1 = m0/e^(dv/ISP) ...I think. //mass flow = thrust/isp deltaV = myNode.DeltaV.magnitude; //The burn's ΔV deltaVRem = myNode.GetBurnVector(FlightGlobals.ActiveVessel.orbit).magnitude; //Remaining ΔV in the burn //res r = calculateThrust(FlightGlobals.ActiveVessel); //Actually calculates thrust, mass, and Isp //Log.Info("Mass: " + Math.Round(r.mass, 2) + " Thrust: " + Math.Round(r.thrust, 2) + " ISP: " + Math.Round(r.isp, 2)); //double mass = SteamShip.Mass / Math.Pow(Math.E, (deltaVRem / (SteamShip.ISP*9.82))); //Mass after burn Changed from 9.821 //Log.Info("Final Mass: " + Math.Round(mass, 2)+" Burn Mass: "+Math.Round(r.mass-mass,2)); //double rate = SteamShip.MaxThrust / (SteamShip.ISP*9.82); //Mass flow rate, rounded to 5 digits //double burnTime = (SteamShip.Mass - mass)/rate; //Mass to burn over rate should give time, but doesn't //burnTime += SteamShip.EngineAccel+SteamShip.EngineDecel; //Compensate for slow throttles double burnTime = SteamShip.BurnTime; NavBallBurnVector bv = UnityEngine.Object.FindObjectsOfType <NavBallBurnVector>()[0]; double bt2 = bv.estimatedBurnTime; if (double.IsInfinity(bt2) || double.IsNaN(bt2)) { bt2 = 0; //Assume its good if not infinity or NaN } //Draw values drawDigits(182, 131, deltaV); //If we are past the node time, time until burn is 0 if (Planetarium.GetUniversalTime() < myNode.UT) { if (useCalculatedBurn) { timeToBurn = (long)((myNode.UT - Planetarium.GetUniversalTime()) - (burnTime / 2.0)); drawTime(219, 198, timeToBurn); //draw actual time to burn start } else { timeToBurn = (long)((myNode.UT - Planetarium.GetUniversalTime()) - (bt2 / 2)); drawTime(219, 198, timeToBurn); //draw actual time to KSP's burn start } } else { drawTime(219, 198, 0); //Use 0 as time to burn if past node } if (useCalculatedBurn) { drawTime(219, 261, burnTime); //Draw burn time. } else { drawTime(219, 261, bt2); //Draw KSP's burn time } }
internal void FixedUpdate() { if (!FlightDriver.Pause) { PatchedConicSolver solver = NodeTools.getSolver(); if (options.removeUsedNodes && solver.maneuverNodes.Count > 0) { ManeuverNode node = solver.maneuverNodes[0]; if (node.GetBurnVector(FlightGlobals.ActiveVessel.orbit).magnitude < options.usedNodeThreshold) { solver.RemoveManeuverNode(node); //TODO: Clean up states after removing the node. } } } }
public string NextNodeCountdown() { if (!vessel.patchedConicsUnlocked() || !vessel.patchedConicSolver.maneuverNodes.Any()) { return("-"); } ManeuverNode node = vessel.patchedConicSolver.maneuverNodes.First(); double dV = node.GetBurnVector(orbit).magnitude; double halfBurnTIme; double burnTIme = BurnTime(dV, out halfBurnTIme); if (double.IsInfinity(halfBurnTIme)) { halfBurnTIme = 0.0; } return(GuiUtils.TimeToDHMS(node.UT - halfBurnTIme - vesselState.time)); }
protected override void Update() { if (!IsActive) { return; } if (!VSL.HasManeuverNode || Node != Solver.maneuverNodes[0]) { reset(); return; } if (Executor.Execute(Node.GetBurnVector(VSL.orbit), MinDeltaV, StartCondition)) { return; } Node.RemoveSelf(); reset(); }
private void RefreshNodeValues() { // Per KSP API wiki http://docuwiki-kspapi.rhcloud.com/#/classes/ManeuverNode: // The x-component of DeltaV represents the delta-V in the radial-plus direction. // The y-component of DeltaV represents the delta-V in the normal-plus direction. // The z-component of DeltaV represents the delta-V in the prograde direction. // However... it is not returned in the basis of the orbit at the time of the // maneuver. It needs transformed into the right basis. maneuverVector = node.GetBurnVector(orbit); nodeDV = maneuverVector.magnitude; // Swizzle these into the right order. Vector3d mnvrVel = orbit.getOrbitalVelocityAtUT(node.UT).xzy; Vector3d mnvrPos = orbit.getRelativePositionAtUT(node.UT).xzy; Vector3d mnvrPrograde = mnvrVel.normalized; // Prograde vector at maneuver time Vector3d mnvrNml = Vector3d.Cross(mnvrVel, mnvrPos).normalized; Vector3d mnvrRadial = Vector3d.Cross(mnvrNml, mnvrPrograde); maneuverNodeComponentVector.x = Vector3d.Dot(maneuverVector, mnvrPrograde); maneuverNodeComponentVector.y = Vector3d.Dot(maneuverVector, mnvrNml); maneuverNodeComponentVector.z = Vector3d.Dot(maneuverVector, mnvrRadial); }
protected override void Update() { if (!IsActive) { return; } if (!VSL.HasManeuverNode || Node != Solver.maneuverNodes[0]) { reset(); return; } NodeDeltaV = Node.GetBurnVector(VSL.orbit); // Log("Node.dV {}", NodeDeltaV);//debug if (Executor.Execute(NodeDeltaV, MinDeltaV, StartCondition)) { within_threshold |= Executor.RemainingDeltaV < ThresholdDeltaV; if (within_threshold) { VSL.Controls.GimbalLimit = 0; var dV = Executor.RemainingDeltaV; if (dV < min_deltaV) { min_deltaV = dV; return; } if (dV - min_deltaV < GLB.THR.MinDeltaV) { return; } } else { return; } } Node.RemoveSelf(); reset(); }
public Vector GetBurnVector() { CheckNodeRef(); return(new Vector(nodeRef.GetBurnVector(vesselRef.GetOrbit()))); }
public override void OnFixedUpdate() { if (!vessel.patchedConicSolver.maneuverNodes.Any()) { Abort(); return; } //check if we've finished a node: ManeuverNode node = vessel.patchedConicSolver.maneuverNodes.First(); double dVLeft = node.GetBurnVector(orbit).magnitude; if (dVLeft < tolerance && core.attitude.attitudeAngleFromTarget() > 5) { burnTriggered = false; vessel.patchedConicSolver.RemoveManeuverNode(node); if (mode == Mode.ONE_NODE) { Abort(); return; } else if (mode == Mode.ALL_NODES) { if (!vessel.patchedConicSolver.maneuverNodes.Any()) { Abort(); return; } else { node = vessel.patchedConicSolver.maneuverNodes.First(); } } } //aim along the node core.attitude.attitudeTo(Vector3d.forward, AttitudeReference.MANEUVER_NODE, this); double burnTime = BurnTime(dVLeft); double timeToNode = node.UT - vesselState.time; if (timeToNode < burnTime * leadFraction) { burnTriggered = true; if (!MuUtils.PhysicsRunning()) { core.warp.MinimumWarp(); } } //autowarp, but only if we're already aligned with the node if (autowarp && !burnTriggered) { if (core.attitude.attitudeAngleFromTarget() < 1 || (core.attitude.attitudeAngleFromTarget() < 10 && !MuUtils.PhysicsRunning())) { core.warp.WarpToUT(node.UT - burnTime * leadFraction - leadTime); } else if (!MuUtils.PhysicsRunning() && core.attitude.attitudeAngleFromTarget() > 10 && timeToNode < 600) { //realign core.warp.MinimumWarp(); } } core.thrust.targetThrottle = 0; if (burnTriggered) { if (alignedForBurn) { if (core.attitude.attitudeAngleFromTarget() < 90) { double timeConstant = (dVLeft > 10 ? 0.5 : 2); double desiredAcceleration = dVLeft / timeConstant; desiredAcceleration = Math.Max(tolerance, desiredAcceleration); core.thrust.targetThrottle = Mathf.Clamp01((float)(desiredAcceleration / vesselState.maxThrustAccel)); } else { alignedForBurn = false; } } else { if (core.attitude.attitudeAngleFromTarget() < 2) { alignedForBurn = true; } } } }
/// <summary> /// Executes the maneuver burn for the configured maneuver node. /// </summary> /// <param name="computer">FlightComputer instance of the computer of the vessel the ManeuverCommand is for.</param> /// <param name="ctrlState">FlightCtrlState instance of the current state of the vessel.</param> /// <returns>true if the command has finished its work, false otherwise.</returns> public override bool Execute(FlightComputer computer, FlightCtrlState ctrlState) { // Halt the command if we reached our target or were command to abort by the previous tick if (this.RemainingDelta <= 0.01 || this.abortOnNextExecute) { this.AbortManeuver(computer); return(true); } // Orientate vessel to maneuver prograde var forward = Node.GetBurnVector(computer.Vessel.orbit).normalized; var up = (computer.SignalProcessor.Body.position - computer.SignalProcessor.Position).normalized; var orientation = Quaternion.LookRotation(forward, up); FlightCore.HoldOrientation(ctrlState, computer, orientation, true); // This represents the theoretical acceleration but is off by a few m/s^2, probably because some parts are partially physicsless double thrustToMass = (FlightCore.GetTotalThrust(computer.Vessel) / computer.Vessel.GetTotalMass()); // We need to know if the engine was activated or not to show the proper info text in the command if (thrustToMass == 0.0) { this.EngineActivated = false; return(false); } this.EngineActivated = true; // Before any throttling, those two values may differ from after the throttling took place this.RemainingDelta = this.getRemainingDeltaV(computer); this.RemainingTime = this.RemainingDelta / thrustToMass; // In case we would overpower with 100% thrust, calculate how much we actually need and set it. if (computer.Vessel.acceleration.magnitude > this.RemainingDelta) { // Formula which leads to this: a = ( vE – vS ) / dT this.throttle = this.RemainingDelta / computer.Vessel.acceleration.magnitude; } ctrlState.mainThrottle = (float)this.throttle; // TODO: THIS CAN PROBABLY BE REMOVED? RemainingDelta = this.getRemainingDeltaV(computer); // After throttling, the remaining time differs from beforehand (dividing delta by throttled thrustToMass) this.RemainingTime = this.RemainingDelta / (ctrlState.mainThrottle * thrustToMass); // We need to abort if the remaining delta was already low enough so it only takes exactly one more tick! double ticksRemaining = this.RemainingTime / TimeWarp.deltaTime; if (ticksRemaining <= 1) { this.throttle *= ticksRemaining; ctrlState.mainThrottle = (float)this.throttle; this.abortOnNextExecute = true; return(false); } // we only compare up to the fiftieth part due to some burn-up delay when just firing up the engines if (this.lowestDeltaV > 0 && // Do ignore the first tick (this.RemainingDelta - 0.02) > this.lowestDeltaV && this.RemainingDelta < 1.0) // be safe that we do not abort the command to early { // Aborting because deltaV was rising again! this.AbortManeuver(computer); return(true); } // Lowest delta always has to be stored to be able to compare it in the next tick if (this.lowestDeltaV == 0 || // Always do it on the first tick this.RemainingDelta < this.lowestDeltaV) { this.lowestDeltaV = this.RemainingDelta; } return(false); }
public static void Update(bool target) { _update = false; if (FlightGlobals.ActiveVessel == null || FlightGlobals.ActiveVessel.patchedConicSolver == null) { _updated = false; return; } PatchedConicSolver solver = FlightGlobals.ActiveVessel.patchedConicSolver; _node = solver.maneuverNodes[0]; if (_node != null || _node.patch == null) { _maneuverTotal = _node.DeltaV.magnitude; _maneuverRemaining = _node.GetBurnVector(_node.patch).magnitude; if (BasicOrbitReflection.BetterBurnTimeLoaded) { if (_bbVesselModule == null || _bbVesselReference != FlightGlobals.ActiveVessel) { for (int i = FlightGlobals.ActiveVessel.vesselModules.Count - 1; i >= 0; i--) { VesselModule vMod = FlightGlobals.ActiveVessel.vesselModules[i]; if (vMod == null) { continue; } if (vMod.GetType().Name != _bbModuleName) { continue; } _bbVesselModule = vMod; _bbVesselReference = FlightGlobals.ActiveVessel; break; } } if (_bbVesselModule != null) { string type = _bbVesselModule.Fields[_bbTypeName].GetValue(_bbVesselModule).ToString(); if (type == "Maneuver") { _burnLength = _bbVesselModule.Fields[_bbLengthName].GetValue <double>(_bbVesselModule); _burnTime = _bbVesselModule.Fields[_bbTimeName].GetValue <double>(_bbVesselModule); if (double.IsNaN(_burnLength) || double.IsNaN(_burnTime)) { _bbTimeLoaded = false; _burnTime = _node.UT; _burnLength = 0; } else { double half = _burnLength / 2; _burnTime -= half; _bbTimeLoaded = true; } } else { _bbTimeLoaded = false; _burnTime = _node.UT; } } else { _bbTimeLoaded = false; _burnTime = _node.UT; } } else { _burnTime = _node.UT; } _showAngle = false; _showPhasing = false; _targetInclination = false; if (target) { if (!BasicTargetting.IsVessel && !BasicTargetting.IsCelestial) { _vesselIntersect = false; _bodyIntersect = false; } else { Orbit targetOrbit = FlightGlobals.ActiveVessel.targetObject.GetOrbit(); Orbit active = FlightGlobals.ActiveVessel.orbit; _targetPhasingOrbit = null; if (active.referenceBody == targetOrbit.referenceBody) { _phasingNodePatch = active; _targetPhasingOrbit = targetOrbit; _targetInclination = true; } else { if (active.referenceBody != Planetarium.fetch.Sun) { _showAngle = true; } _showPhasing = true; DrillDownOrbits(_node.patch, targetOrbit); } Vessel.Situations sit = FlightGlobals.ActiveVessel.situation; if ((sit |= Vessel.Situations.LANDED | Vessel.Situations.SPLASHED | Vessel.Situations.PRELAUNCH) == 0) { _vesselIntersect = false; _bodyIntersect = false; } else { OrbitTargeter oTargeter = FlightGlobals.ActiveVessel.orbitTargeter; if (oTargeter == null || solver == null) { _vesselIntersect = false; _bodyIntersect = false; } else if (!MapView.MapIsEnabled) { if (BasicTargetting.IsVessel) { _bodyIntersect = false; Vessel tgt = FlightGlobals.ActiveVessel.targetObject.GetVessel(); if (tgt == null || tgt.LandedOrSplashed) { _vesselIntersect = false; return; } Orbit _refPatch = BasicOrbitReflection.GetRefPatch(oTargeter); Orbit _tgtRefPatch = BasicOrbitReflection.GetTargetRefPatch(oTargeter); _vesselIntersect = GetClosestVessel(_refPatch, _tgtRefPatch); } else { _vesselIntersect = false; double Pe = GetLowestPeA(solver, BasicTargetting.TargetBody, _node.patch); if (Pe < BasicExtensions.AlmostMaxValue) { _closestDist = Pe; _bodyIntersect = true; } else { Orbit _refPatch = BasicOrbitReflection.GetRefPatch(oTargeter); Orbit _tgtRefPatch = BasicOrbitReflection.GetTargetRefPatch(oTargeter); if (_refPatch != null && _refPatch.closestTgtApprUT <= 0) { _bodyIntersect = false; return; } _bodyIntersect = GetClosestCelestial(_refPatch, _tgtRefPatch); } } } else { if (BasicTargetting.Markers == null || BasicTargetting.Markers.Count <= 0) { BasicTargetting.Markers = BasicOrbitReflection.GetOrbitMarkers(oTargeter); } if (BasicTargetting.IsVessel) { _bodyIntersect = false; OrbitTargeter.ISectMarker _intersectOne = null; OrbitTargeter.ISectMarker _intersectTwo = null; for (int i = BasicTargetting.Markers.Count - 1; i >= 0; i--) { OrbitTargeter.Marker m = BasicTargetting.Markers[i]; if (m == null) { continue; } if (!(m is OrbitTargeter.ISectMarker)) { continue; } int num = ((OrbitTargeter.ISectMarker)m).num; if (num == 1) { _intersectOne = m as OrbitTargeter.ISectMarker; } else if (num == 2) { _intersectTwo = m as OrbitTargeter.ISectMarker; } } OrbitTargeter.ISectMarker _closestIntersect = null; if (_intersectOne != null && _intersectTwo != null) { _closestIntersect = _intersectOne.separation > _intersectTwo.separation ? _intersectTwo : _intersectOne; } else if (_intersectOne != null) { _closestIntersect = _intersectOne; } else if (_intersectTwo != null) { _closestIntersect = _intersectTwo; } else { _closestIntersect = null; } if (_closestIntersect == null) { _vesselIntersect = false; } else { _vesselIntersect = true; _closestDist = _closestIntersect.separation * 1000; _closestRelVel = _closestIntersect.relSpeed; _closestTime = _closestIntersect.UT; } } else { _vesselIntersect = false; double Pe = GetLowestPeA(solver, BasicTargetting.TargetBody, _node.patch); if (Pe < BasicExtensions.AlmostMaxValue) { _closestDist = Pe; _bodyIntersect = true; } else { OrbitTargeter.ClApprMarker _approach = null; for (int i = BasicTargetting.Markers.Count - 1; i >= 0; i--) { OrbitTargeter.Marker m = BasicTargetting.Markers[i]; if (m == null) { continue; } if (!(m is OrbitTargeter.ClApprMarker)) { continue; } _approach = m as OrbitTargeter.ClApprMarker; } if (_approach == null) { _bodyIntersect = false; } else { _bodyIntersect = true; _closestDist = _approach.separation * 1000; _closestTime = (_approach.dT * -1) + Planetarium.GetUniversalTime(); } } } } } } } _updated = true; } }
private void OnHudTextWindow(int windowID) { Vessel vessel = FlightGlobals.fetch.activeVessel; double vel = 0.0d; string speedLabel; GUIStyle hudTextStyle = new GUIStyle(); hudTextStyle.normal.textColor = _values.HudTextColor; GUILayout.BeginVertical(); bool isRCSOn = vessel.ActionGroups[KSPActionGroup.RCS]; bool isSASOn = vessel.ActionGroups[KSPActionGroup.SAS]; GUILayout.BeginHorizontal(); if (isSASOn) { GUILayout.Label("SAS", hudTextStyle); } if (isRCSOn) { GUILayout.Label("RCS", hudTextStyle); } GUILayout.EndHorizontal(); switch (FlightGlobals.speedDisplayMode) // Changed in 1.1 from FlightUIController.speedDisplayMode { case FlightGlobals.SpeedDisplayModes.Surface: vel = FlightGlobals.ship_srfSpeed; speedLabel = "Surface: " + vel.ToString("F2") + "m/s"; break; case FlightGlobals.SpeedDisplayModes.Orbit: vel = FlightGlobals.ship_obtSpeed; speedLabel = "Orbit: " + vel.ToString("F2") + "m/s"; break; case FlightGlobals.SpeedDisplayModes.Target: vel = FlightGlobals.ship_tgtSpeed; speedLabel = "Target: " + vel.ToString("F2") + "m/s"; break; default: throw new ArgumentOutOfRangeException(); } GUILayout.Label(speedLabel, hudTextStyle); GUILayout.Label("Throttle: " + vessel.ctrlState.mainThrottle.ToString("P1"), hudTextStyle); GUILayout.Label("Heading: " + FlightGlobals.ship_heading.ToString("F1"), hudTextStyle); GUILayout.Label("G Force: " + FlightGlobals.ship_geeForce.ToString("F1"), hudTextStyle); if (vessel != null && vessel.patchedConicSolver != null && vessel.patchedConicSolver.maneuverNodes != null && vessel.patchedConicSolver.maneuverNodes.Count > 0) { ManeuverNode node = vessel.patchedConicSolver.maneuverNodes[0]; double burnDV = node.DeltaV.magnitude; double burnRem = node.GetBurnVector(vessel.orbit).magnitude; GUILayout.Label("Burn ΔV: " + burnRem.ToString("F2") + "m/s / " + burnDV.ToString("F2") + "m/s", hudTextStyle); if (burnRem != double.NaN) { double totalThrust = 0.0; double totalIsp = 0.0; calcThrust(ref totalThrust, ref totalIsp); if (vessel.ctrlState.mainThrottle > 0.0) { totalThrust *= vessel.ctrlState.mainThrottle; } double burnTimeRem = calcBurnTime(burnRem, vessel.GetTotalMass(), totalThrust, totalIsp); double burnTimeTotal = calcBurnTime(burnDV, vessel.GetTotalMass(), totalThrust, totalIsp); GUILayout.Label("Burn time: " + GetTimeString(burnTimeRem) + " / " + GetTimeString(burnTimeTotal), hudTextStyle); } double timeToNode = node.UT - Planetarium.GetUniversalTime(); if (Math.Sign(timeToNode) >= 0) { GUILayout.Label("Node in T - " + GetTimeString(Math.Abs(timeToNode)), hudTextStyle); } else { GUILayout.Label("Node in T + " + GetTimeString(Math.Abs(timeToNode)), hudTextStyle); } } GUILayout.EndVertical(); if (!_lockText) { GUI.DragWindow(); } }
/* * TARGET APIs */ public void TargetNode(ManeuverNode node, double burntime) { if (status == PVGStatus.ENABLED) { return; } if (p == null || isCoasting()) { PontryaginNode solver = p as PontryaginNode; if (solver == null) { solver = new PontryaginNode(core: core, mu: mainBody.gravParameter, r0: vesselState.orbitalPosition, v0: vesselState.orbitalVelocity, pv0: node.GetBurnVector(orbit).normalized, pr0: Vector3d.zero, dV: node.GetBurnVector(orbit).magnitude, bt: burntime); } solver.intercept(node.nextPatch); p = solver; } }
public override void OnFixedUpdate() { if (!vessel.patchedConicsUnlocked() || !vessel.patchedConicSolver.maneuverNodes.Any()) { Abort(); return; } //check if we've finished a node: ManeuverNode node = vessel.patchedConicSolver.maneuverNodes.First(); double dVLeft = node.GetBurnVector(orbit).magnitude; if (dVLeft < tolerance && core.attitude.attitudeAngleFromTarget() > 5) { burnTriggered = false; node.RemoveSelf(); if (mode == Mode.ONE_NODE) { Abort(); return; } else if (mode == Mode.ALL_NODES) { if (!vessel.patchedConicSolver.maneuverNodes.Any()) { Abort(); return; } else { node = vessel.patchedConicSolver.maneuverNodes.First(); } } } //aim along the node core.attitude.attitudeTo(Vector3d.forward, AttitudeReference.MANEUVER_NODE, this); double halfBurnTime; double burnTime = BurnTime(dVLeft, out halfBurnTime); double timeToNode = node.UT - vesselState.time; if (timeToNode < halfBurnTime) { burnTriggered = true; if (!MuUtils.PhysicsRunning()) { core.warp.MinimumWarp(); } } //autowarp, but only if we're already aligned with the node if (autowarp && !burnTriggered) { if (core.attitude.attitudeAngleFromTarget() < 1 || (core.attitude.attitudeAngleFromTarget() < 10 && !MuUtils.PhysicsRunning())) { core.warp.WarpToUT(node.UT - halfBurnTime - leadTime); } else if (!MuUtils.PhysicsRunning() && core.attitude.attitudeAngleFromTarget() > 10 && timeToNode < 600) { //realign core.warp.MinimumWarp(); } } core.thrust.targetThrottle = 0; if (burnTriggered) { if (alignedForBurn) { if (core.attitude.attitudeAngleFromTarget() < 90) { double timeConstant = (dVLeft > 10 || vesselState.minThrustAccel > 0.25 * vesselState.maxThrustAccel ? 0.5 : 2); core.thrust.ThrustForDV(dVLeft + tolerance, timeConstant); } else { alignedForBurn = false; } } else { if (core.attitude.attitudeAngleFromTarget() < 2) { alignedForBurn = true; } } } }
public override void OnFixedUpdate() { throttle.target = 0.0; if (vessel.patchedConicSolver.maneuverNodes.Count == 0) { Debug.Log("NodeExecutor: no maneuver node to execute"); Disable(); return; } ManeuverNode node = vessel.patchedConicSolver.maneuverNodes[0]; double dVLeft = node.GetBurnVector(orbit).magnitude; if (dVLeft < tolerance && attitude.AngleFromTarget() > 5) { Debug.Log("NodeExecutor: done with node, removing it"); node.RemoveSelf(); Disable(); return; } attitude.attitudeTo(Vector3d.forward, AttitudeReference.MANEUVER_NODE); double BurnUT = node.UT - BurnTime(dVLeft) / 2.0; if (vesselState.time < (BurnUT - 300)) { /* way before the burn */ if (attitude.AngleFromTarget() < 1 && CheckAngularVelocity(ref checkOneStart)) { warp.WarpToUT(this, BurnUT - leadTime); } } else if (vesselState.time < (BurnUT - leadTime)) { /* before the burn */ if (attitude.AngleFromTarget() < 1 && CheckAngularVelocity(ref checkTwoStart)) { warp.WarpToUT(this, BurnUT - leadTime); } if (attitude.AngleFromTarget() > 5) { warp.MinimumWarp(this); } } else if (vesselState.time < BurnUT) { /* settling time */ warp.MinimumWarp(this); } else { /* feeling the burn */ warp.MinimumWarp(this); throttle.target = 0.0; if (attitude.AngleFromTarget() > 5) { seeking = true; } else if (attitude.AngleFromTarget() < 1 || !seeking) { double thrustToMass = vesselState.thrustMaximum / vesselState.mass; throttle.target = Utils.Clamp(dVLeft / thrustToMass / 2.0, 0.01, 1.0); seeking = false; } } }
public ManeuverNode GetManeuverNode() { if (payload == null || node?.nextPatch == null) { return(null); } var remainingDeltaV = node.nextPatch.GetFrameVelAtUT(node.UT) - payload.orbit.GetFrameVelAtUT(node.UT); var newNode = new ManeuverNode { UT = node.UT, DeltaV = Utils.Orbital2NodeDeltaV(payload.orbit, remainingDeltaV, node.UT), patch = new Orbit(payload.orbit), nextPatch = new Orbit(node.nextPatch) }; Utils.Debug($"new node: {newNode.ToConfigString()}\norbDeltaV: {Utils.Node2OrbitalDeltaV(newNode)}\nnode burn vector: {Utils.formatVector(node.GetBurnVector(payload.orbit))}\nnew node burn vec: {Utils.formatVector(newNode.GetBurnVector(payload.orbit))}"); return(newNode); }
//This method is called periodically by the main thread, //then it decides if it is time to start a new thread or not public static void update() { now = Time.time; //double lc = lastCharge; //float lt = lastTime; //lastTime = now; double dt = TimeWarp.fixedDeltaTime; //Debug.Log("Starting simulation at " + Math.Round(now, 3)); isReadable = false; //Reset some stuff ship = FlightGlobals.ActiveVessel; body = ship.mainBody; _gravity = FlightGlobals.getGeeForceAtPosition(ship.CoM).magnitude; _electricCharge = 0; _electricMax = 0; _evamaxfuel = 0; _evafuel = 0; _liquidFuel = 0; _liquidMax = 0; _monoFuel = 0; _monoMax = 0; _airin = 0; _airreq = 0; _mass = 0; _max_thrust = 0; _cur_thrust = 0; _isp = 0; _engineaccel = 0; _enginedecel = 0; _max_temp_percent = 0; _max_temp_actual = 0; double _best_temp = 0; double _best_ablation = 0; _ablation = 1; _tarpos = new Vector3(); // int cur_stage = ship.currentStage; //Debug.Log("##########################"); //Debug.Log("Current Stage: "+cur_stage); //Part loop - look at all the parts and modules for stuff. foreach (Part P in ship.parts) { /*Debug.Log("-------------------------------"); * Debug.Log(P.name); * Debug.Log("inStageIndex: " + P.inStageIndex); * Debug.Log("inv Stage: " + P.inverseStage); * Debug.Log("Manual Stage Offset: " + P.manualStageOffset); * Debug.Log("Computed Stage: " + (P.inverseStage + P.manualStageOffset)); * Debug.Log("Original Stage: " + P.originalStage); * //Debug.Log("Stage After: " + P.stageAfter); * //Debug.Log("Stage Before: " + P.stageBefore); * Debug.Log("Stage Offset: " + P.stageOffset); */ //Temperature //First, look at internal temps _best_temp = P.temperature / P.maxTemp; if (_best_temp > _max_temp_percent) { _max_temp_percent = _best_temp; _max_temp_actual = P.temperature; } //Now look at skin temps _best_temp = P.skinTemperature / P.skinMaxTemp; if (_best_temp > _max_temp_percent) { _max_temp_percent = _best_temp; _max_temp_actual = P.skinTemperature; } //if (P.temperature/P.maxTemp > .5) Debug.Log(P.partInfo.title + " Temp: " + (int)P.temperature + "⁰/" + (int)P.maxTemp + "⁰"); //Vessel Mass if (P.physicalSignificance == Part.PhysicalSignificance.FULL) { _mass += P.mass; _mass += P.GetResourceMass(); } //Resource loop foreach (PartResource pr in P.Resources) { if (pr.resourceName.Equals("ElectricCharge")) { _electricCharge += pr.amount; _electricMax += pr.maxAmount; } else if (pr.resourceName.Equals("LiquidFuel")) { _liquidFuel += pr.amount; _liquidMax += pr.maxAmount; } else if (pr.resourceName.Equals("MonoPropellant")) { _monoFuel += pr.amount; _monoMax += pr.maxAmount; } else if (pr.resourceName.Equals("Ablator")) { _best_ablation = pr.amount / pr.maxAmount; if (_best_ablation < _ablation) { _ablation = _best_ablation; } } } //Module loop foreach (PartModule pm in P.Modules) { if (pm is KerbalEVA) { KerbalEVA eva = pm as KerbalEVA; _evafuel = eva.Fuel; _evamaxfuel = eva.FuelCapacity; } else if (pm is ModuleWheelDeployment) { _gearstate = 0; //Gear up ModuleWheelDeployment mlg = pm as ModuleWheelDeployment; if (mlg.stateString.Equals("Deployed")) { _gearstate = 3; } if (mlg.stateString.StartsWith("Deploying")) { _gearstate = 2; } if (mlg.stateString.StartsWith("Retracting")) { _gearstate = 1; } } else if (pm is ModuleEngines) { ModuleEngines ME = pm as ModuleEngines; if (ME.engineShutdown || !ME.EngineIgnited) //We don't care about engines that aren't ready to fire { continue; } if (1 / ME.engineAccelerationSpeed > _engineaccel) { _engineaccel = 1 / ME.engineAccelerationSpeed; } if (1 / ME.engineDecelerationSpeed > _enginedecel) { _enginedecel = 1 / ME.engineDecelerationSpeed; } //Thrust/ISP float thrust = ME.maxThrust * ME.thrustPercentage * 0.01f; _isp += thrust / ME.atmosphereCurve.Evaluate((float)ship.staticPressurekPa); _max_thrust += thrust; _cur_thrust += ME.finalThrust; //I think this is the actual thrust being produced by each engine //Intake Air foreach (Propellant Pro in ME.propellants) { if (Pro.name.Equals("IntakeAir")) { _airreq += Pro.currentRequirement; } } //Overheat stuff //if (!ME.flameout) //{ // double temp = P.temperature / P.maxTemp; // if (temp > _max_temp) _max_temp = temp; //} } else if (pm is ModuleEnginesFX) //The newer engines all use this instead of ModuleEngines { ModuleEnginesFX MFX = pm as ModuleEnginesFX; if (MFX.engineShutdown || !MFX.EngineIgnited) { continue; } //Thrust/ISP float thrust = MFX.maxThrust * MFX.thrustPercentage * 0.01f; _isp += thrust / MFX.atmosphereCurve.Evaluate((float)ship.staticPressurekPa); _max_thrust += thrust; _cur_thrust += MFX.finalThrust; //Overheat stuff //if (!MFX.flameout) //{ // double temp = P.temperature / P.maxTemp; // if (temp > _max_temp) _max_temp = temp; //} if (1 / MFX.engineAccelerationSpeed > _engineaccel) { _engineaccel = 1 / MFX.engineAccelerationSpeed; } if (1 / MFX.engineDecelerationSpeed > _enginedecel) { _enginedecel = 1 / MFX.engineDecelerationSpeed; } foreach (Propellant Pro in MFX.propellants) { if (Pro.name.Equals("IntakeAir")) { _airreq += Pro.currentRequirement; } } } else if (pm is ModuleResourceIntake) { ModuleResourceIntake MRI = pm as ModuleResourceIntake; if (MRI.intakeEnabled && MRI.resourceName.Equals("IntakeAir")) { _airin += MRI.airFlow * dt; //Using dt courtesy of FAR } } } } _isp = _max_thrust / _isp; //Complete the average isp if (_engineaccel == 0) { _engineaccel = 2; //Default in case the engines don't define it } //Debug.Log("Engine Acceleration: " + Math.Round(_engineaccel, 2)); //Electric charge rate if (now - lastTime > 0.1) { _chargeRate = (_electricCharge - lastCharge) / (now - lastTime); lastCharge = _electricCharge; lastTime = now; } //Clamp to +30/-30 if (_chargeRate > 30) { _chargeRate = 30; } if (_chargeRate < -30) { _chargeRate = -30; } //Maneuver node burn calculations //dv = Isp * ln (m0 / m1) //e^(dv/ISP) = m0/m1 //m1 = m0/e^(dv/ISP) //mass flow = thrust/isp try { ManeuverNode myNode = FlightGlobals.ActiveVessel.patchedConicSolver.maneuverNodes.ToArray()[0]; double deltaVRem = myNode.GetBurnVector(ship.orbit).magnitude; //Remaining ΔV in the burn, per maneuver node //Debug.Log("dV: " + Math.Round(deltaVRem, 1)); //Debug.Log("Mass: " + Math.Round(_mass, 1) + " ISP: " + Math.Round(_isp, 1)); _final_mass = _mass / Math.Pow(Math.E, (deltaVRem / (_isp * 9.82))); //Mass after burn Changed from 9.821 //Debug.Log("Final Mass: " + Math.Round(_final_mass, 1)); _mass_rate = _max_thrust / (_isp * 9.82); //Mass flow rate //Debug.Log("Mass Rate: " + Math.Round(_mass_rate, 3)); //Debug.Log("Burn Mass: " + Math.Round(_mass - _final_mass, 1)); _burn_time = (_mass - _final_mass);// / _mass_rate; //Time it takes for this burn //Debug.Log("Burn Mass: " + _burn_time); _burn_time = _burn_time / _mass_rate; //Debug.Log("Burn Time: " + _burn_time); _burn_time += 2d; //_burn_time += _engineaccel + _enginedecel; //Factor in slow throttles } catch { _burn_time = 0; _final_mass = 0; _mass_rate = 0; } //Heading, courtesy of MechJeb /*Vector3d CoM, up, north, east, forward, rootPartPos; * Quaternion rotationSurface, rotationVesselSurface; * CoM = ship.findWorldCenterOfMass(); * up = (CoM - body.position).normalized; * Rigidbody rigidBody = ship.rootPart.rigidbody; * if (rigidBody != null) rootPartPos = rigidBody.position; * north = Vector3d.Exclude(up, (body.position + body.transform.up * (float)body.Radius) - CoM).normalized; * east = body.getRFrmVel(CoM).normalized; * forward = ship.GetTransform().up; * rotationSurface = Quaternion.LookRotation(north, up); * rotationVesselSurface = Quaternion.Inverse(Quaternion.Euler(90, 0, 0) * Quaternion.Inverse(ship.GetTransform().rotation) * rotationSurface); * _heading = rotationVesselSurface.eulerAngles.y; */ _heading = FlightGlobals.ship_heading; //Debug.Log("FlightGlobals Heading: " + FlightGlobals.ship_heading + " MechJeb Heading: " + _heading); //good //Debug.Log("Ship temp: " + FlightGlobals.ship_temp); //not useful //Debug.Log("Get dV: " + FlightGlobals.ActiveVessel.GetDeltaV()); //nothing //Debug.Log("Surface Height: " + FlightGlobals.ActiveVessel.GetHeightFromSurface() + " Terrain Height: "+FlightGlobals.ActiveVessel.GetHeightFromTerrain()); //not useful //Debug.Log("GetMass(): " + FlightGlobals.ActiveVessel.GetTotalMass()+" myMass: "+_mass); //good //Debug.Log("IAS: " + FlightGlobals.ActiveVessel.indicatedAirSpeed + " M: " + FlightGlobals.ActiveVessel.mach); //good //Waypoint distance code //Uses the Spherical Law of Cosines from http://www.movable-type.co.uk/scripts/latlong.html NavWaypoint nW = NavWaypoint.fetch; //The active nav waypoint FinePrint.WaypointManager WM; WM = FinePrint.WaypointManager.Instance(); if (nW != null && nW.IsActive) { //Convert these all to radians to do actual math on them. double Aa = ship.latitude * Math.PI / 180; double Ao = ship.longitude * Math.PI / 180; double Ba = nW.Latitude * Math.PI / 180; double Bo = nW.Longitude * Math.PI / 180; _nav_dist = Math.Acos(Math.Sin(Aa) * Math.Sin(Ba) + Math.Cos(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao)) * FlightGlobals.currentMainBody.Radius; //used to be 600000 double y = Math.Sin(Bo - Ao) * Math.Cos(Ba); double x = Math.Cos(Aa) * Math.Sin(Ba) - Math.Sin(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao); double b = Math.Atan2(y, x) * 180 / Math.PI; //Converted to degrees. _nav_heading = (b + 360f) % 360f; //Convert to 0-360 from -180 to 180 } else if (_nav_waypoint != 0) { //defaults _nav_heading = -1; _nav_dist = -1; //now try actually calculating things int count = 0; foreach (FinePrint.Waypoint W in WM.Waypoints) { //we clearly only care about waypoints on this planet if (W.celestialName.Equals(body.name)) { count++; if (count == _nav_waypoint) { //Convert these all to radians to do actual math on them. double Aa = ship.latitude * Math.PI / 180; double Ao = ship.longitude * Math.PI / 180; double Ba = W.latitude * Math.PI / 180; double Bo = W.longitude * Math.PI / 180; _nav_dist = Math.Acos(Math.Sin(Aa) * Math.Sin(Ba) + Math.Cos(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao)) * FlightGlobals.currentMainBody.Radius; double y = Math.Sin(Bo - Ao) * Math.Cos(Ba); double x = Math.Cos(Aa) * Math.Sin(Ba) - Math.Sin(Aa) * Math.Cos(Ba) * Math.Cos(Bo - Ao); double b = Math.Atan2(y, x) * 180 / Math.PI; //Converted to degrees. _nav_heading = (b + 360f) % 360f; //Convert to 0-360 from -180 to 180 } } } } else { _nav_dist = -1; //Both of these at -1 implies no nav data _nav_heading = -1f; } //Pitch, yaw, roll /*NavBall ball = FlightUIController.fetch.GetComponentInChildren<NavBall>(); * Quaternion vesselRot = Quaternion.Inverse(ball.relativeGymbal); * pitch = (vesselRot.eulerAngles.x > 180) ? (360 - vesselRot.eulerAngles.x) : -vesselRot.eulerAngles.x; * roll = (vesselRot.eulerAngles.z > 180) ? (360 - vesselRot.eulerAngles.z) : -vesselRot.eulerAngles.z; * yaw = vesselRot.eulerAngles.y; //Heading, more or less * _rel_yaw = AngleAroundNormal(ship.GetObtVelocity(), ship.ReferenceTransform.up, ship.ReferenceTransform.forward); * _rel_pitch = AngleAroundNormal(ship.GetObtVelocity(), ship.ReferenceTransform.up, ship.ReferenceTransform.right); * * //Polars * if (pitch > 0f) * { * Quaternion polarRot = Quaternion.Euler(90, 0, 0) * vesselRot; * polar_yaw = (polarRot.eulerAngles.y + 180f) % 360f - 180f; * polar_pitch = (polarRot.eulerAngles.x + 180f) % 360f - 180f; * polar_roll = polarRot.eulerAngles.z; * } * else * { * Quaternion polarRot = Quaternion.Euler(-90, 0, 0) * vesselRot; * polar_yaw = (polarRot.eulerAngles.y + 180f) % 360f - 180f; * polar_pitch = (polarRot.eulerAngles.x + 180f) % 360f - 180f; * polar_roll = polarRot.eulerAngles.z; * }*/ //Airspeed stuff getAirspeedInfo(ship, out _mach, out _tas, out _eas); //Max Altitude if (ship.Landed) { max_altitude = ship.altitude; //Reset max altitude upon landing/takeoff } if (ship.altitude > max_altitude) { max_altitude = ship.altitude; } //RA double terrain = ship.terrainAltitude; if (terrain < 0) { terrain = 0; } _ra = (long)(body.GetAltitude(ship.CoM) - terrain); //In orbit? inOrbit = false; //Minimum periapsis of 5km if (ship.orbit.PeA > 5000) { //Make sure our orbit is out of the atmosphere, if there is one. if (body.atmosphere) { if (ship.orbit.PeA > body.atmosphereDepth) //is this the same as max altitude? { inOrbit = true; } } else { inOrbit = true; } } //Closest approach and time, target distance and speed if (FlightGlobals.fetch.VesselTarget != null) { Orbit TO = FlightGlobals.fetch.VesselTarget.GetOrbit(); Vector3d aPos = ship.ReferenceTransform.position; //Control source's position Vector3d tPos = _tarpos = FlightGlobals.fetch.VesselTarget.GetTransform().position; //Rough distance if (FlightGlobals.fetch.VesselTarget is ModuleDockingNode) //Use more precise distance { ModuleDockingNode targetDockingPort = FlightGlobals.fetch.VesselTarget as ModuleDockingNode; tPos = _tarpos = targetDockingPort.controlTransform.position; } // MechJeb guidance targets _don't have an orbit_! else if (TO != null) { // This is in the wrong coordinate system for _tarpos, and only usable for distance aPos = ship.GetOrbit().pos; tPos = TO.pos; } _target_dist = Vector3d.Distance(aPos, tPos); } else { _target_dist = 0; } var target = FlightGlobals.fetch.VesselTarget; // MechJeb guidance targets _don't have an orbit! // Also, landed targets often have invalid orbit that causes error spam and lag. if (target != null && target.GetOrbit() != null) { Orbit O = ship.GetOrbit(); Orbit TO = target.GetOrbit(); double t = Planetarium.GetUniversalTime(); Func <double, Vector3d> targetPosAt = (d => TO.getPositionAtUT(t + d)); // For landed targets the orbit doesn't make sense, so calculate planet rotation if (target.GetVessel() != null && target.GetVessel().LandedOrSplashed) { Vessel ves = target.GetVessel(); CelestialBody vbody = ves.mainBody; Vector3d pos = vbody.GetRelSurfacePosition(ves.latitude, ves.longitude, ves.altitude); targetPosAt = delegate(double d) { double angle = vbody.rotates ? d * 360.0 / vbody.rotationPeriod : 0; return(vbody.position + QuaternionD.AngleAxis(angle, Vector3d.down) * pos); }; } //I chunck my orbit into 100 pieces, and find between which two chuncks the minimum occurs... double period = O.period; double bestDist = double.MaxValue; double bestTime = 0; for (double d = 0; d < period; d += period / 100) //Look at 100 places around the orbit { if (d == 0) { continue; //skip the first time } double dist = Vector3d.Distance(O.getPositionAtUT(t + d), targetPosAt(d)); if (dist < bestDist) { bestDist = dist; bestTime = d; } } //Now, do it again, but over a small section of the orbit centered on the above double start = bestTime - (period / 100); double end = bestTime + (period / 100); for (double d = start; d < end; d += period / 1000) //Look at 100 places within this chunck of orbit { if (d == start) { continue; //Skip the first time } double dist = Vector3d.Distance(O.getPositionAtUT(t + d), targetPosAt(d)); if (dist < bestDist) { bestDist = dist; bestTime = d; } } //And one last time, which is probably overkill start = bestTime - (period / 1000); end = bestTime + (period / 1000); for (double d = start; d < end; d += period / 10000) //Look at 100 places within this chunck of orbit { if (d == start) { continue; //For ease of computation } double dist = Vector3d.Distance(O.getPositionAtUT(t + d), targetPosAt(d)); if (dist < bestDist) { bestDist = dist; bestTime = d; //previous time is my start time } } _closest_time = bestTime; _closest_approach = bestDist; } else { _closest_approach = 0; _closest_time = 0; } //Time to Impact calculations double vertspeed = FlightGlobals.ActiveVessel.verticalSpeed; if (!ship.Landed && vertspeed < 0 && ship.orbit.PeA <= 0 && !ship.mainBody.atmosphere) { double Vf = Math.Sqrt((vertspeed * vertspeed) + 2 * _ra * _gravity); //t = 2d/(Vi+Vf) _tti = (Math.Abs(Vf) - Math.Abs(vertspeed)) / _gravity; _tti++; //So you hit the ground at 0, not 1 second after 0 } else { _tti = 0; } //Suicide Altitude calculations if (ship.Landed || ship.orbit.PeA > 0 || body.atmosphere) { _sa = -1; //Not calculating this } else { _sa = 0; double avgG = body.gravParameter / ((body.Radius + ship.terrainAltitude) * (body.Radius + ship.terrainAltitude)); avgG += FlightGlobals.getGeeForceAtPosition(ship.CoM).magnitude; avgG /= 2; //Debug.Log("Average G: " + Math.Round(avgG, 2)); double vdv = Math.Sqrt((2 * avgG * _ra) + (ship.verticalSpeed * ship.verticalSpeed)); //Debug.Log("Vertical Delta V: " + Math.Round(vdv, 1)); //Altitude Fraction = (Vertical dv ^2) / (2 * 1000 * Thrust (kN)) double altFrac = (vdv * vdv) / (2 * 1000 * _max_thrust); //Debug.Log("Altitude Fraction: " + Math.Round(altFrac, 2)); //m-avg = (m0 + (m0 / e ^ (dv / (Isp * 9.82)))) / 2 double avgMass = (_mass / Math.Pow(Math.E, (vdv / (_isp * 9.82)))); avgMass = (_mass + avgMass) / 2; //Debug.Log("Average mass: " + Math.Round(avgMass, 1)); _sa = (long)Math.Round(altFrac * avgMass * 1000); //Debug.Log("Suicide Altitude: " + Math.Round(_sa)); } //Cleanup isReadable = true; //Debug.Log("Thread simulation complete at " + Math.Round(Time.time, 3)); }
private RawOrbitPlanData GenerateRawOrbitPlanData() { RawOrbitPlanData dataOut = new RawOrbitPlanData { CurrentOrbitPatches = new List <OrbitData>(), ManPatchNum = -1, PlannedOrbitPatches = new List <OrbitData>(), TargetName = "???", Mans = new List <ManData>(), }; Orbit patch, lastPatch = null; patch = AV.orbit; while (patch != null && patch.activePatch) { dataOut.CurrentOrbitPatches.Add(OrbUtil.GetOrbitData(lastPatch = patch)); patch = patch.nextPatch; } if (AV.patchedConicSolver != null) { if (AV.patchedConicSolver.maneuverNodes != null) { if (AV.patchedConicSolver.maneuverNodes.Count > 0) { for (int i = 0; i < AV.patchedConicSolver.flightPlan.Count; i++) { if (AV.patchedConicSolver.flightPlan[i].patchStartTransition == Orbit.PatchTransitionType.MANEUVER) { dataOut.ManPatchNum = i; break; } } patch = AV.patchedConicSolver.maneuverNodes[0].nextPatch; while (patch != null && patch.activePatch) { dataOut.PlannedOrbitPatches.Add(OrbUtil.GetOrbitData(lastPatch = patch)); patch = patch.nextPatch; } for (int i = 0; i < AV.patchedConicSolver.maneuverNodes.Count; i++) { ManeuverNode m = AV.patchedConicSolver.maneuverNodes[i]; dataOut.Mans.Add(new ManData { DV = (float)m.GetBurnVector(m.patch).magnitude, UT = m.UT, X = (float)m.DeltaV.x, Y = (float)m.DeltaV.y, Z = (float)m.DeltaV.z, }); } } } } if (HasTarget()) { dataOut.TargetName = AV.targetObject.GetName(); OrbitData o = OrbUtil.GetOrbitData(AV.targetObject.GetOrbit()); if (AV.targetObject is CelestialBody) { o.transEnd = (byte)PatchTransitionType.FINAL; o.anomolyEnd = o.anomoly + Math.PI * 2; } dataOut.TargetOrbit = (o); //lastPatch.GetDTforTrueAnomaly dataOut.Rendezvous.ANAnom = (float)(FinePrint.Utilities.OrbitUtilities.AngleOfAscendingNode(lastPatch, AV.targetObject.GetOrbit()) * Deg2Rad); dataOut.Rendezvous.TargetANAnom = (float)(FinePrint.Utilities.OrbitUtilities.AngleOfAscendingNode(AV.targetObject.GetOrbit(), lastPatch) * Deg2Rad); dataOut.Rendezvous.T2AN = (int)OrbUtil.T2TAnom(lastPatch, dataOut.Rendezvous.ANAnom) % o.period; dataOut.Rendezvous.T2DN = (int)OrbUtil.T2TAnom(lastPatch, dataOut.Rendezvous.ANAnom + Math.PI) % o.period; dataOut.Rendezvous.RelInc = (float)FinePrint.Utilities.OrbitUtilities.GetRelativeInclination(lastPatch, AV.targetObject.GetOrbit()); double dist; double time = OrbUtil.CalcClosestAproach(lastPatch, AV.targetObject.GetOrbit(), lastPatch.StartUT, out dist); dataOut.Rendezvous.T2CA = (int)(time - Planetarium.GetUniversalTime()); dataOut.Rendezvous.SepAtCA = (int)dist; dataOut.Rendezvous.CAAnom = (float)lastPatch.TrueAnomalyAtUT(time); dataOut.Rendezvous.TargetCAAnom = (float)AV.targetObject.GetOrbit().TrueAnomalyAtUT(time); } return(dataOut); }