diff --git a/.gitignore b/.gitignore index e69de29..cd3d225 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +logs \ No newline at end of file diff --git a/ascent.ks b/ascent.ks deleted file mode 100644 index 949d427..0000000 --- a/ascent.ks +++ /dev/null @@ -1,73 +0,0 @@ -// Inital gravity turn -// #include staging -run once staging. -// #include maneuver -run once maneuver. - -function doAscentSteering { - parameter heading. - - // initial pitch-over - set tarSteer to heading(heading, 90). - lock steering to tarSteer. - until ship:velocity:surface:mag >= 100 { - set tarSteer to heading(heading, 90 - (ship:velocity:surface:mag / 10)). - if defined UPDATE_LOG { UPDATE_LOG(). } - wait 0. - } - - lock steering to ship:srfprograde. - print "Locked steering to surface prograde". -} - -function createCircularizationNode { - // Re-use existing node if already planned - if hasnode { - print "Existing node found. Skipping creation.". - return nextnode. - } - - local rBurn to body:radius + apoapsis. - local vTarget to sqrt(body:mu / rBurn). - local dvEst to vTarget - ship:velocity:orbit:mag. - - local nd to node(time:seconds + eta:apoapsis, 0, 0, dvEst). - add nd. - return nd. -} - -function basicAscent { - parameter heading is 90, targetAltitude is 80000. - - lock throttle to 1. - - print "Counting down:". - from {local countdown is 5.} until countdown = 0 step {set countdown to countdown - 1.} do { - print "..." + countdown. - wait 1. - } - - // initial stage to takeoff - stage. - - // stage everytime thrust drops to zero - basicStaging(). - // basic gravity-ish turn - doAscentSteering(heading). - - // coast to apoapsis - until ship:apoapsis >= targetAltitude { - if defined UPDATE_LOG { UPDATE_LOG(). } - } - lock steering to ship:prograde. - lock throttle to 0. - - // don't do futher calcs until out of the atmosphere or very close to apoapsis - until ship:altitude >= body:atm:height or eta:apoapsis < 60 { - if defined UPDATE_LOG { UPDATE_LOG(). } - } - - executeNode(createCircularizationNode()). - - print "Circularized". -} \ No newline at end of file diff --git a/boot/boot.ks b/boot/boot.ks index 2536d0c..f3d852f 100644 --- a/boot/boot.ks +++ b/boot/boot.ks @@ -1,52 +1,6 @@ -copyPath("0:/ascent.ks", "1:/ascent.ks"). -copyPath("0:/maneuver.ks", "1:/maneuver.ks"). -copyPath("0:/staging.ks", "1:/staging.ks"). -copyPath("0:/logger.ks", "1:/logger.ks"). -// #include logger -run once logger. -// #include ascent -run once ascent. - -createDir("1:/logs"). -set LOG to loggerCreate( - "1:/logs/flight-" + ship:shipName + "-" + time:full + ".json", - list( - "time", - "altitude", - "apoapsis", - "periapsis", - "eccentricity", - "throttle", - "pitch", - "heading", - "mass", - "stageNum" - ), - 1.0 -). -set LOG_DATA to { - return list( - time:seconds, - round(ship:altitude, 1), - round(ship:apoapsis, 1), - round(ship:periapsis, 1), - round(ship:orbit:eccentricity, 4), - round(ship:throttle, 3), - round(ship:pitch, 2), - round(ship:heading, 2), - round(ship:mass, 3), - stage:number - ). -}. - -function UPDATE_LOG { - loggerUpdate(LOG, LOG_DATA()). -} - wait until ship:unpacked. -core:part:getmodule("kOSProcessor"):doevent("Open Terminal"). - -basicAscent(90, 80000). - -core:part:getmodule("kOSProcessor"):doevent("Close Terminal"). -loggerFlush(LOG). \ No newline at end of file +if addons:RT:hasKSCConnection(ship) { + runPath("0:/scripts/basic.ks"). +} else { + print "No connection to the KSC". +} diff --git a/boot/heloBoot.ks b/boot/heloBoot.ks new file mode 100644 index 0000000..632255b --- /dev/null +++ b/boot/heloBoot.ks @@ -0,0 +1,2 @@ +wait until ship:unpacked. +runPath("0:/scripts/helo.ks"). diff --git a/maneuver.ks b/maneuver.ks deleted file mode 100644 index 5422085..0000000 --- a/maneuver.ks +++ /dev/null @@ -1,79 +0,0 @@ -// ═══════════════════════════════════════════════════════════ -// maneuver.ks -// Utility functions related to maneuvers. -// -// Usage: -// executeNode(nd: node) -// ═══════════════════════════════════════════════════════════ - -// #include staging -run once staging. - -function executeNode { - parameter nd is node(0, 0, 0, 0). - - if node:deltav:mag = 0 { - print "Empty node". - return. - } - - local burnTime to calcBurnTime(nd:deltav:mag). - print "Node in: " + round(nd:eta) + "s, dV: " + round(nd:deltav:mag, 1) + " m/s, burn time: " + round(burnTime, 1) + "s". - - // ── Align to node vector. - // If already inside burn window, skip the 60s lead-in wait. - lock steering to lookdirup(nd:deltav, ship:facing:topvector). - - if nd:eta > burnTime / 2 + 60 { - wait until nd:eta <= burnTime / 2 + 60. - } - - // ── Wait until pointed correctly, unless burn is critically overdue - if nd:eta > 0 { - wait until vang(ship:facing:vector, nd:deltav) < 1 - or nd:eta <= burnTime / 2. - } - - // ── Snapshot initial dV for overburn detection - local dv0 to nd:deltav. - - ag1 off. - - until ag1 { - local tBurn to time:seconds + nd:eta. - local vActual to velocityat(ship, tBurn):orbit. - local vTarget to nd:deltav + vActual. - local dvLive to vTarget - ship:velocity:orbit. - - lock steering to lookdirup(dvLive, ship:facing:topvector). - - // ── Overburn detection - if vdot(dv0, nd:deltav) < 0 { - print "Overburn detected. Cutting throttle.". - lock throttle to 0. - break. - } - - // ── Residual dV negligible - if nd:deltav:mag < 0.1 { - lock throttle to 0. - break. - } - - // ── Throttle proportional to remaining dV magnitude - local maxAcc to ship:maxthrust / ship:mass. - if vang(ship:facing:vector, dvLive) > 1 { - lock throttle to 0. - } else { - lock throttle to min(1, dvLive:mag / maxAcc). - } - - if defined UPDATE_LOG { UPDATE_LOG(). } - wait 0. - } - - lock throttle to 0. - unlock throttle. - unlock steering. - remove nd. -} \ No newline at end of file diff --git a/scripts/ascent.ks b/scripts/ascent.ks new file mode 100644 index 0000000..99ceb5e --- /dev/null +++ b/scripts/ascent.ks @@ -0,0 +1,105 @@ +// Inital gravity turn +// #include staging +run once staging. +// #include maneuver +run once maneuver. + +function signedHeading { + local fwd is ship:srfprograde:vector. + local n is north:vector. + local e is vCrs(up:vector, north:vector). + + local hdg is arcTan2(vdot(fwd, e), vdot(fwd, n)). + return mod(hdg + 360, 360). +} + +function basicAscent { + parameter targetHeading is 90, targetAltitude is 80000. + + if ship:altitude > body:atm:height return. + + // get parts of interest + set fairings to ship:partstitledpattern("^AE-FF.*"). + set antennas to ship:partstitledpattern("^(CommTech|Communotron|HG-|RA-|Reflectron).*"). + + lock throttle to 1. + + print "Counting down:". + from {local countdown is 5.} until countdown = 0 step {set countdown to countdown - 1.} do { + print "..." + countdown. + wait 1. + } + + // initial stage to takeoff + stage. + + // stage everytime thrust drops to zero + when ship:maxThrust = 0 then { + print "Staging". + stage. + return true. + }. + + // initial pitch-over + set tarSteer to heading(targetHeading, 90). + lock steering to tarSteer. + until ship:velocity:surface:mag >= 100 { + set tarSteer to heading(targetHeading, 90 - (ship:velocity:surface:mag / 10)). + wait 0. + } + + // lock steering to the target heading, but account for drift, and the pitch + // of the surface normal vector + lock calculatedHeading to mod(targetHeading + 2 * (targetHeading - signedHeading()) + 360, 360). + lock steering to heading( + calculatedHeading, + max(5, 90 - vAng(up:vector, ship:srfprograde:vector)) + ). + print "Locked steering to surface prograde pitch and target heading". + + when ship:q < 0.01 then { + for f in fairings { + // get the module and deploy it + set m to f:getmodule("ModuleProceduralFairing"). + if m:hasevent("deploy") m:doevent("deploy"). + } + + wait 0.1. + + panels on. + radiators on. + for a in antennas { + set m to a:getmodule("ModuleRTAntenna"). + if m:hasevent("activate") m:doevent("activate"). + if m:getfield("target") = "no-target" m:setfield("target", "Mission Control"). + } + + } + + // coast to apoapsis + wait until ship:apoapsis >= targetAltitude. + unlock calculatedHeading. + lock steering to ship:prograde. + lock throttle to 0. + + // don't do futher calcs until out of the atmosphere or very close to apoapsis + until ship:altitude >= body:atm:height or eta:apoapsis < 60. + + + // if we're using a booster to get to orbit drop it before finishing the cirularization + when ship:periapsis > 30000 then { + if ship:stagenum <> 0 and ship:stagedeltav(ship:stagenum - 1):current > 0 { + print "Dropping booster". + + // shutdown engines before dropping them + for e in ship:engines { + if e:ignition e:shutdown(). + } + + stage. + } + } + executeNode(createCircularizationNodeAp()). + + print "Circularized". +} \ No newline at end of file diff --git a/scripts/basic.ks b/scripts/basic.ks new file mode 100644 index 0000000..fdc3d08 --- /dev/null +++ b/scripts/basic.ks @@ -0,0 +1,57 @@ +// core:part:getmodule("kOSProcessor"):doevent("Open Terminal"). + +if ship:bounds:bottomaltradar > 5 { + print "Script shouldn't start off the ground". +} else { + set MODULES to list( + "ascent.ks", + "maneuver.ks", + "staging.ks", + "logger.ks" + ). + + for mod in MODULES { + copyPath("0:/scripts/" + mod, "1:/" + mod). + } + + // #include ascent + run once ascent. + + // --- GUI --- + local targetAlt is 80000. + local targetHdg is 90. + local guiConfirmed is false. + + local g is GUI(300). + set g:x to 100. + set g:y to 100. + + local titleLabel is g:addlabel("Ascent Parameters"). + set titleLabel:style:align to "center". + set titleLabel:style:hstretch to true. + + g:addlabel("Target Altitude (m):"). + local altBox is g:addtextfield(targetAlt:tostring). + set altBox:style:hstretch to true. + + g:addlabel("Heading (degrees):"). + local hdgBox is g:addtextfield(targetHdg:tostring). + set hdgBox:style:hstretch to true. + + local confirmBtn is g:addbutton("Launch"). + set confirmBtn:onclick to { + set targetAlt to altBox:text:tonumber(targetAlt). + set targetHdg to hdgBox:text:tonumber(targetHdg). + set guiConfirmed to true. + g:hide(). + }. + + g:show(). + wait until guiConfirmed. + g:dispose(). + // --- END GUI --- + + basicAscent(targetHdg, targetAlt). + + // core:part:getmodule("kOSProcessor"):doevent("Close Terminal"). +} diff --git a/scripts/circularizeAp.ks b/scripts/circularizeAp.ks new file mode 100644 index 0000000..46d2a2c --- /dev/null +++ b/scripts/circularizeAp.ks @@ -0,0 +1,4 @@ +// #include maneuver +run once maneuver. + +createCircularizationNodeAp(). diff --git a/scripts/circularizePe.ks b/scripts/circularizePe.ks new file mode 100644 index 0000000..af781a2 --- /dev/null +++ b/scripts/circularizePe.ks @@ -0,0 +1,4 @@ +// #include maneuver +run once maneuver. + +createCircularizationNodePe(). diff --git a/scripts/doManeuver.ks b/scripts/doManeuver.ks new file mode 100644 index 0000000..119dc34 --- /dev/null +++ b/scripts/doManeuver.ks @@ -0,0 +1,4 @@ +// #include maneuver +run once maneuver. + +if hasNode executeNode(nextNode). \ No newline at end of file diff --git a/scripts/helo.ks b/scripts/helo.ks new file mode 100644 index 0000000..3f52deb --- /dev/null +++ b/scripts/helo.ks @@ -0,0 +1,19 @@ +core:part:getmodule("kOSProcessor"):doevent("Open Terminal"). + +set tr to ship:partstagged("tailRotor")[0]. +set rm to tr:getModule("ModuleRoboticServoRotor"). +set rpmMax to 410. + +set pid to pidLoop(0.0025, 0, 0, rpmMax, rpmMax). +set pid:setpoint to 0. + +until false { + set L_z to ship:angularMomentum:z. + set rpmLimit to pid:update(time:seconds, L_z). + rm:setfield("rpm limit", rpmLimit). + + clearscreen. + print "Lz : " + round(L_z, 2) at (5, 5). + print "RPM Limit : " + round(rpmLimit, 2) at (5, 6). + wait 0. +} \ No newline at end of file diff --git a/scripts/lander.ks b/scripts/lander.ks new file mode 100644 index 0000000..4e7a740 --- /dev/null +++ b/scripts/lander.ks @@ -0,0 +1,87 @@ +function groundSlope { + parameter xOffset is 0, yOffset is 0. + local east is vCrs(north:vector, up:vector). + + local center is ( + ship:position + + (yOffset * north:vector) + + (xOffset * east) + ). + + local a is body:geopositionof(center + 5 * north:vector). + local b is body:geopositionof(center - 3 * north:vector + 4 * east). + local c is body:geopositionof(center - 3 * north:vector - 4 * east). + + local aVec is a:altitudeposition(a:terrainheight). + local bVec is b:altitudeposition(b:terrainheight). + local cVec is c:altitudeposition(c:terrainheight). + + local slope is vCrs(cVec - aVec, bVec - aVec):normalized. + // local centerPos is body:geopositionof(center). + // set slopeDraw to vecDraw( + // centerPos:altitudeposition(centerPos:terrainheight), + // slope, + // green, + // "", + // 10, + // true + // ). + + return slope. +} + +wait until not hasNode. + +// Suicide burn calcs +set tval to 0. +lock throttle to tval. +lock steering to ship:srfretrograde. + +lock h to ship:bounds:bottomaltradar. +lock v0 to ship:verticalspeed. +lock g to (body:mu / ((body:radius + ship:altitude)^2)). +lock a to ship:maxthrust / ship:mass. +lock d to (v0^2) / (2 * (a - g)). + + +// wait for burn +until h <= (1.1 * d) { + clearscreen. + print "Altitude AGL : " + round(h) at (4, 0). + print "Vertical Speed : " + round(v0, 1) at (4, 1). + print "Gravity : " + round(g, 2) at (4, 2). + print "Acceleration : " + round(a, 2) at (4, 3). + print "Distance : " + round(d, 2) at (4, 4). + wait 0. +} +unlock d. +unlock a. + +set tval to 1. + +set steeringLocked to false. +until h < 1 { + if v0 > -4 { + // equalize acceleration to stop downward velocity + set tval to max(0.001, ship:mass * g / ship:maxThrust). + } + else if v0 > -6 and not steeringLocked { + // slow enough that we just want to kill vertical velocity + lock steering to up. + set steeringLocked to true. + } + + clearscreen. + print "Altitude AGL : " + round(h) at (4, 0). + print "Vertical Speed : " + round(v0, 1) at (4, 1). + print "Gravity : " + round(g, 2) at (4, 2). + print "Throttle : " + 100 * round(tval, 3) at (4, 3). + wait 0. +} + +clearscreen. +print "Holding position". +lock steering to groundSlope(). +wait until ship:angularvel:mag < 0.1. +wait 5. +print "Stable". diff --git a/logger.ks b/scripts/logger.ks similarity index 72% rename from logger.ks rename to scripts/logger.ks index e8bf3af..f052b90 100644 --- a/logger.ks +++ b/scripts/logger.ks @@ -3,14 +3,14 @@ // CSV logger with buffered writes. // // Usage: -// local log to loggerCreate("0:/logs/flight.csv", list("time", "altitude", "apoapsis")). -// loggerUpdate(log, list(time:seconds, altitude, apoapsis)). -// loggerFlush(log). // call at end of script to flush remaining buffer +// local LOGGER to loggerCreate("0:/logs/flight.csv", list("time", "altitude", "apoapsis")). +// loggerUpdate(LOGGER, list(time:seconds, altitude, apoapsis)). +// loggerFlush(LOGGER). // call at end of script to flush remaining buffer // ═══════════════════════════════════════════════════════════ // ── Creates a logger and writes the CSV header. // Parameters: -// filePath : string — destination path, e.g. "0:/logs/flight.csv" +// filePath : string — destination path, e.g. "logs/flight.csv" // columns : list — ordered list of column name strings // flushPeriod : scalar — seconds between buffer flushes (default 1.0) // Returns a logger lexicon to be passed to loggerUpdate / loggerFlush. @@ -20,11 +20,12 @@ function loggerCreate { parameter flushPeriod is 1.0. // Create or overwrite the file and write the header row - local f to open(filePath). + if not archive:exists(filePath) archive:create(filePath). + local f to archive:open(filePath). f:clear(). f:writeln(_loggerFormatRow(columns)). - local logger to lexicon( + local l to lexicon( "file", f, "columns", columns, "buffer", list(), @@ -32,30 +33,30 @@ function loggerCreate { "lastFlush", time:seconds ). - return logger. + return l. } // ── Appends a data row to the buffer. // Values must be in the same order as the columns list passed to loggerCreate. // Flushes the buffer to disk if the flush period has elapsed. function loggerUpdate { - parameter logger. + parameter l. parameter values. - logger["buffer"]:add(_loggerFormatRow(values)). + l["buffer"]:add(_loggerFormatRow(values)). - if time:seconds - logger["lastFlush"] >= logger["flushPeriod"] { - loggerFlush(logger). + if time:seconds - l["lastFlush"] >= l["flushPeriod"] { + loggerFlush(l). } } // ── Flushes all buffered rows to disk immediately. // Call at the end of a script to ensure no data is lost. function loggerFlush { - parameter logger. + parameter l. - local f to logger["file"]. - local buf to logger["buffer"]. + local f to l["file"]. + local buf to l["buffer"]. local i to 0. until i >= buf:length { @@ -64,7 +65,7 @@ function loggerFlush { } buf:clear(). - set logger["lastFlush"] to time:seconds. + set l["lastFlush"] to time:seconds. } // ── Formats a list of values as a CSV row string. diff --git a/scripts/maneuver.ks b/scripts/maneuver.ks new file mode 100644 index 0000000..67e768e --- /dev/null +++ b/scripts/maneuver.ks @@ -0,0 +1,89 @@ +// ═══════════════════════════════════════════════════════════ +// maneuver.ks +// Utility functions related to maneuvers. +// +// Usage: +// executeNode(nd: node) +// ═══════════════════════════════════════════════════════════ + +// #include staging +run once staging. + +function executeNode { + parameter nd is node(0, 0, 0, 0). + print "boof". + + if nd:deltav:mag = 0 { + print "Empty node". + return. + } + + local burnTime to calcBurnTime(nd:deltav:mag). + print "Node in: " + round(nd:eta) + "s, dV: " + round(nd:deltav:mag, 1) + " m/s, burn time: " + round(burnTime, 1) + "s". + + // wait for node to be close then align to vector + wait until nd:eta <= burnTime / 2 + 60. + local np to nd:deltav. + lock steering to np. + wait until vang(ship:facing:vector, nd:deltav) < 1. + print "Maneuver vector lined up". + + // wait until burn + wait until nd:eta <= burnTime / 2. + print "Burn time". + + // ── Snapshot initial dV for overburn detection + local dv0 to nd:deltav. + + local throttleSet to 0. + lock throttle to throttleSet. + + // ag1 off. + // until ag1 { + until False { + local maxAcc to ship:maxThrust / ship:mass. + set throttleSet to min(nd:deltav:mag / maxAcc, 1). + set np to nd:deltav. + + // ── Overburn detection + if vdot(dv0, nd:deltav) < 0 { + print "Overburn detected. Cutting throttle.". + lock throttle to 0. + break. + } + + // ── Residual dV negligible + if nd:deltav:mag < 0.1 { + wait until vdot(dv0, nd:deltav) < 0.5. + lock throttle to 0. + break. + } + + wait 0. + } + + lock throttle to 0. + unlock throttle. + unlock steering. + remove nd. +} + +function createCircularizationNodeAp { + local rBurn to body:radius + apoapsis. + local vTarget to sqrt(body:mu / rBurn). + local dvEst to vTarget - velocityat(ship, time:seconds + eta:apoapsis):orbit:mag. + + local nd to node(time:seconds + eta:apoapsis, 0, 0, dvEst). + add nd. + return nd. +} + +function createCircularizationNodePe { + local rBurn to body:radius + periapsis. + local vTarget to sqrt(body:mu / rBurn). + local dvEst to vTarget - velocityat(ship, time:seconds + eta:periapsis):orbit:mag. + + local nd to node(time:seconds + eta:periapsis, 0, 0, dvEst). + add nd. + return nd. +} diff --git a/staging.ks b/scripts/staging.ks similarity index 87% rename from staging.ks rename to scripts/staging.ks index f6e8803..7d100a0 100644 --- a/staging.ks +++ b/scripts/staging.ks @@ -8,14 +8,6 @@ // ═══════════════════════════════════════════════════════════ -declare function basicStaging { - when ship:maxThrust = 0 then { - print "Staging". - stage. - preserve. - }. -} - function calcBurnTime { parameter dvNeeded. @@ -27,6 +19,7 @@ function calcBurnTime { local stageDv to ship:stagedeltav(stageNum):current. local stageTime to ship:stagedeltav(stageNum):duration. + print "Stage: " + stageNum. if stageDv <= 0 { set stageNum to stageNum - 1. } else if stageDv >= dvRemaining { @@ -37,6 +30,9 @@ function calcBurnTime { set dvRemaining to dvRemaining - stageDv. set stageNum to stageNum - 1. } + + print " - time: " + totalTime. + print " - dv : " + dvRemaining. } if dvRemaining > 0 { @@ -44,4 +40,12 @@ function calcBurnTime { } return totalTime. +} + +function deployables { + ag1 on. + wait 0.5. + panels on. + radiators on. + ag2 on. } \ No newline at end of file