├── .gitignore ├── mainframe ├── .vimrc ├── .gitignore ├── Makefile ├── build.gradle ├── src │ ├── main │ │ └── java │ │ │ └── hall │ │ │ └── john │ │ │ └── ksp │ │ │ └── mainframe │ │ │ ├── Body.java │ │ │ ├── Utils.java │ │ │ ├── Square.java │ │ │ ├── Test.java │ │ │ ├── numericalmethods │ │ │ ├── NewtonsMethod.java │ │ │ └── BrentsMethod.java │ │ │ ├── orbit │ │ │ ├── Orbit.java │ │ │ ├── ManeuverNode.java │ │ │ └── OrbitAtTime.java │ │ │ ├── Main.java │ │ │ ├── LambertSolver.java │ │ │ └── LambertOptimizer.java │ └── test │ │ └── java │ │ └── hall │ │ └── john │ │ └── ksp │ │ └── mainframe │ │ ├── TestUtils.java │ │ ├── numericalmethods │ │ ├── BrentsMethodTest.java │ │ └── NewtonsMethodTest.java │ │ ├── LambertSolverTest.java │ │ ├── orbit │ │ ├── ManeuverNodeTest.java │ │ └── OrbitTest.java │ │ └── LambertOptimizerTest.java ├── .settings │ ├── org.eclipse.buildship.core.prefs │ └── org.eclipse.jdt.core.prefs ├── .project └── .classpath ├── tacls.ods ├── addman.ks ├── warpfor.ks ├── boot ├── lrnd_launch.ks ├── msgwait.ks └── queueloop.ks ├── deleteifexists.ks ├── waitforlan.ks ├── facediff.ks ├── runfuns.ks ├── lib ├── libcosinerule.ks ├── libmanat.ks ├── libvecdraw.ks ├── libsign.ks ├── libtruealt.ks ├── libabsdiffmod.ks ├── libburntime.ks ├── liball.ks ├── libisp.ks ├── libwaitforlan.ks ├── libantenna.ks ├── libmovavg.ks ├── libfacediff.ks ├── libcircincatapo.ks ├── libbisectionmethod.ks ├── libhohmann.ks ├── libtimetotrueanom.ks ├── libclosestapproach.ks ├── libprintfield.ks ├── libtimeto.ks ├── libsmoothturn.ks ├── libsecantmethod.ks ├── libwarpto.ks ├── libradtrig.ks ├── libfuelflow.ks ├── libengine.ks ├── libtimetoburnout.ks ├── libsasrcsstack.ks ├── libexecnode.ks ├── libcloseapproach.ks ├── libwarpfor.ks ├── libwaitforfacing.ks ├── libphaseangle.ks ├── libmainframe.ks ├── libangletoprograde.ks ├── libbrentsmethod.ks ├── liblambertoptimize.ks ├── librendezvous.ks ├── libbmmethod.ks ├── liborbitalstate.ks ├── libdock.ks └── libgenericburn.ks ├── dock.ks ├── closeapproach.ks ├── execnode.ks ├── facesun.ks ├── waitforfacing.ks ├── LICENSE ├── execnodercs.ks ├── rendezvous.ks ├── rendezvousrcs.ks ├── README ├── calc.orbit.circ.ks ├── libmainframe_request - Copy.txt ├── missions ├── firstDock.ks ├── lunarimpactor_probe.ks ├── lrnd.ks ├── meosat_launch.ks ├── meosat_1.ks ├── firstDockLaunch.ks ├── meosat_constellation.txt ├── meosat_2.ks ├── meosat_3.ks ├── meosat_4.ks └── lunarimpactor.ks ├── finetuneperiod.ks ├── deorbit.ks ├── finetuneapoapsis.ks ├── finetuneperiapsis.ks ├── nodetocirc.ks ├── printengines.ks ├── circatapoapsis.ks ├── circatperiapsis.ks ├── driveto.ks ├── land.ks ├── calc.funs.ks ├── changeperiod.ks ├── circincatapo.ks ├── supplyland.ks ├── changeapoapsisrcs.ks ├── changeperiapsisrcs.ks ├── prepareforburn.ks ├── calc.satconst.ks ├── marsland.ks ├── safedeorbit.ks ├── calc.planner.transfer.ks ├── circinc.ks ├── foodland.ks ├── aerobrake.ks ├── lambert_bad_results.txt └── launch.ks /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | /log 3 | -------------------------------------------------------------------------------- /mainframe/.vimrc: -------------------------------------------------------------------------------- 1 | compiler gradle 2 | -------------------------------------------------------------------------------- /mainframe/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .gradle 3 | /bin/ 4 | -------------------------------------------------------------------------------- /mainframe/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | gradle build 3 | -------------------------------------------------------------------------------- /tacls.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwhall/kos-scripts/HEAD/tacls.ods -------------------------------------------------------------------------------- /addman.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | run once funs. 4 | 5 | parameter where. 6 | 7 | add manAt(where). 8 | -------------------------------------------------------------------------------- /warpfor.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter dt. 4 | 5 | runoncepath("lib/libwarpfor"). 6 | warpfor(dt). 7 | -------------------------------------------------------------------------------- /boot/lrnd_launch.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | wait 1. 4 | if mass > 100 { 5 | runpath("missions/lrnd_launch"). 6 | } 7 | -------------------------------------------------------------------------------- /deleteifexists.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter p_fname. 4 | 5 | log "" to p_fname. 6 | deletepath(p_fname). 7 | -------------------------------------------------------------------------------- /waitforlan.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter tgtLAN. 4 | 5 | runoncepath("lib/libwaitforlan"). 6 | 7 | waitForLan(tgtLAN). 8 | -------------------------------------------------------------------------------- /facediff.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter includeRoll. 4 | 5 | runoncepath("lib/libfacediff"). 6 | print faceDiff(includeRoll). 7 | -------------------------------------------------------------------------------- /runfuns.ks: -------------------------------------------------------------------------------- 1 | run once libtimetotrueanom. 2 | run once liblambertoptimize. 3 | 4 | print lambertOptimizeBounded(ship, moon, 8326.77, 8326.77). 5 | -------------------------------------------------------------------------------- /lib/libcosinerule.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function solveCosineRule { 4 | parameter a, b, theta. 5 | return sqrt(a*a + b*b - 2*a*b*cos(theta)). 6 | } 7 | -------------------------------------------------------------------------------- /dock.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | { 4 | parameter tgt, manDist to 25, rotation to 0. 5 | runoncepath("lib/libdock"). 6 | dock(tgt, manDist, rotation). 7 | } 8 | -------------------------------------------------------------------------------- /closeapproach.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | { 4 | parameter p_tgt, p_tgtDist. 5 | runoncepath("lib/libcloseapproach"). 6 | closeApproach(p_tgt, p_tgtDist). 7 | } 8 | -------------------------------------------------------------------------------- /execnode.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | { 4 | parameter p_turnTime, p_ullageTime. 5 | runoncepath("lib/libexecnode"). 6 | execNode(p_turnTime, p_ullageTime). 7 | } 8 | -------------------------------------------------------------------------------- /boot/msgwait.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | wait until not core:messages:empty. 4 | local msg to core:messages:pop(). 5 | runpath(msg:content). 6 | set core:bootfilename to "". 7 | -------------------------------------------------------------------------------- /lib/libmanat.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libtimeto"). 4 | 5 | function manAt { 6 | parameter where. 7 | return node(time:seconds + timeTo1(where), 0, 0, 0). 8 | } 9 | -------------------------------------------------------------------------------- /facesun.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libwaitforfacing"). 4 | 5 | lock steering to lookdirup(body("Sun"):position, ship:facing:topvector). 6 | waitForFacing(0.5, false, true). 7 | -------------------------------------------------------------------------------- /lib/libvecdraw.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function updateVecDraw { 4 | parameter vd, start, vec, show. 5 | set vd:start to start. 6 | set vd:vec to vec. 7 | set vd:show to show. 8 | } 9 | -------------------------------------------------------------------------------- /waitforfacing.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter tgtDiff, includeRoll, stabilizeAtEnd. 4 | 5 | runoncepath("lib/libwaitforfacing"). 6 | 7 | waitForFacing(tgtDiff, includeRoll, stabilizeAtEnd). 8 | -------------------------------------------------------------------------------- /boot/queueloop.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | until false { 4 | print "waiting for message". 5 | wait until not core:messages:empty. 6 | local msg to core:messages:pop(). 7 | runpath(msg:content). 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_US. 2 | -------------------------------------------------------------------------------- /execnodercs.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | { 4 | parameter p_isp, p_thrust, p_turnTime, p_turnWithRCS is false. 5 | runoncepath("lib/libexecnode"). 6 | execNodeRCS(p_isp, p_thrust, p_turnTime, p_turnWithRCS). 7 | } 8 | -------------------------------------------------------------------------------- /lib/libsign.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function sign { 4 | local parameter x. 5 | if x > 0 { 6 | return 1. 7 | } else if x < 0 { 8 | return -1. 9 | } else { 10 | return 0. 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /rendezvous.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter tgt, turnTime, ullageTime, tgtDist, turnWithRCS is false. 4 | 5 | runoncepath("lib/librendezvous"). 6 | 7 | rendezvous(tgt, turnTime, ullageTime, tgtDist, turnWithRCS). 8 | -------------------------------------------------------------------------------- /rendezvousrcs.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter tgt, turnTime, rcsIsp, rcsThrust, tgtDist, turnWithRCS is false. 4 | 5 | runoncepath("lib/librendezvous"). 6 | 7 | rendezvous(tgt, turnTime, 0, tgtDist, turnWithRCS, true, rcsIsp, rcsThrust). 8 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | My personal kOS scripts. You can use them or borrow ideas if you want (see the license file), but they are mainly here for my own convenience and to provide a reference for videos uploaded at https://www.youtube.com/channel/UCdk6vNcmqv0np2xM6N5VhSg 2 | -------------------------------------------------------------------------------- /lib/libtruealt.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function trueAlt { 4 | if abs(altitude) < 0.05 or abs(altitude - alt:radar) / altitude > 0.05 { 5 | return alt:radar. 6 | } else { 7 | return altitude - ship:geoposition:terrainheight. 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/libabsdiffmod.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function absDiffMod { 4 | parameter x1. 5 | parameter x2. 6 | parameter modulus. 7 | 8 | local diff1 to abs(x1 - x2). 9 | local diff2 to abs(abs(x1 - x2) - modulus). 10 | return min(diff1, diff2). 11 | } 12 | -------------------------------------------------------------------------------- /lib/libburntime.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libisp"). 4 | 5 | function burnTime { 6 | parameter myDV, myIsp is isp(), myMaxThrust is maxthrust, myMass is mass. 7 | local ve to myIsp * 9.80665. 8 | return (myMass * ve / myMaxThrust) * (1 - constant:e^(-myDV / ve)). 9 | } 10 | -------------------------------------------------------------------------------- /lib/liball.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | local prevPath to path(). 4 | cd("/lib"). 5 | 6 | local libs to list(). 7 | list files in libs. 8 | 9 | cd(prevPath). 10 | 11 | for lib in libs { 12 | if lib:isfile and (lib:extension = "ks" or lib:extension = "ksm") { 13 | runoncepath("/lib/" + lib). 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /mainframe/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'eclipse' 3 | apply plugin: 'application' 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | dependencies { 10 | testCompile 'junit:junit:4.12' 11 | compile 'org.apache.commons:commons-math3:3.5' 12 | } 13 | 14 | mainClassName = "hall.john.ksp.mainframe.Main" 15 | -------------------------------------------------------------------------------- /lib/libisp.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function isp { 4 | local es to list(). 5 | list engines in es. 6 | 7 | local tSum to 0. 8 | local bSum to 0. 9 | 10 | for e in es { 11 | if (e:ignition) { 12 | set tSum to tSum + e:maxthrust. 13 | set bSum to bSum + (e:maxthrust / e:visp). 14 | } 15 | } 16 | 17 | return tSum / bSum. 18 | } 19 | -------------------------------------------------------------------------------- /lib/libwaitforlan.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libwarpfor"). 4 | 5 | function waitForLAN { 6 | parameter p_tgtLan. 7 | 8 | local lan to ship:orbit:longitudeofascendingnode. 9 | local dlan to p_tgtLan - lan. 10 | if lan > p_tgtLan { 11 | set dlan to (360 - lan) + p_tgtLan. 12 | } 13 | 14 | warpFor1(body:rotationperiod * dlan / 360). 15 | } 16 | -------------------------------------------------------------------------------- /calc.orbit.circ.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter bod, alt. 4 | 5 | run once calc.funs.ks. 6 | 7 | local a to bod:radius + alt. 8 | local v to sqrt(bod:mu / a). 9 | local T to 2 * constant:pi * a / v. 10 | 11 | print "SMA: " + round(a / 1000, 3) + " km". 12 | print "Orbital Velocity: " + round(v, 2) + " m/s". 13 | print "Orbital Period: " + formatYdhms(s2ydhms(T)) + " (" + round(T, 2) + " s)". 14 | -------------------------------------------------------------------------------- /mainframe/src/main/java/hall/john/ksp/mainframe/Body.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe; 2 | 3 | public class Body { 4 | private String _name; 5 | private double _mu; 6 | 7 | public Body(String name, double mu) { 8 | _name = name; 9 | _mu = mu; 10 | } 11 | 12 | public String getName() { 13 | return _name; 14 | } 15 | 16 | public double getMu() { 17 | return _mu; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /mainframe/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | build.commands=org.eclipse.jdt.core.javabuilder 2 | connection.arguments= 3 | connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) 4 | connection.java.home=null 5 | connection.jvm.arguments= 6 | connection.project.dir= 7 | derived.resources=.gradle,build 8 | eclipse.preferences.version=1 9 | natures=org.eclipse.jdt.core.javanature 10 | project.path=\: 11 | -------------------------------------------------------------------------------- /lib/libantenna.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function extendAntennae { 4 | for pm in ship:modulesNamed("ModuleRTAntenna") { 5 | if pm:hasEvent("activate") { 6 | pm:doEvent("activate"). 7 | } 8 | } 9 | } 10 | 11 | function retractAntennae { 12 | for pm in ship:modulesNamed("ModuleRTAntenna") { 13 | if pm:hasEvent("deactivate") { 14 | pm:doEvent("deactivate"). 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /mainframe/src/main/java/hall/john/ksp/mainframe/Utils.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe; 2 | 3 | import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; 4 | 5 | public abstract class Utils { 6 | private Utils() { 7 | } 8 | 9 | public static String formatVector(Vector3D v) { 10 | return v.toString().replaceAll(",", "").replaceAll(";", ",").replaceAll("\\{", "(").replaceAll("\\}", ")"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /libmainframe_request - Copy.txt: -------------------------------------------------------------------------------- 1 | lambertoptimize 2 | 3.986004418E14 3 | 6217625.33521472 4 | -719829.378348098 5 | 1918106.38746952 6 | 1657.6952993448 7 | 7081.1412827167 8 | -2921.2092857134 9 | 5680511.51363976 10 | -2590290.72677682 11 | 2614657.68661689 12 | 3839.9493325346 13 | 6297.5479758191 14 | -2114.4648415501 15 | 190.0 16 | 149488.979965825 17 | 14.9298979965825 18 | 0.0 19 | 5539.93622202688 20 | 11.0798724440538 21 | true 22 | true -------------------------------------------------------------------------------- /missions/firstDock.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libwaitforlan"). 4 | 5 | local turnTime to 30. 6 | local ullageTime to 5. 7 | local tgt to vessel("JSS"). 8 | 9 | for mod in ship:modulesNamed("ModuleEnginesRF") { 10 | if mod:hasEvent("Activate Engine") { 11 | mod:doEvent("Activate Engine"). 12 | } 13 | } 14 | 15 | local manDist to 25. 16 | runpath("rendezvous", tgt, 30, 5, manDist). 17 | runpath("dock", tgt:dockingports[0], manDist). 18 | -------------------------------------------------------------------------------- /mainframe/src/main/java/hall/john/ksp/mainframe/Square.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.HashMap; 6 | 7 | public class Square { 8 | public static Map Square(List args) { 9 | int x = Integer.parseInt(args.get(0)); 10 | 11 | Map result = new HashMap(); 12 | result.put("xSquared", x*x); 13 | 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /finetuneperiod.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter tgtPeriod. 4 | 5 | runoncepath("lib/libwaitforfacing"). 6 | 7 | rcs off. 8 | 9 | if tgtPeriod < ship:obt:period { 10 | lock steering to retrograde. 11 | } else { 12 | lock steering to prograde. 13 | } 14 | 15 | waitForFacing(0.5, false). 16 | rcs on. 17 | set ship:control:fore to 0.06. 18 | 19 | if tgtPeriod < ship:obt:period { 20 | wait until ship:obt:period < tgtPeriod. 21 | } else { 22 | wait until ship:obt:period > tgtPeriod. 23 | } 24 | 25 | rcs off. 26 | -------------------------------------------------------------------------------- /lib/libmovavg.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function initMovAvg { 4 | parameter p_periods. 5 | return list(p_periods, 0, queue()). 6 | } 7 | 8 | function movAvg { 9 | parameter p_state. 10 | parameter p_val. 11 | 12 | local sum to p_state[1] * p_state[2]:length + p_val. 13 | p_state[2]:push(p_val). 14 | if p_state[2]:length > p_state[0] { 15 | local popVal to p_state[2]:pop(). 16 | set sum to sum - popVal. 17 | } 18 | 19 | set p_state[1] to sum / p_state[2]:length. 20 | return p_state[1]. 21 | } 22 | 23 | -------------------------------------------------------------------------------- /lib/libfacediff.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libabsdiffmod"). 4 | 5 | function faceDiff { 6 | parameter includeRoll is true. 7 | 8 | local steer to steering. 9 | if steering:typename() = "vector" { 10 | set steer to steering:direction. 11 | } 12 | 13 | local pitchDiff to absDiffMod(steer:pitch, facing:pitch, 360). 14 | local yawDiff to absDiffMod(steer:yaw, facing:yaw, 360). 15 | local rollDiff to 0. 16 | if includeRoll { 17 | set rollDiff to absDiffMod(steer:roll, facing:roll, 360). 18 | } 19 | return V(pitchDiff, yawDiff, rollDiff):mag. 20 | } 21 | -------------------------------------------------------------------------------- /deorbit.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | { 4 | parameter rcsOnly is false. 5 | 6 | runoncepath("lib/libsasrcsstack"). 7 | runoncepath("lib/libwaitforfacing"). 8 | 9 | pushSAS(false). 10 | pushRCS(true). 11 | 12 | lock steering to lookdirup(retrograde:vector, ship:facing:topvector). 13 | waitForFacing(0.5). 14 | 15 | if rcsOnly { 16 | set ship:control:fore to 1. 17 | } else { 18 | lock throttle to 1. 19 | } 20 | 21 | wait until ship:periapsis < 0. 22 | 23 | if rcsOnly { 24 | set ship:control:fore to 0. 25 | } else { 26 | unlock throttle. 27 | } 28 | 29 | popSAS(). 30 | popRCS(). 31 | } 32 | -------------------------------------------------------------------------------- /missions/lunarimpactor_probe.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | run once libmainframe. 4 | run once liblambertoptimize. 5 | 6 | local es to list(). 7 | list engines in es. 8 | es[0]:activate(). 9 | 10 | wait 5. 11 | 12 | local result to lambertOptimize(ship, moon). 13 | 14 | local t to result[0]. 15 | local pro to result[1]. 16 | local norm to result[2]. 17 | local rad to result[3]. 18 | local dt to result[4]. 19 | 20 | print "t = " + t. 21 | print "pro = " + pro. 22 | print "norm = " + norm. 23 | print "rad = " + rad. 24 | print "dt = " + dt. 25 | 26 | add node(time:seconds + t, rad, norm, pro). 27 | 28 | rcs on. 29 | run execnode(30, 0, 10). 30 | rcs off. 31 | -------------------------------------------------------------------------------- /finetuneapoapsis.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter tgtApoapsis. 4 | 5 | runoncepath("lib/libwaitforfacing"). 6 | runoncepath("lib/libwarpto"). 7 | 8 | rcs off. 9 | 10 | myWarpTo("per", 60). 11 | 12 | if tgtApoapsis < ship:obt:apoapsis { 13 | lock steering to lookdirup(retrograde:vector, ship:facing:topvector). 14 | } else { 15 | lock steering to lookdirup(prograde:vector, ship:facing:topvector). 16 | } 17 | 18 | waitForFacing(0.5, false). 19 | rcs on. 20 | set ship:control:fore to 0.5. 21 | 22 | if tgtApoapsis < ship:obt:apoapsis { 23 | wait until ship:obt:apoapsis < tgtApoapsis. 24 | } else { 25 | wait until ship:obt:apoapsis > tgtApoapsis. 26 | } 27 | 28 | rcs off. 29 | -------------------------------------------------------------------------------- /finetuneperiapsis.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter tgtPeriapsis. 4 | 5 | runoncepath("lib/libwaitforfacing"). 6 | runoncepath("lib/libwarpto"). 7 | 8 | rcs off. 9 | 10 | myWarpTo("apo", 60). 11 | 12 | if tgtPeriapsis < ship:obt:periapsis { 13 | lock steering to lookdirup(retrograde:vector, ship:facing:topvector). 14 | } else { 15 | lock steering to lookdirup(prograde:vector, ship:facing:topvector). 16 | } 17 | 18 | waitForFacing(0.5, false). 19 | rcs on. 20 | set ship:control:fore to 0.5. 21 | 22 | if tgtPeriapsis < ship:obt:periapsis { 23 | wait until ship:obt:periapsis < tgtPeriapsis. 24 | } else { 25 | wait until ship:obt:periapsis > tgtPeriapsis. 26 | } 27 | 28 | rcs off. 29 | -------------------------------------------------------------------------------- /mainframe/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | # 2 | #Tue Jan 12 17:24:22 CST 2016 3 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 4 | org.eclipse.jdt.core.compiler.compliance=1.8 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 7 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 8 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 9 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 10 | eclipse.preferences.version=1 11 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 12 | org.eclipse.jdt.core.compiler.source=1.8 13 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 14 | -------------------------------------------------------------------------------- /mainframe/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | mainframe 4 | Project mainframe created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.buildship.core.gradleprojectnature 21 | org.eclipse.jdt.core.javanature 22 | 23 | 24 | -------------------------------------------------------------------------------- /nodetocirc.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter where. 4 | 5 | if (where = "per" or where = "peri" or where = "periapsis") { 6 | set where to "per". 7 | } else { 8 | print "Unknown where: " + where. 9 | exit. 10 | } 11 | 12 | run once funs. 13 | 14 | local man to manAt(where). 15 | 16 | local vThere to velocityat(ship, time:seconds + man:eta):orbit:mag. 17 | local rThere to (positionat(ship, time:seconds + man:eta) - body:position):mag. 18 | local vCirc to sqrt(body:mu / rThere). 19 | 20 | print ship:obt:semimajoraxis. 21 | 22 | print vThere. 23 | print rThere. 24 | print vCirc. 25 | print (vCirc - vThere). 26 | 27 | set man:prograde to vCirc - vThere. 28 | print man:prograde. 29 | 30 | add man. 31 | -------------------------------------------------------------------------------- /mainframe/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /printengines.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | local es to 0. 4 | list engines in es. 5 | 6 | for e in es { 7 | print e:name. 8 | print " Title: " + e:title. 9 | print " Stage: " + e:stage. 10 | print " Mass: " + e:mass. 11 | print " Thrust Limit: " + e:thrustlimit. 12 | print " Max Thrust: " + e:maxthrust. 13 | print " Thrust: " + e:thrust. 14 | print " Fuel Flow: " + e:fuelflow. 15 | print " Isp: " + e:isp. 16 | print " Vacuum Isp: " + e:visp. 17 | print " Sea Level Isp: " + e:slisp. 18 | print " Flamed Out: " + e:flameout. 19 | print " Ignited: " + e:ignition. 20 | print " Allows Restart: " + e:allowrestart. 21 | print " Allows Shutdown: " + e:allowshutdown. 22 | print " Throttle Locked: " + e:throttlelock. 23 | } 24 | -------------------------------------------------------------------------------- /missions/lrnd.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | { 4 | runoncepath("lib/libsasrcsstack"). 5 | runoncepath("lib/libantenna"). 6 | 7 | local tgt to vessel("JSS"). 8 | local manDist to 50. 9 | //run waitforlan(tgt:obt:lan - 6). 10 | //run launch(5, 75, 75000, tgt:obt:inclination, 300000). 11 | 12 | //wait 5. 13 | 14 | //ship:partstagged("payload decoupler")[0]:getModule("ModuleDecouple"):doEvent("Decouple"). 15 | //wait 2. 16 | //set kuniverse:activevessel to ship. 17 | //wait 2. 18 | 19 | //panels on. 20 | //extendAntennae(). 21 | 22 | //pushRCS(true). 23 | //runpath("faceSun"). 24 | //popRCS(). 25 | //wait 1. 26 | 27 | //stage. 28 | //wait 5. 29 | 30 | run rendezvous(tgt, 120, 10, manDist, true). 31 | run dock(tgt). 32 | } 33 | -------------------------------------------------------------------------------- /circatapoapsis.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | run once funs. 4 | 5 | local vApo to sqrt(body:mu * (2 / (ship:obt:apoapsis + body:radius) - 1 / ship:obt:semimajoraxis)). 6 | local vCirc to sqrt(body:mu / (ship:obt:apoapsis + body:radius)). 7 | local dv to vCirc - vApo. 8 | local bt to burnTime(dv). 9 | 10 | local throt to 0. 11 | lock throttle to throt. 12 | local prevSAS to sas. 13 | sas off. 14 | 15 | warpFor1(eta:apoapsis - bt - 60). 16 | 17 | lock steering to prograde. 18 | wait until faceDiff() < 0.5. 19 | 20 | warpFor1(eta:apoapsis - (bt / 2)). 21 | 22 | local lastEcc to ship:obt:eccentricity. 23 | set throt to 1. 24 | until lastEcc < ship:obt:eccentricity { 25 | set lastEcc to ship:obt:eccentricity. 26 | wait 0.01. 27 | } 28 | 29 | set throt to 0. 30 | set sas to prevSAS. 31 | -------------------------------------------------------------------------------- /circatperiapsis.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | run once funs. 4 | 5 | local vPer to sqrt(body:mu * (2 / (ship:obt:periapsis + body:radius) - 1 / ship:obt:semimajoraxis)). 6 | local vCirc to sqrt(body:mu / (ship:obt:periapsis + body:radius)). 7 | local dv to vPer - vCirc. 8 | local bt to burnTime(dv). 9 | 10 | local throt to 0. 11 | lock throttle to throt. 12 | local prevSAS to sas. 13 | sas off. 14 | 15 | warpFor1(eta:periapsis - bt - 60). 16 | 17 | lock steering to retrograde. 18 | wait until faceDiff() < 0.5. 19 | 20 | warpFor1(eta:periapsis - (bt / 2)). 21 | 22 | local lastEcc to ship:obt:eccentricity. 23 | set throt to 1. 24 | until lastEcc < ship:obt:eccentricity { 25 | set lastEcc to ship:obt:eccentricity. 26 | wait 0.01. 27 | } 28 | 29 | set throt to 0. 30 | set sas to prevSAS. 31 | -------------------------------------------------------------------------------- /lib/libcircincatapo.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libcosinerule"). 4 | runoncepath("lib/libmanat"). 5 | 6 | function nodeToCircIncAtApo { 7 | parameter tgtInc. 8 | 9 | local tgtSMA to body:radius + apoapsis. 10 | local curSMA to ship:orbit:semimajoraxis. 11 | 12 | local Vc to sqrt(body:mu / tgtSMA). 13 | local Vt to sqrt(body:mu * (2 / (body:radius + apoapsis) - 1 / curSMA)). 14 | local Vd to solveCosineRule(Vc, Vt, tgtInc - ship:orbit:inclination). 15 | 16 | local phi to arccos((Vt^2 + Vd^2 - Vc^2) / (2 * Vt * Vd)). 17 | local alpha to 180 - phi. 18 | local Vp to Vd * cos(alpha). 19 | local Vn to Vd * sin(alpha). 20 | 21 | local n to manAt("anode"). 22 | if Vn > 0 { 23 | set n to manAt("dnode"). 24 | } 25 | set n:prograde to Vp. 26 | set n:normal to Vn. 27 | 28 | return n. 29 | } 30 | -------------------------------------------------------------------------------- /missions/meosat_launch.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | // TODO: the probe decoupler forces don't seem to be uniform 4 | 5 | run launch(5, 60). 6 | 7 | panels on. 8 | 9 | // disable probe RCS thrusters so they don't waste fuel 10 | ag1 on. toggle ag1. // stupid KSP 11 | 12 | // extend antennae 13 | toggle ag2. 14 | 15 | // wait for all that to happen 16 | wait 5. 17 | 18 | rcs on. 19 | run changeapoapsis("dnode", 3000000, true). 20 | sas off. 21 | rcs off. 22 | 23 | wait 5. 24 | 25 | // release satellites 26 | stage. 27 | wait 120. 28 | stage. 29 | wait 120. 30 | stage. 31 | wait 120. 32 | stage. 33 | wait 120. 34 | 35 | // deorbit 36 | warpFor1(30 * 60). 37 | rcs on. 38 | lock steering to retrograde. 39 | wait until faceDiff() < 1. 40 | lock throttle to 1. 41 | wait until periapsis < 70000. 42 | lock throttle to 0. 43 | unlock steering. 44 | rcs off. 45 | -------------------------------------------------------------------------------- /lib/libbisectionmethod.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libsign"). 4 | 5 | function bisectionMethod { 6 | parameter p_f. 7 | parameter p_a. 8 | parameter p_b. 9 | parameter p_maxIter. 10 | parameter p_tol. 11 | 12 | local a to p_a. 13 | local b to p_b. 14 | 15 | local iter to 1. 16 | until iter > p_maxIter { 17 | local c to (a + b) / 2. 18 | local fc to p_f(c). 19 | 20 | if abs(fc) < p_tol or (b - a) / 2 < p_tol { 21 | return c. 22 | } 23 | set iter to iter + 1. 24 | if sign(fc) = sign(p_f(a)) { 25 | set a to c. 26 | } else { 27 | set b to c. 28 | } 29 | } 30 | 31 | print "FAILED TO CONVERGE!". 32 | exit. 33 | } 34 | 35 | //function testFun { 36 | // parameter p_x. 37 | // return p_x^3 - p_x - 2. 38 | //} 39 | 40 | //local x to bisectionMethod(testFun@, 1, 2, 20, 1e-5). 41 | //print x. 42 | -------------------------------------------------------------------------------- /lib/libhohmann.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libphaseangle"). 4 | 5 | function nodeForHohmannTransfer { 6 | parameter tgt. 7 | 8 | if tgt:body <> ship:body { 9 | print "CANNOT HOHMANN TRANSFER TO TARGET ORBITING DIFFERENT BODY!". 10 | exit. 11 | } 12 | 13 | local xferSMA to (ship:orbit:semimajoraxis + tgt:orbit:semimajoraxis) / 2. 14 | 15 | local pt to 0.5 * (xferSMA / tgt:orbit:semimajoraxis) ^ 1.5. 16 | local ft to pt - floor(pt). 17 | local theta to 360 * ft. 18 | local pa to 180 - theta. 19 | 20 | local ttpa to timeToPhaseAngle(pa, tgt). 21 | 22 | local xferVelAtHere to sqrt(body:mu * (2 / ship:orbit:semimajoraxis - 1 / xferSMA)). 23 | local xferBurnDV to xferVelAtHere - ship:orbit:velocity:orbit:mag. 24 | 25 | return node(time:seconds + ttpa, 0, 0, xferBurnDV). 26 | } 27 | 28 | add nodeForHohmannTransfer(vessel("JSS")). 29 | -------------------------------------------------------------------------------- /missions/meosat_1.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | run once circincatapo.funs.ks. 4 | run once execnode.funs.ks. 5 | run once funs.ks. 6 | 7 | function partialCircInc { 8 | local n to nodeToCircIncAtApo(0). 9 | add n. 10 | 11 | local startLatSign to sign(ship:geoposition:lat). 12 | local execNodeState to list(). 13 | execNodeStart(false, execNodeState). 14 | until sign(ship:geoposition:lat) <> startLatSign { 15 | if not execNodeContinue(execNodeState) { 16 | break. 17 | } 18 | } 19 | 20 | execNodeStop(execNodeState). 21 | 22 | remove n. 23 | } 24 | 25 | stage. // dummy decoupler stage 26 | wait until stage:ready. 27 | stage. // activate engines 28 | rcs on. 29 | toggle ag1. // turn RCS thrusters back on 30 | 31 | // Do this a few times so it is more accurate 32 | partialCircInc(). 33 | partialCircInc(). 34 | 35 | // And finally finish it 36 | run circincatapo(0, false). 37 | 38 | rcs off. 39 | -------------------------------------------------------------------------------- /lib/libtimetotrueanom.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function eccAnomFromTrueAnom { 4 | parameter ta. 5 | 6 | local sinE to sqrt(1 - ship:obt:eccentricity^2) * sin(ta). 7 | local cosE to ship:obt:eccentricity + cos(ta). 8 | local E to arctan2(sinE, cosE). 9 | if (E < 0) { set E to E + 360. } 10 | return E. 11 | } 12 | 13 | function timeToTrueAnom { 14 | parameter ta. 15 | 16 | local curE to eccAnomFromTrueAnom(ship:obt:trueanomaly). 17 | local curMA to (curE * constant:pi / 180 - ship:obt:eccentricity * sin(curE)) * 180 / constant:pi. 18 | 19 | local tgtE to eccAnomFromTrueAnom(ta). 20 | local tgtMA to tgtE - ship:obt:eccentricity * sin(tgtE). 21 | local tgtMA to (tgtE * constant:pi / 180 - ship:obt:eccentricity * sin(tgtE)) * 180 / constant:pi. 22 | if (tgtMA < curMA) { set tgtMA to tgtMA + 360. } 23 | 24 | local deltaMA to tgtMA - curMA. 25 | local madt to 360 / ship:obt:period. 26 | 27 | return deltaMA / madt. 28 | } 29 | -------------------------------------------------------------------------------- /missions/firstDockLaunch.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libwaitforlan"). 4 | runoncepath("lib/libwarpfor"). 5 | runoncepath("lib/libwaitforfacing"). 6 | 7 | local tgt to vessel("JSS"). 8 | 9 | waitForLan(tgt:obt:lan - 6). 10 | runpath("launch", 5, 80, 75000, tgt:obt:inclination, 250000). 11 | 12 | for mod in ship:modulesNamed("ModuleRTAntenna") { 13 | if mod:hasEvent("Activate") { 14 | mod:doEvent("Activate"). 15 | } 16 | } 17 | 18 | panels on. 19 | sas off. 20 | 21 | ship:modulesnamed("ModuleDecouple")[0]:doEvent("Decouple"). 22 | 23 | // KSP seems to pick a random ship to switch to after decoupling, so don't deorbit automatically 24 | //warpFor(500). 25 | //lock steering to retrograde. 26 | //rcs on. 27 | //waitForFacing(5, false, true). 28 | //set ship:control:fore to 1. 29 | //wait 10. 30 | //lock throttle to 1. 31 | //wait until ship:obt:periapsis < 30000. 32 | //lock throttle to 0. 33 | //unlock steering. 34 | //rcs off. 35 | -------------------------------------------------------------------------------- /mainframe/src/main/java/hall/john/ksp/mainframe/Test.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe; 2 | 3 | import java.io.FileWriter; 4 | 5 | import hall.john.ksp.mainframe.orbit.OrbitAtTime; 6 | 7 | public class Test { 8 | 9 | public static void main(String[] args) throws Exception { 10 | Body b = new Body("Earth", 3.986004418E14); 11 | 12 | OrbitAtTime oat1 = new OrbitAtTime(b, 6571000, 0.0001, 0, 0, 0, 0); 13 | OrbitAtTime oat2 = new OrbitAtTime(b, 6671000, 0.4, 0, 0, 0, 0); 14 | 15 | try (FileWriter fw = new FileWriter( 16 | "C:\\Games\\Steam\\steamapps\\common\\Kerbal Space Program 1.1.3\\Ships\\Script\\data.txt")) { 17 | for (double t = 0; t < 2 * oat1.getPeriod(); t += 5) { 18 | OrbitAtTime newOat1 = oat1.afterTime(t); 19 | OrbitAtTime newOat2 = oat2.afterTime(t); 20 | double dist = newOat1.getRadiusVector().distance(newOat2.getRadiusVector()); 21 | fw.append(t + " " + dist + "\n"); 22 | } 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /missions/meosat_constellation.txt: -------------------------------------------------------------------------------- 1 | run calc.planner.transfer.ks(body, 250000, 28.5, 3000000, 0). 2 | Initial Orbital Velocity: 7759.02 m/s 3 | Transfer Burn Delta-V: 640.67 m/s 4 | Transfer Time: 59m 17.87s (3557.87 s) 5 | Inclination Change: 28.5 deg 6 | Circularization Burn Delta-V: 3118.62 m/s 7 | Circularization Burn Prograde: -203.15 m/s 8 | Circularization Burn Normal: 3111.99 m/s 9 | Target Orbital Velocity: 6521.93 m/s 10 | Total Delta-V: 3759.29 m/s 11 | Delta-V Lost Due to Inclination Change: 2531.43 m/s 12 | 13 | run calc.satconst.ks(body, 3000000, 4, 0.09). 14 | SMA: 9371 km 15 | Orbital Velocity: 6521.93 m/s 16 | Orbital Period: 2h 30m 27.97s (9027.97 s) 17 | Coverage Angle: 94.33 deg 18 | Full Equatorial Coverage: True 19 | Distance Between Satellites: 13252.595 km 20 | Maximum Eclipse Angle: 91.19 deg 21 | Time in Sunlight: 1h 52m 21.19s (6741.19 s) 22 | Time in Eclipse: 38m 6.77s (2286.77 s) 23 | Battery Required: 205.81 EC 24 | Generator Required: 0.12 EC/s (7.23 EC/m) 25 | -------------------------------------------------------------------------------- /lib/libclosestapproach.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libbmmethod"). 4 | 5 | function closestApproach { 6 | parameter tgt, fromTime, toTime, tol is 1. 7 | 8 | function dist { 9 | parameter t. 10 | local pos1 to positionat(ship, t). 11 | local pos2 to positionat(tgt, t). 12 | return (pos1 - pos2):mag. 13 | } 14 | 15 | local res to bracketingMinimizationMethod1(dist@, fromTime, toTime, 10, tol). 16 | local speed to (velocityat(ship, res["x"]):orbit - velocityat(tgt, res["x"]):orbit):mag. 17 | return lexicon("time", res["x"], "minDist", res["min"], "speed", speed). 18 | } 19 | 20 | //local calcStartTime to time:seconds. 21 | //local res to closestApproach(vessel("JSS"), time:seconds + 6 * 60, time:seconds + 6 * 60 + 5, 1). 22 | //local calcEndTime to time:seconds. 23 | //print res["minDist"]. 24 | //print res["time"]. 25 | //print "(" + ((res["time"] - time:seconds)/60) + " min from now)". 26 | //print "calc time: " + (calcEndTime - calcStartTime). 27 | -------------------------------------------------------------------------------- /lib/libprintfield.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function printField { 4 | parameter fieldSpec, value. 5 | 6 | local label to fieldSpec[0]. 7 | local labelWidth to fieldSpec[1]. 8 | local labelAlign to fieldSpec[2]. 9 | local valueWidth to fieldSpec[3]. 10 | local valueAlign to fieldSpec[4]. 11 | local col to fieldSpec[5]. 12 | local line to fieldSpec[6]. 13 | 14 | print "":padleft(labelWidth + 1 + valueWidth) at(col, line). 15 | 16 | set label to label:substring(0, min(labelWidth, label:length)). 17 | local paddedLabel to label:padleft(labelWidth). 18 | if labelAlign = "l" { set paddedLabel to label:padright(labelWidth). } 19 | print paddedLabel at(col, line). 20 | 21 | local stringValue to "" + value. 22 | set stringValue to stringValue:substring(0, min(valueWidth, stringValue:length)). 23 | local paddedValue to stringValue:padleft(valueWidth). 24 | if valueAlign = "l" { set paddedValue to stringValue:padright(valueWidth). } 25 | print paddedValue at(col + labelWidth + 1, line). 26 | } 27 | -------------------------------------------------------------------------------- /lib/libtimeto.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libtimetotrueanom"). 4 | 5 | function timeTo1 { 6 | parameter where, allowed is list("anode", "dnode", "apo", "per"). 7 | 8 | local mappedWhere to where. 9 | if where = "apoapsis" { 10 | set mappedWhere to "apo". 11 | } else if list("peri", "periapsis"):contains(where) { 12 | set mappedWhere to "per". 13 | } 14 | 15 | if not allowed:contains(mappedWhere) { 16 | print mappedWhere + " not in allowed list: " + allowed. 17 | exit. 18 | } 19 | 20 | if (where = "anode") { 21 | return timeToTrueAnom(360 - ship:obt:argumentofperiapsis). 22 | } else if (where = "dnode") { 23 | return timeToTrueAnom(abs(180 - ship:obt:argumentofperiapsis)). 24 | } else if (where = "apo" or where = "apoapsis") { 25 | return eta:apoapsis. 26 | } else if (where = "per" or where = "peri" or where = "periapsis") { 27 | return eta:periapsis. 28 | } else { 29 | print "disallowed where should have been caught earlier". 30 | exit. 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/libsmoothturn.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function smoothScalarBasedTurn { 4 | // TODO: allow endVal < startVal 5 | // (e.g. start a high altitude, finish at low altitude) 6 | parameter curVal, startVal, endVal, startVec, endVec, upVec. 7 | 8 | if curVal < startVal { 9 | return lookdirup(startVec, upVec). 10 | } else if curVal > endVal { 11 | return lookdirup(endVec, upVec). 12 | } else { 13 | local frac to (curVal - startVal) / (endVal - startVal). 14 | local pointVec to frac * endVec + (1 - frac) * startVec. 15 | return lookdirup(pointVec, upVec). 16 | } 17 | } 18 | 19 | function smoothScalarBasedProgression { 20 | // TODO: allow endIn < startIn 21 | // (e.g. start a high altitude, finish at low altitude) 22 | parameter curIn, startIn, endIn, startOut, endOut. 23 | 24 | if curIn < startIn { 25 | return startOut. 26 | } else if curIn > endIn { 27 | return endOut. 28 | } else { 29 | local frac to (curIn - startIn) / (endIn - startIn). 30 | return frac * endOut + (1 - frac) * startOut. 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/libsecantmethod.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function secantMethod { 4 | parameter p_f. 5 | parameter p_x0. 6 | parameter p_x1. 7 | parameter p_maxIter. 8 | parameter p_tol. 9 | 10 | local xnm2 to p_x0. 11 | local xnm1 to p_x1. 12 | 13 | local fxnm1 to p_f(xnm1). 14 | local fxnm2 to p_f(xnm2). 15 | 16 | local iter to 1. 17 | until iter > p_maxIter { 18 | local x to xnm1 - fxnm1 * (xnm1 - xnm2) / (fxnm1 - fxnm2). 19 | local fx to p_f(x). 20 | 21 | if abs(fx) < p_tol { 22 | return x. 23 | } 24 | 25 | set xnm2 to xnm1. 26 | set fxnm2 to fxnm1. 27 | set xnm1 to x. 28 | set fxnm1 to fx. 29 | set iter to iter + 1. 30 | } 31 | 32 | print "FAILED TO CONVERGE!". 33 | exit. 34 | } 35 | 36 | //function testFun { 37 | // parameter p_offset. 38 | // parameter p_x. 39 | // print "Evaluating f(" + p_x + ").". 40 | // return p_x^2 - p_offset. 41 | //} 42 | 43 | //local x to secantMethod(testFun@:bind(612), 10, 30, 7, 1e-5). 44 | //print x. 45 | //local x to secantMethod(testFun@:bind(700), 10, 30, 7, 1e-5). 46 | //print x. 47 | -------------------------------------------------------------------------------- /driveto.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | { 4 | parameter cb. 5 | 6 | runoncepath("lib/libsmoothturn"). 7 | runoncepath("lib/libabsdiffmod"). 8 | 9 | local cbRes to cb(). 10 | local loc to cbRes["loc"]. 11 | lock wheelsteering to loc. 12 | 13 | local throttlepid to pidloop(0.77625, 0.44357, 0, -1, 1). 14 | set throttlepid:setpoint to cbRes["speed"]. 15 | 16 | local whlthrot to 0. 17 | lock wheelthrottle to whlthrot. 18 | 19 | local nextPrintTime to time:seconds. 20 | 21 | until loc:position:mag < 5 { 22 | brakes off. 23 | 24 | if time:seconds >= nextPrintTime { 25 | print "distance: " + loc:position:mag. 26 | set nextPrintTime to time:seconds + 10. 27 | } 28 | 29 | local pidout to throttlepid:update(time:seconds, ship:velocity:surface:mag). 30 | set whlthrot to pidout. 31 | 32 | set cbRes to cb(). 33 | set loc to cbRes["loc"]. 34 | if abs(loc:bearing) > 5 { set throttlepid:setpoint to 0.125 * cbRes["speed"]. } 35 | else { set throttlepid:setpoint to cbRes["speed"]. } 36 | wait 0.05. 37 | } 38 | 39 | brakes on. 40 | } 41 | -------------------------------------------------------------------------------- /lib/libwarpto.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libwarpfor"). 4 | 5 | function myWarpTo { 6 | parameter where, lead. 7 | 8 | if where = "per" or where = "periapsis" { 9 | local lastTT to eta:periapsis - lead. 10 | if lastTT < 0 { set lastTT to lastTT + ship:obt:period. } 11 | 12 | function tt { 13 | local curTT to eta:periapsis - lead. 14 | if curTT < 0 { set curTT to curTT + ship:obt:period. } 15 | if curTT > lastTT { 16 | return 0. 17 | } else { 18 | set lastTT to curTT. 19 | return curTT. 20 | } 21 | } 22 | 23 | warpUntilZero(tt@). 24 | } else if where = "apo" or where = "apoapsis" { 25 | local lastTT to eta:apoapsis - lead. 26 | if lastTT < 0 { set lastTT to lastTT + ship:obt:period. } 27 | 28 | function tt { 29 | local curTT to eta:apoapsis - lead. 30 | if curTT < 0 { set curTT to curTT + ship:obt:period. } 31 | if curTT > lastTT { 32 | return 0. 33 | } else { 34 | set lastTT to curTT. 35 | return curTT. 36 | } 37 | } 38 | 39 | warpUntilZero(tt@). 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /mainframe/src/test/java/hall/john/ksp/mainframe/TestUtils.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; 6 | 7 | public abstract class TestUtils { 8 | private TestUtils() { 9 | // don't construct me! 10 | } 11 | 12 | public static void assertRelativelyEquals(double a, double b, double eps) { 13 | if (a == 0) assertTrue("Got " + b + ", expected " + a, Math.abs(b) < eps); 14 | else if (b == 0) assertTrue("Got " + b + ", expected " + a, Math.abs(a) < eps); 15 | else assertTrue("Got " + b + ", expected " + a, Math.abs((a - b) / a) < eps); 16 | } 17 | 18 | public static void assertVectorRelativelyEquals(Vector3D u, Vector3D v, double eps) { 19 | assertRelativelyEquals(u.getX(), v.getX(), eps); 20 | assertRelativelyEquals(u.getY(), v.getY(), eps); 21 | assertRelativelyEquals(u.getZ(), v.getZ(), eps); 22 | } 23 | 24 | public static void assertVectorRelativelyEquals(double x, double y, double z, Vector3D v, double eps) { 25 | assertVectorRelativelyEquals(new Vector3D(x, y, z), v, eps); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/libradtrig.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function radsin { 4 | parameter p_x. 5 | return sin(180 * p_x / constant:pi). 6 | } 7 | 8 | function radcos { 9 | parameter p_x. 10 | return cos(180 * p_x / constant:pi). 11 | } 12 | 13 | function radtan { 14 | parameter p_x. 15 | return tan(180 * p_x / constant:pi). 16 | } 17 | 18 | function radarcsin { 19 | parameter p_x. 20 | return constant:pi * arcsin(p_x) / 180. 21 | } 22 | 23 | function radarccos { 24 | parameter p_x. 25 | return constant:pi * arccos(p_x) / 180. 26 | } 27 | 28 | function radarctan { 29 | parameter p_x. 30 | return constant:pi * arctan(p_x) / 180. 31 | } 32 | 33 | function radarccot { 34 | parameter p_x. 35 | return 0.5 * constant:pi - radarctan(p_x). 36 | } 37 | 38 | function radarccoth { 39 | parameter p_x. 40 | return 0.5 * ln((p_x + 1) / (p_x - 1)). 41 | } 42 | 43 | //print radsin(constant:pi / 6). 44 | //print radcos(constant:pi / 3). 45 | //print radtan(constant:pi / 4). 46 | //print radarcsin(0.5) / constant:pi. 47 | //print radarccos(0.5) / constant:pi. 48 | //print radarctan(1) / constant:pi. 49 | //print radarccot(0). 50 | //print radarctan(0). 51 | -------------------------------------------------------------------------------- /land.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter myMaxThrust. 4 | 5 | wait until altitude < 30000. 6 | set warp to 0. 7 | 8 | clearscreen. 9 | print "alt:radar :" at (2, 10). 10 | print "tgtVel :" at (2, 11). 11 | print "tgtAccel :" at (2, 12). 12 | print "gHere :" at (2, 13). 13 | print "throt :" at (2, 14). 14 | 15 | local lock tgtVel to -alt:radar / 10 - 2. 16 | 17 | local lock maxEngineAccel to myMaxThrust / mass. 18 | 19 | local gHere to body:mu / (body:radius ^ 2). 20 | 21 | sas off. 22 | 23 | lock steering to lookdirup(-ship:obt:velocity:surface, ship:facing:topvector). 24 | local throt to 1. 25 | lock throttle to throt. 26 | 27 | when (alt:radar < 500) then gear on. 28 | when (ship:obt:velocity:surface:mag < 5) then lock steering to lookdirup(up:vector, ship:facing:topvector). 29 | 30 | until status = "Landed" { 31 | local tgtAccel to tgtVel + ship:obt:velocity:surface:mag. 32 | set throt to (tgtAccel + gHere) / maxEngineAccel. 33 | print alt:radar at (15, 10). 34 | print tgtVel at (15, 11). 35 | print tgtAccel at (15, 12). 36 | print gHere at (15, 13). 37 | print throt at (15, 14). 38 | wait 0.05. 39 | } 40 | 41 | set throt to 0. 42 | -------------------------------------------------------------------------------- /calc.funs.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function s2ydhms { 4 | parameter s. 5 | local y to floor(s / (60 * 60 * 24 * 365)). 6 | set s to mod(s, 60 * 60 * 24 * 365). 7 | local d to floor(s / (60 * 60 * 24)). 8 | set s to mod(s, 60 * 60 * 24). 9 | local h to floor(s / (60 * 60)). 10 | set s to mod(s, 60 * 60). 11 | local m to floor(s / 60). 12 | set s to mod(s, 60). 13 | return list(y, d, h, m, s). 14 | } 15 | 16 | function formatYdhms { 17 | parameter ydhms. 18 | local str to "". 19 | local doRest to false. 20 | 21 | local y to ydhms[0]. 22 | local d to ydhms[1]. 23 | local h to ydhms[2]. 24 | local m to ydhms[3]. 25 | local s to ydhms[4]. 26 | 27 | if y <> 0 { 28 | set doRest to true. 29 | set str to str + y + "y ". 30 | } 31 | 32 | if doRest or d <> 0 { 33 | set doRest to true. 34 | set str to str + d + "d ". 35 | } 36 | 37 | if doRest or h <> 0 { 38 | set doRest to true. 39 | set str to str + h + "h ". 40 | } 41 | 42 | if doRest or m <> 0 { 43 | set doRest to true. 44 | set str to str + m + "m ". 45 | } 46 | 47 | set str to str + round(s, 2) + "s". 48 | 49 | return str. 50 | } 51 | -------------------------------------------------------------------------------- /changeperiod.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter tgtPeriod, turnTime, ullageTime. 4 | 5 | // currently only implemented for increasing period 6 | 7 | runoncepath("lib/libgenericburn"). 8 | runoncepath("lib/libburntime"). 9 | 10 | function valueCallback { 11 | return abs(tgtPeriod - ship:obt:period). 12 | } 13 | 14 | function pointVecCallback { 15 | parameter p_initial. 16 | if p_initial { 17 | return velocityat(ship, time:seconds + eta:apoapsis):orbit. 18 | } else { 19 | return prograde:vector. 20 | } 21 | } 22 | 23 | local tgtSMA to (body:mu * tgtPeriod^2 / (4 * constant:pi^2))^(1/3). 24 | local tgtVelAtApo to sqrt(body:mu * (2 / (body:radius + ship:obt:apoapsis) - 1 / tgtSMA)). 25 | local curVelAtApo to sqrt(body:mu * (2 / (body:radius + ship:obt:apoapsis) - 1 / ship:obt:semimajoraxis)). 26 | local bt to burnTime(abs(tgtVelAtApo - curVelAtApo)). 27 | print "tgtVelAtApo: " + tgtVelAtApo. 28 | print "curVelAtApo: " + curVelAtApo. 29 | print "bt: " + bt. 30 | print "current SMA: " + ship:obt:semimajoraxis. 31 | print "target SMA: " + tgtSMA. 32 | 33 | genericBurn("apo", bt, turnTime, ullageTime, valueCallback@, pointVecCallback@). 34 | 35 | runpath("facesun"). 36 | -------------------------------------------------------------------------------- /lib/libfuelflow.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function fuelflow_init { 4 | local dat to list(time:seconds, stage:resources). 5 | return dat. 6 | } 7 | 8 | function fuelflow_timeToBurnOut { 9 | parameter dat. 10 | 11 | local curTime to time:seconds. 12 | local curResList to stage:resources. 13 | local oldTime to dat[0]. 14 | local oldResList to dat[1]. 15 | local dt to curTime - oldTime. 16 | 17 | local minTTBO to 99999999. 18 | for curRes in curResList { 19 | if (curRes:name <> "ElectricCharge") { 20 | local oldRes to fuelflow_resByName(oldResList, curRes:name). 21 | if (oldRes <> -1) { 22 | local rate to (curRes:amount - oldRes:amount) / dt. 23 | if (rate < 0) { 24 | local ttbo to curRes:amount / abs(rate). 25 | set minTTBO to min(minTTBO, ttbo). 26 | } 27 | } 28 | } 29 | } 30 | 31 | set dat[0] to curTime. 32 | set dat[1] to curResList. 33 | 34 | return minTTBO. 35 | } 36 | 37 | 38 | function fuelflow_resByName { 39 | parameter resList. 40 | parameter name. 41 | 42 | for res in resList { 43 | if (res:name = name) { 44 | return res. 45 | } 46 | } 47 | 48 | return -1. 49 | } 50 | -------------------------------------------------------------------------------- /circincatapo.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter tgtInc, turnTime, ullageTime, maxBurnTime, faceSunBetween. 4 | 5 | runoncepath("lib/libcircincatapo"). 6 | runoncepath("lib/libburntime"). 7 | runoncepath("lib/libisp"). 8 | runoncepath("lib/libgenericburn"). 9 | 10 | local done to false. 11 | until done { 12 | local n to nodeToCircIncAtApo(tgtInc). 13 | add n. 14 | local bt to burnTime(n:deltaV:mag). 15 | 16 | local burnStartTime to 0. 17 | local lastPrint to time:seconds. 18 | function valueCallback { 19 | if burnStartTime = 0 { set burnStartTime to time:seconds. } 20 | 21 | if time:seconds - lastPrint > 60 { 22 | set lastPrint to time:seconds. 23 | print "Burn time remaining: " + ((maxBurnTime + burnStartTime) - time:seconds). 24 | } 25 | 26 | if time:seconds >= burnStartTime + maxBurnTime { return 99999999. } 27 | return nextnode:deltaV:mag. 28 | } 29 | 30 | function nnDV { 31 | parameter p_x. 32 | return nextnode:deltaV. 33 | } 34 | 35 | local realBurnTime to min(bt, maxBurnTime). 36 | if realBurnTime = bt { set done to true. } 37 | genericBurn("apo", realBurnTime, turnTime, ullageTime, valueCallback@, nnDV@). 38 | remove nextnode. 39 | 40 | if not done and faceSunBetween { 41 | runpath("faceSun"). 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/libengine.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function ignitedEngines { 4 | local ignited to list(). 5 | 6 | local es to list(). 7 | list engines in es. 8 | 9 | for e in es { 10 | if (e:ignition) { 11 | ignited:add(e). 12 | } 13 | } 14 | 15 | return ignited. 16 | } 17 | 18 | function atZeroThrust { 19 | local es to list(). 20 | list engines in es. 21 | 22 | for e in es { 23 | if (e:ignition and e:thrust <> 0) { 24 | return false. 25 | } 26 | } 27 | 28 | return true. 29 | } 30 | 31 | function atFullThrust { 32 | local es to list(). 33 | list engines in es. 34 | 35 | for e in es { 36 | if (e:ignition and e:thrust / e:availablethrust < 0.95) { 37 | return false. 38 | } 39 | } 40 | 41 | return true. 42 | } 43 | 44 | function currentThrust { 45 | local igEngs to ignitedEngines(). 46 | local T to 0. 47 | for eng in igEngs { 48 | set T to T + eng:thrust. 49 | } 50 | return T. 51 | } 52 | 53 | function currentTWR { 54 | local T to currentThrust(). 55 | local gHere to body:mu / (body:radius + altitude)^2. 56 | return T / (gHere * mass). 57 | } 58 | 59 | function waitForStablePropellant { 60 | for eng in ignitedEngines() { 61 | wait until eng:getModule("ModuleEnginesRF"):getField("propellant") = "Very Stable". 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/libtimetoburnout.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function elementByName { 4 | parameter name. 5 | parameter l. 6 | for i in l { 7 | if i:name = name { 8 | return i. 9 | } 10 | } 11 | return 0. 12 | } 13 | 14 | function recordIndexByName { 15 | parameter name. 16 | parameter l. 17 | 18 | local i to 0. 19 | until i >= l:length { 20 | if l[i][0] = name { 21 | return i. 22 | } 23 | set i to i + 1. 24 | } 25 | return -1. 26 | } 27 | 28 | function initTimeToBurnOut { 29 | return list(time:seconds, lexicon()). 30 | } 31 | 32 | function timeToBurnOut { 33 | // Won't work well when close to burnout because fuel/oxidizer consumption rate 34 | // tends to trail off rather than abruptly drop to 0. 35 | parameter resources. 36 | parameter state. 37 | 38 | local timeNow to time:seconds. 39 | local dt to timeNow - state[0]. 40 | set state[0] to timeNow. 41 | 42 | local shortestBO to 999999. 43 | 44 | for r in resources { 45 | if state[1]:hasKey(r:name) { 46 | local oldAmount to state[1][r:name]. 47 | 48 | local rate to (oldAmount - r:amount) / dt. 49 | if rate > 0 { 50 | set shortestBO to min(shortestBO, r:amount / rate). 51 | } 52 | } 53 | 54 | set state[1][r:name] to r:amount. 55 | } 56 | 57 | return shortestBO. 58 | } 59 | -------------------------------------------------------------------------------- /mainframe/src/main/java/hall/john/ksp/mainframe/numericalmethods/NewtonsMethod.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe.numericalmethods; 2 | 3 | import java.util.function.DoubleFunction; 4 | 5 | public class NewtonsMethod { 6 | private DoubleFunction _f; 7 | private DoubleFunction _fp; 8 | private double _x0; 9 | private int _maxIter; 10 | private double _tol; 11 | private Double _result; 12 | private boolean _converged; 13 | 14 | public NewtonsMethod(DoubleFunction f, DoubleFunction fp, double x0, int maxIter, double tol) { 15 | _f = f; 16 | _fp = fp; 17 | _x0 = x0; 18 | _maxIter = maxIter; 19 | _tol = tol; 20 | } 21 | 22 | public void execute() { 23 | _converged = false; 24 | _result = null; 25 | 26 | double x = _x0; 27 | int iter = 1; 28 | 29 | while (true) { 30 | if (iter > _maxIter) { 31 | return; 32 | } 33 | 34 | double fx = _f.apply(x); 35 | if (Math.abs(fx) < _tol) { 36 | _converged = true; 37 | _result = x; 38 | return; 39 | } 40 | 41 | double fpx = _fp.apply(x); 42 | if (fpx == 0) { 43 | return; 44 | } 45 | 46 | x = x - fx / fpx; 47 | iter++; 48 | } 49 | } 50 | 51 | public Double getResult() { 52 | return _result; 53 | } 54 | 55 | public boolean getConverged() { 56 | return _converged; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/libsasrcsstack.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | global g_rcsStack to stack(). 4 | global g_sasStack to stack(). 5 | global g_tsStack to stack(). 6 | g_rcsStack:clear(). 7 | g_sasStack:clear(). 8 | g_tsStack:clear(). 9 | 10 | function pushRCS { 11 | parameter newRCS. 12 | g_rcsStack:push(rcs). 13 | set rcs to newRCS. 14 | wait 0.01. 15 | } 16 | 17 | function popRCS { 18 | if not g_rcsStack:empty { 19 | local prevRCS to g_rcsStack:pop(). 20 | set rcs to prevRCS. 21 | } 22 | wait 0.01. 23 | } 24 | 25 | function pushSAS { 26 | parameter newSAS. 27 | g_sasStack:push(sas). 28 | set sas to newSAS. 29 | wait 0.01. 30 | } 31 | 32 | function popSAS { 33 | if not g_sasStack:empty { 34 | local prevSAS to g_sasStack:pop(). 35 | set sas to prevSAS. 36 | } 37 | wait 0.01. 38 | } 39 | 40 | function pushTS { 41 | parameter newTS. 42 | g_tsStack:push(list(steeringmanager:pitchts, steeringmanager:yawts, steeringmanager:rollts)). 43 | set steeringmanager:pitchts to newTS. 44 | set steeringmanager:yawts to newTS. 45 | set steeringmanager:rollts to newTS. 46 | wait 0.01. 47 | } 48 | 49 | function popTS { 50 | local prevTS to g_sasStack:pop(). 51 | set steeringmanager:pitchts to prevTS[0]. 52 | set steeringmanager:yawts to prevTS[1]. 53 | set steeringmanager:rollts to prevTS[2]. 54 | wait 0.01. 55 | } 56 | -------------------------------------------------------------------------------- /mainframe/src/test/java/hall/john/ksp/mainframe/numericalmethods/BrentsMethodTest.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe.numericalmethods; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.junit.Assert.assertFalse; 6 | import static org.junit.Assert.assertNull; 7 | 8 | import java.util.function.DoubleFunction; 9 | 10 | import org.junit.Test; 11 | 12 | public class BrentsMethodTest { 13 | 14 | @Test 15 | public void convergeTest() { 16 | DoubleFunction f = (double x) -> (x + 3) * (x - 1) * (x - 1); 17 | BrentsMethod mth = new BrentsMethod(f, -4, 4.0/3, 100, 1e-6); 18 | mth.execute(); 19 | assertTrue(mth.getConverged()); 20 | assertEquals(-3, mth.getResult(), 1e-6); 21 | } 22 | 23 | @Test 24 | public void maxIterTest() { 25 | DoubleFunction f = (double x) -> (x + 3) * (x - 1) * (x - 1); 26 | BrentsMethod mth = new BrentsMethod(f, -4, 4.0/3, 1, 1e-6); 27 | mth.execute(); 28 | assertFalse(mth.getConverged()); 29 | assertNull(mth.getResult()); 30 | } 31 | 32 | @Test 33 | public void unboundedTest() { 34 | DoubleFunction f = (double x) -> (x + 3) * (x - 1) * (x - 1); 35 | BrentsMethod mth = new BrentsMethod(f, 2, 4, 100, 1e-6); 36 | mth.execute(); 37 | assertFalse(mth.getConverged()); 38 | assertNull(mth.getResult()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /supplyland.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | //parameter myMaxThrust. 4 | local myMaxThrust to 307.30. 5 | when (trueAlt() < 15000) then set warp to 0. 6 | 7 | function trueAlt { 8 | if abs(altitude - alt:radar) / altitude > 0.05 { 9 | return alt:radar. 10 | } else { 11 | return altitude - ship:geoposition:terrainheight. 12 | } 13 | } 14 | 15 | wait until trueAlt() < 6000. 16 | set warp to 0. 17 | 18 | stage. 19 | 20 | clearscreen. 21 | print "trueAlt :" at (2, 11). 22 | print "tgtVel :" at (2, 12). 23 | print "tgtAccel :" at (2, 13). 24 | print "gHere :" at (2, 14). 25 | print "throt :" at (2, 15). 26 | 27 | local lock tgtVel to -trueAlt() / 20 - 2. 28 | local lock maxEngineAccel to myMaxThrust / mass. 29 | 30 | local gHere to body:mu / (body:radius ^ 2). 31 | 32 | sas off. 33 | 34 | lock steering to lookdirup(-ship:obt:velocity:surface, ship:facing:topvector). 35 | local throt to 1. 36 | lock throttle to throt. 37 | 38 | when (trueAlt() < 500) then gear on. 39 | when (ship:obt:velocity:surface:mag < 5) then lock steering to lookdirup(up:vector, ship:facing:topvector). 40 | 41 | until status = "Landed" { 42 | local tgtAccel to tgtVel + ship:obt:velocity:surface:mag. 43 | set throt to (tgtAccel + gHere) / maxEngineAccel. 44 | print trueAlt() at (15, 11). 45 | print tgtVel at (15, 12). 46 | print tgtAccel at (15, 13). 47 | print gHere at (15, 14). 48 | print throt at (15, 15). 49 | wait 0.05. 50 | } 51 | 52 | set throt to 0. 53 | -------------------------------------------------------------------------------- /changeapoapsisrcs.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | { 4 | parameter tgtApo, turnTime, rcsIsp, rcsThrust, turnWithRCS is false, where is "periapsis". 5 | 6 | runoncepath("lib/libtimeto"). 7 | runoncepath("lib/libburntime"). 8 | runoncepath("lib/libsasrcsstack"). 9 | runoncepath("lib/libgenericburn"). 10 | 11 | local tgtSMA to (ship:obt:periapsis + tgtApo + 2 * body:radius) / 2. 12 | local v to sqrt(body:mu * (2 / (ship:obt:periapsis + body:radius) - 1 / ship:obt:semimajoraxis)). 13 | local tgtV to sqrt(body:mu * (2 / (ship:obt:periapsis + body:radius) - 1 / tgtSMA)). 14 | local dv to abs(tgtV - v). 15 | local bt to burnTime(dv, rcsIsp, rcsThrust). 16 | 17 | local dirSign to 1. 18 | if tgtApo < ship:obt:apoapsis { set dirSign to -1. } 19 | 20 | function cb { 21 | parameter predictDir, lastVal, lastTime. 22 | local val to abs(tgtApo - ship:apoapsis). 23 | local throt to 1. 24 | 25 | if lastVal >= 0 { 26 | local rate to (val - lastVal) / (time:seconds - lastTime). 27 | local ttzero to val / (-rate). 28 | set throt to max(0.1, min(1, ttzero)). 29 | //print "ttzero: " + ttzero. 30 | } 31 | 32 | local dir to ship:orbit:velocity:orbit. 33 | if predictDir { set dir to velocityat(ship, time:seconds + timeTo1(where)):orbit. } 34 | set dir to dirSign * dir. 35 | 36 | return lexicon("dir", dir, "throt", throt, "val", val). 37 | } 38 | 39 | genericBurnRCS(where, bt, turnTime, cb@, turnWithRCS). 40 | } 41 | -------------------------------------------------------------------------------- /changeperiapsisrcs.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | { 4 | parameter tgtPer, turnTime, rcsIsp, rcsThrust, turnWithRCS is false, where is "apoapsis". 5 | 6 | runoncepath("lib/libtimeto"). 7 | runoncepath("lib/libburntime"). 8 | runoncepath("lib/libsasrcsstack"). 9 | runoncepath("lib/libgenericburn"). 10 | 11 | local tgtSMA to (ship:obt:apoapsis + tgtPer + 2 * body:radius) / 2. 12 | local v to sqrt(body:mu * (2 / (ship:obt:apoapsis + body:radius) - 1 / ship:obt:semimajoraxis)). 13 | local tgtV to sqrt(body:mu * (2 / (ship:obt:apoapsis + body:radius) - 1 / tgtSMA)). 14 | local dv to abs(tgtV - v). 15 | local bt to burnTime(dv, rcsIsp, rcsThrust). 16 | 17 | local dirSign to 1. 18 | if tgtPer < ship:obt:periapsis { set dirSign to -1. } 19 | 20 | function cb { 21 | parameter predictDir, lastVal, lastTime. 22 | local val to abs(tgtPer - ship:periapsis). 23 | local throt to 1. 24 | 25 | if lastVal >= 0 { 26 | local rate to (val - lastVal) / (time:seconds - lastTime). 27 | local ttzero to val / (-rate). 28 | set throt to max(0.1, min(1, ttzero)). 29 | //print "ttzero: " + ttzero. 30 | } 31 | 32 | local dir to ship:orbit:velocity:orbit. 33 | if predictDir { set dir to velocityat(ship, time:seconds + timeTo1(where)):orbit. } 34 | set dir to dirSign * dir. 35 | 36 | return lexicon("dir", dir, "throt", throt, "val", val). 37 | } 38 | 39 | genericBurnRCS(where, bt, turnTime, cb@, turnWithRCS). 40 | } 41 | -------------------------------------------------------------------------------- /mainframe/src/test/java/hall/john/ksp/mainframe/numericalmethods/NewtonsMethodTest.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe.numericalmethods; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.junit.Assert.assertFalse; 6 | import static org.junit.Assert.assertNull; 7 | 8 | import java.util.function.DoubleFunction; 9 | 10 | import org.junit.Test; 11 | 12 | public class NewtonsMethodTest { 13 | 14 | @Test 15 | public void convergeTest() { 16 | DoubleFunction f = (double x) -> x*x - 612; 17 | DoubleFunction fp = (double x) -> 2*x; 18 | NewtonsMethod mth = new NewtonsMethod(f, fp, 10, 10, 1e-6); 19 | mth.execute(); 20 | assertTrue(mth.getConverged()); 21 | assertEquals(24.7386337537, mth.getResult(), 1e-6); 22 | } 23 | 24 | @Test 25 | public void maxIterTest() { 26 | DoubleFunction f = (double x) -> x*x*x -2*x + 2; 27 | DoubleFunction fp = (double x) -> 3*x*x -2; 28 | NewtonsMethod mth = new NewtonsMethod(f, fp, 0, 10, 1e-6); 29 | mth.execute(); 30 | assertFalse(mth.getConverged()); 31 | assertNull(mth.getResult()); 32 | } 33 | 34 | @Test 35 | public void zeroDerivativeTest() { 36 | DoubleFunction f = (double x) -> x*x*x -2*x + 2; 37 | DoubleFunction fp = (double x) -> 0.0; 38 | NewtonsMethod mth = new NewtonsMethod(f, fp, 0, 10, 1e-6); 39 | mth.execute(); 40 | assertFalse(mth.getConverged()); 41 | assertNull(mth.getResult()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /prepareforburn.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter pointVec, burnMidTime, burnTime, settleFuel. 4 | 5 | function timeStr { 6 | parameter t. 7 | return t + " (" + (t - time:seconds) + " from now)". 8 | } 9 | 10 | runoncepath("lib/libwaitforfacing"). 11 | runoncepath("lib/libwarpfor"). 12 | 13 | local lock timeToBurnMid to burnMidTime - time:seconds. 14 | local settleTime to 0. 15 | if settleFuel { set settleTime to 5. } 16 | 17 | print "Preparing for burn". 18 | print "Current Time: " + time:seconds. 19 | print "Point Vec: " + pointVec. 20 | print "Burn Times:". 21 | print " Duration: " + burnTime. 22 | print " Start : " + timeStr(burnMidTime - burnTime / 2). 23 | print " Middle : " + timeStr(burnMidTime). 24 | print " End : " + timeStr(burnMidTime + burnTime / 2). 25 | print "Settle Time: " + settleTime. 26 | 27 | warpFor1(timeToBurnMid - burnTime - 150). 28 | 29 | lock steering to lookdirup(pointVec, ship:facing:topvector). 30 | waitForFacing(0.5, false). 31 | 32 | print "Facing reached at " + time:seconds. 33 | 34 | print "Warping for " + (timeToBurnMid - (burnTime / 2) - settleTime). 35 | warpFor1(timeToBurnMid - (burnTime / 2) - settleTime). 36 | 37 | wait until timeToBurnMid - settleTime < (burnTime / 2). 38 | 39 | if settleFuel { 40 | print "Settling fuel at " + time:seconds. 41 | local prevRCS to rcs. 42 | rcs on. 43 | set ship:control:fore to 1. 44 | wait settleTime. 45 | set ship:control:fore to 0. 46 | set rcs to prevRCS. 47 | } 48 | 49 | print "Ready for burn at " + time:seconds. 50 | -------------------------------------------------------------------------------- /calc.satconst.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter bod, alt, count, powerReq. 4 | 5 | run once calc.funs.ks. 6 | 7 | function eclipseAngle { 8 | parameter r. // body radius 9 | parameter d. // body's distance from sun 10 | parameter a. // spacecraft sma around body 11 | 12 | return 2 * abs(constant:pi / 2 + arcsin(r / d) - arccos(r / a)). 13 | } 14 | 15 | run calc.orbit.circ.ks(bod, alt). 16 | 17 | local a to bod:radius + alt. 18 | local T to 2 * constant:pi * a / sqrt(bod:mu / a). 19 | 20 | local covgAngle to 2 * arccos(bod:radius / a). 21 | local fullCovg to false. 22 | if count * covgAngle >= 360 { set fullCovg to true. } 23 | local distBetween to a * sqrt(2 * (1 - cos(360 / count))). 24 | local maxEclipseAng to eclipseAngle(body:radius, body:orbit:periapsis, a). 25 | local timeInSunlight to (360 - maxEclipseAng) * T / 360. 26 | local timeInEclipse to maxEclipseAng * T / 360. 27 | local batReq to powerReq * timeInEclipse. 28 | local genReq to powerReq + batReq / timeInSunlight. 29 | 30 | print "Coverage Angle: " + round(covgAngle, 2) + " deg". 31 | print "Full Equatorial Coverage: " + fullCovg. 32 | print "Distance Between Satellites: " + round(distBetween / 1000, 3) + " km". 33 | print "Maximum Eclipse Angle: " + round(maxEclipseAng, 2) + " deg". 34 | print "Time in Sunlight: " + formatYdhms(s2ydhms(timeInSunlight)) + " (" + round(timeInSunlight, 2) + " s)". 35 | print "Time in Eclipse: " + formatYdhms(s2ydhms(timeInEclipse)) + " (" + round(timeInEclipse, 2) + " s)". 36 | print "Battery Required: " + round(batReq, 2) + " EC". 37 | print "Generator Required: " + round(genReq, 2) + " EC/s" + " (" + round(genReq * 60, 2) + " EC/m)". 38 | -------------------------------------------------------------------------------- /lib/libexecnode.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libburntime"). 4 | runoncepath("lib/libgenericburn"). 5 | 6 | function execNode { 7 | parameter p_turnTime, p_ullageTime. 8 | 9 | local bt to burnTime(nextnode:deltav:mag). 10 | local burnMidTime to time:seconds + nextnode:eta. 11 | 12 | function execnode_cb { 13 | parameter predictDir, lastVal, lastTime. 14 | local val to nextnode:deltav:mag. 15 | local throt to 1. 16 | 17 | if lastVal >= 0 { 18 | local rate to (val - lastVal) / (time:seconds - lastTime). 19 | local ttzero to val / (-rate). 20 | set throt to max(0.1, min(1, ttzero)). 21 | //print "ttzero: " + ttzero. 22 | } 23 | 24 | return lexicon("dir", nextnode:deltav, "throt", throt, "val", nextnode:deltav:mag). 25 | } 26 | 27 | genericBurn(burnMidTime, bt, p_turnTime, p_ullageTime, execnode_cb@). 28 | remove nextnode. 29 | } 30 | 31 | function execNodeRCS { 32 | parameter p_isp, p_thrust, p_turnTime, p_turnWithRCS is false. 33 | 34 | local bt to burnTime(nextnode:deltav:mag, p_isp, p_thrust). 35 | local burnMidTime to time:seconds + nextnode:eta. 36 | 37 | function cb { 38 | parameter predictDir, lastVal, lastTime. 39 | local val to nextnode:deltav:mag. 40 | local throt to 1. 41 | 42 | if lastVal >= 0 { 43 | local rate to (val - lastVal) / (time:seconds - lastTime). 44 | local ttzero to val / (-rate). 45 | set throt to max(0.1, min(1, ttzero)). 46 | //print "ttzero: " + ttzero. 47 | } 48 | 49 | return lexicon("dir", nextnode:deltav, "throt", throt, "val", nextnode:deltav:mag). 50 | } 51 | 52 | genericBurnRCS(burnMidTime, bt, p_turnTime, cb@, p_turnWithRCS). 53 | remove nextnode. 54 | } 55 | -------------------------------------------------------------------------------- /marsland.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | //parameter myMaxThrust. 4 | local myMaxThrust to 180.16. 5 | 6 | function trueAlt { 7 | if abs(altitude) < 0.05 or abs(altitude - alt:radar) / altitude > 0.05 { 8 | return alt:radar. 9 | } else { 10 | return altitude - ship:geoposition:terrainheight. 11 | } 12 | } 13 | 14 | local gSurf to body:mu / (body:radius ^ 2). 15 | sas off. 16 | gear off. 17 | 18 | when (trueAlt() < 15000) then set warp to 0. 19 | 20 | wait until trueAlt() < 4000. 21 | 22 | clearscreen. 23 | print "trueAlt :" at (2, 10). 24 | print "tgtVel :" at (2, 11). 25 | print "tgtAccel :" at (2, 12). 26 | print "gSurf :" at (2, 13). 27 | print "throt :" at (2, 14). 28 | 29 | local lock tgtVel to -trueAlt() / 20 - 2. 30 | local lock maxEngineAccel to myMaxThrust / mass. 31 | 32 | lock steering to lookdirup(-ship:obt:velocity:surface, ship:facing:topvector). 33 | local throt to 0. 34 | lock throttle to throt. 35 | 36 | when (ship:obt:velocity:surface:mag < 150) then stage. 37 | when (ship:obt:velocity:surface:mag < 50) then { 38 | ag2 on. // cut parachutes 39 | steeringmanager:resetpids(). // forget about accumulated error from parachutes 40 | } 41 | when (trueAlt() < 500) then gear on. 42 | when (ship:obt:velocity:surface:mag < 5) then lock steering to lookdirup(up:vector, ship:facing:topvector). 43 | 44 | // this might be helping or it might not 45 | set steeringmanager:maxstoppingtime to 10. 46 | 47 | until status = "Landed" { 48 | local tgtAccel to tgtVel + ship:obt:velocity:surface:mag. 49 | set throt to (tgtAccel + gSurf) / maxEngineAccel. 50 | print trueAlt() at (15, 10). 51 | print tgtVel at (15, 11). 52 | print tgtAccel at (15, 12). 53 | print gSurf at (15, 13). 54 | print throt at (15, 14). 55 | wait 0.05. 56 | } 57 | 58 | set throt to 0. 59 | -------------------------------------------------------------------------------- /lib/libcloseapproach.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libsasrcsstack"). 4 | runoncepath("lib/libwaitforfacing"). 5 | runoncepath("lib/libsign"). 6 | 7 | function closeApproach { 8 | parameter tgt, p_tgtDist. 9 | 10 | local tgtDist to p_tgtDist. 11 | 12 | function tgtVel { 13 | parameter tgt. 14 | if tgt:istype("Orbitable") { return tgt:obt:velocity:orbit. } 15 | else if tgt:istype("Part") { return tgt:ship:obt:velocity:orbit. } 16 | else { print "Unknown target type: " + tgt:typename(). exit. } 17 | } 18 | 19 | pushSAS(false). 20 | pushRCS(true). 21 | 22 | local nextPrintTime to time:seconds. 23 | 24 | local lock tgtRelVel to ship:obt:velocity:orbit - tgtVel(tgt). 25 | 26 | lock steering to lookdirup(tgt:position, ship:facing:topvector). 27 | waitForFacing(0.5, false, false, false). 28 | 29 | until tgt:position:mag < p_tgtDist and tgtRelVel:mag < 0.05 { 30 | if time:seconds >= nextPrintTime { 31 | local rate to tgtRelVel:mag * -sign(vdot(tgtRelVel, tgt:position)). 32 | print "Approaching target. Current distance: " + tgt:position:mag + " Current rate: " + rate. 33 | set nextPrintTime to time:seconds + 10. 34 | } 35 | set ship:control:top to max(-1, min(1, 5 * -vdot(ship:facing:topvector, tgtRelVel))). 36 | set ship:control:starboard to max(-1, min(1, 5 * -vdot(ship:facing:starvector, tgtRelVel))). 37 | 38 | local tgtCloseRate to max(0.2, min(2, (tgt:position:mag - p_tgtDist) / 50)). 39 | if tgt:position:mag > 1000 + p_tgtDist { set tgtCloseRate to 5. } 40 | if tgt:position:mag < p_tgtDist { set tgtCloseRate to 0. } 41 | 42 | local actualCloseRate to vdot(tgt:position, tgtRelVel) / tgt:position:mag. 43 | set ship:control:fore to max(-1, min(1, 5 * (tgtCloseRate - actualCloseRate))). 44 | } 45 | 46 | popRCS(). 47 | popSAS(). 48 | } 49 | -------------------------------------------------------------------------------- /safedeorbit.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | { 4 | parameter rcsOnly is false, tgtPeri is 50000. 5 | 6 | runoncepath("lib/libengine"). 7 | runoncepath("lib/libsasrcsstack"). 8 | runoncepath("lib/libwaitforfacing"). 9 | runoncepath("lib/libwarpfor"). 10 | runoncepath("lib/libantenna"). 11 | 12 | pushSAS(false). 13 | pushRCS(true). 14 | 15 | lock steering to lookdirup(retrograde:vector, ship:facing:topvector). 16 | waitForFacing(0.5). 17 | 18 | print "Lowering periapsis". 19 | set ship:control:fore to 1. 20 | if not rcsOnly { 21 | print "Waiting for stable propellant". 22 | waitForStablePropellant(). 23 | print "Increasing throttle". 24 | lock throttle to 1. 25 | print "Stopping ullage". 26 | set ship:control:fore to 0. 27 | } 28 | print "Waiting for periapsis change". 29 | 30 | wait until ship:periapsis < tgtPeri. 31 | 32 | set ship:control:fore to 0. 33 | if not rcsOnly { 34 | unlock throttle. 35 | } 36 | 37 | panels off. 38 | retractAntennae(). 39 | 40 | print "Turning to retrograde". 41 | lock steering to lookdirup(-ship:orbit:velocity:surface, ship:facing:topvector). 42 | waitForFacing(0.5). 43 | print "Decoupling everything below heat shield". 44 | stage. // Decouple engine/tanks/etc. We might not have steering control after this 45 | wait 1. 46 | set kuniverse:activevessel to ship. 47 | 48 | print "Waiting to enter atmosphere". 49 | warpUntilZero({ return ship:altitude - body:atm:height. }). 50 | 51 | print "Waiting for 5 km altitude". 52 | wait until ship:altitude < 5000. 53 | unlock steering. 54 | print "Decoupling heat shield". 55 | stage. // Decouple heat shield 56 | set kuniverse:activevessel to ship. 57 | wait until stage:ready. 58 | print "Deploying parachutes". 59 | chutes on. 60 | stage. // Deploy parachutes 61 | 62 | popSAS(). 63 | popRCS(). 64 | 65 | wait until status = "Landed" or status = "Splashed". 66 | } 67 | -------------------------------------------------------------------------------- /lib/libwarpfor.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function warpFor1 { 4 | parameter p_dt. 5 | 6 | local endTime to time:seconds + p_dt. 7 | warpUntilZero({ return endTime - time:seconds. }). 8 | } 9 | 10 | function warpFor { 11 | parameter p_dt. 12 | warpFor1(p_dt). 13 | } 14 | 15 | function warpUntilZero { 16 | parameter callback. 17 | 18 | local curVal to callback(). 19 | if (curVal < 0) { 20 | print "Skipping warp. Current value: " + curVal. 21 | return. 22 | } 23 | 24 | print "Warping. Current value: " + curVal. 25 | 26 | local warpSpeeds to kuniverse:timewarp:railsratelist. 27 | local i to 0. 28 | local lastTime to time:seconds. 29 | local lastVal to callback(). 30 | 31 | wait 1. 32 | 33 | local curVal to lastVal. 34 | local curTime to time:seconds. 35 | local lock dt to curTime - lastTime. 36 | 37 | function getRate { 38 | parameter curVal, lastVal, dt. 39 | if curVal = lastVal { 40 | return -999999999. 41 | } else { 42 | return (curVal - lastVal) / dt. 43 | } 44 | } 45 | 46 | local lock rate to getRate(curVal, lastVal, dt). 47 | local lock timeToZero to -curVal / rate. 48 | 49 | until curVal <= 0 { 50 | if timeToZero < warpSpeeds[i] * 1.5 { 51 | // if it takes less than 1.5 seconds to reach zero, slow down 52 | set i to max(0, i - 1). 53 | } else if i < warpSpeeds:length - 1 and timeToZero / 1.5 > warpSpeeds[i+1] { 54 | // if the next highest speed takes more than 1.5 seconds to reach zero, speed up 55 | set i to min(warpSpeeds:length, i + 1). 56 | } 57 | 58 | if warp <> i { 59 | set warp to i. 60 | set warpmode to "RAILS". 61 | } 62 | 63 | wait min(timeToZero, 0.5). 64 | 65 | set lastVal to curVal. 66 | set curVal to callback(). 67 | set lastTime to curTime. 68 | set curTime to time:seconds. 69 | } 70 | 71 | print "ending warp". 72 | set warp to 0. // just in case 73 | } 74 | -------------------------------------------------------------------------------- /lib/libwaitforfacing.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libfacediff"). 4 | 5 | function waitForFacing { 6 | parameter tgtDiff, includeRoll is true, stabilizeAtEnd is true, stabilizeDuring is true, tgtRate is 99999. 7 | 8 | if kuniverse:timewarp:mode = "RAILS" { 9 | // Just in case something accidentally left us in rails warp, 10 | // set it to zero so we can turn 11 | set kuniverse:timewarp:warp to 0. 12 | } 13 | 14 | local nextPrintTime to time:seconds. 15 | 16 | local minDiff to faceDiff(includeRoll). 17 | local lastDiff to minDiff. 18 | local lastTime to time:seconds. 19 | 20 | wait 0.05. 21 | 22 | local curDiff to faceDiff(includeRoll). 23 | local curTime to time:seconds. 24 | local lock diffRate to (curDiff - lastDiff) / (curTime - lastTime). 25 | local lock done to curDiff < tgtDiff and abs(diffRate) < tgtRate. 26 | until done { 27 | if time:seconds >= nextPrintTime { 28 | print "Waiting for facing. Current offset: " + curDiff + " Current rate: " + diffRate. 29 | set nextPrintTime to time:seconds + 10. 30 | } 31 | 32 | if stabilizeDuring { 33 | if curDiff < minDiff { 34 | set minDiff to curDiff. 35 | } else if curDiff > 1.05 * minDiff { 36 | set kuniverse:timewarp:mode to "RAILS". 37 | set kuniverse:timewarp:warp to 1. 38 | wait until kuniverse:timewarp:issettled. 39 | set kuniverse:timewarp:warp to 0. 40 | set minDiff to faceDiff(includeRoll). 41 | } 42 | } 43 | 44 | wait 0.05. 45 | set lastDiff to curDiff. 46 | set lastTime to curTime. 47 | set curDiff to faceDiff(includeRoll). 48 | set curTime to time:seconds. 49 | } 50 | 51 | if stabilizeAtEnd { 52 | set kuniverse:timewarp:mode to "RAILS". 53 | set kuniverse:timewarp:warp to 1. 54 | wait until kuniverse:timewarp:issettled. 55 | set kuniverse:timewarp:warp to 0. 56 | wait until kuniverse:timewarp:issettled. 57 | } 58 | 59 | // Force some game ticks so flight controls work immediately after returning. 60 | // For some reason, a single tick isn't enough. 61 | wait 1. 62 | } 63 | -------------------------------------------------------------------------------- /lib/libphaseangle.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libabsdiffmod"). 4 | runoncepath("lib/libsecantmethod"). 5 | 6 | function phaseAngle { 7 | parameter p_atTime. 8 | parameter p_tgt. 9 | 10 | local bcShipPos to positionat(ship, p_atTime) - body:position. 11 | local bcTgtPos to positionat(p_tgt, p_atTime) - body:position. 12 | 13 | // local bcShipPosProj to vxcl(north:vector, bcShipPos). 14 | // local bcTgtPosProj to vxcl(north:vector, bcTgtPos). 15 | local bcShipPosProj to bcShipPos. 16 | local bcTgtPosProj to bcTgtPos. 17 | 18 | local ang to vang(bcShipPosProj, bcTgtPosProj). 19 | if vdot(vcrs(bcShipPosProj, bcTgtPosProj), north:vector) > 0 { 20 | set ang to 360 - ang. 21 | } 22 | 23 | return ang. 24 | } 25 | 26 | function timeToPhaseAngle { 27 | // Probably won't work well for targets with similar orbital periods. 28 | parameter p_tgtAngle. 29 | parameter p_tgt. 30 | 31 | local curAngle to phaseAngle(time:seconds, p_tgt). 32 | 33 | local deltaAngle to curAngle - p_tgtAngle. 34 | if deltaAngle < 0 { 35 | set deltaAngle to 360 + deltaAngle. 36 | } 37 | 38 | function cb { 39 | parameter p_deltaTimeGuess. 40 | 41 | local timeGuess to time:seconds + p_deltaTimeGuess. 42 | local guessSCShipPos to positionat(ship, timeGuess). 43 | local guessBCShipPos to guessSCShipPos - body:position. 44 | local guessAngle to phaseAngle(timeGuess, p_tgt). 45 | return absDiffMod(guessAngle, p_tgtAngle, 360). 46 | } 47 | 48 | local synodicPeriod to 1 / ((1 / ship:orbit:period) - (1 / p_tgt:orbit:period)). 49 | local guess1 to deltaAngle / (360 / synodicPeriod). 50 | local guess2 to guess1 + 100. 51 | local result to secantMethod(cb@, guess1, guess2, 100, 1e-4). 52 | 53 | return result. 54 | } 55 | 56 | //local tgtAngle to 3.97. 57 | //local tt to timeToPhaseAngle(tgtAngle, vessel("JSS")). 58 | //add node(time:seconds + tt, 0, 0, 58.27). 59 | 60 | //print "Current Phase Angle: " + phaseAngle(time:seconds, vessel("JSS")). 61 | //print "Target Phase Angle: " + tgtAngle. 62 | //print "Target time: " + (time:seconds + tt). 63 | //print "Time to Target: " + tt. 64 | //print "Calculated Phase Angle: " + phaseAngle(time:seconds + tt, vessel("JSS")). 65 | -------------------------------------------------------------------------------- /calc.planner.transfer.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter bod, initAlt, initInc, tgtAlt, tgtInc. 4 | 5 | run once calc.funs.ks. 6 | 7 | local initRadius to bod:radius + initAlt. 8 | local tgtRadius to bod:radius + tgtAlt. 9 | 10 | local initVel to sqrt(bod:mu / initRadius). 11 | local tgtVel to sqrt(bod:mu / tgtRadius). 12 | 13 | local xferSMA to (initRadius + tgtRadius) / 2. 14 | local xferBurnVel to sqrt(bod:mu * (2 / initRadius - 1 / xferSMA)). 15 | local xferBurnDeltaV to abs(xferBurnVel - initVel). 16 | 17 | local xferTime to constant:pi * xferSMA / sqrt(bod:mu / xferSMA). 18 | 19 | local deltaInc to tgtInc - initInc. 20 | 21 | // Zero inclination change case 22 | local circBurnInitialVel to sqrt(bod:mu * (2 / tgtRadius - 1 / xferSMA)). 23 | local circBurnDeltaV to abs(tgtVel - circBurnInitialVel). 24 | local circBurnProgradeDeltaV to tgtVel - circBurnInitialVel. 25 | local circBurnNormalDeltaV to 0. 26 | 27 | if abs(deltaInc) > 1e-5 { 28 | // Nonzero inclination change case 29 | set circBurnDeltaV to solveCosineRule(circBurnInitialVel, tgtVel, deltaInc). 30 | local phi to arccos((circBurnInitialVel^2 + circBurnDeltaV^2 - tgtVel^2) / (2 * circBurnInitialVel * circBurnDeltaV)). 31 | local alpha to 180 - phi. 32 | set circBurnProgradeDeltaV to circBurnDeltaV * cos(alpha). 33 | set circBurnNormalDeltaV to circBurnDeltaV * sin(alpha). 34 | } 35 | 36 | local totalDetlaV to xferBurnDeltaV + circBurnDeltaV. 37 | local totalDeltaVNoInc to solveCosineRule(circBurnInitialVel, tgtVel, 0) + xferBurnDeltaV. 38 | local inclinationDeltaV to totalDetlaV - totalDeltaVNoInc. 39 | 40 | print "Initial Orbital Velocity: " + round(initVel, 2) + " m/s". 41 | print "Transfer Burn Delta-V: " + round(xferBurnDeltaV, 2) + " m/s". 42 | print "Transfer Time: " + formatYdhms(s2ydhms(xferTime)) + " (" + round(xferTime, 2) + " s)". 43 | print "Inclination Change: " + round(deltaInc, 2) + " deg". 44 | print "Circularization Burn Delta-V: " + round(circBurnDeltaV, 2) + " m/s". 45 | print "Circularization Burn Prograde: " + round(circBurnProgradeDeltaV, 2) + " m/s". 46 | print "Circularization Burn Normal: " + round(circBurnNormalDeltaV, 2) + " m/s". 47 | print "Target Orbital Velocity: " + round(tgtVel, 2) + " m/s". 48 | print "Total Delta-V: " + round(totalDetlaV, 2) + " m/s". 49 | print "Delta-V Lost Due to Inclination Change: " + round(inclinationDeltaV, 2) + " m/s". 50 | -------------------------------------------------------------------------------- /circinc.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | parameter tgtInc, settleFuel. 4 | 5 | run once funs. 6 | 7 | function calcRealAdjAmtForApo { 8 | parameter n. 9 | parameter adjAmt. 10 | 11 | local curEcc to orbitat(ship, time:seconds + n:eta + 1):eccentricity. 12 | 13 | set n:prograde to n:prograde + adjAmt. 14 | local newEcc to orbitat(ship, time:seconds + n:eta + 1):eccentricity. 15 | set n:prograde to n:prograde - adjAmt. 16 | 17 | if (newEcc < curEcc) { 18 | return adjAmt. 19 | } else { 20 | return -1 * adjAmt. 21 | } 22 | } 23 | 24 | function adjustApo { 25 | parameter n. 26 | parameter adjAmt. 27 | 28 | local realAdjAmt to calcRealAdjAmtForApo(n, adjAmt). 29 | 30 | local curEcc to orbitat(ship, time:seconds + n:eta + 1):eccentricity. 31 | local lastEcc to curEcc. 32 | until (lastEcc < curEcc) { 33 | set lastEcc to curEcc. 34 | set n:prograde to n:prograde + realAdjAmt. 35 | set curEcc to orbitat(ship, time:seconds + n:eta + 1):eccentricity. 36 | } 37 | } 38 | 39 | function calcRealAdjAmtForInc { 40 | parameter tgtInc. 41 | parameter n. 42 | parameter adjAmt. 43 | 44 | local curInc to orbitat(ship, time:seconds + n:eta + 1):inclination. 45 | local curDiff to absDiffMod(tgtInc, curInc, 360). 46 | 47 | set n:normal to n:normal + adjAmt. 48 | local newInc to orbitat(ship, time:seconds + n:eta + 1):inclination. 49 | local newDiff to absDiffMod(tgtInc, newInc, 360). 50 | set n:normal to n:normal - adjAmt. 51 | 52 | if (newDiff < curDiff) { 53 | return adjAmt. 54 | } else { 55 | return -1 * adjAmt. 56 | } 57 | } 58 | 59 | function adjustInc { 60 | parameter tgtInc. 61 | parameter n. 62 | parameter adjAmt. 63 | 64 | local realAdjAmt to calcRealAdjAmtForInc(tgtInc, n, adjAmt). 65 | 66 | local curDiff to absDiffMod(orbitat(ship, time:seconds + n:eta + 1):inclination, tgtInc, 360). 67 | local lastDiff to curDiff. 68 | until (lastDiff < curDiff) { 69 | set lastDiff to curDiff. 70 | set n:normal to n:normal + realAdjAmt. 71 | set curDiff to absDiffMod(orbitat(ship, time:seconds + n:eta + 1):inclination, tgtInc, 360). 72 | } 73 | } 74 | 75 | local n to node(time:seconds + eta:apoapsis, 0, 0, 0). 76 | add n. 77 | 78 | local i to 1. 79 | until i = 20 { 80 | local adjAmt to (20 - i) / 4. 81 | adjustApo(n, adjAmt). 82 | adjustInc(tgtInc, n, adjAmt). 83 | set i to i + 1. 84 | } 85 | 86 | run execNode(settleFuel). 87 | -------------------------------------------------------------------------------- /missions/meosat_2.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | run once circincatapo.funs.ks. 4 | run once execnode.funs.ks. 5 | run once funs.ks. 6 | 7 | function partialCircInc { 8 | local n to nodeToCircIncAtApo(0). 9 | add n. 10 | 11 | local startLatSign to sign(ship:geoposition:lat). 12 | local execNodeState to list(). 13 | execNodeStart(false, execNodeState). 14 | until sign(ship:geoposition:lat) <> startLatSign { 15 | if not execNodeContinue(execNodeState) { 16 | break. 17 | } 18 | } 19 | 20 | execNodeStop(execNodeState). 21 | 22 | remove n. 23 | } 24 | 25 | function ensurePeriodDifference { 26 | parameter tgt, tgtPDiff. 27 | 28 | if abs(tgt:orbit:period - ship:orbit:period) < tgtPDiff { 29 | warpFor1(eta:periapsis - 30). 30 | 31 | lock steering to lookdirup(ship:orbit:velocity:orbit, ship:facing:topvector). 32 | if (ship:orbit:period < tgt:orbit:period) { 33 | lock steering to lookdirup(-ship:orbit:velocity:orbit, ship:facing:topvector). 34 | } 35 | wait until faceDiff() < 0.5. 36 | 37 | set ship:control:fore to 1. 38 | wait until abs(tgt:orbit:period - ship:orbit:period) > tgtPDiff. 39 | set ship:control:fore to 0. 40 | unlock steering. 41 | } 42 | } 43 | 44 | function matchPeriod { 45 | parameter tgt. 46 | 47 | warpFor1(eta:periapsis - 30). 48 | lock steering to lookdirup(ship:orbit:velocity:orbit, ship:facing:topvector). 49 | if (ship:orbit:period > tgt:orbit:period) { 50 | lock steering to lookdirup(-ship:orbit:velocity:orbit, ship:facing:topvector). 51 | } 52 | wait until faceDiff() < 0.5. 53 | 54 | local lastDiff to abs(tgt:orbit:period - ship:orbit:period). 55 | set ship:control:fore to 0.25. 56 | wait 1. 57 | until abs(tgt:orbit:period - ship:orbit:period) > lastDiff { 58 | set lastDiff to abs(tgt:orbit:period - ship:orbit:period). 59 | wait 0.05. 60 | } 61 | 62 | set ship:control:fore to 0. 63 | unlock steering. 64 | } 65 | 66 | stage. // dummy decoupler stage 67 | wait until stage:ready. 68 | stage. // activate engines 69 | rcs on. 70 | toggle ag1. // turn RCS thrusters back on 71 | 72 | // Do this a few times so it is more accurate 73 | partialCircInc(). 74 | partialCircInc(). 75 | 76 | // And finally finish it 77 | run circincatapo(0, false). 78 | 79 | local sat1 to vessel("MEO Comsat 1"). 80 | ensurePeriodDifference(sat1, 60). 81 | set warp to 4. 82 | wait until abs(vang(-body:position, sat1:position - body:position) - 90) < 1. 83 | set warp to 0. 84 | matchPeriod(sat1). 85 | 86 | rcs off. 87 | -------------------------------------------------------------------------------- /missions/meosat_3.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | run once circincatapo.funs.ks. 4 | run once execnode.funs.ks. 5 | run once funs.ks. 6 | 7 | function partialCircInc { 8 | local n to nodeToCircIncAtApo(0). 9 | add n. 10 | 11 | local startLatSign to sign(ship:geoposition:lat). 12 | local execNodeState to list(). 13 | execNodeStart(false, execNodeState). 14 | until sign(ship:geoposition:lat) <> startLatSign { 15 | if not execNodeContinue(execNodeState) { 16 | break. 17 | } 18 | } 19 | 20 | execNodeStop(execNodeState). 21 | 22 | remove n. 23 | } 24 | 25 | function ensurePeriodDifference { 26 | parameter tgt, tgtPDiff. 27 | 28 | if abs(tgt:orbit:period - ship:orbit:period) < tgtPDiff { 29 | warpFor1(eta:periapsis - 30). 30 | 31 | lock steering to lookdirup(ship:orbit:velocity:orbit, ship:facing:topvector). 32 | if (ship:orbit:period < tgt:orbit:period) { 33 | lock steering to lookdirup(-ship:orbit:velocity:orbit, ship:facing:topvector). 34 | } 35 | wait until faceDiff() < 0.5. 36 | 37 | set ship:control:fore to 1. 38 | wait until abs(tgt:orbit:period - ship:orbit:period) > tgtPDiff. 39 | set ship:control:fore to 0. 40 | unlock steering. 41 | } 42 | } 43 | 44 | function matchPeriod { 45 | parameter tgt. 46 | 47 | warpFor1(eta:periapsis - 30). 48 | lock steering to lookdirup(ship:orbit:velocity:orbit, ship:facing:topvector). 49 | if (ship:orbit:period > tgt:orbit:period) { 50 | lock steering to lookdirup(-ship:orbit:velocity:orbit, ship:facing:topvector). 51 | } 52 | wait until faceDiff() < 0.5. 53 | 54 | local lastDiff to abs(tgt:orbit:period - ship:orbit:period). 55 | set ship:control:fore to 0.25. 56 | wait 1. 57 | until abs(tgt:orbit:period - ship:orbit:period) > lastDiff { 58 | set lastDiff to abs(tgt:orbit:period - ship:orbit:period). 59 | wait 0.05. 60 | } 61 | 62 | set ship:control:fore to 0. 63 | unlock steering. 64 | } 65 | 66 | stage. // dummy decoupler stage 67 | wait until stage:ready. 68 | stage. // activate engines 69 | rcs on. 70 | toggle ag1. // turn RCS thrusters back on 71 | 72 | // Do this a few times so it is more accurate 73 | partialCircInc(). 74 | partialCircInc(). 75 | 76 | // And finally finish it 77 | run circincatapo(0, false). 78 | 79 | local sat1 to vessel("MEO Comsat 1"). 80 | ensurePeriodDifference(sat1, 60). 81 | set warp to 4. 82 | wait until abs(vang(-body:position, sat1:position - body:position) - 180) < 1. 83 | set warp to 0. 84 | matchPeriod(sat1). 85 | 86 | rcs off. 87 | -------------------------------------------------------------------------------- /missions/meosat_4.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | run once circincatapo.funs.ks. 4 | run once execnode.funs.ks. 5 | run once funs.ks. 6 | 7 | function partialCircInc { 8 | local n to nodeToCircIncAtApo(0). 9 | add n. 10 | 11 | local startLatSign to sign(ship:geoposition:lat). 12 | local execNodeState to list(). 13 | execNodeStart(false, execNodeState). 14 | until sign(ship:geoposition:lat) <> startLatSign { 15 | if not execNodeContinue(execNodeState) { 16 | break. 17 | } 18 | } 19 | 20 | execNodeStop(execNodeState). 21 | 22 | remove n. 23 | } 24 | 25 | function ensurePeriodDifference { 26 | parameter tgt, tgtPDiff. 27 | 28 | if abs(tgt:orbit:period - ship:orbit:period) < tgtPDiff { 29 | warpFor1(eta:periapsis - 30). 30 | 31 | lock steering to lookdirup(ship:orbit:velocity:orbit, ship:facing:topvector). 32 | if (ship:orbit:period < tgt:orbit:period) { 33 | lock steering to lookdirup(-ship:orbit:velocity:orbit, ship:facing:topvector). 34 | } 35 | wait until faceDiff() < 0.5. 36 | 37 | set ship:control:fore to 1. 38 | wait until abs(tgt:orbit:period - ship:orbit:period) > tgtPDiff. 39 | set ship:control:fore to 0. 40 | unlock steering. 41 | } 42 | } 43 | 44 | function matchPeriod { 45 | parameter tgt. 46 | 47 | warpFor1(eta:periapsis - 30). 48 | lock steering to lookdirup(ship:orbit:velocity:orbit, ship:facing:topvector). 49 | if (ship:orbit:period > tgt:orbit:period) { 50 | lock steering to lookdirup(-ship:orbit:velocity:orbit, ship:facing:topvector). 51 | } 52 | wait until faceDiff() < 0.5. 53 | 54 | local lastDiff to abs(tgt:orbit:period - ship:orbit:period). 55 | set ship:control:fore to 0.25. 56 | wait 1. 57 | until abs(tgt:orbit:period - ship:orbit:period) > lastDiff { 58 | set lastDiff to abs(tgt:orbit:period - ship:orbit:period). 59 | wait 0.05. 60 | } 61 | 62 | set ship:control:fore to 0. 63 | unlock steering. 64 | } 65 | 66 | stage. // dummy decoupler stage 67 | wait until stage:ready. 68 | stage. // activate engines 69 | rcs on. 70 | toggle ag1. // turn RCS thrusters back on 71 | 72 | // Do this a few times so it is more accurate 73 | partialCircInc(). 74 | partialCircInc(). 75 | 76 | // And finally finish it 77 | run circincatapo(0, false). 78 | 79 | local sat2 to vessel("MEO Comsat 2"). 80 | ensurePeriodDifference(sat2, 60). 81 | set warp to 4. 82 | wait until abs(vang(-body:position, sat2:position - body:position) - 180) < 1. 83 | set warp to 0. 84 | matchPeriod(sat2). 85 | 86 | rcs off. 87 | -------------------------------------------------------------------------------- /lib/libmainframe.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function mainframeClean { 4 | deletepath("libmainframe_request.txt"). 5 | deletepath("libmainframe_request_done.txt"). 6 | deletepath("libmainframe_result.json"). 7 | deletepath("libmainframe_result_done.txt"). 8 | } 9 | 10 | function mainframeDo { 11 | log "" to "libmainframe_request_done.txt". 12 | 13 | // wait for response 14 | wait until archive:files:haskey("libmainframe_result_done.txt"). 15 | deletepath("libmainframe_request_done.txt"). 16 | wait 0.25. // wait for the mainframe to close the file before we delete it 17 | deletepath("libmainframe_result_done.txt"). 18 | 19 | local resultLex to readjson("libmainframe_result.json"). 20 | mainframeClean(). 21 | 22 | return resultLex. 23 | } 24 | 25 | function mainframeSquare { 26 | parameter p_x. 27 | 28 | mainframeClean(). 29 | log "square" to libmainframe_request.txt. 30 | log p_x to libmainframe_request.txt. 31 | return mainframeDo()["xSquared"]. 32 | } 33 | 34 | function mainframeLambertOptimizeVecs { 35 | parameter p_mu. 36 | parameter p_r1. 37 | parameter p_v1. 38 | parameter p_r2. 39 | parameter p_v2. 40 | parameter p_tMin. 41 | parameter p_tMax. 42 | parameter p_tStep. 43 | parameter p_dtMin. 44 | parameter p_dtMax. 45 | parameter p_dtStep. 46 | parameter p_allowLob. 47 | parameter p_optArrival. 48 | 49 | mainframeClean(). 50 | 51 | log "lambertoptimize" to libmainframe_request.txt. 52 | log p_mu to libmainframe_request.txt. 53 | 54 | log p_r1:x to libmainframe_request.txt. 55 | log p_r1:y to libmainframe_request.txt. 56 | log p_r1:z to libmainframe_request.txt. 57 | log p_v1:x to libmainframe_request.txt. 58 | log p_v1:y to libmainframe_request.txt. 59 | log p_v1:z to libmainframe_request.txt. 60 | 61 | log p_r2:x to libmainframe_request.txt. 62 | log p_r2:y to libmainframe_request.txt. 63 | log p_r2:z to libmainframe_request.txt. 64 | log p_v2:x to libmainframe_request.txt. 65 | log p_v2:y to libmainframe_request.txt. 66 | log p_v2:z to libmainframe_request.txt. 67 | 68 | log p_tMin to libmainframe_request.txt. 69 | log p_tMax to libmainframe_request.txt. 70 | log p_tStep to libmainframe_request.txt. 71 | 72 | log p_dtMin to libmainframe_request.txt. 73 | log p_dtMax to libmainframe_request.txt. 74 | log p_dtStep to libmainframe_request.txt. 75 | 76 | log p_allowLob to libmainframe_request.txt. 77 | log p_optArrival to libmainframe_request.txt. 78 | 79 | return mainframeDo(). 80 | } 81 | 82 | //print mainframeSquare(3). 83 | //print mainframeSquare(6). 84 | -------------------------------------------------------------------------------- /foodland.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | //parameter myMaxThrust. 4 | local myMaxThrust to 180.16. 5 | when (trueAlt() < 15000) then set warp to 0. 6 | 7 | function trueAlt { 8 | if abs(altitude - alt:radar) / altitude > 0.05 { 9 | return alt:radar. 10 | } else { 11 | return altitude - ship:geoposition:terrainheight. 12 | } 13 | } 14 | 15 | sas off. 16 | gear off. 17 | 18 | wait until trueAlt() < 3000. 19 | 20 | clearscreen. 21 | print "trueAlt :" at (2, 11). 22 | print "sVel :" at (2, 12). 23 | print "tgtVel :" at (2, 13). 24 | print "tgtAccel :" at (2, 14). 25 | print "gHere :" at (2, 15). 26 | print "throt :" at (2, 16). 27 | 28 | local lock tgtVel to -trueAlt() / 20 - 2. 29 | 30 | local lock maxEngineAccel to myMaxThrust / mass. 31 | 32 | local gHere to body:mu / (body:radius ^ 2). 33 | 34 | lock steering to lookdirup(-ship:obt:velocity:surface, ship:facing:topvector). 35 | local throt to 0. 36 | lock throttle to throt. 37 | 38 | when (throt > 0.005) then stage. 39 | when (trueAlt() < 500) then gear on. 40 | when (ship:obt:velocity:surface:mag < 25) then ag2 on. 41 | when (ship:obt:velocity:surface:mag < 5) then lock steering to lookdirup(up:vector, ship:facing:topvector). 42 | 43 | local steerArrow to vecdraw(). 44 | set steerArrow:vec to steering:vector. 45 | set steerArrow:show to true. 46 | set steerArrow:scale to 10. 47 | set steerArrow:color to rgb(1,0,0). 48 | 49 | local steerTopArrow to vecdraw(). 50 | set steerTopArrow:vec to steering:topvector. 51 | set steerTopArrow:show to true. 52 | set steerTopArrow:scale to 10. 53 | set steerTopArrow:color to rgb(1,1,1). 54 | 55 | local retroArrow to vecdraw(). 56 | set retroArrow:vec to -ship:obt:velocity:surface. 57 | set retroArrow:show to true. 58 | set retroArrow:scale to 10. 59 | set retroArrow:color to rgb(0,0,1). 60 | 61 | local topArrow to vecdraw(). 62 | set topArrow:vec to ship:facing:topvector. 63 | set topArrow:show to true. 64 | set topArrow:scale to 10. 65 | set topArrow:color to rgb(0,1,0). 66 | 67 | until status = "Landed" { 68 | set steerArrow:vec to steering:vector. 69 | set steerTopArrow:vec to steering:topvector. 70 | set retroArrow:vec to -ship:obt:velocity:surface. 71 | set topArrow:vec to ship:facing:topvector. 72 | 73 | local tgtAccel to tgtVel + ship:obt:velocity:surface:mag. 74 | set throt to (tgtAccel + gHere) / maxEngineAccel. 75 | print trueAlt() at (15, 11). 76 | print ship:obt:velocity:surface:mag at (15, 12). 77 | print tgtVel at (15, 13). 78 | print tgtAccel at (15, 14). 79 | print gHere at (15, 15). 80 | print throt at (15, 16). 81 | wait 0.05. 82 | } 83 | 84 | set throt to 1. 85 | stage. 86 | -------------------------------------------------------------------------------- /lib/libangletoprograde.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libabsdiffmod"). 4 | runoncepath("lib/libsecantmethod"). 5 | 6 | function angleToPrograde { 7 | // This is slightly inaccurate due to the fact that it doesn't account for the 8 | // body's motion around its body (e.g. for ship orbiting earth: earth's motion 9 | // around sun). But this is only a problem when 10 | // p_bcShipPos != ship:position - body:position. 11 | 12 | // bc = body-centric (for ship orbiting earth: earth-centric) 13 | // bb = body's body (for ship orbiting earth: sun) 14 | // b = body (for ship orbiting earth: earth) 15 | 16 | parameter p_body. 17 | parameter p_bcShipPos. 18 | 19 | local bcBBPos to p_body:body:position - p_body:position. 20 | local bcBPro to p_body:orbit:velocity:orbit. 21 | 22 | local bObtNrml to vcrs(bcBBPos, bcBPro). 23 | local bcShipProjOntoPlane to vxcl(bObtNrml, p_bcShipPos). 24 | 25 | local angToPro to vang(bcShipProjOntoPlane, bcBPro). 26 | 27 | if vdot(vcrs(bcShipProjOntoPlane, bcBPro), bObtNrml) > 0 { 28 | set angToPro to 360 - angToPro. 29 | } 30 | 31 | return angToPro. 32 | } 33 | 34 | function timeToAngleToPrograde { 35 | // This is slightly inaccurate due to the fact that it doesn't account for the 36 | // body's motion around its body (e.g. for ship orbiting earth: earth's motion 37 | // around sun). 38 | parameter p_tgtAngle. 39 | 40 | local curAngle to angleToPrograde(body, ship:position - body:position). 41 | 42 | function cb { 43 | parameter p_deltaTimeGuess. 44 | local guessSCShipPos to positionat(ship, time:seconds + p_deltaTimeGuess). 45 | local guessBCShipPos to guessSCShipPos - body:position. 46 | local guessAngle to angleToPrograde(body, guessBCShipPos). 47 | return absDiffMod(guessAngle, p_tgtAngle, 360). 48 | } 49 | 50 | local deltaAngle to curAngle - p_tgtAngle. 51 | if deltaAngle < 0 { 52 | set deltaAngle to deltaAngle + 360. 53 | } 54 | 55 | local guess1 to deltaAngle / (360 / ship:obt:period). 56 | local guess2 to guess1 + 100. 57 | local result to secantMethod(cb@, guess1, guess2, 10, 1e-2). 58 | 59 | return result. 60 | } 61 | 62 | //local tgtAngle to 270. 63 | //local ttatp to timeToAngleToPrograde(tgtAngle). 64 | ////add node(time:seconds + ttatp, 0, 0, 0). 65 | // 66 | //local scShipPosAt to positionat(ship, time:seconds + ttatp). 67 | //local bcShipPosAt to scShipPosAt - body:position. 68 | // 69 | //print "Current ATP: " + angleToPrograde(body, ship:position - body:position). 70 | //print "Target ATP: " + tgtAngle. 71 | //print "Time to Target: " + ttatp. 72 | //print "Position at Target Time: " + scShipPosAt. 73 | //print "Calculated ATP: " + angleToPrograde(body, bcShipPosAt). 74 | -------------------------------------------------------------------------------- /lib/libbrentsmethod.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libsign"). 4 | 5 | local eps to 1e-10. 6 | 7 | function brentsMethod { 8 | // Converted from https://alexmoon.github.io/ksp/javascripts/roots.js 9 | parameter p_f. 10 | parameter p_a. 11 | parameter p_b. 12 | parameter p_maxIter. 13 | parameter p_relAcc. 14 | 15 | // print "brent => p_a = " + p_a. 16 | // print "brent => p_b = " + p_b. 17 | 18 | local a to p_a. 19 | local b to p_b. 20 | local fa to p_f(a). 21 | local fb to p_f(b). 22 | local c to a. 23 | local fc to fa. 24 | local d to b - a. 25 | local e to d. 26 | local relAcc to p_relAcc + 0.5 * eps. 27 | 28 | // print "brent => fa = " + fa. 29 | // print "brent => fb = " + fb. 30 | 31 | if sign(fa) = sign(fb) { 32 | return list(false, 0). 33 | } 34 | 35 | local i to 0. 36 | until false { 37 | if abs(fc) < abs(fb) { 38 | set a to b. 39 | set b to c. 40 | set c to a. 41 | set fa to fb. 42 | set fb to fc. 43 | set fc to fa. 44 | } 45 | 46 | local tol to relAcc * abs(b). 47 | local m to 0.5 * (c - b). 48 | 49 | if abs(fb) < eps or abs(m) <= tol { 50 | return list(true, b). 51 | } 52 | 53 | if i > p_maxIter { 54 | return list(false, 1). 55 | } 56 | 57 | if abs(e) < tol or abs(fa) <= abs(fb) { 58 | set d to m. 59 | set e to m. 60 | } else { 61 | local s to fb / fa. 62 | 63 | local p to 2 * m * s. 64 | local q to 1 - s. 65 | if a <> c { 66 | set q to fa / fc. 67 | local r to fb / fc. 68 | set p to s * (2 * m * q * (q - r) - (b - a) * (r - 1)). 69 | set q to (q - 1) * (r - 1) * (s - 1). 70 | } 71 | 72 | if p > 0 { 73 | set q to -q. 74 | } else { 75 | set p to -p. 76 | } 77 | 78 | if 2 * p < min(3 * m * q - abs(tol * q), abs(e * q)) { 79 | set e to d. 80 | set d to p / q. 81 | } else { 82 | set d to m. 83 | set e to m. 84 | } 85 | } 86 | 87 | set a to b. 88 | set fa to fb. 89 | if abs(d) > tol { 90 | set b to b + d. 91 | } else { 92 | local v to -tol. 93 | if m > 0 { set v to tol. } 94 | set b to b + v. 95 | } 96 | 97 | set fb to p_f(b). 98 | if (fb < 0 and fc < 0) or (fb > 0 and fc > 0) { 99 | set c to a. 100 | set fc to fa. 101 | set d to b - a. 102 | set e to b - a. 103 | } 104 | 105 | set i to i + 1. 106 | } 107 | } 108 | 109 | // function testFun { 110 | // parameter p_x. 111 | // return (p_x + 3) * (p_x - 1)^2. 112 | // } 113 | // 114 | // local x to brentsMethod(testFun@, -4, 4/3, 20, 1e-4). 115 | // print x. 116 | -------------------------------------------------------------------------------- /mainframe/src/main/java/hall/john/ksp/mainframe/orbit/Orbit.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe.orbit; 2 | 3 | import org.apache.commons.math3.util.MathUtils; 4 | 5 | import hall.john.ksp.mainframe.Body; 6 | 7 | public class Orbit { 8 | protected Body _body; 9 | protected double _a; 10 | protected double _e; 11 | protected double _i; // radians 12 | protected double _lan; // radians 13 | protected double _aop; // radians 14 | 15 | public Orbit(Body body, double a, double e, double i, double lan, double aop) { 16 | _body = body; 17 | _a = a; 18 | _e = e; 19 | _i = i; 20 | _lan = lan; 21 | _aop = aop; 22 | } 23 | 24 | public Body getBody() { 25 | return _body; 26 | } 27 | 28 | public double getSemiMajorAxis() { 29 | return _a; 30 | } 31 | 32 | public double getSMA() { 33 | return _a; 34 | } 35 | 36 | public double getEccentricity() { 37 | return _e; 38 | } 39 | 40 | public double getApoapsis() { 41 | return _a * (1 + _e); 42 | } 43 | 44 | public double getPeriapsis() { 45 | return _a * (1 - _e); 46 | } 47 | 48 | public double getInclination() { 49 | return _i; 50 | } 51 | 52 | public double getLongitudeOfAscendingNode() { 53 | return _lan; 54 | } 55 | 56 | public double getLAN() { 57 | return _lan; 58 | } 59 | 60 | public double getArgumentOfPeriapsis() { 61 | return _aop; 62 | } 63 | 64 | public double getAOP() { 65 | return _aop; 66 | } 67 | 68 | public double getPeriod() { 69 | return MathUtils.TWO_PI * Math.sqrt((_a * _a * _a) / _body.getMu()); 70 | } 71 | 72 | public double getMeanAngularMotion() { 73 | return MathUtils.TWO_PI / getPeriod(); 74 | } 75 | 76 | public Orbit withApoapsis(double apo) { // including body radius 77 | double per = getPeriapsis(); 78 | double a = (apo + per) / 2; 79 | double e = (apo - per) / (apo + per); 80 | return new Orbit(_body, a, e, _i, _lan, _aop); 81 | } 82 | 83 | public Orbit withPeriapsis(double per) { // including body radius 84 | double apo = getApoapsis(); 85 | double a = (apo + per) / 2; 86 | double e = (apo - per) / (apo + per); 87 | return new Orbit(_body, a, e, _i, _lan, _aop); 88 | } 89 | 90 | public Orbit withSMA(double a) { 91 | return new Orbit(_body, a, _e, _i, _lan, _aop); 92 | } 93 | 94 | public Orbit withEccentricity(double e) { 95 | return new Orbit(_body, _a, e, _i, _lan, _aop); 96 | } 97 | 98 | public Orbit withInclination(double i) { 99 | return new Orbit(_body, _a, _e, i, _lan, _aop); 100 | } 101 | 102 | public Orbit withLAN(double lan) { 103 | return new Orbit(_body, _a, _e, _i, lan, _aop); 104 | } 105 | 106 | public Orbit withAOP(double aop) { 107 | return new Orbit(_body, _a, _e, _i, _lan, aop); 108 | } 109 | 110 | public OrbitAtTime withTrueAnomaly(double ta) { 111 | return new OrbitAtTime(this, ta); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /mainframe/src/main/java/hall/john/ksp/mainframe/numericalmethods/BrentsMethod.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe.numericalmethods; 2 | 3 | import java.util.function.DoubleFunction; 4 | 5 | public class BrentsMethod { 6 | // Converted from https://alexmoon.github.io/ksp/javascripts/root.js 7 | 8 | public static double EPS = 1e-10; 9 | 10 | private DoubleFunction _f; 11 | private double _a; 12 | private double _b; 13 | private int _maxIter; 14 | private double _relAcc; 15 | private Double _result; 16 | private boolean _converged; 17 | 18 | public BrentsMethod(DoubleFunction f, double a, double b, int maxIter, double relAcc) { 19 | _f = f; 20 | _a = a; 21 | _b = b; 22 | _maxIter = maxIter; 23 | _relAcc = relAcc; 24 | } 25 | 26 | private int sign(double x) { 27 | if (x > 0) return 1; 28 | else if (x < 0) return -1; 29 | else return 0; 30 | } 31 | 32 | public void execute() { 33 | double a = _a; 34 | double b = _b; 35 | double fa = _f.apply(a); 36 | double fb = _f.apply(b); 37 | double c = a; 38 | double fc = fa; 39 | double d = b - a; 40 | double e = d; 41 | double relAcc = _relAcc + 0.5 * EPS; 42 | 43 | _converged = false; 44 | _result = null; 45 | 46 | if (sign(fa) == sign(fb)) { 47 | return; 48 | } 49 | 50 | int i = 0; 51 | while (true) { 52 | if (Math.abs(fc) < Math.abs(fb)) { 53 | a = b; 54 | b = c; 55 | c = a; 56 | fa = fb; 57 | fb = fc; 58 | fc = fa; 59 | } 60 | 61 | double tol = relAcc * Math.abs(b); 62 | double m = 0.5 * (c - b); 63 | 64 | if (Math.abs(fb) < EPS || Math.abs(m) <= tol) { 65 | _converged = true; 66 | _result = b; 67 | } 68 | 69 | if (i > _maxIter) { 70 | return; 71 | } 72 | 73 | if (Math.abs(e) < tol || Math.abs(fa) <= Math.abs(fb)) { 74 | d = m; 75 | e = m; 76 | } else { 77 | double s = fb / fa; 78 | double p = 2 * m * s; 79 | double q = 1 - s; 80 | if (a != c) { 81 | q = fa / fc; 82 | double r = fb / fc; 83 | p = s * (2 * m * q * (q - r) - (b - a) * (r - 1)); 84 | q = (q - 1) * (r - 1) * (s - 1); 85 | } 86 | 87 | if (p > 0) { 88 | q = -q; 89 | } else { 90 | p = -p; 91 | } 92 | 93 | if (2 * p < Math.min(3 * m * q - Math.abs(tol * q), Math.abs(e * q))) { 94 | e = d; 95 | d = p / q; 96 | } else { 97 | d = m; 98 | e = m; 99 | } 100 | } 101 | 102 | a = b; 103 | fa = fb; 104 | if (Math.abs(d) > tol) { 105 | b = b + d; 106 | } else { 107 | double v = -tol; 108 | if (m > 0) v = tol; 109 | b = b + v; 110 | } 111 | 112 | fb = _f.apply(b); 113 | if ((fb < 0 && fc < 0) || (fb > 0 && fc > 0)) { 114 | c = a; 115 | fc = fa; 116 | d = b - a; 117 | e = b - a; 118 | } 119 | 120 | i++; 121 | } 122 | } 123 | 124 | public Double getResult() { 125 | return _result; 126 | } 127 | 128 | public boolean getConverged() { 129 | return _converged; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /mainframe/src/main/java/hall/john/ksp/mainframe/orbit/ManeuverNode.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe.orbit; 2 | 3 | import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; 4 | 5 | public class ManeuverNode { 6 | private OrbitAtTime _oat; 7 | private Vector3D _dvVec; 8 | private Double _dv; 9 | private Double _prograde; 10 | private Double _normal; 11 | private Double _radial; 12 | 13 | public ManeuverNode(OrbitAtTime oat, Vector3D dvVec) { 14 | _oat = oat; 15 | _dvVec = dvVec; 16 | } 17 | 18 | public ManeuverNode(OrbitAtTime oat, double prograde, double normal, double radial) { 19 | _oat = oat; 20 | _prograde = prograde; 21 | _normal = normal; 22 | _radial = radial; 23 | } 24 | 25 | public OrbitAtTime getOAT() { 26 | return _oat; 27 | } 28 | 29 | public OrbitAtTime getResultingOAT() { 30 | Vector3D r = _oat.getRadiusVector(); 31 | Vector3D v0 = _oat.getVelocityVector(); 32 | Vector3D v1 = v0.add(getDVVec()); 33 | return new OrbitAtTime(_oat.getBody(), r, v1); 34 | } 35 | 36 | private Vector3D proDir() { 37 | return _oat.getVelocityVector().normalize(); 38 | } 39 | 40 | private Vector3D normDir() { 41 | return _oat.getRadiusVector().crossProduct(_oat.getVelocityVector()).normalize(); 42 | } 43 | 44 | private Vector3D radialDir() { 45 | return _oat.getVelocityVector().crossProduct(normDir()).normalize(); 46 | } 47 | 48 | private void calcDVVec() { 49 | if (_dvVec == null) { 50 | if (_prograde == null || _normal == null || _radial == null) 51 | throw new RuntimeException("need pnr to calc dvVec"); 52 | Vector3D pro = proDir().scalarMultiply(_prograde); 53 | Vector3D norm = normDir().scalarMultiply(_normal); 54 | Vector3D rad = radialDir().scalarMultiply(_radial); 55 | _dvVec = pro.add(norm).add(rad); 56 | } 57 | } 58 | 59 | public Vector3D getDVVec() { 60 | calcDVVec(); 61 | return _dvVec; 62 | } 63 | 64 | public double getDV() { 65 | if (_dv == null) { 66 | calcDVVec(); 67 | _dv = _dvVec.getNorm(); 68 | } 69 | return _dv; 70 | } 71 | 72 | public double getPrograde() { 73 | if (_prograde == null) { 74 | if (_dvVec == null) 75 | throw new RuntimeException("need dvVec to calc prograde"); 76 | _prograde = projectionOnto(_dvVec, proDir()); 77 | } 78 | 79 | return _prograde; 80 | } 81 | 82 | public double getNormal() { 83 | if (_normal == null) { 84 | if (_dvVec == null) 85 | throw new RuntimeException("need dvVec to calc normal"); 86 | _normal = projectionOnto(_dvVec, normDir()); 87 | } 88 | 89 | return _normal; 90 | } 91 | 92 | public double getRadial() { 93 | if (_radial == null) { 94 | if (_dvVec == null) 95 | throw new RuntimeException("need dvVec to calc radial"); 96 | _radial = projectionOnto(_dvVec, radialDir()); 97 | } 98 | 99 | return _radial; 100 | } 101 | 102 | private static double projectionOnto(Vector3D v, Vector3D onto) { 103 | Vector3D on = onto.normalize(); 104 | double vdo = v.dotProduct(on); 105 | double norm = on.scalarMultiply(vdo).getNorm(); 106 | if (vdo > 0) { 107 | return norm; 108 | } else { 109 | return -norm; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /mainframe/src/main/java/hall/john/ksp/mainframe/Main.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe; 2 | 3 | import java.io.FileOutputStream; 4 | import java.io.PrintWriter; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | import java.util.Iterator; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public class Main { 13 | public static String KSP_SCRIPT_DIR = "C:\\Games\\Steam\\steamapps\\common\\Kerbal Space Program 1.1.3\\Ships\\Script"; 14 | public static String KSP_REQUEST_FILE = KSP_SCRIPT_DIR + "\\libmainframe_request.txt"; 15 | public static Path KSP_REQUEST_PATH = Paths.get(KSP_REQUEST_FILE); 16 | public static String KSP_REQUEST_DONE_FILE = KSP_SCRIPT_DIR + "\\libmainframe_request_done.txt"; 17 | public static Path KSP_REQUEST_DONE_PATH = Paths.get(KSP_REQUEST_DONE_FILE); 18 | public static String KSP_RESULT_FILE = KSP_SCRIPT_DIR + "\\libmainframe_result.json"; 19 | public static Path KSP_RESULT_PATH = Paths.get(KSP_RESULT_FILE); 20 | public static String KSP_RESULT_DONE_FILE = KSP_SCRIPT_DIR + "\\libmainframe_result_done.txt"; 21 | public static Path KSP_RESULT_DONE_PATH = Paths.get(KSP_RESULT_DONE_FILE); 22 | 23 | public static void main(String[] args) throws Exception { 24 | while (true) { 25 | System.out.println("\nWaiting for requests"); 26 | 27 | while (!Files.exists(KSP_REQUEST_DONE_PATH)) { 28 | Thread.sleep(250); 29 | } 30 | 31 | List lines = Files.readAllLines(KSP_REQUEST_PATH); 32 | String requestType = lines.get(0); 33 | List requestArgs = lines.subList(1, lines.size()); 34 | 35 | System.out.println("Received request: " + requestType); 36 | 37 | Map result = null; 38 | switch (requestType) { 39 | case "square": 40 | result = Square.Square(requestArgs); 41 | break; 42 | case "lambertoptimize": 43 | result = LambertOptimizer.mainframe(requestArgs); 44 | break; 45 | default: 46 | throw new IllegalArgumentException("unknown request type: " + requestType); 47 | } 48 | 49 | try (PrintWriter pw = new PrintWriter(KSP_RESULT_FILE)) { 50 | writeMap(result, pw, ""); 51 | } 52 | 53 | try (FileOutputStream fos = new FileOutputStream(KSP_RESULT_DONE_FILE)) { 54 | } 55 | 56 | Thread.sleep(1000);// wait for kOS to delete the request done file 57 | } 58 | } 59 | 60 | private static void writeMapEntry(String key, Object value, boolean isLast, PrintWriter pw, String indentation) { 61 | pw.println(indentation + "\"" + key + "\","); 62 | String str = indentation; 63 | 64 | if (value instanceof String) { 65 | str += "\"" + value + "\""; 66 | } else if (value instanceof Number) { 67 | str += value; 68 | } else { 69 | throw new RuntimeException("unknown value type (value = " + value + ")"); 70 | } 71 | 72 | if (!isLast) { 73 | str += ","; 74 | } 75 | 76 | pw.println(str); 77 | } 78 | 79 | private static void writeMap(Map map, PrintWriter pw, String indentation) { 80 | pw.println(indentation + "{"); 81 | pw.println(indentation + " \"entries\": ["); 82 | 83 | Iterator it = map.entrySet().iterator(); 84 | while (it.hasNext()) { 85 | Map.Entry entry = (Map.Entry) it.next(); 86 | writeMapEntry(entry.getKey(), entry.getValue(), !it.hasNext(), pw, indentation + " "); 87 | } 88 | 89 | pw.println(indentation + " ],"); 90 | pw.println(indentation + " \"$type\": \"kOS.Safe.Encapsulation.Lexicon\""); 91 | pw.println(indentation + "}"); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /missions/lunarimpactor.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | run once libfacediff. 4 | run once libwarpfor. 5 | run once libwaitforlan. 6 | run once libmainframe. 7 | run once liblambertoptimize. 8 | run once libburntime. 9 | run once libgenericburn. 10 | run once libtimetotrueanom. 11 | 12 | function burnToIntercept { 13 | parameter lambertResult. 14 | 15 | local t to lambertResult["t"]. 16 | local pro to lambertResult["prograde"]. 17 | local norm to lambertResult["normal"]. 18 | local rad to lambertResult["radial"]. 19 | local dt to lambertResult["dt"]. 20 | 21 | print "t = " + t. 22 | print "pro = " + pro. 23 | print "norm = " + norm. 24 | print "rad = " + rad. 25 | print "dt = " + dt. 26 | 27 | add node(time:seconds + t, rad, norm, pro). 28 | 29 | // Burn for the Moon 30 | local turnTime to 30. 31 | local ullageTime to 0. 32 | local ts to 10. 33 | 34 | local bt to burnTime(nextnode:deltav:mag). 35 | local burnMidTime to time:seconds + nextnode:eta. 36 | 37 | function cb { 38 | local soirad to moon:soiradius. 39 | local moonrad to moon:radius. 40 | if not ship:orbit:hasnextpatch { 41 | // wait until we have an encounter 42 | return 1. 43 | } else { 44 | until ship:orbit:hasnextpatch { wait 0.05. } 45 | local per to ship:orbit:nextpatch:periapsis. 46 | if per > soirad / 2 { 47 | // wait until the patch becomes stable enough to measure 48 | return 1. 49 | } else { 50 | return (per + moonrad) / soirad. 51 | } 52 | } 53 | } 54 | 55 | genericBurn(nextnode:deltav, burnMidTime, bt, turnTime, ullageTime, ts, cb@). 56 | 57 | remove nextnode. 58 | } 59 | 60 | mainframeClean(). 61 | 62 | waitForLAN(0). 63 | 64 | wait 10. 65 | 66 | run launch(5, 55). 67 | 68 | unlock steering. 69 | sas off. 70 | 71 | panels on. 72 | 73 | local modRT to ship:partstitled("Reflectron KR-7")[0]:getModule("ModuleRTAntenna"). 74 | modRT:doEvent("activate"). 75 | modRT:setField("target", earth). 76 | 77 | for p in ship:partstitled("Communotron 16") { 78 | local m to p:getModule("ModuleRTAntenna"). 79 | m:doEvent("activate"). 80 | } 81 | 82 | wait 10. 83 | 84 | stage. 85 | 86 | wait 45. 87 | 88 | local es to list(). 89 | list engines in es. 90 | es[0]:activate(). 91 | 92 | wait 1. 93 | 94 | local tliResult to lambertOptimize(ship, moon, false). 95 | burnToIntercept(tliResult). 96 | 97 | if ship:orbit:nextpatch:periapsis > 0 { 98 | local tt120 to timeToTrueAnom(120). 99 | local tt270 to timeToTrueAnom(270). 100 | local corrResult to lambertOptimizeBounded(ship, moon, tt120, tt120, 0, tt270 - tt120, false). 101 | burnToIntercept(corrResult). 102 | } 103 | 104 | warpFor1(eta:transition - 10). 105 | wait until ship:body = moon. 106 | 107 | if ship:orbit:periapsis > 0 { 108 | lock steering to vcrs(vcrs(ship:orbit:position, ship:orbit:velocity:orbit), ship:orbit:velocity:orbit). 109 | wait until faceDiff() < 0.5. 110 | lock throttle to 1. 111 | wait until ship:orbit:periapsis < -1000. 112 | unlock throttle. 113 | unlock steering. 114 | } 115 | 116 | warpFor1(eta:periapsis - 15 * 60). 117 | set warp to 1. 118 | wait until altitude < 200000. 119 | set warp to 0. 120 | wait 1. 121 | 122 | for p in ship:parts { 123 | for mName in p:modules { 124 | if mName = "ModuleScienceExperiment" { 125 | local m to p:getModule(mName). 126 | m:deploy(). 127 | wait until m:hasData. 128 | m:transmit(). 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /mainframe/src/test/java/hall/john/ksp/mainframe/LambertSolverTest.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; 6 | import org.junit.Test; 7 | 8 | import hall.john.ksp.mainframe.LambertSolver.Solution; 9 | 10 | public class LambertSolverTest { 11 | private static Vector3D rotateVector(Vector3D v, double angleRads) { 12 | double x = v.getX(); 13 | double y = v.getY(); 14 | double z = v.getZ(); 15 | double sin = Math.sin(angleRads); 16 | double cos = Math.cos(angleRads); 17 | return new Vector3D(x * cos - y * sin, x * sin + y * cos, z); 18 | } 19 | 20 | private static void assertSolutionEquals(double v1x, double v1y, double v1z, double v2x, double v2y, double v2z, double a, Solution sol) { 21 | TestUtils.assertRelativelyEquals(v1x, sol.v1.getX(), 1e-4); 22 | TestUtils.assertRelativelyEquals(v1y, sol.v1.getY(), 1e-4); 23 | TestUtils.assertRelativelyEquals(v1z, sol.v1.getZ(), 1e-4); 24 | TestUtils.assertRelativelyEquals(v2x, sol.v2.getX(), 1e-4); 25 | TestUtils.assertRelativelyEquals(v2y, sol.v2.getY(), 1e-4); 26 | TestUtils.assertRelativelyEquals(v2z, sol.v2.getZ(), 1e-4); 27 | TestUtils.assertRelativelyEquals(a, sol.angle, 1e-4); 28 | } 29 | 30 | @Test 31 | public void test1() { 32 | double mu = 398603.0 * 1000 * 1000 * 1000; 33 | Vector3D r1Vec = new Vector3D(10000000, 0, 0); 34 | Vector3D r2Vec = rotateVector(r1Vec.scalarMultiply(1.6), Math.toRadians(100)); 35 | LambertSolver ls = new LambertSolver(mu, r1Vec, r2Vec, 3072, 0, true); 36 | ls.solve(); 37 | assertEquals(1, ls.getSolutions().size()); 38 | assertSolutionEquals(-377.3168363349114, 7889.69293204125, 0, -5352.761738375782, 1960.1885952794946, 0, 1.7453292519943295, ls.getSolutions().get(0)); 39 | } 40 | 41 | @Test 42 | public void test2() { 43 | double mu = 398603.0 * 1000 * 1000 * 1000; 44 | Vector3D r1Vec = new Vector3D(10000000, 0, 0); 45 | Vector3D r2Vec = rotateVector(r1Vec.scalarMultiply(1.6), Math.toRadians(260)); 46 | LambertSolver ls = new LambertSolver(mu, r1Vec, r2Vec, 31645, 0, true); 47 | ls.solve(); 48 | assertEquals(1, ls.getSolutions().size()); 49 | assertSolutionEquals(377.4228058979588, 7889.7602524861995, 0, 5352.825254283218, 1960.3065100210342, 0, 4.537856055185256, ls.getSolutions().get(0)); 50 | } 51 | 52 | @Test 53 | public void test3() { 54 | double mu = 398603.0 * 1000 * 1000 * 1000; 55 | Vector3D r1Vec = new Vector3D(10000000, 0, 0); 56 | Vector3D r2Vec = rotateVector(r1Vec.scalarMultiply(1.6), Math.toRadians(260)); 57 | LambertSolver ls = new LambertSolver(mu, r1Vec, r2Vec, 31645, 0, false); 58 | ls.solve(); 59 | assertEquals(1, ls.getSolutions().size()); 60 | assertSolutionEquals(6373.561127399767, -4673.784071715375, 0, -2025.35758066653, 5335.657472529362, 0, 1.7453292519943302, ls.getSolutions().get(0)); 61 | } 62 | 63 | @Test 64 | public void test4() { 65 | double mu = 398603.0 * 1000 * 1000 * 1000; 66 | Vector3D r1Vec = new Vector3D(10000000, 0, 0); 67 | Vector3D r2Vec = rotateVector(r1Vec.scalarMultiply(1.6), Math.toRadians(260)); 68 | LambertSolver ls = new LambertSolver(mu, r1Vec, r2Vec, 31645, 1, false); 69 | ls.solve(); 70 | assertEquals(3, ls.getSolutions().size()); 71 | assertSolutionEquals(6373.561127399767, -4673.784071715375, 0, -2025.35758066653, 5335.657472529362, 0, 1.7453292519943302, ls.getSolutions().get(0)); 72 | assertSolutionEquals(5154.48441880469, -5109.118189834746, 0, -2528.785182492637, 4047.445960381232, 0, 8.028514559173917, ls.getSolutions().get(1)); 73 | assertSolutionEquals(-139.89148838325855, -7740.2603938265975, 0, -5211.391732574132, -1696.2817579697057, 0, 8.028514559173917, ls.getSolutions().get(2)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/liblambertoptimize.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/liborbitalstate"). 4 | runoncepath("lib/libmainframe"). 5 | 6 | function lambertOptimizeBounded { 7 | parameter s1. 8 | parameter s2. 9 | parameter tMin. 10 | parameter tMax. 11 | parameter dtMin is 0. 12 | parameter dtMax is max(s1:orbit:period, s2:orbit:period). 13 | parameter allowLob is true. 14 | parameter optArrival is true. 15 | 16 | local calcStartTime to time:seconds. 17 | print "Calc start time: " + calcStartTime. 18 | print "Passed tMin: " + tMin. 19 | print "Passed tMax: " + tMax. 20 | 21 | set tMin to tMin - time:seconds. 22 | set tMax to tMax - time:seconds. 23 | print "Adjusted tMin: " + tMin. 24 | print "Adjusted tMax: " + tMax. 25 | 26 | local MIN_STEP_T to 1. 27 | local MIN_STEP_DT to 1. 28 | 29 | set tMin to max(0, tMin). 30 | set tMax to max(tMin + 1e-8, tMax). 31 | 32 | set dtMin to max(0, dtMin). 33 | set dtMax to max(dtMin + 1e-8, dtMax). 34 | 35 | local b to s1:body. 36 | if s2:body <> b { 37 | print "bodies must be the same". 38 | exit. 39 | } 40 | 41 | local rv1 to getECIVecs(s1:orbit). 42 | local rv2 to getECIVecs(s2:orbit). 43 | 44 | local res to lexicon(). 45 | 46 | // initialize these to force us into the loop, then immediately recalculate 47 | local tStep to 2 * MIN_STEP_T. 48 | local dtStep to 2 * MIN_STEP_DT. 49 | 50 | // pure equality check is ok since these are assigned directly, not computed 51 | until tStep = MIN_STEP_T and dtStep = MIN_STEP_DT { 52 | // do this at the beginning of the loop (after checking the until condition) so 53 | // we loop at least once with tStep = dtStep = 1 54 | set tStep to max(MIN_STEP_T, (tMax - tMin) / 10000). 55 | set dtStep to max(MIN_STEP_DT, (dtMax - dtMin) / 500). 56 | 57 | set res to mainframeLambertOptimizeVecs(b:mu, 58 | rv1[0], rv1[1], 59 | rv2[0], rv2[1], 60 | tMin, tMax, tStep, 61 | dtMin, dtMax, dtStep, 62 | allowLob, optArrival). 63 | 64 | set tMin to max(0, res["t"] - tStep). 65 | set tMax to tMin + 2 * tStep. 66 | 67 | set dtMin to max(0, res["dt"] - dtStep). 68 | set dtMax to dtMin + 2 * dtStep. 69 | 70 | //set tStep to MIN_STEP_T. 71 | //set dtStep to MIN_STEP_DT. 72 | } 73 | 74 | local calcEndTime to time:seconds. 75 | print "Calc end time: " + calcEndTime + " (total time: " + (calcEndTime - calcStartTime) + ")". 76 | print "answer from mainframe: " + res["t"]. 77 | 78 | set res["t"] to res["t"] + calcStartTime. 79 | print "returning " + res["t"] + " (" + (res["t"] - time:seconds) + " from now)". 80 | 81 | return res. 82 | } 83 | 84 | function lambertOptimize { 85 | parameter s1. 86 | parameter s2. 87 | parameter allowLob is true. 88 | parameter optArrival is true. 89 | parameter startTime is time:seconds. 90 | 91 | local synodicPeriod to 1 / abs((1 / s1:orbit:period) - (1 / s2:orbit:period)). 92 | local dtMin to 0. 93 | local dtMax to max(s1:orbit:period, s2:orbit:period). 94 | 95 | local res to lambertOptimizeBounded(s1, s2, startTime, startTime + synodicPeriod, dtMin, dtMax, allowLob, optArrival). 96 | 97 | return res. 98 | } 99 | 100 | //local res to lambertOptimize(ship, target, true). 101 | //local res to lambertOptimize(ship, moon, false). 102 | //local t to res["t"]. 103 | //local dt to res["dt"]. 104 | //local pro to res["prograde"]. 105 | //local norm to res["normal"]. 106 | //local rad to res["radial"]. 107 | //add node(rad, norm, pro). 108 | -------------------------------------------------------------------------------- /lib/librendezvous.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/liblambertoptimize"). 4 | runoncepath("lib/libclosestapproach"). 5 | runoncepath("lib/libgenericburn"). 6 | runoncepath("lib/libburntime"). 7 | runoncepath("lib/libsasrcsstack"). 8 | runoncepath("lib/libcloseapproach"). 9 | runoncepath("lib/libexecnode"). 10 | 11 | function rendezvous_sync { 12 | parameter syncTime, syncBT, turnTime, ullageTime, turnWithRCS, rcsOnly, stopBeforeRCSSync. 13 | 14 | function syncCallback { 15 | parameter predictDir, lastVal, lastTime. 16 | 17 | local dir to tgt:obt:velocity:orbit - ship:obt:velocity:orbit. 18 | local val to dir:mag. 19 | 20 | local throt to 1. 21 | if lastVal >= 0 { 22 | local rate to (val - lastVal) / (time:seconds - lastTime). 23 | local ttzero to val / (-rate). 24 | set throt to max(0.1, min(1, ttzero)). 25 | } 26 | 27 | return lexicon("dir", dir, "throt", throt, "val", val). 28 | } 29 | 30 | if rcsOnly { 31 | if not stopBeforeRCSSync { 32 | genericBurnRCS(syncTime, syncBT, turnTime, syncCallback@, turnWithRCS). 33 | } 34 | } else { 35 | genericBurn(syncTime, syncBT, turnTime, ullageTime, syncCallback@). 36 | if not stopBeforeRCSSync { 37 | genericBurnRCS(time:seconds + turnTime, 1, turnTime, syncCallback@, turnWithRCS). 38 | } 39 | } 40 | } 41 | 42 | function rendezvous { 43 | parameter tgt, turnTime, ullageTime, tgtDist, turnWithRCS is false, rcsOnly is false, rcsIsp is 0, rcsThrust is 0, stopBeforeRCSSync is false. 44 | 45 | pushSAS(false). 46 | 47 | local lambertCalcTime to 60. 48 | 49 | local res to lambertOptimize(ship, tgt, true, true, time:seconds + turnTime + ullageTime + lambertCalcTime). 50 | add node(res["t"], res["radial"], res["normal"], res["prograde"]). 51 | if turnWithRCS { pushRCS(true). } 52 | if rcsOnly { 53 | execNodeRCS(rcsIsp, rcsThrust, turnTime, turnWithRCS). 54 | } else { 55 | execNode(turnTime, ullageTime). 56 | } 57 | 58 | local fineTuneTime to time:seconds + turnTime + lambertCalcTime. 59 | set res to lambertOptimizeBounded(ship, tgt, fineTuneTime, fineTuneTime + 5). 60 | add node(res["t"], res["radial"], res["normal"], res["prograde"]). 61 | 62 | local lastCATime to res["t"] + res["dt"]. 63 | local dir to nextnode:deltav. 64 | local burnMidTime to time:seconds + nextnode:eta. 65 | remove nextnode. 66 | 67 | function fineTuneCallback { 68 | parameter predictDir, lastVal, lastTime. 69 | 70 | local ca to closestApproach(tgt, lastCATime - 5, lastCATime + 5, 0.05). 71 | local val to ca["minDist"]. 72 | set lastCATime to ca["time"]. 73 | print "minDist: " + val. 74 | 75 | local throt to 1. 76 | if lastVal >= 0 { 77 | local rate to (val - lastVal) / (time:seconds - lastTime). 78 | local ttzero to val / (-rate). 79 | set throt to max(0.1, min(1, ttzero)). 80 | } 81 | 82 | return lexicon("dir", dir, "throt", throt, "val", val). 83 | } 84 | 85 | genericBurnRCS(burnMidTime, 1, turnTime, fineTuneCallback@, turnWithRCS). 86 | 87 | local ca to closestApproach(tgt, lastCATime - 5, lastCATime + 5, 0.05). 88 | //local ca to closestApproach(tgt, time:seconds, time:seconds + ship:obt:period, 0.05)["time"]. 89 | local caTime to ca["time"]. 90 | local syncDV to ca["speed"]. 91 | local syncBT to 0. 92 | if rcsOnly { set syncBT to burnTime(syncDV, rcsIsp, rcsThrust, mass). } 93 | else { set syncBT to burnTime(syncDV). } 94 | local syncTime to caTime - syncBT. 95 | 96 | rendezvous_sync(syncTime, syncBT, turnTime, ullageTime, turnWithRCS, rcsOnly, stopBeforeRCSSync). 97 | 98 | if turnWithRCS { popRCS(). } 99 | popSAS(). 100 | 101 | if not stopBeforeRCSSync { 102 | print "Approaching target". 103 | closeApproach(tgt, tgtDist). 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /aerobrake.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | run once funs. 4 | 5 | // call while coasting to apoapsis 6 | 7 | parameter tgtApo. // not including body radius 8 | parameter minPer. // not including body radius 9 | parameter maxPer. // not including body radius 10 | parameter useRCS. 11 | 12 | clearscreen. 13 | print "tgtApo : " at (5, 10). 14 | print "minPer : " at (5, 11). 15 | print "maxPer : " at (5, 12). 16 | print "useRCS : " at (5, 13). 17 | print "curApo : " at (5, 14). 18 | print "curPer : " at (5, 15). 19 | print "curPer : " at (5, 15). 20 | print "taAtmo : " at (5, 16). 21 | print "tAtmo : " at (5, 17). 22 | 23 | until ship:obt:apoapsis <= tgtApo { 24 | 25 | print " " at (15, 10). 26 | print " " at (15, 11). 27 | print " " at (15, 12). 28 | print " " at (15, 13). 29 | print " " at (15, 14). 30 | print " " at (15, 15). 31 | print tgtApo at (15, 10). 32 | print minPer at (15, 11). 33 | print maxPer at (15, 12). 34 | print useRCS at (15, 13). 35 | print ship:obt:apoapsis at (15, 14). 36 | print ship:obt:periapsis at (15, 15). 37 | 38 | // TODO: panels on, face sun 39 | 40 | // if ship:obt:periapsis > maxPer or ship:obt:periapsis < minPer { 41 | // local tgtSMA to (ship:obt:apoapsis + (minPer + maxPer)/2 + 2 * body:radius) / 2. 42 | // local v to sqrt(body:mu * (2 / (ship:obt:apoapsis + body:radius) - 1 / ship:obt:semimajoraxis)). 43 | // local tgtV to sqrt(body:mu * (2 / (ship:obt:apoapsis + body:radius) - 1 / tgtSMA)). 44 | // local dv to abs(tgtV - v). 45 | // 46 | // if ship:obt:periapsis > maxPer { set dv to -1 * dv. } 47 | // 48 | // print "creating node with deltav prograde of " + dv. 49 | // add node(time:seconds + eta:apoapsis, 0, 0, dv). 50 | // 51 | // print "nextnode:prograde is " + nextnode:prograde. 52 | // 53 | // if useRCS { run execNodeRCS. } 54 | // else { run execNode. } 55 | // } 56 | 57 | // TODO: Do this if using reaction wheels. With RCS it affects the periapsis too much and wastes fuel. 58 | //lock steering to ship:prograde. 59 | //wait until faceDiff() < 0.5. 60 | //unlock steering. 61 | 62 | // print ship:obt:semimajoraxis at (2, 30). 63 | // print ship:obt:eccentricity at (2, 31). 64 | // print body:atm:height at (2, 32). 65 | // print body:radius at (2, 33). 66 | // print (1 - ship:obt:eccentricity^2) at (2, 34). 67 | // print (ship:obt:eccentricity * (body:atm:height + body:radius)) at (2, 35). 68 | // print (ship:obt:semimajoraxis * (1 - ship:obt:eccentricity^2)) at (2, 36). 69 | // print (ship:obt:semimajoraxis * (1 - ship:obt:eccentricity^2) / (ship:obt:eccentricity * (body:atm:height + body:radius)) - 1) at (2, 37). 70 | // 71 | // local a to ship:obt:semimajoraxis. 72 | // local e to ship:obt:eccentricity. 73 | // local r to body:atm:height + body:radius. 74 | // print (a * (1 - e^2)/(e*r) - 1) at (2, 38). 75 | // print ((-a*e^2 + a - r) / (r * e)) at (2, 39). 76 | // 77 | // local trueAnomAtAtmoEntry to 360 - arccos((-a*e^2 + a - r) / (r * e)). 78 | // 79 | // //local trueAnomAtAtmoEntry to 360 - arccos(ship:obt:semimajoraxis * (1 - ship:obt:eccentricity^2) / (ship:obt:eccentricity * (body:atm:height + body:radius)) - 1). 80 | // local timeToAtmoEntry to timeToTrueAnom(trueAnomAtAtmoEntry). 81 | // print " " at (15, 16). 82 | // print " " at (15, 17). 83 | // print trueAnomAtAtmoEntry at (15, 16). 84 | // print timeToAtmoEntry at (15, 17). 85 | // 86 | // warpFor1(timeToAtmoEntry). 87 | 88 | warpFor1(eta:periapsis - 1200). 89 | set warp to 2. 90 | wait until warp = 0. 91 | wait 5. 92 | set warp to 3. 93 | 94 | wait until eta:periapsis > eta:apoapsis. 95 | wait until altitude > body:atm:height. 96 | wait 2. 97 | set warp to 0. 98 | wait 2. 99 | } 100 | -------------------------------------------------------------------------------- /lib/libbmmethod.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function pplist { 4 | parameter lst. 5 | local s to "(". 6 | from { local i to 0. } UNTIL i >= lst:length STEP { set i to i + 1. } DO { 7 | if i <> 0 { 8 | set s to s + ", ". 9 | } 10 | set s to s + lst[i]. 11 | } 12 | return s + ")". 13 | } 14 | 15 | function bruteForce { 16 | parameter f, xMin, xMax, xStep. 17 | 18 | local x to xMin:copy. 19 | local minVal to 999999999999999999. 20 | local minValX to x. 21 | 22 | local exhausted to false. 23 | until exhausted { 24 | local val to f(x). 25 | if val < minVal { 26 | set minVal to val. 27 | set minValX to x:copy. 28 | // print "set minValX to " + pplist(x). 29 | } 30 | 31 | local stopValueSearch to false. 32 | from { local i to xMin:length - 1. } UNTIL i < 0 or stopValueSearch STEP { set i to i - 1. } DO { 33 | set x[i] to x[i] + xStep[i]. 34 | if x[i] > xMax[i] { 35 | set x[i] to xMin[i]. 36 | } else { 37 | set stopValueSearch to true. 38 | } 39 | } 40 | 41 | if not stopValueSearch and x[0] = xMin[0] { 42 | set exhausted to true. 43 | } 44 | } 45 | 46 | return lexicon("min", minVal, "x", minValX). 47 | } 48 | 49 | function bracketingMinimizationMethod { 50 | parameter f, xMin, xMax, numBrackets, tols. 51 | 52 | local res to lexicon(). 53 | 54 | local stop to false. 55 | until stop { 56 | local xStep to list(). 57 | from { local i to 0. } UNTIL i >= xMin:length STEP { set i to i + 1. } DO { 58 | xStep:add(max(tols[i], (xMax[i] - xMin[i]) / numBrackets[i])). 59 | } 60 | 61 | // print " ". 62 | // print "xMin: " + pplist(xMin). 63 | // print "xMax: " + pplist(xMax). 64 | // print "xStep: " + pplist(xStep). 65 | 66 | set res to bruteForce(f, xMin, xMax, xStep). 67 | 68 | // print "min: " + res["min"]. 69 | // print "x: " + pplist(res["x"]). 70 | 71 | set stop to true. 72 | from { local i to 0. } UNTIL i >= xMin:length STEP { set i to i + 1. } DO { 73 | // print "Checking for stop: i = " + i + " xMax[i] = " + xMax[i] + " xMin[i] = " + xMin[i] + " tols[i] = " + tols[i]. 74 | if (xMax[i] - xMin[i]) / 2 >= tols[i] { 75 | set stop to false. 76 | } 77 | } 78 | 79 | from { local i to 0. } UNTIL i >= xMin:length STEP { set i to i + 1. } DO { 80 | // print "Setting xMin[i]: i = " + i + " xMin[i] = " + xMin[i] + " res['x'][i] = " + res["x"][i] + " xStep[i] = " + xStep[i]. 81 | set xMin[i] to max(xMin[i], res["x"][i] - xStep[i] / 2). 82 | // print "Setting xMax[i]: i = " + i + " xMax[i] = " + xMax[i] + " res['x'][i] = " + res["x"][i] + " xStep[i] = " + xStep[i]. 83 | set xMax[i] to min(xMax[i], res["x"][i] + xStep[i] / 2). 84 | } 85 | } 86 | 87 | return res. 88 | } 89 | 90 | function bruteForce1 { 91 | parameter f, xMin, xMax, xStep. 92 | 93 | local x to xMin. 94 | local minVal to 999999999999999999. 95 | local minValX to x. 96 | 97 | until x > xMax { 98 | local val to f(x). 99 | if val < minVal { 100 | set minVal to val. 101 | set minValX to x. 102 | } 103 | 104 | set x to x + xStep. 105 | } 106 | 107 | return lexicon("min", minVal, "x", minValX). 108 | } 109 | 110 | function bracketingMinimizationMethod1 { 111 | parameter f, xMin, xMax, numBrackets, tol. 112 | 113 | local res to lexicon(). 114 | 115 | local stop to false. 116 | until stop { 117 | local xStep to max(tol, (xMax - xMin) / numBrackets). 118 | 119 | set res to bruteForce1(f, xMin, xMax, xStep). 120 | set stop to (xMax - xMin) / 2 < tol. 121 | 122 | set xMin to max(xMin, res["x"] - xStep / 2). 123 | set xMax to min(xMax, res["x"] + xStep / 2). 124 | } 125 | 126 | return res. 127 | } 128 | 129 | //function p { 130 | // parameter x. 131 | // //local val to cos(x[0] - 15) + abs(x[0] - 15) / 10. 132 | // local val to (x[0] - 2)^2 + 3 + (x[1]+4)^2 * x[0]^2. 133 | //// print "f" + pplist(x) + " = " + val. 134 | // return val. 135 | //} 136 | // 137 | //bruteForce(p@, list(-3, 0, 3), list(10, 9, 3), list(3, 3, 1)). 138 | //local startTime to time:seconds. 139 | //print bracketingMinimizationMethod(p@, list(-15, -15), list(15, 15), list(5, 5), list(0.01, 0.01)). 140 | //print "time taken: " + (time:seconds - startTime). 141 | -------------------------------------------------------------------------------- /mainframe/src/test/java/hall/john/ksp/mainframe/orbit/ManeuverNodeTest.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe.orbit; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import hall.john.ksp.mainframe.Body; 10 | import hall.john.ksp.mainframe.TestUtils; 11 | 12 | public class ManeuverNodeTest { 13 | private OrbitAtTime _oat; 14 | 15 | @Before 16 | public void setUp() { 17 | _oat = new OrbitAtTime(new Body("A", 1), new Vector3D(1, 0, 0), new Vector3D(1, 2, 3)); 18 | } 19 | 20 | @Test 21 | public void dvVecCtorAndRetrieval() { 22 | ManeuverNode m = new ManeuverNode(_oat, new Vector3D(3, 2, 1)); 23 | assertEquals(_oat, m.getOAT()); 24 | TestUtils.assertVectorRelativelyEquals(3, 2, 1, m.getDVVec(), 1e-9); 25 | } 26 | 27 | @Test 28 | public void pnrCtorAndRetrieval() { 29 | ManeuverNode m = new ManeuverNode(_oat, 3, 2, 1); 30 | assertEquals(_oat, m.getOAT()); 31 | TestUtils.assertRelativelyEquals(3, m.getPrograde(), 1e-9); 32 | TestUtils.assertRelativelyEquals(2, m.getNormal(), 1e-9); 33 | TestUtils.assertRelativelyEquals(1, m.getRadial(), 1e-9); 34 | } 35 | 36 | @Test 37 | public void magnitudeCalculation() { 38 | ManeuverNode m = new ManeuverNode(_oat, new Vector3D(3, 2, 1)); 39 | TestUtils.assertRelativelyEquals(3.74165738677, m.getDV(), 1e-9); 40 | } 41 | 42 | @Test 43 | public void progradeOnlyCalculation() { 44 | Body b = new Body("A", 1); 45 | Vector3D r = new Vector3D(1, 0, 0); 46 | Vector3D v = new Vector3D(0, 1, 0); 47 | OrbitAtTime oat = new OrbitAtTime(b, r, v); 48 | ManeuverNode m = new ManeuverNode(oat, new Vector3D(0, 2, 0)); 49 | TestUtils.assertRelativelyEquals(2, m.getPrograde(), 1e-9); 50 | TestUtils.assertRelativelyEquals(0, m.getNormal(), 1e-9); 51 | TestUtils.assertRelativelyEquals(0, m.getRadial(), 1e-9); 52 | 53 | ManeuverNode m2 = new ManeuverNode(oat, new Vector3D(0, -2, 0)); 54 | TestUtils.assertRelativelyEquals(-2, m2.getPrograde(), 1e-9); 55 | TestUtils.assertRelativelyEquals(0, m2.getNormal(), 1e-9); 56 | TestUtils.assertRelativelyEquals(0, m2.getRadial(), 1e-9); 57 | } 58 | 59 | @Test 60 | public void normalOnlyCalculation() { 61 | Body b = new Body("A", 1); 62 | Vector3D r = new Vector3D(1, 0, 0); 63 | Vector3D v = new Vector3D(0, 1, 0); 64 | OrbitAtTime oat = new OrbitAtTime(b, r, v); 65 | ManeuverNode m = new ManeuverNode(oat, new Vector3D(0, 0, 2)); 66 | TestUtils.assertRelativelyEquals(0, m.getPrograde(), 1e-9); 67 | TestUtils.assertRelativelyEquals(2, m.getNormal(), 1e-9); 68 | TestUtils.assertRelativelyEquals(0, m.getRadial(), 1e-9); 69 | 70 | ManeuverNode m2 = new ManeuverNode(oat, new Vector3D(0, 0, -2)); 71 | TestUtils.assertRelativelyEquals(0, m2.getPrograde(), 1e-9); 72 | TestUtils.assertRelativelyEquals(-2, m2.getNormal(), 1e-9); 73 | TestUtils.assertRelativelyEquals(0, m2.getRadial(), 1e-9); 74 | } 75 | 76 | @Test 77 | public void radialOnlyCalculation() { 78 | Body b = new Body("A", 1); 79 | Vector3D r = new Vector3D(1, 0, 0); 80 | Vector3D v = new Vector3D(0, 1, 0); 81 | OrbitAtTime oat = new OrbitAtTime(b, r, v); 82 | ManeuverNode m = new ManeuverNode(oat, new Vector3D(2, 0, 0)); 83 | TestUtils.assertRelativelyEquals(0, m.getPrograde(), 1e-9); 84 | TestUtils.assertRelativelyEquals(0, m.getNormal(), 1e-9); 85 | TestUtils.assertRelativelyEquals(2, m.getRadial(), 1e-9); 86 | 87 | ManeuverNode m2 = new ManeuverNode(oat, new Vector3D(-2, 0, 0)); 88 | TestUtils.assertRelativelyEquals(0, m2.getPrograde(), 1e-9); 89 | TestUtils.assertRelativelyEquals(0, m2.getNormal(), 1e-9); 90 | TestUtils.assertRelativelyEquals(-2, m2.getRadial(), 1e-9); 91 | } 92 | 93 | @Test 94 | public void pnrCalculation() { 95 | Body b = new Body("A", 1); 96 | Vector3D r = new Vector3D(1, 2, 0); 97 | Vector3D v = new Vector3D(1, 1, 0); 98 | OrbitAtTime oat = new OrbitAtTime(b, r, v); 99 | ManeuverNode m = new ManeuverNode(oat, new Vector3D(1, 3, 1)); 100 | TestUtils.assertRelativelyEquals(2.8284271247461894, m.getPrograde(), 1e-9); 101 | TestUtils.assertRelativelyEquals(-1, m.getNormal(), 1e-9); 102 | TestUtils.assertRelativelyEquals(1.4142135623730954, m.getRadial(), 1e-9); 103 | } 104 | 105 | @Test 106 | public void dvVecCalculation() { 107 | Body b = new Body("A", 1); 108 | Vector3D r = new Vector3D(1, 2, 0); 109 | Vector3D v = new Vector3D(1, 1, 0); 110 | OrbitAtTime oat = new OrbitAtTime(b, r, v); 111 | ManeuverNode m = new ManeuverNode(oat, 2.8284271247461894, -1, 1.4142135623730954); 112 | TestUtils.assertVectorRelativelyEquals(1, 3, 1, m.getDVVec(), 1e-9); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /lib/liborbitalstate.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | function eciVecsToKepElem { 4 | parameter mu, r, v. 5 | 6 | // Semi-major Axis 7 | local a to 1 / ((2 / r:mag) - (v:sqrmagnitude / mu)). 8 | 9 | // Eccentricity 10 | local h to vcrs(r, v). 11 | local eVec to vcrs(v, h) / mu - r:normalized. 12 | local e to eVec:mag. 13 | 14 | // Inclination 15 | local i to arccos(h:z / h:mag). 16 | 17 | // Longitude of Ascending Node 18 | local n to V(-h:y, h:x, 0). 19 | local lan to 0. 20 | if n:mag <> 0 { 21 | set lan to arccos(n:x / n:mag). 22 | if n:y < 0 { set lan to 360 - lan. } 23 | } 24 | 25 | // Argument of Periapsis 26 | local aop to 0. 27 | if n:mag <> 0 and e <> 0 { 28 | set aop to arccos(vdot(n, eVec) / (n:mag * e)). 29 | if eVec:z < 0 { set aop to 360 - aop. } 30 | } 31 | 32 | // True Anomaly 33 | local ta to 0. 34 | if e <> 0 { // TODO: do something reasonable when the orbit is circular 35 | set ta to arccos(vdot(eVec, r) / (e * r:mag)). 36 | if vdot(r, v) < 0 { set ta to 360 - ta. } 37 | } 38 | 39 | // // Eccentric Anomaly 40 | // local ea to arccos((e + cos(ta)) / (1 + e * cos(ta))). 41 | // if 180 < ta and ta < 360 { set ea to 360 - ea. } 42 | // 43 | // // Mean Anomaly 44 | // local ma to ea - e * sin(ea). 45 | 46 | // TODO: change ta to ma? 47 | return list(a, e, i, lan, aop, ta). 48 | } 49 | 50 | function kepElemToEciVecs { 51 | // Best reference I've found: 52 | // http://ccar.colorado.edu/ASEN5070/handouts/kep2cart_2002.doc 53 | parameter mu, elems. 54 | 55 | local a to elems[0]. 56 | local e to elems[1]. 57 | local i to elems[2]. 58 | local lan to elems[3]. 59 | local aop to elems[4]. 60 | local ta to elems[5]. 61 | 62 | local p to a * (1 - e^2). 63 | local r to p / (1 + e * cos(ta)). 64 | local h to sqrt(mu * p). 65 | 66 | local coslan to cos(lan). 67 | local sinlan to sin(lan). 68 | local cosaopta to cos(aop + ta). 69 | local sinaopta to sin(aop + ta). 70 | local cosi to cos(i). 71 | local sini to sin(i). 72 | 73 | local x to r * (coslan * cosaopta - sinlan * sinaopta * cosi). 74 | local y to r * (sinlan * cosaopta + coslan * sinaopta * cosi). 75 | local z to r * (sini * sinaopta). 76 | 77 | local sinta to sin(ta). 78 | local herpsinta to h * e * sinta / (r * p). 79 | local hr to h / r. 80 | 81 | local vx to x * herpsinta - hr * (coslan * sinaopta + sinlan * cosaopta * cosi). 82 | local vy to y * herpsinta - hr * (sinlan * sinaopta - coslan * cosaopta * cosi). 83 | local vz to z * herpsinta + hr * sini * cosaopta. 84 | 85 | return list(V(x, y, z), V(vx, vy, vz)). 86 | } 87 | 88 | function getECIVecs 89 | { 90 | parameter p_obt. 91 | return kepElemToEciVecs(p_obt:body:mu, list(p_obt:semimajoraxis, 92 | p_obt:eccentricity, 93 | p_obt:inclination, 94 | p_obt:longitudeofascendingnode, 95 | p_obt:argumentofperiapsis, 96 | p_obt:trueanomaly)). 97 | } 98 | 99 | //print body:mu. 100 | //print getECIVecs(ship:orbit). 101 | //print getECIVecs(orbitat(ship, time:seconds + 60 * 60)). 102 | //print kepElemToEciVecs(body:mu, list(ship:orbit:semimajoraxis, 103 | // ship:orbit:eccentricity, 104 | // ship:orbit:inclination, 105 | // ship:orbit:longitudeofascendingnode, 106 | // ship:orbit:argumentofperiapsis, 107 | // 0)). 108 | 109 | //function hlist { 110 | // parameter l. 111 | // local s to "(". 112 | // for i in l { 113 | // set s to s + round(i, 5) + ", ". 114 | // } 115 | // set s to s:substring(0, s:length - 2) + ")". 116 | // return s. 117 | //} 118 | // 119 | //function roundVec { 120 | // parameter v. 121 | // return"V(" + round(v:x, 5) + ", " + round(v:y, 5) + ", " + round(v:z, 5) + ")". 122 | //} 123 | // 124 | //local tests to list(). 125 | //tests:add(list(1, V(1, 0, 0), V(0, 1, 0), list(1, 0, 0, 0, 0, 0))). 126 | //tests:add(list(1, V(1, 0, 0), V(0, 0.5, 0.5), list(0.66667, 0.5, 45, 0, 180, 180))). 127 | //tests:add(list(1, V(1, 0, 0), V(0.5, 0.5, 0.5), list(0.8, 0.61237244, 45, 0, 215.26438968, 144.73561032))). 128 | //tests:add(list(1, V(0, 0, -1), V(0.5, 0, 0.5), list(0.66667, 0.79056942, 90, 0, 71.56505118, 198.43494882))). 129 | //tests:add(list(1, V(0, -1, 0), V(0.5, 0, 0.5), list(0.66667, 0.5, 45, 270, 180, 180))). 130 | // 131 | //print "eciVecsToKepElem:". 132 | // 133 | //for t in tests { 134 | // local out to eciVecsToKepElem(t[0], t[1], t[2]). 135 | // print "" + t[0] + ", " + t[1] + ", " + t[2] + ", " + hlist(t[3]) + " --> " + hlist(out). 136 | // if hlist(t[3]) <> hlist(out) { 137 | // print "FAIL!". 138 | // exit. 139 | // } 140 | //} 141 | // 142 | //print "kepElemToEciVecs:". 143 | // 144 | //for t in tests { 145 | // local out to kepElemToEciVecs(t[0], t[3]). 146 | // print "" + t[0] + ", " + t[1] + ", " + t[2] + ", " + hlist(t[3]) + " --> " + roundVec(out[0]) + ", " + roundVec(out[1]). 147 | // if roundVec(t[1]) <> roundVec(out[0]) or roundVec(t[2]) <> roundVec(out[1]) { 148 | // print "FAIL!". 149 | // exit. 150 | // } 151 | //} 152 | -------------------------------------------------------------------------------- /lib/libdock.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libsasrcsstack"). 4 | runoncepath("lib/libvecdraw"). 5 | runoncepath("lib/libwaitforfacing"). 6 | runoncepath("lib/libcloseapproach"). 7 | 8 | function dock { 9 | parameter tgt, manDist to 25, rotation to 0. 10 | 11 | print "Moving to maneuver distance". 12 | closeApproach(tgt, manDist). 13 | 14 | local port to tgt. 15 | if tgt:istype("Vessel") { 16 | for dp in tgt:dockingports { 17 | if dp:state = "Ready" { 18 | set port to dp. 19 | } 20 | } 21 | } 22 | 23 | clearVecDraws(). 24 | local highlightPort to highlight(port, rgb(0, 1, 1)). 25 | set highlightPort:enabled to true. 26 | 27 | local tgtDist to manDist. 28 | local lock desiredPosition to port:nodeposition + tgtDist * port:portfacing:vector. 29 | 30 | local lastTime to time:seconds. 31 | local lastDist to port:nodeposition:mag. 32 | local lastTopDistVec to vxcl(ship:facing:vector, vxcl(ship:facing:starvector, desiredPosition)). 33 | local lock lastTopDist to lastTopDistVec:mag. 34 | local lastStarDistVec to vxcl(ship:facing:vector, vxcl(ship:facing:topvector, desiredPosition)). 35 | local lock lastStarDist to lastStarDistVec:mag. 36 | 37 | wait 0.05. 38 | 39 | local curTime to time:seconds. 40 | local curDist to port:nodeposition:mag. 41 | local lock distRate to (curDist - lastDist) / (curTime - lastTime). 42 | local curTopDistVec to vxcl(ship:facing:vector, vxcl(ship:facing:starvector, desiredPosition)). 43 | local lock topVel to (curTopDistVec - lastTopDistVec) / (curTime - lastTime). 44 | local lock curTopDist to curTopDistVec:mag. 45 | local lock topRate to (curTopDist - lastTopDist) / (curTime - lastTime). 46 | local curStarDistVec to vxcl(ship:facing:vector, vxcl(ship:facing:topvector, desiredPosition)). 47 | local lock starVel to (curStarDistVec - lastStarDistVec) / (curTime - lastTime). 48 | local lock curStarDist to curStarDistVec:mag. 49 | local lock starRate to (curStarDist - lastStarDist) / (curTime - lastTime). 50 | 51 | local desPosDraw to vecdraw(port:nodeposition, desiredPosition - port:nodeposition, rgb(1, 0, 0)). 52 | local topDistVecDraw to vecdraw(ship:position, V(0, 0, 0), rgb(0, 1, 0)). 53 | local starDistVecDraw to vecdraw(ship:position, V(0, 0, 0), rgb(0, 0, 1)). 54 | local tgtRotVecDraw to vecdraw(port:nodeposition, 3 * port:rotation:vector, rgb(1, 1, 0)). 55 | local topVecDraw to vecdraw(ship:position, 3 * ship:facing:topvector, rgb(1, 1, 0)). 56 | 57 | function dock_updateDists { 58 | set lastTime to curTime. 59 | set lastDist to curDist. 60 | set lastTopDistVec to curTopDistVec. 61 | set lastStarDistVec to curStarDistVec. 62 | 63 | set curTime to time:seconds. 64 | set curDist to port:nodeposition:mag. 65 | set curTopDistVec to vxcl(ship:facing:vector, vxcl(ship:facing:starvector, desiredPosition)). 66 | set curStarDistVec to vxcl(ship:facing:vector, vxcl(ship:facing:topvector, desiredPosition)). 67 | } 68 | 69 | function dock_updateVecDraws { 70 | parameter showDetailVecs. 71 | updateVecDraw(desPosDraw, port:nodeposition, desiredPosition - port:nodeposition, true). 72 | updateVecDraw(topDistVecDraw, ship:position, curTopDistVec, showDetailVecs). 73 | updateVecDraw(starDistVecDraw, ship:position, curStarDistVec, showDetailVecs). 74 | updateVecDraw(tgtRotVecDraw, port:nodeposition, 3 * port:rotation:vector, showDetailVecs). 75 | updateVecDraw(topVecDraw, ship:position, 3 * ship:facing:topvector, showDetailVecs). 76 | } 77 | 78 | function dock_setFore { 79 | parameter tgtRate. 80 | set ship:control:fore to max(-1, min(1, 10 * (distRate - tgtRate))). 81 | //print "curDist: " + curDist + " distRate: " + distRate + " tgtRate: " + tgtRate. 82 | } 83 | 84 | function dock_setTop { 85 | parameter tgtTopRate. 86 | local topScale to max(-1, min(1, 20 * (topRate - tgtTopRate))). 87 | local topSign to 1. 88 | if vang(ship:facing:topvector, curTopDistVec) > 90 { set topSign to -1. } 89 | print "curTopDist: " + curTopDist + " top rate: " + topRate + " tgtTopRate: " + tgtTopRate + " top: " + (topSign * topScale). 90 | set ship:control:top to max(-1, min(1, topSign * topScale)). 91 | } 92 | 93 | function dock_setStar { 94 | parameter tgtStarRate. 95 | local starScale to max(-1, min(1, 20 * (starRate - tgtStarRate))). 96 | local starSign to 1. 97 | if vang(ship:facing:starvector, curStarDistVec) > 90 { set starSign to -1. } 98 | print "curStarDist: " + curStarDist + " star rate: " + starRate + " tgtStarRate: " + tgtStarRate + " star: " + (starSign * starScale). 99 | set ship:control:starboard to max(-1, min(1, starSign * starScale)). 100 | } 101 | 102 | pushRCS(true). 103 | pushSAS(false). 104 | 105 | print "Facing target". 106 | lock steering to lookdirup(port:nodeposition, angleaxis(rotation, port:nodeposition) * port:rotation:vector). 107 | waitForFacing(0.5, true, false, false, 0.5). 108 | 109 | print "Lining up". 110 | until desiredPosition:mag < 0.5 { 111 | // move to in front of the target docking port 112 | dock_updateDists(). 113 | dock_setFore(max(-2, min(2, (tgtDist - curDist) / 20))). 114 | dock_setTop(-curTopDist / 50). 115 | dock_setStar(-curStarDist / 50). 116 | dock_updateVecDraws(true). 117 | wait 0.05. 118 | } 119 | 120 | print "Moving in to dock". 121 | until port:state <> "Ready" { 122 | // move in to dock 123 | set tgtDist to curDist. 124 | dock_updateDists(). 125 | local tgtForeRate to -0.2. 126 | if curDist < 5 { set tgtForeRate to -0.1. } 127 | dock_setFore(tgtForeRate). 128 | dock_setTop(-curTopDist / 50). 129 | dock_setStar(-curStarDist / 50). 130 | dock_updateVecDraws(true). 131 | wait 0.05. 132 | } 133 | 134 | popRCS(). 135 | popSAS(). 136 | unlock steering. 137 | set highlightPort:enabled to false. 138 | clearVecDraws(). 139 | } 140 | -------------------------------------------------------------------------------- /lambert_bad_results.txt: -------------------------------------------------------------------------------- 1 | Problematic Input: 2 | 3 | Waiting for requests 4 | Received request: lambertoptimize 5 | mu = 3.986004418E14 6 | r1 = {3,938,956.41505757; -4,322,861.25055128; 3,040,146.61265606} 7 | v1 = {5,939.5573519328; 4,962.012202433; -834.0506005418} 8 | r2 = {2,458,925.23200791; -5,430,340.44689457; 3,210,143.03667287} 9 | v2 = {6,959.5693297714; 3,223.7857434967; 117.8637326148} 10 | t: (min: 75254.1753168424 step: 1.0 max: 75284.0278423214) 11 | dt: (min: 2160.57512659049 step: 1.0 max: 2182.7348714786) 12 | allowLob = true 13 | optArrival = true 14 | Initial Phase Angle: 15.897844118434648 degrees 15 | dv1 = {-2,948.244329398; -1,169.382571798; -236.8136000966} (norm: 3180.5158239258694) 16 | dv2 = {1,362.4545716688; 2,478.3064151034; -937.111924043} (norm: 2979.339508211069) 17 | t = 75254.1753168424 18 | dt = 2182.57512659049 19 | radiusAtBurn = {5,996,410.689678423; -1,453,126.3617169827; 2,208,056.138858098} 20 | targetRadiusAtBurn = {-5,218,964.532809843; 3,250,101.6686417796; -2,821,960.936131854} 21 | radiusAtIntercept = {1,586,497.4655064063; -5,771,408.3339554155; 3,164,955.690493053} 22 | targetRadiusAtIntercept = {1,586,500.6560227785; -5,771,408.407325827; 3,164,956.530079909} 23 | distance = 3.299952125085273 24 | prograde = -1920.3888155090854 25 | normal = -35.49866086947204 26 | radial = -2535.0596735751496 27 | 28 | 29 | 30 | 31 | 32 | ==================================================================================================== 33 | 34 | Received request: lambertoptimize 35 | mu = 3.986004418E14 36 | r1 = {6,217,625.33521472; -719,829.378348098; 1,918,106.38746952} 37 | v1 = {1,657.6952993448; 7,081.1412827167; -2,921.2092857134} 38 | r2 = {5,680,511.51363976; -2,590,290.72677682; 2,614,657.68661689} 39 | v2 = {3,839.9493325346; 6,297.5479758191; -2,114.4648415501} 40 | t: (min: 190.0 step: 14.9298979965825 max: 149488.979965825) 41 | dt: (min: 0.0 step: 11.0798724440538 max: 5539.93622202688) 42 | allowLob = true 43 | optArrival = true 44 | Initial Phase Angle: 17.758927542621613 degrees 45 | dv1 = {36.1718119621; -3.4181586872; -103.4022132167} (norm: 109.59973306323899) 46 | dv2 = {0.2241259544; -48.0139977397; 53.1725252737} (norm: 71.64294560788775) 47 | t = 104803.79526205358 48 | dt = 3578.798799429377 49 | radiusAtBurn = {2,397,323.2595065525; -5,316,138.2570925215; 3,117,582.957572464} 50 | targetRadiusAtBurn = {3,270,798.6350434; -4,993,300.924792698; 3,194,487.4141093334} 51 | radiusAtIntercept = {-6,553,983.388197374; 201,029.7062235872; -1,672,221.2195989983} 52 | targetRadiusAtIntercept = {-6,553,981.441169958; 201,042.9932766374; -1,672,227.1024555508} 53 | distance = 14.660992317535293 54 | prograde = 28.646483150439785 55 | normal = -100.65529799693682 56 | radial = -32.55751028246257 57 | 58 | Waiting for requests 59 | Received request: lambertoptimize 60 | mu = 3.986004418E14 61 | r1 = {6,217,625.33521472; -719,829.378348098; 1,918,106.38746952} 62 | v1 = {1,657.6952993448; 7,081.1412827167; -2,921.2092857134} 63 | r2 = {5,680,511.51363976; -2,590,290.72677682; 2,614,657.68661689} 64 | v2 = {3,839.9493325346; 6,297.5479758191; -2,114.4648415501} 65 | t: (min: 104788.865364057 step: 1.0 max: 104818.72516005) 66 | dt: (min: 3567.71892698532 step: 1.0 max: 3589.87867187343) 67 | allowLob = true 68 | optArrival = true 69 | Initial Phase Angle: 17.758927542621613 degrees 70 | dv1 = {229.697157747; 845.1046166559; -488.8006375814} (norm: 1002.9400085100767) 71 | dv2 = {303.0673821869; -656.8930277273; 579.4689707986} (norm: 926.8994423025715) 72 | t = 104817.865364057 73 | dt = 3589.71892698532 74 | radiusAtBurn = {-5,527,489.927202652; -3,736,389.3429410746; 360,249.9077536459} 75 | targetRadiusAtBurn = {3,362,062.448350604; -4,935,960.674074877; 3,188,766.7551667937} 76 | radiusAtIntercept = {-6,576,770.614695702; 26,436.2896780877; -1,593,519.0467401377} 77 | targetRadiusAtIntercept = {-6,576,596.451533881; 27,430.8578385213; -1,593,926.87928144} 78 | distance = 1088.9563877532582 79 | prograde = -716.5535627194386 80 | normal = -130.67267000077098 81 | radial = -689.4666821095432 82 | 83 | 84 | 85 | ============================================================================================== 86 | 87 | Waiting for requests 88 | Received request: lambertoptimize 89 | mu = 3.986004418E14 90 | r1 = {4,480,837.14911992; -3,821,666.64348226; 2,941,160.82415483} 91 | v1 = {5,377.285568757; 5,505.8612015047; -1,233.4920531533} 92 | r2 = {3,109,070.95484681; -5,090,599.42124707; 3,202,544.42969868} 93 | v2 = {6,617.1091083119; 3,870.8756761674; -276.5485294795} 94 | t: (min: 190.0 step: 14.9274315436404 max: 149464.315436404) 95 | dt: (min: 0.0 step: 11.0798724440538 max: 5539.93622202688) 96 | allowLob = true 97 | optArrival = true 98 | Initial Phase Angle: 16.167436500949947 degrees 99 | dv1 = {-52.749099108; -7.7872280383; 34.8199267317} (norm: 63.683087824266856) 100 | dv2 = {99.3139202982; -70.8020411881; -27.9296537852} (norm: 125.12493501283942) 101 | t = 141403.5024028382 102 | dt = 2692.4090039050734 103 | radiusAtBurn = {-3,853,601.924382693; 4,418,022.611236052; -3,063,389.7883030917} 104 | targetRadiusAtBurn = {-3,968,644.700196883; 4,499,303.307524087; -3,123,953.3617341886} 105 | radiusAtIntercept = {3,483,129.8215670753; -4,857,080.876573138; 3,179,820.5824919245} 106 | targetRadiusAtIntercept = {3,483,129.8215967338; -4,857,080.876551585; 3,179,820.5824890723} 107 | distance = 3.6773292090216E-5 108 | prograde = 49.362341527712324 109 | normal = 39.25396076353462 110 | radial = 8.832976741147942 111 | 112 | Waiting for requests 113 | Received request: lambertoptimize 114 | mu = 3.986004418E14 115 | r1 = {4,480,837.14911992; -3,821,666.64348226; 2,941,160.82415483} 116 | v1 = {5,377.285568757; 5,505.8612015047; -1,233.4920531533} 117 | r2 = {3,109,070.95484681; -5,090,599.42124707; 3,202,544.42969868} 118 | v2 = {6,617.1091083119; 3,870.8756761674; -276.5485294795} 119 | t: (min: 141388.574971295 step: 1.0 max: 141418.429834382) 120 | dt: (min: 2681.32913146102 step: 1.0 max: 2703.48887634913) 121 | allowLob = true 122 | optArrival = true 123 | Initial Phase Angle: 16.167436500949947 degrees 124 | dv1 = {-59.196338424; 5.8089713347; 60.1251518668} (norm: 84.57531860881848) 125 | dv2 = {91.8591067108; -60.3212543944; -2.189639715} (norm: 109.91607589201665) 126 | t = 141389.574971295 127 | dt = 2681.32913146102 128 | radiusAtBurn = {-90,356.3519469159; -6,014,612.180287246; 2,815,719.819591378} 129 | targetRadiusAtBurn = {-3,884,538.5036123535; 4,564,392.198347737; -3,135,131.653785221} 130 | radiusAtIntercept = {-532,341.0590013969; 5,733,112.551771271; -2,847,396.982011537} 131 | targetRadiusAtIntercept = {3,322,373.622422461; -4,961,117.189614433; 3,191,360.846969457} 132 | distance = 1.2872139338657053E7 133 | prograde = -44.89826974655253 134 | normal = 68.656761081182 135 | radial = 20.576176743760136 -------------------------------------------------------------------------------- /mainframe/src/test/java/hall/john/ksp/mainframe/LambertOptimizerTest.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe; 2 | 3 | import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import hall.john.ksp.mainframe.orbit.OrbitAtTime; 8 | 9 | public class LambertOptimizerTest { 10 | 11 | @Test 12 | public void test() { 13 | Body b = new Body("Earth", 3.986004418E14); 14 | double mu = b.getMu(); 15 | 16 | OrbitAtTime oat0 = new OrbitAtTime(b, new Vector3D(280845.599191302, -5887127.19186784, -3177868.58724645), 17 | new Vector3D(7690.8927747241, 347.4010718063, 187.5151928961)); 18 | OrbitAtTime tgtOat0 = new OrbitAtTime(b, new Vector3D(-355482138.167304, 80409146.2590527, 43409396.101458), 19 | new Vector3D(-235.5335940609, -914.1890350466, -493.5308454351)); 20 | 21 | double tMin = 0; 22 | double tMax = 6000; 23 | double tStep = 60; 24 | double dtMin = 2 * 24 * 60 * 60; 25 | double dtMax = 8 * 24 * 60 * 60; 26 | double dtStep = 1954; 27 | 28 | LambertOptimizer lo = new LambertOptimizer(mu, oat0, tgtOat0, tMin, tMax, tStep, dtMin, dtMax, dtStep, true, 29 | false); 30 | lo.execute(); 31 | 32 | Vector3D dv1 = lo.getDV1(); 33 | Vector3D dv2 = lo.getDV2(); 34 | double t = lo.getT(); 35 | double dt = lo.getDT(); 36 | 37 | Assert.assertEquals(26866, lo.getCount()); 38 | TestUtils.assertVectorRelativelyEquals(-2336.2055666333, 1777.9481837768, 943.6883666524, dv1, 1e-9); 39 | TestUtils.assertVectorRelativelyEquals(695.681151565, -465.8924054596, -251.8111713676, dv2, 1e-9); 40 | TestUtils.assertRelativelyEquals(2040, t, 1e-9); 41 | TestUtils.assertRelativelyEquals(368200, dt, 1e-9); 42 | } 43 | 44 | @Test 45 | public void test2() { 46 | for (int i = 0; i < 100; i++) { 47 | Body b = new Body("Earth", 3.986004418E14); 48 | double mu = b.getMu(); 49 | 50 | OrbitAtTime oat0 = new OrbitAtTime(b, new Vector3D(5507077.05790552, -3584709.45138777, -6.256561471), 51 | new Vector3D(4248.8908942288, 6527.4400999656, 0.0113926318)); 52 | OrbitAtTime tgtOat0 = new OrbitAtTime(b, new Vector3D(5912477.98807351, -3299855.30599648, -5.7593520033), 53 | new Vector3D(3739.2505332585, 6699.759359006, 0.0116932746)); 54 | 55 | double tMin = 0; 56 | double tMax = 120538.278779887 * 1; 57 | double tStep = 1205.38278779887 / 100; 58 | double dtMin = 5172.92991419679 / 1; 59 | double dtMax = 5672.92991419679 * 1; 60 | double dtStep = 1; 61 | 62 | LambertOptimizer lo = new LambertOptimizer(mu, oat0, tgtOat0, tMin, tMax, tStep, dtMin, dtMax, dtStep, true, 63 | false); 64 | lo.execute(); 65 | 66 | Vector3D dv1 = lo.getDV1(); 67 | Vector3D dv2 = lo.getDV2(); 68 | double t = lo.getT(); 69 | double dt = lo.getDT(); 70 | double dv = dv1.getNorm(); 71 | 72 | Assert.assertEquals(5010501, lo.getCount()); 73 | System.out.println(dv1 + " " + dv2 + " " + dv + " " + t + " " + dt); 74 | TestUtils.assertVectorRelativelyEquals(79.2032486357, -259.367961919, -0.00045215772593656077, dv1, 1e-9); 75 | TestUtils.assertVectorRelativelyEquals(-164.2847412747, 424.4642747304, 0.0007407846716598644, dv2, 1e-9); 76 | TestUtils.assertRelativelyEquals(55459.662066626006, t, 1e-9); 77 | TestUtils.assertRelativelyEquals(5172.92991419679, dt, 1e-9); 78 | } 79 | } 80 | 81 | @Test 82 | public void test3() { 83 | for (int i = 0; i < 100; i++) { 84 | Body b = new Body("Earth", 3.986004418E14); 85 | double mu = b.getMu(); 86 | 87 | OrbitAtTime oat0 = new OrbitAtTime(b, new Vector3D(6217625.33521472, -719829.378348098, 1918106.38746952), 88 | new Vector3D(1657.6952993448, 7081.1412827167, -2921.2092857134)); 89 | OrbitAtTime tgtOat0 = new OrbitAtTime(b, 90 | new Vector3D(5680511.51363976, -2590290.72677682, 2614657.68661689), 91 | new Vector3D(3839.9493325346, 6297.5479758191, -2114.4648415501)); 92 | 93 | double tMin = 190.0; 94 | double tMax = 149488.979965825; 95 | double tStep = 14.9298979965825; 96 | double dtMin = 0; 97 | double dtMax = 5539.93622202688; 98 | double dtStep = 11.0798724440538; 99 | 100 | LambertOptimizer lo = new LambertOptimizer(mu, oat0, tgtOat0, tMin, tMax, tStep, dtMin, dtMax, dtStep, true, 101 | true); 102 | lo.execute(); 103 | 104 | Vector3D dv1 = lo.getDV1(); 105 | Vector3D dv2 = lo.getDV2(); 106 | double t = lo.getT(); 107 | double dt = lo.getDT(); 108 | double dv = dv1.getNorm(); 109 | 110 | // Assert.assertEquals(5010501, lo.getCount()); 111 | System.out.println(Utils.formatVector(dv1) + " " + Utils.formatVector(dv2) + " " + dv + " " + t + " " + dt); 112 | TestUtils.assertVectorRelativelyEquals(-53.8684534113, -5.4429244818, 38.1615193021, dv1, 1e-9); 113 | TestUtils.assertVectorRelativelyEquals(96.4589692851, -68.725636472, -25.1248120869, dv2, 1e-9); 114 | TestUtils.assertRelativelyEquals(140949.07831177983, t, 1e-9); 115 | TestUtils.assertRelativelyEquals(2648.0895141288584, dt, 1e-9); 116 | } 117 | } 118 | 119 | @Test 120 | public void test4() { 121 | Body b = new Body("Earth", 3.986004418E14); 122 | double mu = b.getMu(); 123 | 124 | OrbitAtTime oat0 = new OrbitAtTime(b, new Vector3D(6217625.33521472, -719829.378348098, 1918106.38746952), 125 | new Vector3D(1657.6952993448, 7081.1412827167, -2921.2092857134)); 126 | OrbitAtTime tgtOat0 = new OrbitAtTime(b, new Vector3D(5680511.51363976, -2590290.72677682, 2614657.68661689), 127 | new Vector3D(3839.9493325346, 6297.5479758191, -2114.4648415501)); 128 | 129 | double tMin = 140904.28861779007; 130 | double tMax = 140904.28861779007; 131 | double tStep = 1; 132 | double dtMin = 2736.7284936812885; 133 | double dtMax = 2736.7284936812885; 134 | double dtStep = 1; 135 | 136 | // double tMin = 104817.865364057; 137 | // double tMax = 104817.865364057; 138 | // double tStep = 1; 139 | // double dtMin = 3589.71892698532; 140 | // double dtMax = 3589.71892698532; 141 | // double dtStep = 1; 142 | 143 | LambertOptimizer lo = new LambertOptimizer(mu, oat0, tgtOat0, tMin, tMax, tStep, dtMin, dtMax, dtStep, true, 144 | false); 145 | lo.execute(); 146 | 147 | Vector3D dv1 = lo.getDV1(); 148 | Vector3D dv2 = lo.getDV2(); 149 | double t = lo.getT(); 150 | double dt = lo.getDT(); 151 | double dv = dv1.getNorm(); 152 | 153 | // Assert.assertEquals(5010501, lo.getCount()); 154 | System.out.println(dv1 + " " + dv2 + " " + dv + " " + t + " " + dt); 155 | TestUtils.assertVectorRelativelyEquals(79.2032486357, -259.367961919, -0.00045215772593656077, dv1, 1e-9); 156 | TestUtils.assertVectorRelativelyEquals(-164.2847412747, 424.4642747304, 0.0007407846716598644, dv2, 1e-9); 157 | TestUtils.assertRelativelyEquals(55459.662066626006, t, 1e-9); 158 | TestUtils.assertRelativelyEquals(5172.92991419679, dt, 1e-9); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /mainframe/src/main/java/hall/john/ksp/mainframe/orbit/OrbitAtTime.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe.orbit; 2 | 3 | import java.util.function.DoubleFunction; 4 | 5 | import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; 6 | import org.apache.commons.math3.util.MathUtils; 7 | 8 | import hall.john.ksp.mainframe.Body; 9 | import hall.john.ksp.mainframe.numericalmethods.NewtonsMethod; 10 | 11 | public class OrbitAtTime extends Orbit { 12 | private Double _ta; // radians 13 | private Double _ea; // radians 14 | private Double _ma; // radians 15 | private Vector3D _rVec; 16 | private Vector3D _vVec; 17 | private Double _r; 18 | private Double _v; 19 | 20 | public OrbitAtTime(Body body, double a, double e, double i, double lan, double aop, double ta) { 21 | super(body, a, e, i, lan, aop); 22 | _ta = ta; 23 | } 24 | 25 | public OrbitAtTime(Orbit orbit, double ta) { 26 | this(orbit.getBody(), orbit._a, orbit._e, orbit._i, orbit._lan, orbit._aop, ta); 27 | } 28 | 29 | public OrbitAtTime(Body b, Vector3D rVec, Vector3D vVec) { 30 | this(fromECI(b, rVec, vVec)); 31 | } 32 | 33 | public OrbitAtTime(OrbitAtTime other) { 34 | super(other.getBody(), other._a, other._e, other._i, other._lan, other._aop); 35 | _ta = other._ta; 36 | _ea = other._ea; 37 | _ma = other._ma; 38 | _rVec = other._rVec; 39 | _vVec = other._vVec; 40 | _r = other._r; 41 | _v = other._v; 42 | } 43 | 44 | private void calcTrueAnomaly() { 45 | if (_ta == null) { 46 | throw new RuntimeException("not yet implemented"); 47 | } 48 | } 49 | 50 | public double getTrueAnomaly() { 51 | calcTrueAnomaly(); 52 | return _ta; 53 | } 54 | 55 | private void calcEccentricAnomaly() { 56 | if (_ea == null) { 57 | calcTrueAnomaly(); 58 | _ea = Math.acos((_e + Math.cos(_ta)) / (1 + _e * Math.cos(_ta))); 59 | if (Math.PI < _ta && _ta < MathUtils.TWO_PI) 60 | _ea = MathUtils.TWO_PI - _ea; 61 | } 62 | } 63 | 64 | public double getEccentricAnomaly() { 65 | calcEccentricAnomaly(); 66 | return _ea; 67 | } 68 | 69 | private void calcMeanAnomaly() { 70 | if (_ma == null) { 71 | calcEccentricAnomaly(); 72 | _ma = _ea - _e * Math.sin(_ea); 73 | } 74 | } 75 | 76 | public double getMeanAnomaly() { 77 | calcMeanAnomaly(); 78 | return _ma; 79 | } 80 | 81 | private void calcECIVectors() { 82 | if (_ta == null) 83 | throw new RuntimeException("can't calc eci vecs without true anomaly"); 84 | if (_rVec == null || _vVec == null) { 85 | double p = _a * (1 - _e * _e); 86 | double r = p / (1 + _e * Math.cos(_ta)); 87 | double h = Math.sqrt(_body.getMu() * p); 88 | 89 | double coslan = Math.cos(_lan); 90 | double sinlan = Math.sin(_lan); 91 | double cosaopta = Math.cos(_aop + _ta); 92 | double sinaopta = Math.sin(_aop + _ta); 93 | double cosi = Math.cos(_i); 94 | double sini = Math.sin(_i); 95 | 96 | double x = r * (coslan * cosaopta - sinlan * sinaopta * cosi); 97 | double y = r * (sinlan * cosaopta + coslan * sinaopta * cosi); 98 | double z = r * (sini * sinaopta); 99 | 100 | _rVec = new Vector3D(x, y, z); 101 | 102 | double sinta = Math.sin(_ta); 103 | double herpsinta = h * _e * sinta / (r * p); 104 | double hr = h / r; 105 | 106 | double vx = x * herpsinta - hr * (coslan * sinaopta + sinlan * cosaopta * cosi); 107 | double vy = y * herpsinta - hr * (sinlan * sinaopta - coslan * cosaopta * cosi); 108 | double vz = z * herpsinta + hr * sini * cosaopta; 109 | 110 | _vVec = new Vector3D(vx, vy, vz); 111 | } 112 | } 113 | 114 | public Vector3D getRadiusVector() { 115 | calcECIVectors(); 116 | return _rVec; 117 | } 118 | 119 | public Vector3D getVelocityVector() { 120 | calcECIVectors(); 121 | return _vVec; 122 | } 123 | 124 | private void calcRadius() { 125 | if (_r == null) { 126 | calcECIVectors(); 127 | _r = _rVec.getNorm(); 128 | } 129 | } 130 | 131 | public double getRadius() { 132 | calcRadius(); 133 | return _r; 134 | } 135 | 136 | private void calcVelocity() { 137 | if (_v == null) { 138 | calcECIVectors(); 139 | _v = _vVec.getNorm(); 140 | } 141 | } 142 | 143 | public double getVelocity() { 144 | calcVelocity(); 145 | return _v; 146 | } 147 | 148 | public OrbitAtTime afterTime(double dt) { 149 | double ma = getMeanAnomaly(); 150 | double n = getMeanAngularMotion(); 151 | 152 | double newMA = (ma + n * dt) % MathUtils.TWO_PI; 153 | double newEA = eccentricAnomalyFromMeanAnomaly(_e, newMA); 154 | double newTA = 2 * Math.atan2(Math.sqrt(1 + _e) * Math.sin(newEA / 2), Math.sqrt(1 - _e) * Math.cos(newEA / 2)); 155 | return new OrbitAtTime(_body, _a, _e, _i, _lan, _aop, newTA); 156 | } 157 | 158 | public double phaseAngleWith(OrbitAtTime oat) { 159 | return Vector3D.angle(_rVec, oat.getRadiusVector()); 160 | } 161 | 162 | private static double eccentricAnomalyFromMeanAnomaly(double e, double ma) { 163 | if (Math.abs(e) < 1e-9) { 164 | return ma; 165 | } 166 | 167 | DoubleFunction f = (double x) -> x - e * Math.sin(x) - ma; 168 | DoubleFunction fp = (double x) -> 1 - e * Math.cos(x); 169 | NewtonsMethod mth = new NewtonsMethod(f, fp, ma, 100, 1e-9); 170 | mth.execute(); 171 | if (!mth.getConverged()) 172 | throw new RuntimeException("failed to converge"); 173 | return mth.getResult(); 174 | } 175 | 176 | private static OrbitAtTime fromECI(Body b, Vector3D rVec, Vector3D vVec) { 177 | double r = rVec.getNorm(); 178 | double mu = b.getMu(); 179 | 180 | // Semi-major Axis 181 | double a = 1 / ((2 / r) - (vVec.getNormSq() / b.getMu())); 182 | 183 | // Eccentricity 184 | Vector3D h = rVec.crossProduct(vVec); 185 | Vector3D eVec = vVec.crossProduct(h).scalarMultiply(1 / mu).subtract(rVec.normalize()); 186 | double e = eVec.getNorm(); 187 | 188 | // Inclination 189 | double i = Math.acos(h.getZ() / h.getNorm()); 190 | 191 | // Longitude of Ascending Node 192 | Vector3D n = new Vector3D(-h.getY(), h.getX(), 0); 193 | double lan = 0; 194 | if (n.getNorm() != 0) { 195 | lan = Math.acos(n.getX() / n.getNorm()); 196 | if (n.getY() < 0) 197 | lan = MathUtils.TWO_PI - lan; 198 | } 199 | 200 | // Argument of Periapsis 201 | double aop = 0; 202 | if (n.getNorm() != 0) { 203 | aop = Math.acos(n.dotProduct(eVec) / (n.getNorm() * e)); 204 | if (eVec.getZ() < 0) 205 | aop = MathUtils.TWO_PI - aop; 206 | } 207 | 208 | // True Anomaly 209 | double ta = 0; 210 | if (Math.abs(e) > 1e-6) { 211 | ta = Math.acos(eVec.dotProduct(rVec) / (r * e)); 212 | if (rVec.dotProduct(vVec) < 0) 213 | ta = MathUtils.TWO_PI - ta; 214 | } else if (Math.abs(i) > 1e-6) { 215 | // Circular orbit 216 | ta = Math.acos(n.dotProduct(rVec) / (r * n.getNorm())); 217 | if (n.dotProduct(vVec) > 0) 218 | ta = MathUtils.TWO_PI - ta; 219 | } else { 220 | // Circular orbit with zero inclination 221 | ta = Math.acos(rVec.getX() / r); 222 | if (vVec.getX() > 0) 223 | ta = MathUtils.TWO_PI - ta; 224 | } 225 | 226 | OrbitAtTime oat = new OrbitAtTime(b, a, e, i, lan, aop, ta); 227 | oat._rVec = rVec; 228 | oat._vVec = vVec; 229 | return oat; 230 | } 231 | 232 | } 233 | -------------------------------------------------------------------------------- /launch.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | // LAN increases approx. 6 degrees during Cape Canaveral eastward launch 4 | 5 | parameter initialTurnAngle, initialTurnStartSpeed, orbitalTurnEndAltitude, targetInclination, targetAltitude. 6 | 7 | runoncepath("lib/libsasrcsstack"). 8 | runoncepath("lib/libengine"). 9 | runoncepath("lib/libsmoothturn"). 10 | runoncepath("lib/libburntime"). 11 | 12 | local LOGFILE to "log/lastlaunch.log". 13 | deletepath(LOGFILE). 14 | 15 | function engineFlamedOut { 16 | parameter igEs. 17 | for e in igEs { 18 | if e:flameout { 19 | return true. 20 | } 21 | } 22 | return false. 23 | } 24 | 25 | // Source: http://www.orbiterwiki.org/wiki/Launch_Azimuth 26 | local val to cos(targetInclination) / cos(ship:latitude). 27 | local betaInertial to arcsin(max(-1, min(1, cos(targetInclination) / cos(ship:latitude)))). 28 | local vEqRot to 2 * constant:pi * body:radius / body:rotationperiod. 29 | local vOrbit to sqrt(body:mu / (body:radius + targetAltitude)). 30 | local launchAzimuth to arctan((vOrbit * sin(betaInertial) - vEqRot * cos(ship:latitude)) / (vOrbit * cos(betaInertial))). 31 | 32 | log "Launching at " + time:seconds to LOGFILE. 33 | log "Inputs:" to LOGFILE. 34 | log " Initial Turn Angle: " + initialTurnAngle to LOGFILE. 35 | log " Initial Turn Start Speed: " + initialTurnStartSpeed to LOGFILE. 36 | log " Orbital Turn End Altitude: " + orbitalTurnEndAltitude to LOGFILE. 37 | log " Target Inclination: " + targetInclination to LOGFILE. 38 | log " Target Altitude: " + targetAltitude to LOGFILE. 39 | log "launch azimuth: " + launchAzimuth to LOGFILE. 40 | 41 | local lock obtProProjOntoHorizon to ship:velocity:orbit - vdot(ship:velocity:orbit, up:vector) * up:vector. 42 | local lock srfProProjOntoHorizon to ship:velocity:surface - vdot(ship:velocity:surface, up:vector) * up:vector. 43 | local lock obtHdng to vang(obtProProjOntoHorizon, north:vector). 44 | local lock srfHdng to vang(srfProProjOntoHorizon, north:vector). 45 | local lock obtPitch to 90 - vang(ship:velocity:orbit, up:vector). 46 | local lock srfPitch to 90 - vang(ship:velocity:surface, up:vector). 47 | 48 | set ship:control:pilotmainthrottle to 0. 49 | pushSAS(true). 50 | gear off. 51 | lock throttle to 1. 52 | wait 1. // wait for lock throttle to take effect 53 | stage. 54 | 55 | wait until currentTWR() > 1. 56 | wait until stage:ready. 57 | 58 | stage. 59 | 60 | wait 1. 61 | 62 | lock steering to lookdirup(up:vector, ship:facing:topvector). 63 | 64 | wait until ship:verticalspeed > initialTurnStartSpeed. 65 | 66 | lock steering to smoothScalarBasedTurn(ship:verticalspeed, initialTurnStartSpeed, initialTurnStartSpeed * 1.25, 67 | heading(launchAzimuth, 90):vector, 68 | heading(launchAzimuth, 90 - initialTurnAngle):vector, 69 | ship:facing:topvector). 70 | 71 | wait until vang(ship:velocity:surface, up:vector) > initialTurnAngle. 72 | lock steering to lookdirup(heading(launchAzimuth, srfPitch):vector, ship:facing:topvector). 73 | 74 | local maxQ to ship:q. 75 | until ship:q < 0.75 * maxQ { 76 | set maxQ to max(maxQ, ship:q). 77 | wait 0.05. 78 | } 79 | 80 | local startAlt to ship:altitude. 81 | local igEs to ignitedEngines(). 82 | until engineFlamedOut(igEs) { 83 | local lock turnStart to lookdirup(heading(launchAzimuth, srfPitch):vector, ship:facing:topvector):vector. 84 | local lock turnEnd to lookdirup(heading(launchAzimuth, obtPitch):vector, ship:facing:topvector):vector. 85 | lock steering to smoothScalarBasedTurn(ship:altitude, startAlt, orbitalTurnEndAltitude, 86 | turnStart, turnEnd, ship:facing:topvector). 87 | 88 | // Limit acceleration (doesn't limit to exactly 5g, but it's close enough) 89 | lock throttle to max(0, min(1, mass * 5 * 9.82 / max(0.1, maxthrust))). 90 | } 91 | 92 | pushRCS(true). 93 | set ship:control:fore to 1. 94 | stage. // jettison interstage fairing and decouple booster 95 | wait 2. 96 | 97 | // We probably had no steering control for a while. Instead of abruptly snapping back to 98 | // the intended steering as soon as the engines turn on, unlock steering for a few 99 | // seconds. 100 | unlock steering. 101 | 102 | stage. // second stage ignition 103 | set ship:control:fore to 0. 104 | popRCS(). 105 | wait 8. // wait for SAS and the engine to help stabilize us 106 | when ship:altitude > body:atm:height then { stage. } // jettison payload fairing 107 | 108 | local lastEcc to ship:orbit:eccentricity. 109 | local lastLAN to ship:orbit:longitudeofascendingnode. 110 | local lastETA to eta:apoapsis. 111 | local lastTime to time:seconds. 112 | local lanPID to PIDLoop(1, 0, 0, -1, 1). 113 | local pitchUpdatePeriod to 5. 114 | local lastPitchUpdateTime to 0. 115 | local pitchOffset to 0. 116 | local pitchStart to obtPitch. 117 | local pitchEnd to pitchStart. 118 | wait 1. 119 | 120 | local startTime to time:seconds. 121 | until ship:orbit:periapsis > body:atm:height and lastEcc < ship:orbit:eccentricity { 122 | local dvToCirc to sqrt(body:mu / (body:radius + apoapsis)) - ship:velocity:orbit:mag. 123 | local ttcirc to burnTime(dvToCirc). 124 | 125 | local dt to time:seconds - lastTime. 126 | //print "lastEta = " + lastEta.. 127 | //print "eta:apoapsis = " + eta:apoapsis.. 128 | //print "dt = " + dt. 129 | local etaRate to (lastETA - eta:apoapsis) / dt. 130 | //print "etaRate = " + etaRate. 131 | local tteta to eta:apoapsis / etaRate. 132 | if eta:periapsis < eta:apoapsis { 133 | set tteta to ttcirc. 134 | } 135 | 136 | if time:seconds - lastPitchUpdateTime > pitchUpdatePeriod { 137 | // for every 1 minute we are short of tteta, raise the nose by 7 138 | // degrees (or point down if we are accelerating too fast) 139 | local pitchOffsetMax to 30. 140 | set pitchOffset to 7 * (ttcirc - tteta) / 60. 141 | set pitchOffset to min(pitchOffsetMax, max(-pitchOffsetMax, pitchOffset)). 142 | 143 | if etaRate < 0 and eta:apoapsis < eta:periapsis { 144 | set pitchOffset to -pitchOffsetMax. 145 | } 146 | 147 | set lastPitchUpdateTime to time:seconds. 148 | local curPitch to 90 - vang(ship:facing:vector, up:vector). 149 | set pitchStart to curPitch. 150 | set pitchEnd to obtPitch + pitchOffset. 151 | } 152 | 153 | local smoothPitch to smoothScalarBasedProgression(time:seconds, lastPitchUpdateTime, lastPitchUpdateTime + pitchUpdatePeriod * 4, pitchStart, pitchEnd). 154 | 155 | // try to keep LAN stable 156 | local curLAN to ship:orbit:longitudeofascendingnode. 157 | local lanROC to (curLAN - lastLAN) / dt. 158 | local hdngOffset to -lanPID:update(time:seconds, lanROC). 159 | 160 | log "lastEcc: " + lastEcc to LOGFILE. 161 | log "ship:orbit:eccentricity: " + ship:orbit:eccentricity to LOGFILE. 162 | log "lastETA: " + lastETA to LOGFILE. 163 | log "eta:apoapsis: " + eta:apoapsis to LOGFILE. 164 | log "dt: " + dt to LOGFILE. 165 | log "etaRate: " + etaRate to LOGFILE. 166 | log "tteta: " + tteta to LOGFILE. 167 | log "ttcirc: " + ttcirc to LOGFILE. 168 | log "obtPitch: " + obtPitch to LOGFILE. 169 | log "pitchStart: " + pitchStart to LOGFILE. 170 | log "pitchEnd: " + pitchEnd to LOGFILE. 171 | log "pitchOffset: " + pitchOffset to LOGFILE. 172 | log "lastPitchUpdateTime: " + lastPitchUpdateTime to LOGFILE. 173 | log "smoothPitch: " + smoothPitch to LOGFILE. 174 | log "curLAN: " + curLAN to LOGFILE. 175 | log "lastLAN: " + lastLAN to LOGFILE. 176 | log "lanROC: " + lanROC to LOGFILE. 177 | log "hdngOffset: " + hdngOffset to LOGFILE. 178 | 179 | lock steering to lookdirup(heading(launchAzimuth, smoothPitch):vector, ship:facing:topvector). 180 | 181 | log "-----------------------------------------------------------------------------" to LOGFILE. 182 | 183 | set lastEcc to ship:orbit:eccentricity. 184 | set lastLAN to curLAN. 185 | set lastETA to eta:apoapsis. 186 | set lastTime to time:seconds. 187 | wait 0.05. 188 | } 189 | 190 | unlock steering. 191 | lock throttle to 0. 192 | popSAS(). 193 | 194 | wait 1. // wait for throttle change to take effect. 195 | unlock throttle. 196 | -------------------------------------------------------------------------------- /lib/libgenericburn.ks: -------------------------------------------------------------------------------- 1 | @lazyglobal off. 2 | 3 | runoncepath("lib/libwarpfor"). 4 | runoncepath("lib/libwarpto"). 5 | runoncepath("lib/libsasrcsstack"). 6 | runoncepath("lib/libengine"). 7 | runoncepath("lib/libwaitforfacing"). 8 | runoncepath("lib/libtimeto"). 9 | 10 | function genericBurnRCS { 11 | parameter p_when. 12 | parameter p_burnTime. 13 | parameter p_turnTime. 14 | parameter p_callback. 15 | parameter p_turnWithRCS is false. 16 | 17 | lock throttle to 0. 18 | 19 | local burnMidTime to libgenericburn_getBurnMidTime(p_when). 20 | 21 | local lock timeToBurnMid to burnMidTime - time:seconds. 22 | local lock burnStartTime to time:seconds + timeToBurnMid - p_burnTime / 2. 23 | local lock burnEndTime to time:seconds + timeToBurnMid + p_burnTime / 2. 24 | local lock turnStartTime to burnStartTime - p_turnTime. 25 | 26 | print " ". 27 | print "Waiting for turn at " + time:seconds. 28 | print "Time to turn start: " + (turnStartTime - time:seconds). 29 | print "Time to burn start: " + (burnStartTime - time:seconds). 30 | print "Time to burn middle: " + timeToBurnMid. 31 | print "Time to burn end: " + (burnEndTime - time:seconds). 32 | print "Burn time: " + p_burnTime. 33 | print "Burn mid time: " + burnMidTime. 34 | 35 | libgenericburn_warpToTurn(p_when, p_turnTime, 0, p_burnTime, turnStartTime). 36 | 37 | // We might have just warped a lot, so update the values 38 | set burnMidTime to libgenericburn_getBurnMidTime(p_when). 39 | 40 | if p_turnWithRCS { pushRCS(true). } 41 | else { pushRCS(false). } 42 | pushSAS(false). 43 | 44 | // call callback with predictDir=true so it can predict the value ahead of time 45 | local callbackResult to p_callback(true, -1, -1). 46 | local dir to callbackResult["dir"]. 47 | lock steering to lookdirup(dir, ship:facing:topvector). 48 | 49 | print " ". 50 | print "Waiting for facing at " + time:seconds. 51 | print "Time to burn start: " + (burnStartTime - time:seconds). 52 | print "Time to burn middle: " + timeToBurnMid. 53 | print "Time to burn end: " + (burnEndTime - time:seconds). 54 | print "Burn time: " + p_burnTime. 55 | print "Burn mid time: " + burnMidTime. 56 | 57 | waitForFacing(0.5, false, false, false). 58 | print "Facing achieved. Waiting to start burn". 59 | wait until time:seconds > burnStartTime. 60 | 61 | print " ". 62 | print "Starting burn at " + time:seconds. 63 | print "Time to burn middle: " + timeToBurnMid. 64 | print "Time to burn end: " + (burnEndTime - time:seconds). 65 | print "Burn time: " + p_burnTime. 66 | 67 | popRCS(). 68 | pushRCS(true). 69 | 70 | // call callback with predictDir=false so it can give the most up-to-date value 71 | set callbackResult to p_callback(false, -1, -1). 72 | set dir to callbackResult["dir"]. 73 | set ship:control:fore to callbackResult["throt"]. 74 | 75 | local curVal to callbackResult["val"]. 76 | local initVal to curVal. 77 | 78 | // The numbers go a little haywire at first, so wait until the value 79 | // has changed at least 5% before starting the main loop. 80 | until abs(curVal - initVal) / initVal > 0.05 { 81 | wait 0.05. 82 | set callbackResult to p_callback(false, -1, -1). 83 | set curVal to callbackResult["val"]. 84 | set dir to callbackResult["dir"]. 85 | set ship:control:fore to callbackResult["throt"]. 86 | } 87 | 88 | local lastVal to curVal. 89 | local lastTime to time:seconds. 90 | 91 | until curVal > lastVal { 92 | set lastVal to curVal. 93 | wait 0.05. 94 | set callbackResult to p_callback(false, lastVal, lastTime). 95 | set lastTime to time:seconds. 96 | set curVal to callbackResult["val"]. 97 | set dir to callbackResult["dir"]. 98 | set ship:control:fore to callbackResult["throt"]. 99 | } 100 | 101 | unlock steering. 102 | set ship:control:fore to 0. 103 | popRCS(). 104 | popSAS(). 105 | wait 0.05. 106 | } 107 | 108 | function genericBurn { 109 | parameter p_when. 110 | parameter p_burnTime. 111 | parameter p_turnTime. 112 | parameter p_ullageTime. 113 | parameter p_callback. 114 | 115 | local throt to 0. 116 | lock throttle to throt. 117 | 118 | local burnMidTime to libgenericburn_getBurnMidTime(p_when). 119 | 120 | local lock timeToBurnMid to burnMidTime - time:seconds. 121 | local lock burnStartTime to time:seconds + timeToBurnMid - p_burnTime / 2. 122 | local lock burnEndTime to time:seconds + timeToBurnMid + p_burnTime / 2. 123 | local lock ullageStartTime to burnStartTime - p_ullageTime. 124 | local lock turnStartTime to ullageStartTime - p_turnTime. 125 | 126 | libgenericburn_warpToTurn(p_when, p_turnTime, p_ullageTime, p_burnTime, turnStartTime). 127 | 128 | // We might have just warped a lot, so update the values 129 | set burnMidTime to libgenericburn_getBurnMidTime(p_when). 130 | 131 | pushSAS(false). 132 | 133 | // call callback with predictDir=true so it can predict the value ahead of time 134 | local callbackResult to p_callback(true, -1, -1). 135 | local dir to callbackResult["dir"]. 136 | lock steering to lookdirup(dir, ship:facing:topvector). 137 | 138 | print " ". 139 | print "Waiting for facing at " + time:seconds. 140 | print "Time to ullage start: " + (ullageStartTime - time:seconds). 141 | print "Time to burn start: " + (burnStartTime - time:seconds). 142 | print "Time to burn middle: " + timeToBurnMid. 143 | print "Time to burn end: " + (burnEndTime - time:seconds). 144 | print "Burn time: " + p_burnTime. 145 | print "Burn mid time: " + burnMidTime. 146 | 147 | waitForFacing(0.5, false, false). 148 | warpFor1(ullageStartTime - time:seconds). 149 | 150 | print " ". 151 | print "Settling fuel at " + time:seconds. 152 | print "Time to burn start: " + (burnStartTime - time:seconds). 153 | print "Time to burn middle: " + timeToBurnMid. 154 | print "Time to burn end: " + (burnEndTime - time:seconds). 155 | print "Burn time: " + p_burnTime. 156 | 157 | if p_ullageTime <> 0 { 158 | pushRCS(true). 159 | set ship:control:fore to 1. 160 | wait p_ullageTime. 161 | set ship:control:fore to 0. 162 | popRCS(). 163 | } 164 | 165 | print " ". 166 | print "Starting burn at " + time:seconds. 167 | print "Time to burn middle: " + timeToBurnMid. 168 | print "Time to burn end: " + (burnEndTime - time:seconds). 169 | print "Burn time: " + p_burnTime. 170 | 171 | // call callback with predictDir=false so it can give the most up-to-date value 172 | set callbackResult to p_callback(false, -1, -1). 173 | set dir to callbackResult["dir"]. 174 | set throt to callbackResult["throt"]. 175 | 176 | local curVal to callbackResult["val"]. 177 | local initVal to curVal. 178 | 179 | wait until atFullThrust(). 180 | 181 | // The numbers go a little haywire at first, so wait until the value 182 | // has changed at least 5% before starting the main loop. 183 | until abs(curVal - initVal) / initVal > 0.05 { 184 | wait 0.05. 185 | set callbackResult to p_callback(false, -1, -1). 186 | set curVal to callbackResult["val"]. 187 | set dir to callbackResult["dir"]. 188 | set throt to callbackResult["throt"]. 189 | } 190 | 191 | local lastVal to curVal. 192 | local lastTime to time:seconds. 193 | 194 | until curVal > lastVal { 195 | set lastVal to curVal. 196 | wait 0.05. 197 | set callbackResult to p_callback(false, lastVal, lastTime). 198 | set lastTime to time:seconds. 199 | set curVal to callbackResult["val"]. 200 | set dir to callbackResult["dir"]. 201 | set throt to callbackResult["throt"]. 202 | } 203 | 204 | lock throttle to 0. 205 | unlock steering. 206 | popSAS(). 207 | wait until atZeroThrust(). 208 | wait 0.05. 209 | } 210 | 211 | function libgenericburn_getBurnMidTime { 212 | parameter p_when. 213 | if p_when:typename() = "string" { 214 | return time:seconds + timeTo1(p_when). 215 | } else { 216 | return p_when. 217 | } 218 | } 219 | 220 | function libgenericburn_warpToTurn { 221 | parameter p_when, p_turnTime, p_ullageTime, p_burnTime, turnStartTime. 222 | 223 | if p_when:typename() = "string" { 224 | myWarpTo(p_when, p_turnTime + p_ullageTime + p_burnTime / 2). 225 | } else { 226 | warpFor1(turnStartTime - time:seconds). 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /mainframe/src/test/java/hall/john/ksp/mainframe/orbit/OrbitTest.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe.orbit; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import org.junit.Test; 5 | 6 | import hall.john.ksp.mainframe.Body; 7 | import hall.john.ksp.mainframe.TestUtils; 8 | 9 | public class OrbitTest { 10 | @Test 11 | public void ctorAndRetrieval() { 12 | Body b = new Body("A", 1); 13 | double a = 1; 14 | double e = 0.5; 15 | double i = Math.PI / 4; 16 | double lan = Math.PI / 2; 17 | double aop = Math.PI; 18 | 19 | Orbit o = new Orbit(b, a, e, i, lan, aop); 20 | assertEquals(b, o.getBody()); 21 | TestUtils.assertRelativelyEquals(a, o.getSemiMajorAxis(), 1e-9); 22 | TestUtils.assertRelativelyEquals(a, o.getSMA(), 1e-9); 23 | TestUtils.assertRelativelyEquals(e, o.getEccentricity(), 1e-9); 24 | TestUtils.assertRelativelyEquals(i, o.getInclination(), 1e-9); 25 | TestUtils.assertRelativelyEquals(lan, o.getLongitudeOfAscendingNode(), 1e-9); 26 | TestUtils.assertRelativelyEquals(lan, o.getLAN(), 1e-9); 27 | TestUtils.assertRelativelyEquals(aop, o.getArgumentOfPeriapsis(), 1e-9); 28 | TestUtils.assertRelativelyEquals(aop, o.getAOP(), 1e-9); 29 | } 30 | 31 | @Test 32 | public void apoapsis() { 33 | Body b = new Body("A", 1); 34 | double a = 3; 35 | double e = 0.5; 36 | 37 | Orbit o = new Orbit(b, a, e, 0, 0, 0); 38 | TestUtils.assertRelativelyEquals(4.5, o.getApoapsis(), 1e-9); 39 | } 40 | 41 | @Test 42 | public void periapsis() { 43 | Body b = new Body("A", 1); 44 | double a = 3; 45 | double e = 0.5; 46 | 47 | Orbit o = new Orbit(b, a, e, 0, 0, 0); 48 | TestUtils.assertRelativelyEquals(1.5, o.getPeriapsis(), 1e-9); 49 | } 50 | 51 | @Test 52 | public void withApoapsis() { 53 | Body b = new Body("A", 1); 54 | double a = 1; 55 | double e = 0.5; 56 | double i = Math.PI / 4; 57 | double lan = Math.PI / 2; 58 | double aop = Math.PI; 59 | 60 | Orbit o = new Orbit(b, a, e, i, lan, aop); 61 | Orbit o2 = o.withApoapsis(2); 62 | 63 | assertEquals(b, o2.getBody()); 64 | TestUtils.assertRelativelyEquals(1.25, o2.getSMA(), 1e-9); 65 | TestUtils.assertRelativelyEquals(0.6, o2.getEccentricity(), 1e-9); 66 | TestUtils.assertRelativelyEquals(i, o2.getInclination(), 1e-9); 67 | TestUtils.assertRelativelyEquals(lan, o2.getLAN(), 1e-9); 68 | TestUtils.assertRelativelyEquals(aop, o2.getAOP(), 1e-9); 69 | TestUtils.assertRelativelyEquals(o.getPeriapsis(), o2.getPeriapsis(), 1e-9); 70 | TestUtils.assertRelativelyEquals(2, o2.getApoapsis(), 1e-9); 71 | } 72 | 73 | @Test 74 | public void withPeriapsis() { 75 | Body b = new Body("A", 1); 76 | double a = 1; 77 | double e = 0.5; 78 | double i = Math.PI / 4; 79 | double lan = Math.PI / 2; 80 | double aop = Math.PI; 81 | 82 | Orbit o = new Orbit(b, a, e, i, lan, aop); 83 | Orbit o2 = o.withPeriapsis(0.75); 84 | 85 | assertEquals(b, o2.getBody()); 86 | TestUtils.assertRelativelyEquals(1.125, o2.getSMA(), 1e-9); 87 | TestUtils.assertRelativelyEquals(0.3333333333, o2.getEccentricity(), 1e-9); 88 | TestUtils.assertRelativelyEquals(i, o2.getInclination(), 1e-9); 89 | TestUtils.assertRelativelyEquals(lan, o2.getLAN(), 1e-9); 90 | TestUtils.assertRelativelyEquals(aop, o2.getAOP(), 1e-9); 91 | TestUtils.assertRelativelyEquals(0.75, o2.getPeriapsis(), 1e-9); 92 | TestUtils.assertRelativelyEquals(o.getApoapsis(), o2.getApoapsis(), 1e-9); 93 | } 94 | 95 | @Test 96 | public void withSMA() { 97 | Body b = new Body("A", 1); 98 | double a = 1; 99 | double e = 0.5; 100 | double i = Math.PI / 4; 101 | double lan = Math.PI / 2; 102 | double aop = Math.PI; 103 | 104 | Orbit o = new Orbit(b, a, e, i, lan, aop); 105 | Orbit o2 = o.withSMA(2); 106 | assertEquals(b, o2.getBody()); 107 | TestUtils.assertRelativelyEquals(2, o2.getSMA(), 1e-9); 108 | TestUtils.assertRelativelyEquals(e, o2.getEccentricity(), 1e-9); 109 | TestUtils.assertRelativelyEquals(i, o2.getInclination(), 1e-9); 110 | TestUtils.assertRelativelyEquals(lan, o2.getLAN(), 1e-9); 111 | TestUtils.assertRelativelyEquals(aop, o2.getAOP(), 1e-9); 112 | } 113 | 114 | @Test 115 | public void withEccentricity() { 116 | Body b = new Body("A", 1); 117 | double a = 1; 118 | double e = 0.5; 119 | double i = Math.PI / 4; 120 | double lan = Math.PI / 2; 121 | double aop = Math.PI; 122 | 123 | Orbit o = new Orbit(b, a, e, i, lan, aop); 124 | Orbit o2 = o.withEccentricity(0.25); 125 | assertEquals(b, o2.getBody()); 126 | TestUtils.assertRelativelyEquals(a, o2.getSMA(), 1e-9); 127 | TestUtils.assertRelativelyEquals(0.25, o2.getEccentricity(), 1e-9); 128 | TestUtils.assertRelativelyEquals(i, o2.getInclination(), 1e-9); 129 | TestUtils.assertRelativelyEquals(lan, o2.getLAN(), 1e-9); 130 | TestUtils.assertRelativelyEquals(aop, o2.getAOP(), 1e-9); 131 | } 132 | 133 | @Test 134 | public void withInclination() { 135 | Body b = new Body("A", 1); 136 | double a = 1; 137 | double e = 0.5; 138 | double i = Math.PI / 4; 139 | double lan = Math.PI / 2; 140 | double aop = Math.PI; 141 | 142 | Orbit o = new Orbit(b, a, e, i, lan, aop); 143 | Orbit o2 = o.withInclination(Math.PI / 2); 144 | assertEquals(b, o2.getBody()); 145 | TestUtils.assertRelativelyEquals(a, o2.getSMA(), 1e-9); 146 | TestUtils.assertRelativelyEquals(e, o2.getEccentricity(), 1e-9); 147 | TestUtils.assertRelativelyEquals(Math.PI / 2, o2.getInclination(), 1e-9); 148 | TestUtils.assertRelativelyEquals(lan, o2.getLAN(), 1e-9); 149 | TestUtils.assertRelativelyEquals(aop, o2.getAOP(), 1e-9); 150 | } 151 | 152 | @Test 153 | public void withLAN() { 154 | Body b = new Body("A", 1); 155 | double a = 1; 156 | double e = 0.5; 157 | double i = Math.PI / 4; 158 | double lan = Math.PI / 2; 159 | double aop = Math.PI; 160 | 161 | Orbit o = new Orbit(b, a, e, i, lan, aop); 162 | Orbit o2 = o.withLAN(Math.PI / 4); 163 | assertEquals(b, o2.getBody()); 164 | TestUtils.assertRelativelyEquals(a, o2.getSMA(), 1e-9); 165 | TestUtils.assertRelativelyEquals(e, o2.getEccentricity(), 1e-9); 166 | TestUtils.assertRelativelyEquals(i, o2.getInclination(), 1e-9); 167 | TestUtils.assertRelativelyEquals(Math.PI / 4, o2.getLAN(), 1e-9); 168 | TestUtils.assertRelativelyEquals(aop, o2.getAOP(), 1e-9); 169 | } 170 | 171 | @Test 172 | public void withAOP() { 173 | Body b = new Body("A", 1); 174 | double a = 1; 175 | double e = 0.5; 176 | double i = Math.PI / 4; 177 | double lan = Math.PI / 2; 178 | double aop = Math.PI; 179 | 180 | Orbit o = new Orbit(b, a, e, i, lan, aop); 181 | Orbit o2 = o.withAOP(3 * Math.PI / 2); 182 | assertEquals(b, o2.getBody()); 183 | TestUtils.assertRelativelyEquals(a, o2.getSMA(), 1e-9); 184 | TestUtils.assertRelativelyEquals(e, o2.getEccentricity(), 1e-9); 185 | TestUtils.assertRelativelyEquals(i, o2.getInclination(), 1e-9); 186 | TestUtils.assertRelativelyEquals(lan, o2.getLAN(), 1e-9); 187 | TestUtils.assertRelativelyEquals(3 * Math.PI / 2, o2.getAOP(), 1e-9); 188 | } 189 | 190 | @Test 191 | public void withTrueAnomaly() { 192 | Body b = new Body("A", 1); 193 | double a = 1; 194 | double e = 0.5; 195 | double i = Math.PI / 4; 196 | double lan = Math.PI / 2; 197 | double aop = Math.PI; 198 | double ta = Math.PI / 2; 199 | 200 | Orbit o = new Orbit(b, a, e, i, lan, aop); 201 | OrbitAtTime oat = o.withTrueAnomaly(ta); 202 | 203 | assertEquals(b, oat.getBody()); 204 | TestUtils.assertRelativelyEquals(a, oat.getSMA(), 1e-9); 205 | TestUtils.assertRelativelyEquals(e, oat.getEccentricity(), 1e-9); 206 | TestUtils.assertRelativelyEquals(i, oat.getInclination(), 1e-9); 207 | TestUtils.assertRelativelyEquals(lan, oat.getLAN(), 1e-9); 208 | TestUtils.assertRelativelyEquals(aop, oat.getAOP(), 1e-9); 209 | TestUtils.assertRelativelyEquals(ta, oat.getTrueAnomaly(), 1e-9); 210 | } 211 | 212 | @Test 213 | public void period() { 214 | Body b = new Body("Earth", 398600441800000.0); 215 | double a = 6774500; 216 | double e = 0.0006531; 217 | double i = Math.toRadians(51.6443); 218 | double lan = Math.toRadians(117.8784); 219 | double aop = Math.toRadians(15.1371); 220 | 221 | Orbit o = new Orbit(b, a, e, i, lan, aop); 222 | 223 | TestUtils.assertRelativelyEquals(5549.154941027046, o.getPeriod(), 1e-9); 224 | } 225 | 226 | @Test 227 | public void meanAngularMotion() { 228 | Body b = new Body("Earth", 398600441800000.0); 229 | double a = 6774500; 230 | double e = 0.0006531; 231 | double i = Math.toRadians(51.6443); 232 | double lan = Math.toRadians(117.8784); 233 | double aop = Math.toRadians(15.1371); 234 | 235 | Orbit o = new Orbit(b, a, e, i, lan, aop); 236 | 237 | TestUtils.assertRelativelyEquals(0.00113227786, o.getMeanAngularMotion(), 1e-6); 238 | } 239 | 240 | } 241 | -------------------------------------------------------------------------------- /mainframe/src/main/java/hall/john/ksp/mainframe/LambertSolver.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; 7 | import org.apache.commons.math3.util.MathUtils; 8 | 9 | import hall.john.ksp.mainframe.numericalmethods.BrentsMethod; 10 | 11 | public class LambertSolver { 12 | // Converted from https://alexmoon.github.io/ksp/javascripts/lambert.js 13 | // Originally Based on Sun, F.T. "On the Minium Time Trajectory and Multiple 14 | // Solutions of Lambert's Problem" 15 | // AAS/AIAA Astrodynamics Conference, Provincetown, Massachusetts, AAS 16 | // 79-164, June 25-27, 1979 17 | 18 | public static double EPS = 1e-10; 19 | 20 | public static class Solution { 21 | public Vector3D v1; 22 | public Vector3D v2; 23 | public double angle; 24 | 25 | @Override 26 | public String toString() { 27 | StringBuilder strb = new StringBuilder(); 28 | strb.append(v1); 29 | strb.append(" "); 30 | strb.append(v2); 31 | strb.append(" "); 32 | strb.append(angle); 33 | return strb.toString(); 34 | } 35 | } 36 | 37 | // Provided 38 | private double _mu; 39 | private Vector3D _r1Vec; 40 | private Vector3D _r2Vec; 41 | private double _dt; 42 | private int _maxRevs; 43 | private boolean _prograde; 44 | 45 | // Calculated 46 | private double _sqrtMu; 47 | private double _invSqrtM; 48 | private double _invSqrtN; 49 | private double _c; 50 | private Vector3D _deltaPos; 51 | private double _r1; 52 | private double _r2; 53 | private double _transferAngle; 54 | private double _angleParameter; 55 | private double _angleParameter2; 56 | private double _angleParameter3; 57 | private double _parabolicNormalizedTime; 58 | private double _normalizedTime; 59 | private int _N; 60 | 61 | private List _solutions; 62 | 63 | public LambertSolver(double mu, Vector3D r1Vec, Vector3D r2Vec, double dt, int maxRevs, boolean prograde) { 64 | _mu = mu; 65 | _r1Vec = r1Vec; 66 | _r2Vec = r2Vec; 67 | _dt = dt; 68 | _maxRevs = maxRevs; 69 | _prograde = prograde; 70 | } 71 | 72 | public double getMu() { 73 | return _mu; 74 | } 75 | 76 | public Vector3D getR1Vec() { 77 | return _r1Vec; 78 | } 79 | 80 | public Vector3D getR2Vec() { 81 | return _r2Vec; 82 | } 83 | 84 | public double getDT() { 85 | return _dt; 86 | } 87 | 88 | public int getMaxRevs() { 89 | return _maxRevs; 90 | } 91 | 92 | public boolean getPrograde() { 93 | return _prograde; 94 | } 95 | 96 | public List getSolutions() { 97 | return _solutions; 98 | } 99 | 100 | private double acot(double x) { 101 | return 0.5 * Math.PI - Math.atan(x); 102 | } 103 | 104 | private double acoth(double x) { 105 | return 0.5 * Math.log((x + 1) / (x - 1)); 106 | } 107 | 108 | private double fy(double x) { 109 | double y = Math.sqrt(1 - _angleParameter * _angleParameter * (1 - x * x)); 110 | if (_angleParameter < 0) { 111 | return -y; 112 | } else { 113 | return y; 114 | } 115 | } 116 | 117 | private double ftau(double x) { 118 | if (x == 1) { 119 | return _parabolicNormalizedTime - _normalizedTime; 120 | } else { 121 | double y = fy(x); 122 | if (x > 1) { 123 | double g = Math.sqrt(x * x - 1); 124 | double h = Math.sqrt(y * y - 1); 125 | return (-acoth(x / g) + acoth(y / h) + x * g - y * h) / (g * g * g) - _normalizedTime; 126 | } else { 127 | double g = Math.sqrt(1 - x * x); 128 | double h = Math.sqrt(1 - y * y); 129 | return (acot(x / g) - Math.atan(h / y) - x * g + y * h + _N * Math.PI) / (g * g * g) - _normalizedTime; 130 | } 131 | } 132 | } 133 | 134 | private double phix(double x) { 135 | if (x == 0) 136 | x = EPS; 137 | if (x == 1) 138 | x = 1 - EPS; 139 | double g = Math.sqrt(1 - x * x); 140 | return acot(x / g) - (2 + x * x) * g / (3 * x); 141 | } 142 | 143 | private double phiy(double y) { 144 | if (y == 0) 145 | y = EPS; 146 | double h = Math.sqrt(1 - y * y); 147 | return Math.atan(h / y) - (2 + y * y) * h / (3 * y); 148 | } 149 | 150 | private void pushSolution(double x, double y, double N) { 151 | double vc = _sqrtMu * (y * _invSqrtN + x * _invSqrtM); 152 | double vr = _sqrtMu * (y * _invSqrtN - x * _invSqrtM); 153 | Vector3D ec = _deltaPos.scalarMultiply(vc / _c); 154 | Solution sol = new Solution(); 155 | sol.v1 = ec.add(_r1Vec.scalarMultiply(vr / _r1)); 156 | sol.v2 = ec.subtract(_r2Vec.scalarMultiply(vr / _r2)); 157 | sol.angle = MathUtils.TWO_PI * N + _transferAngle; 158 | _solutions.add(sol); 159 | } 160 | 161 | private void pushIfConverges(BrentsMethod mth, int N) { 162 | mth.execute(); 163 | if (mth.getConverged()) { 164 | pushSolution(mth.getResult(), fy(mth.getResult()), N); 165 | } 166 | } 167 | 168 | private double relativeError(double a, double b) { 169 | return Math.abs(1 - a / b); 170 | } 171 | 172 | public void solve() { 173 | _r1 = _r1Vec.getNorm(); 174 | _r2 = _r2Vec.getNorm(); 175 | _deltaPos = _r2Vec.subtract(_r1Vec); 176 | _c = _deltaPos.getNorm(); 177 | double m = _r1 + _r2 + _c; 178 | double n = _r1 + _r2 - _c; 179 | 180 | _transferAngle = Vector3D.angle(_r1Vec, _r2Vec); 181 | int pro = _prograde ? 1 : 0; 182 | if ((_r1Vec.getX() * _r2Vec.getY() - _r1Vec.getY() * _r2Vec.getX()) * pro < 0) { 183 | _transferAngle = MathUtils.TWO_PI - _transferAngle; 184 | } 185 | 186 | _angleParameter = Math.sqrt(n / m); 187 | if (_transferAngle > Math.PI) { 188 | _angleParameter *= -1; 189 | } 190 | _angleParameter2 = _angleParameter * _angleParameter; 191 | _angleParameter3 = _angleParameter2 * _angleParameter; 192 | 193 | _normalizedTime = 4 * _dt * Math.sqrt(_mu / (m * m * m)); 194 | _parabolicNormalizedTime = 2.0 / 3 * (1 - _angleParameter3); 195 | 196 | _N = 0; 197 | 198 | _sqrtMu = Math.sqrt(_mu); 199 | _invSqrtM = 1 / Math.sqrt(m); 200 | _invSqrtN = 1 / Math.sqrt(n); 201 | 202 | _solutions = new ArrayList(); 203 | 204 | if (relativeError(_normalizedTime, _parabolicNormalizedTime) < 1e-6) { 205 | double x = 1; 206 | double y = _angleParameter < 0 ? -1 : 1; 207 | pushSolution(x, y, 0); 208 | } else if (_normalizedTime < _parabolicNormalizedTime) { 209 | double x1 = 1; 210 | double x2 = 2; 211 | while (ftau(x2) >= 0) { 212 | x1 = x2; 213 | x2 = x2 * 2; 214 | } 215 | 216 | BrentsMethod mth = new BrentsMethod(this::ftau, x1, x2, 100, 1e-4); 217 | mth.execute(); 218 | if (!mth.getConverged()) 219 | throw new RuntimeException("failure"); 220 | double x = mth.getResult(); 221 | pushSolution(x, fy(x), _N); 222 | } else { 223 | int maxRevs = (int) Math.min(_maxRevs, Math.floor(_normalizedTime / Math.PI)); 224 | double minimumEnergyNormalizedTime = Math.acos(_angleParameter) 225 | + _angleParameter * Math.sqrt(1 - _angleParameter2); 226 | 227 | int i = 0; 228 | while (Math.abs(i) <= Math.abs(maxRevs)) { 229 | double minimumNormalizedTime = 0; 230 | double xMT = 0; 231 | 232 | if (_N > 0 && _N == maxRevs) { 233 | if (_angleParameter == 1) { 234 | xMT = 0; 235 | minimumNormalizedTime = minimumEnergyNormalizedTime; 236 | } else if (_angleParameter == 0) { 237 | BrentsMethod mth = new BrentsMethod(x -> phix(x) + _N * Math.PI, 0, 1, 100, 1e-4); 238 | mth.execute(); 239 | if (!mth.getConverged()) 240 | throw new RuntimeException("failure"); 241 | xMT = mth.getResult(); 242 | minimumNormalizedTime = 2 / (3 * xMT); 243 | } else { 244 | BrentsMethod mth = new BrentsMethod(x -> phix(x) - phiy(fy(x)) + _N * Math.PI, 0, 1, 100, 1e-4); 245 | mth.execute(); 246 | if (!mth.getConverged()) 247 | throw new RuntimeException("failure"); 248 | xMT = mth.getResult(); 249 | minimumNormalizedTime = (2 / 3.0) * (1 / xMT - _angleParameter3 / Math.abs(fy(xMT))); 250 | } 251 | 252 | if (relativeError(_normalizedTime, minimumNormalizedTime) < 1e-6) { 253 | pushSolution(xMT, fy(xMT), (_N + 1) * MathUtils.TWO_PI - _transferAngle); 254 | break; 255 | } else if (_normalizedTime < minimumNormalizedTime) { 256 | break; 257 | } else if (_normalizedTime < minimumEnergyNormalizedTime) { 258 | pushIfConverges(new BrentsMethod(this::ftau, 0, xMT, 100, 1e-4), _N); 259 | pushIfConverges(new BrentsMethod(this::ftau, xMT, 1 - EPS, 100, 1e-4), _N); 260 | break; 261 | } 262 | } 263 | 264 | if (relativeError(_normalizedTime, minimumEnergyNormalizedTime) < 1e-6) { 265 | pushSolution(0, fy(0), _N); 266 | if (_N > 0) { 267 | pushIfConverges(new BrentsMethod(this::ftau, 1e-6, 1 - EPS, 100, 1e-4), _N); 268 | } 269 | } else { 270 | if (_N > 0 || _normalizedTime > minimumEnergyNormalizedTime) { 271 | pushIfConverges(new BrentsMethod(this::ftau, -1 + EPS, 0, 100, 1e-4), _N); 272 | } 273 | if (_N > 0 || _normalizedTime < minimumEnergyNormalizedTime) { 274 | pushIfConverges(new BrentsMethod(this::ftau, 0, 1 - EPS, 100, 1e-4), _N); 275 | } 276 | } 277 | 278 | minimumEnergyNormalizedTime = minimumEnergyNormalizedTime + Math.PI; 279 | 280 | if (maxRevs >= 0) { 281 | i++; 282 | } else { 283 | i--; 284 | } 285 | _N = i; 286 | } 287 | } 288 | } 289 | 290 | } 291 | -------------------------------------------------------------------------------- /mainframe/src/main/java/hall/john/ksp/mainframe/LambertOptimizer.java: -------------------------------------------------------------------------------- 1 | package hall.john.ksp.mainframe; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.concurrent.ExecutorService; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; 11 | 12 | import hall.john.ksp.mainframe.LambertSolver.Solution; 13 | import hall.john.ksp.mainframe.orbit.ManeuverNode; 14 | import hall.john.ksp.mainframe.orbit.OrbitAtTime; 15 | 16 | public class LambertOptimizer { 17 | // Provided 18 | private double _mu; 19 | private OrbitAtTime _tgtOat0; 20 | private OrbitAtTime _oat0; 21 | private double _tMin; 22 | private double _tMax; 23 | private double _tStep; 24 | private double _dtMin; 25 | private double _dtMax; 26 | private double _dtStep; 27 | private boolean _allowLob; 28 | private boolean _optArrival; 29 | 30 | // Calculated 31 | private Candidate _best; 32 | private long _count; 33 | private long _numTCalcs; 34 | private long _numDTCalcs; 35 | 36 | private static int NUM_THREADS = 8; 37 | 38 | public LambertOptimizer(double mu, OrbitAtTime oat0, OrbitAtTime tgtOat0, double tMin, double tMax, double tStep, 39 | double dtMin, double dtMax, double dtStep, boolean allowLob, boolean optArrival) { 40 | _mu = mu; 41 | _tgtOat0 = tgtOat0; 42 | _oat0 = oat0; 43 | _tMin = tMin; 44 | _tMax = tMax; 45 | _tStep = tStep; 46 | _dtMin = dtMin; 47 | _dtMax = dtMax; 48 | _dtStep = dtStep; 49 | _allowLob = allowLob; 50 | _optArrival = optArrival; 51 | _numTCalcs = (long) ((_tMax - _tMin) / (_tStep)) + 1L; 52 | _numDTCalcs = (long) ((_dtMax - _dtMin) / (_dtStep)) + 1L; 53 | } 54 | 55 | private Candidate executeSingle(double t, double dt) { 56 | OrbitAtTime oatAtBurn1 = _oat0.afterTime(t); 57 | Vector3D r1 = oatAtBurn1.getRadiusVector(); 58 | 59 | OrbitAtTime tgtOatAtIntercept = _tgtOat0.afterTime(t + dt); 60 | Vector3D r2 = tgtOatAtIntercept.getRadiusVector(); 61 | 62 | LambertSolver ls = new LambertSolver(_mu, r1, r2, dt, 0, true); 63 | ls.solve(); 64 | 65 | List sols = ls.getSolutions(); 66 | if (sols.isEmpty()) { 67 | return null; 68 | } else { 69 | Vector3D v1 = sols.get(0).v1; 70 | Vector3D v2 = sols.get(0).v2; 71 | 72 | Candidate candidate = new Candidate(); 73 | candidate.dv1 = v1.subtract(oatAtBurn1.getVelocityVector()); 74 | candidate.dv2 = tgtOatAtIntercept.getVelocityVector().subtract(v2); 75 | candidate.dv = candidate.dv1.getNorm(); 76 | candidate.t = t; 77 | candidate.dt = dt; 78 | 79 | if (_optArrival) { 80 | candidate.dv += candidate.dv2.getNorm(); 81 | } 82 | 83 | if (!_allowLob) { 84 | double a = 1 / ((2 / r1.getNorm()) - (v1.getNormSq() / _mu)); 85 | Vector3D eVec = (v1.crossProduct(r1.crossProduct(v1))).scalarMultiply(1 / _mu).subtract(r1.normalize()); 86 | double e = eVec.getNorm(); 87 | double apoapsis = a * (1 + e); 88 | if (apoapsis > r2.getNorm()) 89 | return null; 90 | } 91 | 92 | return candidate; 93 | } 94 | } 95 | 96 | public void execute() { 97 | ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS); 98 | 99 | for (int i = 0; i < NUM_THREADS; i++) { 100 | final int threadNum = i; 101 | Runnable run = () -> { 102 | long count = 0; 103 | Candidate best = null; 104 | for (long tIdx = threadNum; tIdx < _numTCalcs; tIdx += NUM_THREADS) { 105 | double t = _tMin + tIdx * _tStep; 106 | for (double dtIdx = 0; dtIdx < _numDTCalcs; dtIdx++) { 107 | double dt = _dtMin + dtIdx * _dtStep; 108 | Candidate candidate = executeSingle(t, dt); 109 | if (candidate != null) { 110 | // OrbitAtTime orbitAtBurn = _oat0.afterTime(t); 111 | // ManeuverNode node = new ManeuverNode(orbitAtBurn, candidate.dv1); 112 | // OrbitAtTime newOAT = node.getResultingOAT(); 113 | // if (newOAT.getPeriapsis() < 6571000) { 114 | // continue; 115 | // } 116 | 117 | // OrbitAtTime orbitAtIntercept = newOAT.afterTime(dt); 118 | // OrbitAtTime targetAtIntercept = _tgtOat0.afterTime(t + dt); 119 | // Vector3D radiusAtIntercept = orbitAtIntercept.getRadiusVector(); 120 | // Vector3D targetRadiusAtIntercept = targetAtIntercept.getRadiusVector(); 121 | // double dist = targetRadiusAtIntercept.subtract(radiusAtIntercept).getNorm(); 122 | 123 | if (best == null || candidate.dv < best.dv) { 124 | best = candidate; 125 | } 126 | } 127 | count++; 128 | } 129 | } 130 | 131 | synchronized (LambertOptimizer.this) { 132 | if (_best == null || best.dv < _best.dv) { 133 | _best = best; 134 | } 135 | _count += count; 136 | } 137 | }; 138 | 139 | executor.execute(run); 140 | } 141 | 142 | try { 143 | executor.shutdown(); 144 | executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); 145 | } catch (InterruptedException e) { 146 | throw new RuntimeException(e); 147 | } 148 | } 149 | 150 | public Vector3D getDV1() { 151 | return _best.dv1; 152 | } 153 | 154 | public Vector3D getDV2() { 155 | return _best.dv2; 156 | } 157 | 158 | public Double getT() { 159 | return _best.t; 160 | } 161 | 162 | public Double getDT() { 163 | return _best.dt; 164 | } 165 | 166 | public long getCount() { 167 | return _count; 168 | } 169 | 170 | public static Map mainframe(List requestArgs) { 171 | double mu = Double.parseDouble(requestArgs.get(0)); 172 | Body b = new Body("mfBody", mu); 173 | 174 | double rx1 = Double.parseDouble(requestArgs.get(1)); 175 | double ry1 = Double.parseDouble(requestArgs.get(2)); 176 | double rz1 = Double.parseDouble(requestArgs.get(3)); 177 | Vector3D r1 = new Vector3D(rx1, ry1, rz1); 178 | 179 | double vx1 = Double.parseDouble(requestArgs.get(4)); 180 | double vy1 = Double.parseDouble(requestArgs.get(5)); 181 | double vz1 = Double.parseDouble(requestArgs.get(6)); 182 | Vector3D v1 = new Vector3D(vx1, vy1, vz1); 183 | OrbitAtTime oat0 = new OrbitAtTime(b, r1, v1); 184 | 185 | double rx2 = Double.parseDouble(requestArgs.get(7)); 186 | double ry2 = Double.parseDouble(requestArgs.get(8)); 187 | double rz2 = Double.parseDouble(requestArgs.get(9)); 188 | Vector3D r2 = new Vector3D(rx2, ry2, rz2); 189 | 190 | double vx2 = Double.parseDouble(requestArgs.get(10)); 191 | double vy2 = Double.parseDouble(requestArgs.get(11)); 192 | double vz2 = Double.parseDouble(requestArgs.get(12)); 193 | Vector3D v2 = new Vector3D(vx2, vy2, vz2); 194 | OrbitAtTime tgtOat0 = new OrbitAtTime(b, r2, v2); 195 | 196 | double tMin = Double.parseDouble(requestArgs.get(13)); 197 | double tMax = Double.parseDouble(requestArgs.get(14)); 198 | double tStep = Double.parseDouble(requestArgs.get(15)); 199 | 200 | double dtMin = Double.parseDouble(requestArgs.get(16)); 201 | double dtMax = Double.parseDouble(requestArgs.get(17)); 202 | double dtStep = Double.parseDouble(requestArgs.get(18)); 203 | 204 | boolean allowLob = Boolean.parseBoolean(requestArgs.get(19)); 205 | boolean optArrival = Boolean.parseBoolean(requestArgs.get(20)); 206 | 207 | System.out.println("mu = " + mu); 208 | System.out.println("r1 = " + Utils.formatVector(r1)); 209 | System.out.println("v1 = " + Utils.formatVector(v1)); 210 | System.out.println("r2 = " + Utils.formatVector(r2)); 211 | System.out.println("v2 = " + Utils.formatVector(v2)); 212 | System.out.println("t: (min: " + tMin + " step: " + tStep + " max: " + tMax + ")"); 213 | System.out.println("dt: (min: " + dtMin + " step: " + dtStep + " max: " + dtMax + ")"); 214 | System.out.println("allowLob = " + allowLob); 215 | System.out.println("optArrival = " + optArrival); 216 | 217 | double initialPhaseAngle = Math.toDegrees(oat0.phaseAngleWith(tgtOat0)); 218 | System.out.println("Initial Phase Angle: " + initialPhaseAngle + " degrees"); 219 | 220 | LambertOptimizer lo = new LambertOptimizer(mu, oat0, tgtOat0, tMin, tMax, tStep, dtMin, dtMax, dtStep, allowLob, 221 | optArrival); 222 | lo.execute(); 223 | 224 | Vector3D dv1 = lo.getDV1(); 225 | Vector3D dv2 = lo.getDV2(); 226 | double t = lo.getT(); 227 | double dt = lo.getDT(); 228 | 229 | System.out.println("dv1 = " + Utils.formatVector(dv1) + " (norm: " + dv1.getNorm() + ")"); 230 | System.out.println("dv2 = " + Utils.formatVector(dv2) + " (norm: " + dv2.getNorm() + ")"); 231 | System.out.println("t = " + t); 232 | System.out.println("dt = " + dt); 233 | 234 | OrbitAtTime orbitAtBurn = oat0.afterTime(t); 235 | OrbitAtTime targetAtBurn = tgtOat0.afterTime(t); 236 | System.out.println("radiusAtBurn = " + Utils.formatVector(orbitAtBurn.getRadiusVector())); 237 | System.out.println("targetRadiusAtBurn = " + Utils.formatVector(targetAtBurn.getRadiusVector())); 238 | 239 | ManeuverNode node = new ManeuverNode(orbitAtBurn, dv1); 240 | OrbitAtTime newOAT = node.getResultingOAT(); 241 | OrbitAtTime orbitAtIntercept = newOAT.afterTime(dt); 242 | OrbitAtTime targetAtIntercept = tgtOat0.afterTime(t + dt); 243 | Vector3D radiusAtIntercept = orbitAtIntercept.getRadiusVector(); 244 | Vector3D targetRadiusAtIntercept = targetAtIntercept.getRadiusVector(); 245 | 246 | System.out.println("radiusAtIntercept = " + Utils.formatVector(radiusAtIntercept)); 247 | System.out.println("targetRadiusAtIntercept = " + Utils.formatVector(targetRadiusAtIntercept)); 248 | double dist = targetRadiusAtIntercept.subtract(radiusAtIntercept).getNorm(); 249 | System.out.println("distance = " + dist); 250 | System.out.println("prograde = " + node.getPrograde()); 251 | System.out.println("normal = " + node.getNormal()); 252 | System.out.println("radial = " + node.getRadial()); 253 | 254 | Map result = new HashMap(); 255 | result.put("t", t); 256 | result.put("dt", dt); 257 | result.put("prograde", node.getPrograde()); 258 | result.put("normal", node.getNormal()); 259 | result.put("radial", node.getRadial()); 260 | return result; 261 | } 262 | 263 | private static class Candidate { 264 | public Vector3D dv1, dv2; 265 | public double t, dt, dv; 266 | } 267 | } 268 | --------------------------------------------------------------------------------