Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 229 additions & 2 deletions glib/maneuver_atmosphere.to2
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Atmospheric Maneuvers
//

use { Vessel, VesselSituation, AutopilotMode, DeltaVSituation, ParachuteSafeStates } from ksp::vessel
use { Vessel, VesselSituation, AutopilotMode, DeltaVSituation, ParachuteSafeStates, FlightCtrlState } from ksp::vessel
use { GeoCoordinates, find_body } from ksp::orbit
use { sleep, current_time, wait_until, yield } from ksp::game
use { warp_to } from ksp::game::warp
Expand All @@ -16,7 +16,7 @@ use { format } from core::str
use { warning, panic, pretty_time, pretty_trend, pretty_distance } from glib::display
use { Node, ErrorNode, circularize_at_ap, circularize_at_pe, change_pe_at_ap, change_ap_at_pe } from glib::maneuver_vacuum
use { Mission } from glib::mission
use { Universe, Locations, lon_sunny_side, lat_nice_spot, check_and_finish_ssb } from glib::utility
use { Universe, Locations, lon_sunny_side, lat_nice_spot, check_and_finish_ssb, has_ssb } from glib::utility

// Burn a maneuver node that is part of an atmospheric ascent
// This means:
Expand Down Expand Up @@ -119,6 +119,233 @@ pub fn burnNextNodeAtm(mission: Mission, ap_target: float) -> Unit = {
node.remove()
}

//
// Launch the vessel from a planet with an atmosphere into orbit using a gravity turn
//
pub fn gravity_turn(mission: Mission, target_apoapsis: float,
turn_start: float, turn_pitch: float, heading: float) -> Unit = {

const vessel = mission.vessel
const con = mission.console.value
const startDV = mission.getTotalDV()

// If we are landed or pre-launch, launch
if (vessel.situation == VesselSituation.Landed || vessel.situation == VesselSituation.PreLaunch) {4
con.log("Vessel is on the ground. Launch.")
vessel.set_throttle(1)
sleep(0.2)
vessel.autopilot.mode = AutopilotMode.StabilityAssist
vessel.staging.next()
sleep(0.3)

// find launch clamps and check if they are still latched
for (part in vessel.parts)
{
if (part.is_launch_clamp)
{
con.log("Found a launch clamp")
if (part.launch_clamp.defined)
{
con.log("Releasing launch clamps!")
let lac = part.launch_clamp.value
if (!lac.is_released)
lac.release()
}
}
}
}

// calculate gravity turn altitudes
const atm_depth = vessel.main_body.atmosphere_depth
con.log(format("Target Apoapsis {0:N0}km, heading {1:N0} deg.", (target_apoapsis/1000.0, heading)))
con.h3 = format("Gravity turn will start at {0:N1} m/s", turn_start)

let spd = 0.0
let alt = vessel.altitude_terrain
let t_apo = 0.0
// Phase one: burn until turn start speed in m/s
while(vessel.surface_velocity.magnitude < turn_start-5) {
spd = vessel.vertical_surface_speed
con.h4 = format(" speed: {0,6:N1}m/s, alt: {0,6:N1}", (spd, vessel.altitude_terrain))
con.update()
t_apo = vessel.orbit.next_apoapsis_time().value - current_time()
sleep(0.1)
}
vessel.override_input_roll(0)

//
// start pitching maneuver
//
con.h3 = format("Pitching over to {0:N1}°...",(turn_pitch))
con.update()
ksp::game::warp::cancel()
vessel.autopilot.mode = AutopilotMode.Autopilot

// pitch over
const target_pitch = turn_pitch
for (pitch in 0..(target_pitch*5).to_int-1) {
sleep(0.15)
con.update()
vessel.autopilot.target_orientation = vessel.heading_direction(heading, 90.0-pitch/5, 0).vector
}
sleep(2)
vessel.autopilot.target_orientation = vessel.heading_direction(heading, 90-target_pitch, 0).vector
sleep(4)

spd = vessel.surface_velocity.magnitude
t_apo = vessel.orbit.next_apoapsis_time().value - current_time()

//
// start gravity turn
//
con.h3 = "Gravity turn..."
ksp::game::warp::cancel()
vessel.autopilot.mode = AutopilotMode.Prograde

let th = 100.0
let targetApTime = 50.0
let accApt = 0.0
let lastApt = t_apo
let hasSsb = has_ssb(mission)

// Phase two: burn until trajectory reaches apoapsis
while(vessel.orbit.apoapsis.value < target_apoapsis) {
spd = vessel.surface_velocity.magnitude
t_apo = vessel.orbit.next_apoapsis_time().value - current_time()

accApt = t_apo - lastApt
lastApt = t_apo

// TODO: should probably use a PID controller for this
let apTimeDiff = t_apo - targetApTime
let throttleDiff = abs(apTimeDiff/accApt)/1.7
if (abs(accApt) > 0.05)
{
if (throttleDiff > 5)
throttleDiff = 5.0
}
else
{
if (throttleDiff > 1)
throttleDiff = 1.0
}

// make sure we don't over- or undershoot!
if (t_apo >= targetApTime && accApt > 0)
th -= throttleDiff
if (t_apo <= targetApTime && accApt < 0)
th += throttleDiff

if (th >= 100)
th = 100
else
con.h3 = "Throttling down to keep apoapsis..."

if (th <= 30)
{
th = 30
if (hasSsb)
{
targetApTime = lastApt
}
}

let dv = vessel.delta_v.stage(vessel.staging.current)
let burnTime = dv.value.burn_time

// cancel warp before staging
if (burnTime < 5.0 && !has_ssb(mission) && ksp::game::warp::current_rate() > 1)
{
ksp::game::warp::cancel()
}
// increase throttle before staging
if (burnTime < 3.0 && !has_ssb(mission))
{
th *= 2
}

// set throttle according to calculations
vessel.set_throttle(th/100.0)

// Check if we need to stage.
// Sometimes stage command is not executed,
// also check if there is any ignited engine and repeat if not
if( mission.flameout_any() || !mission.ignited_any()) {

if (mission.flameout()) // all flamed out?
th = 50

vessel.set_throttle(.05)
con.log("Flameout, advancing to new stage.")
mission.stage()
vessel.set_throttle(th/100.0)
}

con.h4 = format(" apoapsis in: {0,6:N1}s", (t_apo))
con.h5 = format(" stage burn remaining:{0,6:N1}s", (burnTime))

con.update_slow()
sleep(0.1/ksp::game::warp::current_rate())
}

ksp::game::warp::cancel()
vessel.set_throttle(0)
vessel.autopilot.mode = AutopilotMode.Prograde
sleep(0.1)

// If a solid state booster is still burning, wait for it to finish and stage
check_and_finish_ssb(mission)

t_apo = vessel.orbit.next_apoapsis_time().value
con.h5 = format("Apoapsis {0:N0}km in {1:N0} seconds.", (vessel.orbit.apoapsis.value/1000, t_apo))

//
// start gravity turn
//
con.h3 = "Coasting..."
con.update()
sleep(1)
// use time warp out of atmosphere during coasting
warp_to(t_apo)

while(vessel.altitude_sealevel < vessel.main_body.atmosphere_depth) {
// boost if needed
if (vessel.orbit.apoapsis.value < target_apoapsis)
{
for(i in 1..7 )
{
vessel.set_throttle(i/100.0)
sleep(0.04)
}
for(i in 5..1 )
{
vessel.set_throttle(i/100.0)
sleep(0.04)
}
vessel.set_throttle(0)
}
con.update()
sleep(0.5)
}
sleep(0.5)
ksp::game::warp::cancel()
sleep(0.5)

// Circularize at apoapsis
let n1 = circularize_at_ap(vessel)
mission.addManeuver(n1)
// align with maneuver
vessel.autopilot.mode = AutopilotMode.Maneuver
sleep(1) // give it some time

// execute
mission.burnNextNode()

const endDV = mission.getTotalDV()
con.log("circularization burn complete.")
con.log(format("Gravity turn launch complete. DV used {0:N0}m/s", (startDV - endDV)))
}

// Launch the vessel from a planet with an atmosphere into orbit
// - It doesn't trust maneuver nodes until it is in space
// - Less likely to overshoot apoapsis
Expand Down
4 changes: 2 additions & 2 deletions glib/maneuver_vacuum.to2
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl Node {

// Plan: We need to step through every remaining stage, and calculate burn time for the stage
let stage = vessel.staging.count
let dv_remaining = self.delta_v.magnitude
let dv_remaining = 0.0 //mission.getTotalDV()
self.burn_time = 0.0
self.burn_stages = 0
self.burn_info = "stages: "
Expand Down Expand Up @@ -137,7 +137,7 @@ pub sync fn ErrorNode() -> Node = {
pub sync fn t_hohmann(orbit : Orbit, alt1: float, alt2: float) -> float = {
let body_radius = 1.0 // XXX
let body_mu = orbit.reference_body.grav_parameter
return PI * sqrt( (alt1+alt2+2*body_radius)^3/(8*body_mu) )
return PI * sqrt( (alt1+alt2+2*body_radius)**3/(8*body_mu) )
}

// Velocity at a specific altitude for current orbit.
Expand Down
35 changes: 34 additions & 1 deletion glib/mission.to2
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ impl Mission {
return true
}

// Check if any engines have flamed out.
sync fn flameout_any(self) -> bool = {
for(engine in self.vessel.engines)
if(engine.has_ignited && engine.is_flameout) return true
return false
}

// Check if any engines are ignited.
sync fn ignited_any(self) -> bool = {
for(engine in self.vessel.engines)
if(engine.has_ignited) return true
return false
}

// Check if we have landed or splashed down
sync fn landed(self) -> bool = {
if (self.vessel.situation != VesselSituation.Landed && self.vessel.situation != VesselSituation.Splashed) return false
Expand Down Expand Up @@ -97,9 +111,11 @@ impl Mission {
} else {
eng_name = ""
}
if (deltav <= 0.1)
continue

let s = format(" {0} dv: {1,5:N0} m/s mass: {2,4:N1} t T/W: {3,4:N1} {4}",
(stage, deltav, mass, thrust/mass, eng_name))
(vessel.staging.count-stage, deltav, mass, thrust/mass/10.0, eng_name))
con.log(s)
}

Expand Down Expand Up @@ -275,5 +291,22 @@ impl Mission {
sleep(self.stage_delay_post)
self.new_stage(true)
}

fn getTotalDV(self) -> float = {
let dv = 0.0

for(stage in 1..self.vessel.staging.current)
{
const maybe_stage_info = self.vessel.delta_v.stage(stage)
if(!maybe_stage_info.defined) continue

const stage_info = maybe_stage_info.value
const dv_stage = stage_info.get_deltav(DeltaVSituation.Vaccum)

dv += dv_stage
}

return dv
}
}

4 changes: 2 additions & 2 deletions glib/transfer.to2
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ impl Transfer {

// Calculate correct phase angle for a Hohmann Transfer
sync fn hohmannPhaseAngle(self, alt_start: float, alt_end: float, o: Orbit) -> float = {
return 180*(1-(1/(2*sqrt(2))*sqrt((1+(alt_start+o.reference_body.radius)/(alt_end+o.reference_body.radius))^3)))
return 180*(1-(1/(2*sqrt(2))*sqrt((1+(alt_start+o.reference_body.radius)/(alt_end+o.reference_body.radius))**3)))
}

// Time needed for Hohmann transfer from alt1->alt2

sync fn hohmannTime(self, orbit: Orbit, new_ap: float, time: float) -> float = {
return PI*( (orbit.radius(time)+new_ap+2*orbit.reference_body.radius)^3/(8*orbit.reference_body.grav_parameter) )^0.5
return PI*( (orbit.radius(time)+new_ap+2*orbit.reference_body.radius)**3/(8*orbit.reference_body.grav_parameter) )**0.5
}

// Current True Anomaly in deg. Will work even with circular/eccentric orbits
Expand Down
15 changes: 14 additions & 1 deletion glib/utility.to2
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,17 @@ pub fn check_and_finish_ssb(mission: Mission) -> Unit = {
mission.stage()
return
}
}
}

pub fn has_ssb(mission: Mission) -> bool = {

const vessel = mission.vessel

for(engine in vessel.engines)
if(engine.has_ignited && engine.current_engine_mode.engine_type == ksp::vessel::EngineType.SolidBooster)
{
return true
}

return false
}
Loading