From fd84a0f386c72b36dbe90bf266e34f0237881e57 Mon Sep 17 00:00:00 2001 From: Guido Appenzeller Date: Sun, 4 Feb 2024 17:47:53 -0800 Subject: [PATCH] Major update with a number of changes: - For transfers, we now do a two stage burn. Initial and correction. - Correction burn uses the new Maneuver Node functionality to optimize the encounter - There is now a mission to fly to minmus (go_minmus) and it seems to work - Updated debugging information - More reliable warp code --- README.md | 28 +++- glib/display.to2 | 20 ++- glib/maneuver_vacuum.to2 | 4 +- glib/mission.to2 | 44 +++++- glib/transfer.to2 | 126 +++++++++++++++--- glib/utility.to2 | 56 +++++++- mission/go-minmus.to2 | 112 ++++++++++++++++ mission/go-mun.to2 | 36 ++--- test/{classes.to2 => test_classes.to2} | 20 ++- ...orbit.to2 => test_global_position_bug.to2} | 14 +- test/test_patched_conics.to2 | 32 +++++ 11 files changed, 431 insertions(+), 61 deletions(-) create mode 100644 mission/go-minmus.to2 rename test/{classes.to2 => test_classes.to2} (75%) rename test/{check_orbit.to2 => test_global_position_bug.to2} (54%) create mode 100644 test/test_patched_conics.to2 diff --git a/README.md b/README.md index 38a3329..178eede 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,32 @@ Right now there are the following missions: - launch_to_orbit: launches a vessel into low Kerbin orbit. - land_athmosphere: lands a vessel from orbit around Kerbin. Assumes you have parachutes. Tries to land near the KSC. - go_mun : Launches a vessel to the Mun and lands in a flat area near the Mun's equator. Needs enough delta_v to get there. -- return_to_kerbin: returns to kerbin from a moon in the Kerbin system. Tested with the Mun. Assumes you have parachutes. +- go_minmus : Launches a vessel to Minmus and lands it... somewhere. +- return_to_kerbin: returns to kerbin from a moon in the Kerbin system. Tested with the Mun. Assumes you have parachutes The scripts will stage as needed. Debug and status output is shown in the console, opening it is highly recommended. + +## Phases of a Mission + +Initially the vessel is launched, gravity turn is performed and we climb to close to the target apoapsis. Usually an additional burn is performed to fune tune once we have exited the atmosphere. After circularization, we check if the inclination needs to be change. + +Then the transfer is planned. For anything but Mun, the transfer from Kerbin will happen in two phases: +1. An initial burn to get on an escape trajectory from Kerbin +2. A second burn executed above 100k km altitude to fine tune the trajectory and get a good encounter + +After the correction burn, we wait until we are in the spehere of influence (SOI) of the target body. Once that is the case, we circularize, lower periapsis and lower apopasis to the target orbit height. + +Last we pick a spot to land, slow down close to that spot and the auto lander plans and executed a (hopefully) soft landing. Right now landing spots are hardcoded, we do no check the terrain for suitability. + +Return starts with an initial burn to an apoapsis that is circularized. We then claculate ejection angles (at least in the Kerbin system) and perform an ejection burn. This should put us into an elliptical orbit around Kerbin. We then wait for the Apoapsis, lower periapsis and attempt aero capture. This may happen over a number of passes to reduce heat. + +Once Periapsis is below sea level, the landing code takes over, waits for safe opening of parachutes and lands. + +Most scripts are written that they can be aborted and re-started at most time. Quicksave is your friend. + +## Where to get help with the scripts + +Best place to get help are the [KSP forums](https://forum.kerbalspaceprogram.com/topic/214543-release-kontrolsystem2-042/). + +If you encounter a bug or have a suggestions, please file it here on GitHub. + diff --git a/glib/display.to2 b/glib/display.to2 index fc859e9..e3d0380 100644 --- a/glib/display.to2 +++ b/glib/display.to2 @@ -27,23 +27,33 @@ pub sync fn warning( s : string) -> Unit = { } pub sync fn debug( s : string) -> Unit = { - //CONSOLE.print_line(" Debug: "+s) + CONSOLE.print_line(" > "+s) } // Pretty distance print -pub sync fn pretty_distance(d : float) -> string = { +pub sync fn pretty_distance(dd : float) -> string = { + let sign = "" + let d = dd + if (dd < 0) { + sign = "-" + d = -dd + } if (d>1000000) - return(format("{0:N0} km", d/1000 ) ) + return(format("{1}{0:N0} km", (d/1000, sign) ) ) if (d>1000) - return(format("{0:N2} km", d/1000 ) ) - return(format("{0:N1} m", d ) ) + return(format("{1}{0:N2} km", (d/1000, sign) ) ) + return(format("{1}{0:N1} m", (d, sign) ) ) } // Pretty print time pub sync fn pretty_time(time : float) -> string = { let s = time.to_int + if (s>21600) + return(format("{0:N0}d {1:N0}:{2,2:00}:{3,2:00}", (s/21600, (s/3600)%6, (s/60)%60, s%60) )) + if (s>3600) + return(format("{0:N0}:{1,2:00}:{2,2:00}", (s/3600, (s/60)%60, s%60) )) if (s>200) return(format("{0:N0}:{1,2:00}", (s/60, s%60) ) ) return time.to_int.to_string()+" s" diff --git a/glib/maneuver_vacuum.to2 b/glib/maneuver_vacuum.to2 index a67d08d..7674204 100644 --- a/glib/maneuver_vacuum.to2 +++ b/glib/maneuver_vacuum.to2 @@ -125,7 +125,7 @@ impl Node { // Returns an invalid node pub sync fn ErrorNode() -> Node = { - return Node(-1, vec3(1,2,3)) + return Node(-1, vec3(0,0,0)) } // @@ -176,7 +176,7 @@ pub sync fn change_incl(vessel: Vessel, target_orbit: Orbit) -> Node = { const v_vessel = vessel.orbit.global_velocity(t_node).to_local(frame) const dv_mag = -target_orbit.orbit_normal.normalized.dot(v_vessel) const dv = dv_mag*vessel.orbit.orbit_normal - //warning(format("{0:N3} {1:N3} delta: {2:N3} mean_v: {3:N4} t: {4}", (ma_v, dn_ta, (dn_ta-ma_v), vessel.orbit.mean_motion, pretty_dt(t_node) ))) + warning(format("{0:N3} {1:N3} delta: {2:N3} mean_v: {3:N4} t: {4}", (ma_v, dn_ta, (dn_ta-ma_v), vessel.orbit.mean_motion, pretty_dt(t_node) ))) return Node(t_node,dv) } diff --git a/glib/mission.to2 b/glib/mission.to2 index 7462759..5ec3716 100644 --- a/glib/mission.to2 +++ b/glib/mission.to2 @@ -55,6 +55,13 @@ impl Mission { self.name } + // Helper function to return Apoapsis of the vessel in a safe way + // Returns -1 if undefined. + sync fn apoapsis(self) -> float = { + if (self.vessel.orbit.apoapsis.defined) return self.vessel.orbit.apoapsis.value + return 0 + } + // Check if all engines have flamed out. sync fn flameout(self) -> bool = { for(engine in self.vessel.engines) @@ -172,6 +179,37 @@ impl Mission { self.burnNextNode() } + // Warp to a specific time. Don't return until we are there. + fn warpToTime(self, t: float) -> Unit = { + const con = self.console.value + warp_to(t) + while(current_time() Unit = { + const orbit = self.vessel.orbit + const con = self.console.value + + con.log(format("Warping to altitude {0}", pretty_distance(alt_target))) + const now = current_time() + const eta = orbit.next_time_of_radius(now, alt_target+orbit.reference_body.radius) + if (eta.defined) { + self.warpToTime(eta.value) + con.log(format(" arrived at altitude {0} m", pretty_distance(self.vessel.altitude_sealevel))) + } else { + panic("Orbit won't reach altitude.") + } + } + // Warp to the SOI change. This is surprisingly hard. fn warpToSOI(self, dst: string) -> Unit = { const vessel = self.vessel @@ -183,9 +221,9 @@ impl Mission { let t = find_encounter(vessel, body_dst, body_dst.SOI_radius*0.9) let d = find_distance_t(vessel, body_dst, t) - con.log(format(" encounter in {0} distance {1}", (pretty_distance(d),pretty_dt(t)))) + con.log(format(" encounter in {1} distance {0}", (pretty_distance(d),pretty_dt(t)))) - warp_to(t) + self.warpToTime(t) con.h5 = "" while(current_time() Unit = { - let con = mission.console.value - let vessel = mission.vessel + fn getTransferNode(self)-> Node = { + let con = self.mission.console.value + let vessel = self.mission.vessel let orbit = vessel.orbit self.src = orbit.reference_body.name - self.dst = dst const t = self.table - if ( !find_body(dst).success ) { + if ( !find_body(self.dst).success ) { panic(format("Transfer destination {1} not found", (self.dst))) - return + return ErrorNode() } const body_dst = find_body(self.dst).value - con.log(format("Planning Transfer: {0} -> {1}",(self.src, target_alt))) // Check if we transfer planet->moon or star->planet (vs. planet to planet) @@ -146,7 +146,7 @@ impl Transfer { // Estimate altitude we need to burn to. // Basic formula is: radiums of orbit of target - radius of current body - radius of target body - target altitude // TODO: Need to adjust this for non-circular orbits by estimating rendevous time better. For now we just take 1/4 rotation. - self.alt_new = body_dst.orbit.radius(self.t_eject+body_dst.orbit.period/4)-orbit.reference_body.radius-body_dst.radius-target_alt + self.alt_new = body_dst.orbit.radius(self.t_eject+body_dst.orbit.period/4)-orbit.reference_body.radius-body_dst.radius-self.target_alt // Calculate dV needed to the new altitude self.v_eject = self.transferDeltaV(orbit, self.alt_new, self.t_eject) @@ -160,7 +160,7 @@ impl Transfer { //self.v_eject = t[i].v_eject } panic("Planet to Planet not implemented yet.") - return + return ErrorNode() } panic(format("No data found for transfer {0} -> {1}", (self.src, self.dst))) } @@ -170,14 +170,106 @@ impl Transfer { ( self.alt_new, pretty_dt(self.t_eject)) )) con.log(format(" eject at phase angle {0:N1} ejection angle {1:N1} dV: {2:N} m/s", (self.a_phase, self.a_eject, self.v_eject.magnitude))) + + return Node(self.t_eject, self.v_eject) } - fn getTransferNode(self)-> Node = { - if (self.t_eject == 0) { - panic("Can't add transfer node as none is planned.") + // Plan a Maneuver to fine-tune a transfer to a different body. + // + // The basic idea is that the initial transfer Node will get us close to a rendezvous, but + // an additional correction burn is required to get us in a good orbit. + // - This should be called after ejection burn has been executed + // - Main parameter is the target Periapsis at the target body + + fn getCorrectionNode(self)-> Node = { + const con = self.mission.console.value + const vessel = self.mission.vessel + const dst_body = find_body(self.dst).value + const target_dist = self.target_alt+dst_body.radius + + con.log(format("Planning correction burn to {0} alt {1}",(self.dst, self.target_alt))) + + // Basic idea, do a gradient descent on a pro/retrograde burn to get the desired periapsis + // We use the game's maneuver nodes to do the calculation for us. + + // Add a maneuver node to 2 minutes in the future + const maneuver_time = current_time()+120 + vessel.maneuver.remove_all() + vessel.maneuver.add_burn_vector(maneuver_time, vec3(0,0,0)) + yield() + + // Get orbit after the maneuver. Run checks to make sure it worked. + if (vessel.maneuver.trajectory.length == 0) { + panic("Creating maneuver node failed.") return ErrorNode() } - return Node(self.t_eject, self.v_eject) + + const new_orbit = vessel.maneuver.trajectory[0] + const node = vessel.maneuver.nodes[0] + sleep(0.5) + + // Set up gradient descent + const speed = 0.05 + let i = 0 + let d_dv = 0.02 + let d_best = 0.0 + let t = 0.0 + + con.h3 = format("Planning correction burn for {0}",dst_body.name) + con.h5 = "No encounter yet." + + // Estimate current distance + const t0 = find_encounter_o(new_orbit, dst_body.orbit) + let d_now = find_distance_o(new_orbit, dst_body.orbit, t0) + con.log(format(" pre-maneuver closest approach : {0} trying dV: {1} prograde",(pretty_distance(d_now), d_dv))) + + // Optimize until we either have reached a minimum, or we are below the target altitude + while( (d_best > d_now && d_best > target_dist) || i < 10) { + i += 1 + // Adjust burn and verify effect. We need to wait briefly after adjusting to let KSP catch up. + node.prograde = node.prograde+d_dv + sleep(0.1) + + // Check if we are getting into the SOI of another body + if (vessel.maneuver.trajectory.length > 1) { + con.h5 = format(" Encounter with {0} detected.",(vessel.maneuver.trajectory[1].reference_body.name)) + // This needs code to deal with this situation. For now, we just ignore. + } + + // Calculate time and distance of closest approach + t = find_encounter_o(new_orbit, dst_body.orbit) + const d_next = find_distance_o(new_orbit, dst_body.orbit, t) + + // Gradient descent + const d_remaining = d_next-dst_body.radius // Remaining distance to target altitude + const d_delta = (d_next-d_now)/d_dv // Change in distance from 1m/s prograde + const dv_est = -d_remaining/d_delta // Linear estimation of dV to reach target altitude + d_dv = clamp(dv_est*speed,-0.1,0.1) // Actual change per iteration is adjusted by speed + con.h4 = format("# {0} dV:{1:N3} distance old/new {2}/{3} (delta: {4})", + (i, node.prograde, pretty_distance(d_now), pretty_distance(d_next), pretty_distance(d_delta)) ) + con.update_slow() + + //con.log(format(" est. change for 1m/s dv: {0:N1} required/current dV: {1:N3}/{2:N3} (delta: {3:N3}) adjust {4:n3}", + // (pretty_distance(d_delta), dv_est+node.prograde, node.prograde, dv_est, d_dv))) + //con.log(format(" d_delta: {0:N3} = (d_next: {1:N3} - d_now: {2:N3})/d_dv: {3:N3}", + // (pretty_distance(d_delta), pretty_distance(d_next), pretty_distance(d_now), d_dv))) + //con.log(format(" dv_est: {0:N3} = d_remaining: {1:N3} / d_delta: {2:N3}", + // (dv_est, d_remaining, d_delta))) + + d_best = d_now + d_now = d_next + } + + con.log(format(" post-maneuver closest approach: {0} in {1} ({1} iterations)",(pretty_distance(d_now), pretty_dt(t), i))) + + // Check how well we have done + if(vessel.maneuver.trajectory.length > 1) { + con.log(format(" encounter with {0}",(vessel.maneuver.trajectory[1].reference_body.name))) + } + + const n_final = Node(maneuver_time, node.burn_vector) + vessel.maneuver.remove_all() + return n_final } } diff --git a/glib/utility.to2 b/glib/utility.to2 index b1da9d5..b714aaa 100644 --- a/glib/utility.to2 +++ b/glib/utility.to2 @@ -14,7 +14,7 @@ pub struct Universe() { KERBOL : Body = find_body("Kerbol").value KERBIN : Body = find_body("Kerbin").value MUN : Body = find_body("Mun").value - MINIMUS : Body = find_body("Minimus").value + MINMUS : Body = find_body("Minmus").value } pub struct Location(body_name : string, latitude : float, longitude : float ) { @@ -66,15 +66,60 @@ sync pub fn find_distance_t(t1: Targetable, t2: Targetable, t: float) -> float = return p1.distance(p2) } +// Same as above, but with orbits +sync pub fn find_distance_o(o1: Orbit, o2: Orbit, t: float) -> float = { + const p1 = o1.global_position(t) + const p2 = o2.global_position(t) + return p1.distance(p2) +} + sync pub fn find_distance(t1: Targetable, t2: Targetable) -> float = { return find_distance_t(t1,t2,current_time()) } +// Find the earliest time when two orbits come closest to each other via iteration +pub fn find_encounter_o(o1: Orbit, o2: Orbit) -> float = { + + // Initial time step is 1% of the smaller of the two periods, minimum is 1s + let time_step = 0.05*min(o1.period, o2.period) + const min_time_step = 1.0 + + let i = 0 + let t = current_time()+time_step + let dx = 1.0 + let d_now = 0.0 + + while( time_step > min_time_step) { + i += 1 + d_now = find_distance_o(o1,o2,t) + const d_forw = find_distance_o(o1,o2,t+time_step) + const d_back = find_distance_o(o1,o2,t-time_step) + + //const s = format("{0,4:N0} {1} in {2} (d_now: {3} d_next: {4} speed: {5})",(i,pretty_distance(d_now), + // pretty_dt(t), pretty_distance(d_forw), pretty_distance(d_back), pretty_time(time_step)) ) + //debug(s) + + // Check if we are getting closer + if(d_back > d_now && d_now > d_forw) { + t = t+time_step + } else if (d_forw > d_now && d_now > d_back) { + t = t-time_step + } else { + time_step = time_step/2 + } + } + + //let s = format("Encounter at {0} in {1} ({2} iternations)",(pretty_distance(d_now), pretty_dt(t), i)) + //debug(s) + + return t +} + // Find the earliest time when two targtables come within a certain distance // - Uses gradient descent // - Returns time to closest encounter if it can't go below distance pub fn find_encounter(t1: Targetable, t2: Targetable, distance: float) -> float = { - const speed = 0.01 + const speed = 0.1 let i = 0 let t = current_time() let dx = 1.0 @@ -82,20 +127,23 @@ pub fn find_encounter(t1: Targetable, t2: Targetable, distance: float) -> float while( dx > 0 && d_best > distance) { i += 1 - const d_now = find_distance_t(t1,t2,t) + const d_now = find_distance_t(t1,t2,t) const d_next = find_distance_t(t1,t2,t+1) dx = d_now-d_next if(dx > 0) { t = t+d_now/dx*speed } d_best = find_distance_t(t1,t2,t) - //const s = format("{0,4:N0} {1} in {2}",(i,pretty_distance(d_best), pretty_dt(t))) + //const s = format("{0,4:N0} {1} in {2} (dx: {3} d_now: {4} d_next: {5})",(i,pretty_distance(d_best), + // pretty_dt(t), pretty_distance(dx), pretty_distance(d_now), pretty_distance(d_next))) //warning(s) } return t } + + // Find the earliest time when a targetable is a certain distance from its body // - Uses gradient descent // - Returns time to furthest encounter if it can't reach distance diff --git a/mission/go-minmus.to2 b/mission/go-minmus.to2 new file mode 100644 index 0000000..ae3009a --- /dev/null +++ b/mission/go-minmus.to2 @@ -0,0 +1,112 @@ +use { Vessel } from ksp::vessel +use { Mission } from glib::mission +use { launch_atmosphere } from glib::maneuver_atmosphere +use { Transfer } from glib::transfer +use { circularize_at_pe, change_pe_at_ap, change_ap_at_pe, change_incl } from glib::maneuver_vacuum +use { Universe, lon_sunny_side } from glib::utility +use { land_vessel } from glib::landing +use { sleep, current_time } from ksp::game +use { format } from core::str + +pub fn main_flight(vessel: Vessel, target_apoapsis: int = 80000) -> Unit = { + + const orbit_alt = 20000.0 + const deorbit_angle = 5.0 + + // Standard Setup + let mission = Mission("To minmus!",vessel) + let con = mission.start() + let MINMUS = Universe().MINMUS + + // Check if we are still in the process of getting to Orbit + if (vessel.orbit.reference_body.name == "Kerbin" && vessel.orbit.periapsis < 75000) { + launch_atmosphere(mission, target_apoapsis, 0.01, 0.7, 90) + } + + // We made it to Orbit, if we are we still around Kerbin plan transfer and correction + if (vessel.orbit.reference_body.name == "Kerbin") { + + // Are we still low? If yes, check if we need to change inclination + if (mission.apoapsis() < 100000) { + let ni = change_incl(vessel, MINMUS.orbit) + if (mission.apoapsis() < 100000 && ni.delta_v.magnitude > 10) { + con.log(format("Inclination change needed, dV={0:N1} m/s",(ni.delta_v.magnitude))) + mission.burnManeuver(ni) + } + } + + // Create the transfer plan to Minmus + con.log("Starting transfer to Minmus") + const t = Transfer(mission, "Minmus", MINMUS.SOI_radius*0.2) + + // If we are still low do the initial ejection burn from Kerbin orbit + if (mission.apoapsis() < 100000) { + const n = t.getTransferNode() + mission.burnManeuver(n) + con.log(" ejection burn complete.") + } + + // Second burn to correct trajectory an altitude of 1000 km + if (vessel.altitude_sealevel < 1000000) { + mission.warpToAltitude(1000000) + } + const n = t.getCorrectionNode() + mission.addManeuver(n) + mission.burnManeuver(n) + con.log(" correction burn complete.") + } + + // Wait for SOI of the Minmus + if (vessel.orbit.reference_body.name == "Kerbin") { + mission.warpToSOI("Minmus") + } + + // We are in the SOI of Minmus. First, check if we have to circularize + if (!vessel.orbit.apoapsis.defined) { + con.log("Lowering Apoapsis.") + const n2 = circularize_at_pe(vessel) + mission.burnManeuver(n2) + } + + // Lower orbit + if ( vessel.orbit.apoapsis.value > orbit_alt*1.1) { + con.log("Lowering Orbit to 10k.") + let n3 = change_pe_at_ap(vessel,orbit_alt) + mission.burnManeuver(n3) + + let n4 = change_ap_at_pe(vessel,orbit_alt) + mission.burnManeuver(n4) + } + + // Land at the default site + const lat = 0.0 + const lon = 6.0 + //const lon = lon_sunny_side(mission) + const body = vessel.orbit.reference_body + const frame = vessel.main_body.celestial_frame + const landing_site = body.geo_coordinates ( lat,lon ) + con.log(format(" landing at site: {0:N1}/{1:N1} (ship long: {2:N1})", + (lat, lon, vessel.geo_coordinates.longitude)) ) + let ground = ksp::debug::DEBUG.add_ground_marker(landing_site, ksp::console::RED, 0) + + if (vessel.orbit.periapsis > 1000) { + // Location picked, let's land there. + const rel_angle = (landing_site.longitude - vessel.geo_coordinates.longitude - deorbit_angle + 720) % 360 + + // Correct for rotation + let rot_corr = 1+vessel.orbit.period/body.rotation_period + const dt = rel_angle/360*vessel.orbit.period*rot_corr + con.log(format(" rel angle to deorbit: {0:N} eta {1}",(rel_angle,dt))) + + let n = change_pe_at_ap(vessel, 0) + + // Burn this amount of dV, but right time and direction + n.time = current_time()+dt + n.delta_v = vessel.orbit.global_velocity(n.time).to_local(frame).normalized*n.delta_v.magnitude + mission.addManeuver(n) + mission.burnNextNode() + } + land_vessel(mission, landing_site) + + mission.end() +} \ No newline at end of file diff --git a/mission/go-mun.to2 b/mission/go-mun.to2 index 01b0880..f73fd5e 100644 --- a/mission/go-mun.to2 +++ b/mission/go-mun.to2 @@ -24,18 +24,25 @@ pub fn main_flight(vessel: Vessel, target_apoapsis: int = 80000) -> Unit = { // Adjust inclination let ni = change_incl(vessel, MUN.orbit) - mission.addManeuver(ni) - mission.burnNextNode() + mission.burnManeuver(ni) } - // We made it to Orbit, are we still around Kerbin - if (vessel.orbit.reference_body.name == "Kerbin" && vessel.orbit.apoapsis.defined && vessel.orbit.apoapsis.value < 5000000) { - const t = Transfer() - t.planTransfer(mission,"Mun", MUN.SOI_radius*0.1) - const n = t.getTransferNode() - mission.addManeuver(n) - mission.burnNextNode() - con.log(" ejection burn complete.") + // We made it to Orbit, if we are we still around Kerbin plan transfer and correction + if (vessel.orbit.reference_body.name == "Kerbin") { + + const t = Transfer(mission, "Mun", MUN.SOI_radius*0.1) + + if (vessel.orbit.apoapsis.defined && vessel.orbit.apoapsis.value < 5000000) { + const n = t.getTransferNode() + mission.burnManeuver(n) + con.log(" ejection burn complete.") + } + + // Done wiht ejection burn, do a correction burn to fine-tune our trajectory + if (vessel.orbit.apoapsis.defined && vessel.orbit.apoapsis.value > 5000000) { + //const n = t.getCorrectionNode() + //mission.burnManeuver(n) + } } // Wait for SOI of the Mun @@ -47,20 +54,17 @@ pub fn main_flight(vessel: Vessel, target_apoapsis: int = 80000) -> Unit = { if (!vessel.orbit.apoapsis.defined) { con.log("Lowering Apoapsis.") const n2 = circularize_at_pe(vessel) - mission.addManeuver(n2) - mission.burnNextNode() + mission.burnManeuver(n2) } // Lower orbit to 50k meters if ( vessel.orbit.apoapsis.value > orbit_alt*1.1) { con.log("Lowering Orbit to 10k.") let n3 = change_pe_at_ap(vessel,orbit_alt) - mission.addManeuver(n3) - mission.burnNextNode() + mission.burnManeuver(n3) let n4 = change_ap_at_pe(vessel,orbit_alt) - mission.addManeuver(n4) - mission.burnNextNode() + mission.burnManeuver(n4) } // Land at the default site diff --git a/test/classes.to2 b/test/test_classes.to2 similarity index 75% rename from test/classes.to2 rename to test/test_classes.to2 index eed4421..f1afaed 100644 --- a/test/classes.to2 +++ b/test/test_classes.to2 @@ -14,11 +14,11 @@ pub struct foo() { } pub struct bar() { - foo1 : test::classes::foo = test::classes::foo() + foo1 : test::test_classes::foo = test::test_classes::foo() } -pub struct bar2(f : Option ) { - foo1 : Option = f +pub struct bar2(f : Option ) { + foo1 : Option = f } // So how do we create a structure with a pointer to another structure @@ -26,6 +26,13 @@ pub struct bar2(f : Option ) { // // Naively we would do this: // foo1 : Option = None() + +// Update: This may work now in 0.5.2.5? I may have to re-write some code + +pub struct bar2a() { + foo1 : Option = None() +} + // // But it fails with the following error. // ERROR: [test\classes.to2(17, 42)] InvalidType @@ -34,12 +41,12 @@ pub struct bar2(f : Option ) { // We can instantiate an empty struct inside a struct if we use a function // instead. It's a little bit hacky. -pub sync fn no_foo() -> Option = { +pub sync fn no_foo() -> Option = { return None() } pub struct bar3() { - foo1 : Option = no_foo() + foo1 : Option = no_foo() } pub struct arrayTest() { @@ -61,8 +68,9 @@ fn main_flight(vessel: Vessel) -> Unit = { let b = bar() // We can do lazy initialization - let f : Option = None() + let f : Option = None() let b2 = bar2(f) + let b2a = bar2a() let f2 = b2.foo1 // Function works too diff --git a/test/check_orbit.to2 b/test/test_global_position_bug.to2 similarity index 54% rename from test/check_orbit.to2 rename to test/test_global_position_bug.to2 index a1c7f38..fc46958 100644 --- a/test/check_orbit.to2 +++ b/test/test_global_position_bug.to2 @@ -4,21 +4,21 @@ use { sleep, current_time } from ksp::game use { format } from core::str use { find_body } from ksp::orbit -// Print is much useful info about our current state as possible +// Script to verify a bug in the calculation of global positions in KS2 -pub fn main_flight(vessel: Vessel) -> Unit = { +fn main_flight(vessel: Vessel) -> Unit = { - const t1 = find_body("Kerbin").value - const t2 = find_body("Mun").value + const t1 = find_body("Minmus").value + const t2 = vessel + const c_time = current_time() // Calculate distance to a target for the next few days for(i in 0..20) { - const t = current_time()+i*24*3600 + const t = c_time+i*6*3600 const p1 = t1.orbit.global_position(t) const p2 = t2.orbit.global_position(t) const d = p1.distance(p2) - //const d = (p1-p2).magnitude - CONSOLE.print_line(format(" day {1}: {0:N0} km", (d/1000.0, i))) + CONSOLE.print_line(format("day {1}: {0:N0} km", (d/1000.0, i))) } } \ No newline at end of file diff --git a/test/test_patched_conics.to2 b/test/test_patched_conics.to2 new file mode 100644 index 0000000..fd44677 --- /dev/null +++ b/test/test_patched_conics.to2 @@ -0,0 +1,32 @@ +use { Vessel, AutopilotMode } from ksp::vessel +use { CONSOLE } from ksp::console +use { sleep, current_time } from ksp::game +use { format } from core::str +use { find_body } from ksp::orbit + +// Script to test new patched conic functionality + +fn main_flight(vessel: Vessel) -> Unit = { + + // Check if we have a maneuver planned + + CONSOLE.print_line(format("Maneuver Nodes: {0}", (vessel.maneuver.nodes.length))) + CONSOLE.print_line(format("Trajectories: {0}", (vessel.maneuver.trajectory.length))) + + if (vessel.maneuver.trajectory.length == 0) { + CONSOLE.print_line("No maneuver nodes found. Exiting.") + return + } + + // Get trajectory after first maneuver node + let orbit = vessel.maneuver.trajectory[0] + + // Continously print the first trajectory's apoapsis + while (true) { + //CONSOLE.print_line(format("AP static orbit: {0}", (orbit))) + CONSOLE.print_line(format("AP static orbit: {0}", (orbit.apoapsis.value))) + CONSOLE.print_line(format("AP dynamic orbit: {0}", ( vessel.maneuver.trajectory[0].apoapsis.value))) + sleep(1) + } + +} \ No newline at end of file