Skip to content

Instantly share code, notes, and snippets.

@JonnyOThan
Last active December 16, 2018 04:38
Show Gist options
  • Save JonnyOThan/7e29e9556fb7d2694eb912a793270293 to your computer and use it in GitHub Desktop.
Save JonnyOThan/7e29e9556fb7d2694eb912a793270293 to your computer and use it in GitHub Desktop.
parameter desired_roll is 0.
parameter desired_heading is 90.
parameter target_apoapsis is body:atm:height + 10000.
parameter g_turn_target_altitude is target_apoapsis + 20000. // slightly higher than target AP
parameter throttle_down_altitude is target_apoapsis - 20000.
run once "logging.ks".
run once "util.ks".
log_message("=== ascent ===").
lock throttle to 1.
sas off.
function g_turn {
parameter target_altitude is g_turn_target_altitude.
parameter exponent is 0.5.
lock steering to R(0, 0, desired_roll) * heading(desired_heading, max(0, 90*(1-(apoapsis/target_altitude)^exponent))).
}
g_turn().
local old_thrust to ship:maxthrustat(0).
local tanks_by_stage to list().
from { local i to 0. } until i = stage:number+1 step {set i to i+1.} do {
tanks_by_stage:add(list()).
}
local all_parts to list().
list parts in all_parts.
for p in all_parts {
if (p:stage >= 0) {
for r in p:resources {
if (r:name = "liquidfuel" and r:enabled) {
tanks_by_stage[p:stage]:add(p).
break.
}
}
}
}
log_debug(tanks_by_stage).
lock gravity to body:mu/(altitude+body:radius)^2.
lock twr to (ship:availablethrust / ship:mass / gravity).
when apoapsis > throttle_down_altitude and twr > 1 then lock throttle to 0.5.
until apoapsis > target_apoapsis {
local should_stage to false.
if ship:maxthrustat(0) < old_thrust {
set should_stage to true.
}
if stage:number > 1 and not tanks_by_stage[stage:number-1]:empty {
local fuel_in_stage to 0.
for t in tanks_by_stage[stage:number-1] {
for r in t:resources {
if r:name = "liquidfuel" {
set fuel_in_stage to fuel_in_stage + r:amount.
break.
}
}
}
if (fuel_in_stage < 0.001) {
log_message("empty fuel tank detected.").
set should_stage to true.
}
}
if should_stage {
if (body:atm:exists and body:atm:altitudepressure(altitude) > 0.01) {
lock steering to srfprograde.
log_message("turning prograde for staging").
local start_stage_time to time:seconds.
wait until vang(ship:facing:vector, srfprograde:vector) < 1 or time:seconds - start_stage_time > 5.
}
stage_to_next_engine().
set old_thrust to ship:maxthrustat(0).
wait 1.
log_message("resuming g-turn").
g_turn().
}
wait 0.5.
}
// TODO: fairings
lock throttle to 0.
wait 0.
wait until altitude > body:atm:height.
run once "logging.ks".
run once "util.ks".
run once "orbit_util.ks".
log_message("=== finish orbit ===").
local engines to get_active_engines().
list engines in all_engines.
if (engines_are_vacuum(engines) or engines:length = all_engines:length) {
log_message("finishing orbit with this stage").
run plan_circularize.
run execute_node.
} else {
local booster_separation_pe to get_maximum_periapsis_for_destruction().
local booster_sep_sma to (apoapsis + booster_separation_pe + body:radius*2) / 2.
local speed_for_booster_sep to get_orbital_speed_at_altitude(apoapsis, booster_sep_sma).
local speed_for_orbit to get_orbital_speed_at_altitude(apoapsis, apoapsis + body:radius).
local speed_at_ap to get_orbital_speed_at_altitude(apoapsis).
local fuel_mass_in_stage to get_fuel_mass_of_current_stage().
local isp to get_combined_isp(engines).
local dv_of_stage to isp * g0 * ln(ship:mass / (ship:mass - fuel_mass_in_stage)).
local dv_to_booster_sep to speed_for_booster_sep - speed_at_ap.
if (dv_of_stage < dv_to_booster_sep) {
log_message("booster will expire before maximum_pe - finishing orbit with one node").
run plan_circularize.
run execute_node.
} else {
local dv_for_sep to min(dv_to_booster_sep, dv_of_stage).
local speed_after_sep to speed_at_ap + dv_for_sep.
local dv_for_orbit to speed_for_orbit - speed_after_sep.
log_debug("speed at ap: " + speed_at_ap).
log_debug("speed for sep: " + speed_for_booster_sep).
log_debug("speed for orbit: " + speed_for_orbit).
log_debug("dv of stage: " + dv_of_stage).
log_debug("dv for sep: " + dv_for_sep).
log_debug("dv for orbit: " + dv_for_orbit).
// magic number: if the dv remaining in the booster could get us
// to a significant chunk of a mun transfer, keep the booster around
// even though it's going to become space trash
local wasted_booster_dv to dv_of_stage - dv_for_sep - dv_for_orbit.
if (wasted_booster_dv > 500) {
log_message("keeping booster because it has " + round(wasted_booster_dv) + " dv remaining").
run plan_circularize.
run execute_node.
} else {
log_message("dropping booster early - leaving " + round(dv_of_stage - dv_for_sep, 1) + " dV behind").
// TODO: need to figure out the total burn time for both burns, and start the first one earlier
local apoapsis_time to time:seconds + eta:apoapsis.
local n to node(apoapsis_time, 0, 0, dv_for_sep).
add n.
run execute_node(0).
log_debug("executing second half of circularization").
stage_to_next_engine().
set n to node(apoapsis_time, 0, 0, dv_for_orbit).
add n.
run execute_node.
}
}
}
parameter countdown is 3.
run once "logging".
run once "util.ks".
log_message("=== launch ===").
local v0 is getvoice(0).
from {local i is countdown.} until i = 0 step { set i to i-1.} do {
log_message(i).
v0:play(note("f4", 0.1, 0.5)).
wait 1.
}
log_message("0").
v0:play(note("c5", 0.3, 0.5)).
sas on.
lock throttle to 1.
stage.
local clamps to ship:modulesnamed("LaunchClamp").
for clamp in clamps {
if clamp:part:stage = stage:number-1 {
wait until stage:ready.
stage.
break.
}
}
wait until velocity:surface:mag > 50.
sas off.
run once "util.ks".
run once "orbit_util.ks".
set h to apoapsis.
if h < 0 {
set h to periapsis.
set duration_until_node to eta:periapsis.
} else {
set duration_until_node to eta:apoapsis.
}
log_message("=== circularizing at " + round(h/1000, 1) + "km ===").
run "remove_all_nodes".
set necessary_speed to get_orbital_speed_at_altitude(h, h + body:radius).
set current_speed to get_orbital_speed_at_altitude(h).
set n to node(time:seconds + duration_until_node, 0, 0, necessary_speed - current_speed).
add n.
run once "logging".
set g0 to Kerbin:mu / Kerbin:radius^2.
lock normal to vcrs(ship:velocity:orbit, -body:position).
lock radialin to vcrs(ship:velocity:orbit, normal).
function get_active_engines {
list engines in all_engines.
set result to list().
for e in all_engines
if (e:ignition and not e:flameout) result:add(e).
return result.
}
function get_burn_duration {
parameter deltav.
local engines to get_active_engines().
local isp to get_combined_isp(engines).
local final_mass to ship:mass / (constant:e ^ (deltav / g0 / isp)).
local fuel_mass_remaining to get_fuel_mass_of_current_stage().
local burn_duration to (ship:mass - final_mass) / get_mass_flow_rate(engines).
log_debug("isp: " + isp).
log_debug("current mass: " + round(ship:mass, 2)).
log_debug("final mass: " + round(final_mass, 2)).
log_debug("delta mass: " + round(ship:mass - final_mass, 2)).
log_debug("fuel remaining: " + round(fuel_mass_remaining, 2)).
log_debug("mass flow rate: " + get_mass_flow_rate(engines)).
log_debug("burn time: " + round(burn_duration, 2)).
return burn_duration.
}
function get_with_default {
parameter lex.
parameter key.
parameter default is 0.
set result to default.
if lex:haskey(key) set result to lex[key].
return result.
}
function get_fuel_mass_of_current_stage {
// WARNING: stage:resourceslex can be wrong!
set liquid_fuel to get_with_default(stage:resourceslex, "liquidfuel", 0):amount.
set oxidizer to get_with_default(stage:resourceslex, "oxidizer", 0):amount.
set density_t_per_L to 5/1000.
return density_t_per_L * (liquid_fuel + oxidizer).
}
function get_combined_isp {
parameter engines.
set numerator to 0.
for e in engines {
set numerator to numerator + e:availablethrust.
}
set mass_flow_rate to get_mass_flow_rate(engines).
if mass_flow_rate > 0
return numerator / mass_flow_rate / g0.
return 0.
}
function engines_are_vacuum {
parameter engines.
local result to true.
for e in engines {
if (e:ispat(0) / e:ispat(1) < 2) {
set result to false.
}
}
return result.
}
function get_mass_flow_rate {
parameter engines.
set result to 0.
for e in engines
set result to result + e:availablethrustat(0) / (e:ispat(0) * g0).
return result.
}
function warp_and_wait {
parameter duration.
if duration < 0 return.
log_message("waiting for " + format_time(duration)).
if duration > 10 {
kuniverse:timewarp:cancelwarp().
kuniverse:timewarp:warpto(time:seconds + duration - 5).
}
wait duration.
set warp to 0.
}
function stage_to_next_engine {
stage.
until ship:maxthrustat(0) > 0 {
wait until stage:ready.
stage.
}
wait 0.
}
function format_time {
parameter t.
local h to floor(t/60/60).
local m to mod(floor(t/60), 60).
local s to mod(t, 60).
return h + "h" + m + "m" + round(s, 2).
}
function get_maximum_periapsis_for_destruction {
parameter b is body.
if b:atm:exists {
local upper_bound to b:atm:height.
local lower_bound to 0.
until upper_bound - lower_bound < 1000 {
local midpoint to (upper_bound + lower_bound) / 2.
local pressure to b:atm:altitudepressure(midpoint).
if pressure > 0.01 {
set lower_bound to midpoint.
} else {
set upper_bound to midpoint.
}
}
return lower_bound.
} else {
return 0.
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment