function ExecNode { if not HASNODE { print "No node to execute.". return. } SAS off. // begin the burn at leadT seconds before the node. local leadT is BurnTime(NEXTNODE:DELTAV:MAG / 2). local t is BurnTime(NEXTNODE:DELTAV:MAG). if willStage(NEXTNODE:DELTAV:MAG) { print "WARNING: kOS will stage during this node execution. Safe cancellation requires reboot.". when flameOut() then { print "Flameout detected. Staging.". stage. } } print "Adjusting heading". lock STEERING to LookDirUp(NEXTNODE:DELTAV, SHIP:FACING:TOPVECTOR). wait until VAng(SHIP:FACING:FOREVECTOR, STEERINGMANAGER:TARGET:FOREVECTOR) <= 0.1. print "Warping to node.". KUNIVERSE:TIMEWARP:WarpTo(NEXTNODE:TIME - leadT - 5). wait until NEXTNODE:ETA <= leadT. print "Executing burn.". local dvMin is NEXTNODE:DELTAV:MAG. lock THROTTLE to 1.0. wait t. lock THROTTLE to 0.0. unlock THROTTLE. unlock STEERING. SAS on. print "Node execution complete.". } // Calculate the time required to burn a given dV. // Assumes a perfectly spherical Kerbal in a vacuum. function BurnTime { parameter totaldV, s is STAGE:NUMBER. local totalT is 0.0. local lastStage is false. // We allow a small tolerance to deal with potential floating point errors. until totaldV <= 0.001 { local F is stageThrust(). local Isp is stageISP(). local m is stageMass(s). // TODO: handle node execution in atmosphere? local dV is min(totaldV, SHIP:StageDeltaV(s):VACUUM). local t is calcBurnTime(dV, m, Isp, F). print "DEBUG: " + dV + " m/s^2 in stage " + s + ", in " + t + " seconds.". set totaldV to totaldV - dV. set s to s - 1. set totalT to totalT + t. } print "DEBUG: Total Burn Time: " + totalT + " s.". return totalT. } // Convenience function to wrap the actual calculation for burn time. function calcBurnTime { parameter dV, m, Isp, F. if F = 0 { print "WARNING: Tried to calculate burn time with a thrust of 0. Returning 0. Your calculations are probably wrong.". return 0. } local g0 is CONSTANT:G0. return g0 * m * Isp * (1 - CONSTANT():E^(-dV/(g0*Isp))) / F. } // Calculate the ISP for a given stage. // Defaults to current stage. Assumes your ship is designed so that // engines are discarded immediately when they flame out. function stageISP { parameter s is STAGE:NUMBER. local en is list(). list ENGINES in en. local ispSum is 0. local eCount is 0. for e in en { if e:STAGE >= s and e:DECOUPLEDIN < s { set ispSum to ispSum + e:ISP. set eCount to eCount + 1. } } if eCount = 0 { return 0. } return ispSum / eCount. } // Calculates the total thrust for the given stage, in kN. // Defaults to current stage. Assumes your ship is designed so that // engines are discarded immediately when they flame out. function stageThrust { parameter s is STAGE:NUMBER. local en is list(). list ENGINES in en. local sum is 0. for e in en { if e:STAGE >= s and e:DECOUPLEDIN < s { set sum to sum + e:POSSIBLETHRUST. } } return sum. } // Determine mass at start of target stage. // This can handle Delta V-style launchers but // only if the central rocket remains in the stack // for exactly two stages, one of which is the current stage. // More complex staging with partially depleted tanks may produce // undefined behavior. function stageMass { parameter s is STAGE:NUMBER. local m is SHIP:MASS. if s = SHIP:STAGENUM { return m. } local ps is List(). list PARTS in ps. for part in ps { if part:DECOUPLEDIN <= s { set m to m - part:MASS. } else if part:HasSuffix("AVAILABLETHRUST") and part:AVAILABLETHRUST > 0 { // This is an engine and it is currently active. Subtract the fuel // it will burn before the next stage. set m to m - (part:MAXMASSFLOW * SHIP:StageDeltaV(SHIP:STAGENUM):DURATION). } } return m. } function flameOut { local ens is List(). list engines in ens. for en in ens { if en:FLAMEOUT { return true. } } return false. }