// 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
// Can't use flameout or MAXTHRUSTAT on the initial staging, since the rocket may be held down by a launch tower
until STAGE:SOLIDFUEL + STAGE:LIQUIDFUEL <> 0 stage_when_ready().
// Auto-stage when any of the engines flameout or if there are no active engines (e.g. when we have an intermediate stage for decouplers before activating the next engine)
when any_flameout() or SHIP:MAXTHRUSTAT(0) = 0 then {
// 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.
print "--- PITCHOVER ---".
lock STEERING to HEADING(launch_azimuth, 90-pitchover_tilt).
wait until actual_prograde_pitch() > pitchover_tilt. // wait until the prograde "catches up" to our tilted heading
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.
wait until APOAPSIS > target_orbit_altitude.
lock THROTTLE TO 0.
unlock_control().
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
// 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.
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.
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).