|
|
|
@ -1,7 +1,5 @@
|
|
|
|
|
// functions for calculating steering values.
|
|
|
|
|
|
|
|
|
|
runoncepath("/lib/node").
|
|
|
|
|
|
|
|
|
|
function GetPitch {
|
|
|
|
|
parameter v is SHIP:FACING:FOREVECTOR.
|
|
|
|
|
return 90 - vectorangle(SHIP:UP:FOREVECTOR, v).
|
|
|
|
@ -61,27 +59,177 @@ function StoppingDistance {
|
|
|
|
|
return dV*BurnTime(dV)/2.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Figure out our closest approach to our current target within the next
|
|
|
|
|
// N seconds. (default orbital_period * 2)
|
|
|
|
|
// This is a horrible, terrible, no good, very bad brute force hack.
|
|
|
|
|
// function timeAtClosestApproach {
|
|
|
|
|
// parameter maxSeconds is SHIP:ORBIT:PERIOD * 2.
|
|
|
|
|
function ExecNode {
|
|
|
|
|
if not HASNODE {
|
|
|
|
|
print "No node to execute.".
|
|
|
|
|
return.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// // find the closest approach to pos
|
|
|
|
|
// local t is TIME.
|
|
|
|
|
// local minD is (PositionAt(TARGET, t) - PositionAt(SHIP, t)):MAG.
|
|
|
|
|
// local minT is t.
|
|
|
|
|
// until false {
|
|
|
|
|
// local d is (PositionAt(TARGET, t) - PositionAt(SHIP, t)):MAG.
|
|
|
|
|
// if d < minD {
|
|
|
|
|
// set minD to d.
|
|
|
|
|
// set minT to t.
|
|
|
|
|
// }
|
|
|
|
|
// set t to t+1.
|
|
|
|
|
// }
|
|
|
|
|
SAS off.
|
|
|
|
|
|
|
|
|
|
// return minT.
|
|
|
|
|
// }
|
|
|
|
|
// 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".
|
|
|
|
|
// The vector constant here should always align our "sides" with the universal up/down axis, so we can predictably place solar panels.
|
|
|
|
|
lock STEERING to LookDirUp(NEXTNODE:DELTAV, V(0,0,90)).
|
|
|
|
|
wait until VAng(SHIP:FACING:FOREVECTOR, STEERINGMANAGER:TARGET:FOREVECTOR) <= 0.1.
|
|
|
|
|
|
|
|
|
|
print "Warping to node.".
|
|
|
|
|
KUNIVERSE:TIMEWARP:WarpTo(NEXTNODE:TIME - leadT - 2).
|
|
|
|
|
wait until SHIP:UNPACKED.
|
|
|
|
|
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.".
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// currently only works for testing against *current* stage.
|
|
|
|
|
function WillStage {
|
|
|
|
|
parameter dV.
|
|
|
|
|
if not HASNODE { return false. }
|
|
|
|
|
return dV > SHIP:StageDeltaV(SHIP:STAGENUM):VACUUM.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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(s).
|
|
|
|
|
local Isp is stageISP(s).
|
|
|
|
|
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).
|
|
|
|
|
|
|
|
|
|
set totaldV to totaldV - dV.
|
|
|
|
|
set s to s - 1.
|
|
|
|
|
set totalT to totalT + t.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return totalT.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convenience function to wrap the actual calculation for burn time.
|
|
|
|
|
function calcBurnTime {
|
|
|
|
|
parameter dV, m, Isp, F.
|
|
|
|
|
|
|
|
|
|
if F = 0 or Isp = 0 {
|
|
|
|
|
print "WARNING: Tried to calculate burn time with a denominator value of 0. Returning 0. Your calculations are probably wrong.".
|
|
|
|
|
print "F: " + F .
|
|
|
|
|
print "Isp: " + Isp.
|
|
|
|
|
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:VACUUMISP.
|
|
|
|
|
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.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list ENGINES in ps.
|
|
|
|
|
for part in ps {
|
|
|
|
|
if part:DECOUPLEDIN < s and part:AVAILABLETHRUST > 0 {
|
|
|
|
|
set m to m - (part:MAXMASSFLOW * SHIP:StageDeltaV(SHIP:STAGENUM):DURATION).
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return m.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: This would be better in throttle.ks or perhaps some sort of ship status library,
|
|
|
|
|
// but we want to avoid too many inter-dependencies for now.
|
|
|
|
|
function FlameOut {
|
|
|
|
|
local ens is List().
|
|
|
|
|
list engines in ens.
|
|
|
|
|
for en in ens {
|
|
|
|
|
if en:FLAMEOUT {
|
|
|
|
|
return true.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// function PredictGeo {
|
|
|
|
|
// parameter t.
|
|
|
|
|