Implement launch azimuth and refine node execution.

This commit is contained in:
Casper V. Kristensen 2018-07-26 04:06:14 +02:00
parent f346414b1a
commit 08246d4d83
Signed by: caspervk
GPG key ID: B1156723DB3BDDA8
6 changed files with 263 additions and 151 deletions

View file

@ -1,5 +1,39 @@
CLEARSCREEN.
print "=================== BOOTING ===================".
switch to 0.
if HOMECONNECTION:ISCONNECTED {
update_scripts().
} else {
print "No connection to KSC: not updating scripts.".
}
print "============= BOOT SEQUENCE COMPLETE =============".
function update_scripts {
// Copy all .ks-files from the archive to the CPU's internal storage.
// Get lexicon of existing items (files/directories) and sizes on the internal storage
local existing is lexicon().
for item in CORE:VOLUME:FILES:VALUES {
set existing[item:NAME] to item:SIZE.
}
// Check if items from the archive match those in the internal storage
for item in VOLUME("ARCHIVE"):FILES:VALUES {
local skip is false. // kOS doesn't support the 'continue' keyword...
if not skip and item:NAME[0] = "." set skip to true. // skip linux hidden dirs (e.g. .git)
if not skip and item:ISFILE
and item:EXTENSION <> "ks" set skip to true. // skip non-script items (e.g. README.md)
if not skip and existing:HASKEY(item:NAME)
and item:SIZE = existing[item:NAME] set skip to true. // skip if local item is same size
if not skip {
print "Copying " + item:NAME + "..".
COPYPATH(PATH(VOLUME("ARCHIVE")):COMBINE(item:NAME), CORE:VOLUME).
if item:NAME = "boot" reboot. // reboot CPU if boot/ directory was updated
}
}
}

155
launch.ks
View file

@ -2,81 +2,124 @@ run once util.
run once vectors.
run once node.
print "====================== LAUNCHING ======================".
parameter orbit_height is 100_000.
parameter pitchover_tilt is 20.
parameter pitchover_altitude is 1000.
parameter pitchover_velocity is 100.
function launch {
// Launch to orbit of provided altitude and inclination.
parameter target_orbit_altitude is 100_000. // meters
parameter target_orbit_inclination is 0. // 0-180 degrees
parameter pitchover_tilt is 20. // how many degrees to tilt at pitchover maneuver
parameter pitchover_altitude is 1000. // perform pitchover at this altitude
parameter pitchover_velocity is 100. // or this velocity
// Auto-stage when the stage has no solid- and liquid fuel left
when (STAGE:SOLIDFUEL + STAGE:LIQUIDFUEL < 1) and STAGE:READY then {
print "STAGING (zero fuel)".
STAGE.
return true. // preserve trigger
}
print "========= LAUNCHING =========".
print "Target orbital altitude: " + target_orbit_altitude + "m".
print "Target orbital inclination: " + target_orbit_inclination + "°".
print "Pitchover tilt: " + pitchover_tilt + "°".
print "Pitchover at: " + pitchover_altitude + "m or " + pitchover_velocity + " m/s".
print " ".
// Automatically deploy solar panels at 0 atmosphere
when SHIP:DYNAMICPRESSURE = 0 then {
print "Deploying solar panels (zero pressure)".
PANELS ON.
}
// Disable engine gimbal when engine is off
on (not THROTTLE) { // using not to cast to boolean
list engines in engines_list.
for engine in engines_list {
if engine:HASGIMBAL set engine:GIMBAL:LOCK to not THROTTLE.
// LAUNCH AZIMUTH
// Since the "center" of an orbit must be at the center of gravity of the body, the latitude of the launch site establishes the minimum absolute orbital inclination.
// KSC is almost on the equator, so we're going to always round the latitude towards zero, and accept the inaccuracies it may introduce.
local launch_site_latitude is round_towards_zero(SHIP:LATITUDE).
local target_orbit_inclination is MAX(target_orbit_inclination, launch_site_latitude).
// Calculate the launch azimuth; the compass heading we head for when launching to achieve orbit of desired inclination
local launch_azimuth is calculate_launch_azimuth(target_orbit_inclination, target_orbit_altitude, launch_site_latitude).
// If the latitude of the launch site is negative (ship is in the southern hemisphere), launch southwards instead of northwards
local southwards is launch_site_latitude < 0.
if southwards {
set launch_azimuth to 180 - launch_azimuth.
}
print "Launch site latitude: " + ROUND(SHIP:LATITUDE, 3) + "° (~" + launch_site_latitude + "°)".
print "Available orbital inclination: " + target_orbit_inclination + "°".
print "Launch azimuth: " + ROUND(launch_azimuth, 3) + "°".
print "Launch southwards: " + southwards.
print "--- VERTICAL CLIMB ---".
SAS OFF.
set NAVMODE to "SURFACE".
lock STEERING to HEADING(launch_azimuth, 90). // roll to launch azimuth
lock THROTTLE to 1.0.
STAGE.
// Enable auto-stage when the maximum engine thrust changes (i.e. some engines ran out of fuel)
on SHIP:MAXTHRUSTAT(0) {
when STAGE:READY then {
print "STAGING".
STAGE.
}.
return true. // preserve trigger
}
}
// VERTICAL CLIMB
print "VERTICAL CLIMB to " + pitchover_altitude + "m or " + pitchover_velocity + "m/s".
SAS OFF.
set NAVMODE to "SURFACE".
lock THROTTLE to 1.0.
lock STEERING to HEADING(90,90).
// Once a certain altitude is reached, a slight turn is made, called the pitchover maneuver
wait until SHIP:ALTITUDE > pitchover_altitude
// Once a certain altitude (or velocity) is reached, a slight turn is made, called the pitchover maneuver
wait until SHIP:ALTITUDE > pitchover_altitude
or SHIP:VELOCITY:SURFACE:MAG > pitchover_velocity.
// PITCHOVER MANEUVER
print "--- PITCHOVER ---".
lock STEERING to HEADING(launch_azimuth, 90-pitchover_tilt).
print "PITCHOVER by " + pitchover_tilt + "°".
lock STEERING to HEADING(90,90-pitchover_tilt).
wait until actual_prograde_pitch() > pitchover_tilt.
wait until actual_prograde_pitch() > pitchover_tilt. // wait until the prograde "catches up" to our tilted heading
// GRAVITY TURN
print "--- GRAVITY TURN ---".
// TODO: the angle of the launch azimuth will not account for the fact that our compass will change as we move north/south.
lock STEERING to HEADING(launch_azimuth, 90-actual_prograde_pitch()). // Follow prograde pitch to get 0 deg angle of attack, but force compass heading at launch azimuth.
print "GRAVITY TURN".
lock STEERING to HEADING(90, 90-actual_prograde_pitch()). // follow prograde to get 0 deg angle of attack, but force compass heading 90 (east)
wait until APOAPSIS > orbit_height.
lock THROTTLE TO 0.
unlock STEERING.
wait until APOAPSIS > target_orbit_altitude.
lock THROTTLE TO 0.
unlock_control().
// CREATE AND EXECUTE CIRCULARIZATION MANEUVER NODE
create_circularization_node().
execute_node().
print "--- CIRCULARIZE ---".
print "Waiting for ship to leave the atmosphere..".
set KUNIVERSE:TIMEWARP:RATE to 4.
wait until SHIP:DYNAMICPRESSURE = 0. // don't create maneuver node until we are out of the atmosphere - otherwise the apoapsis altitude and eta will change due to drag
KUNIVERSE:TIMEWARP:CANCELWARP().
print "Deploying solar panels".
PANELS ON.
// Create maneuver node that will circularize the orbit.
// NOTE: Potential errors in the inclination are not fixed, since we are most likely going to change our orbit, which will make the inclination change cheaper later on.
local node is NODE(TIME:SECONDS + ETA:APOAPSIS, 0, 0, 0).
// Burn magnitude in the prograde direction is the difference between the required velocity to acheive orbit at apoapsis altitude and our velocity at apoapsis
set node:PROGRADE to orbital_velocity(APOAPSIS) - VELOCITYAT(SHIP, TIME:SECONDS + ETA:APOAPSIS):ORBIT:MAG.
add node.
execute_node().
// EPILOGUE
print "========= LAUNCH SEQUENCE COMPLETE =========".
unlock_control().
}
// Ensure that the throttle will be 0 when execution stops
set SHIP:CONTROL:PILOTMAINTHROTTLE to 0.
// Ensure that the player is not locked out of control.
set SHIP:CONTROL:NEUTRALIZE to true.
unlock STEERING.
unlock THROTTLE.
function calculate_launch_azimuth {
// Calculate the launch azimuth; the compass heading we head for when launching to achieve orbit of desired inclination.
// http://www.orbiterwiki.org/wiki/Launch_Azimuth
// https://www.princeton.edu/~stengel/MAE342Lecture4.pdf
parameter target_orbit_inclination.
parameter target_orbit_altitude. // to compensate for the rotation of the body, we need to know the velocity of the target orbit, which is calculated from its altitude
parameter launch_site_latitude.
print "====================== LAUNCH SEQUENCE COMPLETE ======================".
local inertial_azimuth is ARCSIN(COS(target_orbit_inclination) / COS(launch_site_latitude)). // azimuth in inertial space, that is, disregarding the rotation of the body
local equatorial_rotational_velocity is (2 * CONSTANT:PI * BODY:RADIUS) / BODY:ROTATIONPERIOD.
local target_orbit_velocity is orbital_velocity(target_orbit_altitude). // velocity of target orbit
local launch_vector_x_component is target_orbit_velocity * SIN(inertial_azimuth) - equatorial_rotational_velocity * COS(launch_site_latitude).
local launch_vector_y_component is target_orbit_velocity * COS(inertial_azimuth).
return ARCTAN2(launch_vector_x_component, launch_vector_y_component).
}
launch(100_000, 0).

81
node.ks
View file

@ -1,72 +1,57 @@
run once util.
function estimated_burn_duration {
// Calculate estimated burn duration for a maneuver node
// https://en.wikipedia.org/wiki/Tsiolkovsky_rocket_equation
// https://space.stackexchange.com/questions/27375/how-do-i-calculate-a-rockets-burn-time-from-required-velocity
parameter node is NEXTNODE.
local exhaust_velocity is isp_sum() * (CONSTANT:G * KERBIN:MASS).
return ((SHIP:MASS * exhaust_velocity) / SHIP:MAXTHRUST) * (1 - CONSTANT:E^(-node:DELTAV:MAG/exhaust_velocity)).
}
function execute_node {
parameter node is NEXTNODE.
parameter precision is 0.05. // m/s delta-v
print "EXECUTE MANEUVER NODE".
local burn_duration is estimated_burn_duration(node).
// Calculate estimated burn time
// https://en.wikipedia.org/wiki/Tsiolkovsky_rocket_equation
// https://space.stackexchange.com/questions/27375/how-do-i-calculate-a-rockets-burn-time-from-required-velocity
local isp_sum is 0.
list ENGINES in engines_list.
for engine in engines_list {
if engine:STAGE = STAGE:NUMBER set ISP_sum to ISP_sum + engine:VISP.
}
print "=== EXECUTE MANEUVER NODE ===".
print "Estimated burn duration: " + ROUND(burn_duration, 1) + "s".
local ve is ISP_sum * (CONSTANT:G * KERBIN:MASS). // exhaust velocity
local burn_time is ((SHIP:MASS * ve) / SHIP:MAXTHRUST) * (1 - CONSTANT:E^(-node:BURNVECTOR:MAG/ve)).
print "Estimated burn time: " + ROUND(burn_time).
print "Aligning ship with burn vector".
print "Aligning ship with burn vector..".
SAS OFF.
lock STEERING to node:BURNVECTOR.
wait until VANG(SHIP:FACING:VECTOR, node:BURNVECTOR) < 0.1.
lock STEERING to node:DELTAV.
wait until VANG(SHIP:FACING:VECTOR, node:DELTAV) < 0.1.
print "Initializing warp".
unlock STEERING. // TODO: Doesnt work?
warp_for(MAX(0, node:ETA - (burn_time/2) - 5)). // Warp until 5s before node
unlock_control().
warp_for(MAX(0, node:ETA - (burn_duration/2) - 5)). // warp until 5s before node
print "Approaching".
lock STEERING to node:BURNVECTOR.
wait until node:ETA <= burn_time/2.
lock STEERING to node:DELTAV.
wait until node:ETA <= ROUND(burn_duration/2).
print "Burn!".
lock THROTTLE to 1.0.
//throttle is 100% until there is less than 1 second of time left to burn
//when there is less than 1 second - decrease the throttle linearly
// TODO: Smooth at the end
wait until node:DELTAV:MAG < 5.0. // TODO
// Decrease throttle linearly when the burn duration is less than 1 second
wait until estimated_burn_duration(node) <= 1.
lock THROTTLE to MAX(0.01, estimated_burn_duration(node)). // ensure we always finish by burning with at least 1% power
wait until node:DELTAV:MAG < precision.
set THROTTLE to 0.0.
print "DONE! Removing node and unlocking steering".
print "=== MANEUVER NODE EXECUTED ===".
print ROUND(node:DELTAV:MAG, 3) + "m/s delta-v remaining".
unlock_control().
wait 1.
remove node.
unlock STEERING.
unlock THROTTLE.
SET SHIP:CONTROL:PILOTMAINTHROTTLE TO 0.
}
function create_circularization_node {
// parameter where.
print "CREATE CIRCULARIZATION NODE".
local node is NODE(TIME:SECONDS + ETA:APOAPSIS, 0, 0, 0).
add node.
// Calculate required velocity to acheive orbit at altitude equal to apoapsis
// https://en.wikipedia.org/wiki/Orbital_speed#Mean_orbital_speed
local orbital_speed is SQRT((CONSTANT:G * ORBIT:BODY:MASS) / (ORBIT:BODY:RADIUS + APOAPSIS)).
// Calculate the difference between the required velocity and our velocity at apoapsis
set node:PROGRADE to orbital_speed - VELOCITYAT(SHIP, TIME:SECONDS + ETA:APOAPSIS):ORBIT:MAG.
}
function create_orbitchange_node {
function change_slash_set_orbit {
parameter what.
}

View file

@ -1,3 +0,0 @@
print "TEST".
run once vectors.

57
util.ks
View file

@ -1,11 +1,64 @@
function warp_to {
parameter timestamp.
KUNIVERSE:TIMEWARP:WARPTO(timestamp). // todo: improve.
KUNIVERSE:TIMEWARP:WARPTO(timestamp). // TODO: improve.
// wait until TIME >= timestamp. // TODO
}
function warp_for {
parameter seconds.
return warp_to(TIME:SECONDS + seconds).
}
function isp_sum {
local sum is 0.
list ENGINES in engines_list.
for engine in engines_list {
if engine:STAGE = STAGE:NUMBER set sum to sum + engine:VACUUMISP.
}
return sum.
}
function round_towards_zero {
parameter n.
if n < 0 return CEILING(n).
return FLOOR(n).
}
function orbital_velocity {
// Calculate the required velocity to acheive orbit at the provided altitude.
// https://en.wikipedia.org/wiki/Orbital_speed#Mean_orbital_speed
// http://www.orbiterwiki.org/wiki/Front_Cover_Equations
parameter altitude.
return SQRT(BODY:MU / (BODY:RADIUS + altitude)). // BODY:MU = CONSTANT:G * BODY:MASS
}
function unlock_control {
// Ensure that the throttle will be 0 when execution stops
set SHIP:CONTROL:PILOTMAINTHROTTLE to 0.
// Ensure that the player is not locked out of control.
set SHIP:CONTROL:NEUTRALIZE to true.
unlock STEERING.
unlock THROTTLE.
}
function disable_engine_gimbal {
// Disable engine gimbal when engine is off
on (THROTTLE = 0) {
list ENGINES in engines_list.
for engine in engines_list {
if engine:HASGIMBAL set engine:GIMBAL:LOCK to (not THROTTLE).
}
return true. // preserve trigger
}
}