Created
February 24, 2019 16:41
-
-
Save fellipec/5da81d8df9b4c139f395bc47d69b7c3d to your computer and use it in GitHub Desktop.
Kerbal kOS landing script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///////////////////////////////////////////////////////////////////////////// | |
// Land | |
///////////////////////////////////////////////////////////////////////////// | |
// Make groundfall. Try to avoid a rapid unplanned disassemble. | |
// Warranty void if used with air | |
// | |
// Usage: RUN LANDVAC(<mode>,<latitude>,<longitude>). | |
// | |
// Parameters: | |
// <mode>: Can be TARG, COOR or SHIP. | |
// -TARG (default) will try to land on the selected target. | |
// If has no valid target falls back to SHIP. | |
// -COOR will try to land on <latitude> and <longitude>. | |
// -SHIP will try to land in the coordinates that ship is | |
// flying over when the program start. | |
///////////////////////////////////////////////////////////////////////////// | |
// General logic: | |
// 0) Be in a circular, zero inclination orbit. | |
// 1) Calculate a Hohmann Transfer with: | |
// - Target altitude = 1% of body radius above ground | |
// 2) Calculate a phase angle so the periapsis of the new orbit will be right over the landing site | |
// 3) Take a point 270º before the landing site and do the plane change | |
// 4) Do the deorbit burn | |
// LandMode defines how this program will work | |
PARAMETER LandMode is "TARG". | |
PARAMETER LandLat is ship:geoposition:lat. | |
PARAMETER LandLng is ship:geoposition:lng. | |
LOCAL MaxHVel is 1. | |
LOCAL FinalBurnHeight is 50. | |
runoncepath("lib_ui"). | |
runoncepath("lib_util"). | |
runoncepath("lib_land"). | |
SAS OFF. | |
BAYS OFF. | |
GEAR OFF. | |
LADDERS OFF. | |
DrawDebugVectors on. | |
// ************ | |
// MAIN PROGRAM | |
// ************ | |
// DEORBIT SEQUENCE | |
if ship:status = "ORBITING" { | |
if NOT body:atm:exists uiWarning("Deorbit","Warning: This program works best with atmosphere."). | |
// Zero the orbit inclination | |
IF abs(OBT:INCLINATION) > 0.1 { | |
uiBanner("Deorbit","Setting an equatorial orbit"). | |
RUNPATH("node_inc_equ.ks",0). | |
RUNPATH("node.ks"). | |
} | |
// Circularize the orbit | |
if obt:eccentricity > 0.01 { | |
uiBanner("Deorbit","Circularizing the orbit"). | |
run circ. | |
} | |
// Find where to land | |
if LandMode:contains("TARG") { | |
if hastarget and TARGET:BODY = SHIP:BODY { // Make sure have a target in the same planet at least! Note it doesn't check if target is landed/splashed, will just use it's position, for all it cares. | |
set LandLat to utilLongitudeTo360(TARGET:GEOPOSITION:LAT). | |
set LandLng to utilLongitudeTo360(TARGET:GEOPOSITION:LNG). | |
} | |
else { //KSC Coordinates | |
set LandLat to -0.0483334. | |
set LandLng to -74.724722. | |
} | |
} | |
else if LandMode:contains("COOR") { | |
set LandLat to utilLongitudeTo360(LandLat). | |
set LandLng to utilLongitudeTo360(LandLng). | |
} | |
else if LandMode:contains("SHIP") { | |
set LandLat to utilLongitudeTo360(ship:geoposition:lat). | |
set LandLng to utilLongitudeTo360(ship:geoposition:lng). | |
} | |
else { | |
uiFatal("Land","Invalid mode"). | |
} | |
SET LandingSite to LATLNG(LandLat,LandLng). | |
//Define the deorbit periapsis | |
local DeorbitRad to ship:body:radius/2.5. | |
// Find a phase angle for the landing | |
// The landing burning is like a Hohmann transfer, but to an orbit close to the body surface | |
local r1 is ship:orbit:semimajoraxis. //Orbit now | |
local r2 is DeorbitRad . // Target orbit | |
local pt is 0.5 * ((r1+r2) / (2*r2))^1.5. // How many orbits of a target in the target (deorbit) orbit will do. | |
local sp is sqrt( ( 4 * constant:pi^2 * r2^3 ) / body:mu ). // Period of the target orbit. | |
local DeorbitTravelTime is pt*sp. // Transit time | |
local phi is (DeorbitTravelTime/ship:body:rotationperiod) * 360. // Phi in this case is not the angle between two orbits, but the angle the body rotates during the transit time | |
local IncTravelTime is ship:obt:period / 4. // Travel time between change of inclinationa and lower perigee | |
local phiIncManeuver is (IncTravelTime/ship:body:rotationperiod) * 360. | |
// Deorbit and plane change longitudes | |
Set Deorbit_Long to utilLongitudeTo360(LandLng - 60). | |
Set PlaneChangeLong to utilLongitudeTo360(LandLng - 150). | |
// Plane change for landing site | |
local vel is velocityat(ship, landTimeToLong(PlaneChangeLong)):orbit. | |
local inc is LandingSite:lat. | |
local TotIncDV is 2 * vel:mag * sin(inc / 2). | |
local nDv is vel:mag * sin(inc). | |
local pDV is vel:mag * (cos(inc) - 1 ). | |
if TotIncDV > 0.1 { // Only burn if it matters. | |
uiBanner("Deorbit","Burning dV of " + round(TotIncDV,1) + " m/s @ anti-normal to change plane."). | |
LOCAL nd IS NODE(time:seconds + landTimeToLong(PlaneChangeLong+phiIncManeuver), 0, -nDv, pDv). | |
add nd. run node. | |
} | |
// Lower orbit over landing site | |
local Deorbit_dV is landDeorbitDeltaV(DeorbitRad-body:radius). | |
uiBanner("Deorbit","Burning dV of " + round(Deorbit_dV,1) + " m/s retrograde to deorbit."). | |
LOCAL nd IS NODE(time:seconds + landTimeToLong(Deorbit_Long+phi) , 0, 0, Deorbit_dV). | |
add nd. run node. | |
uiBanner("Deorbit","Deorbit burn done"). | |
wait 5. // Let's have some time to breath and look what's happening | |
// Warp to ATM | |
uiBanner("Deorbit","Time warping until atmosphere"). | |
SAS OFF. | |
SET KUNIVERSE:TIMEWARP:MODE TO "RAILS". | |
SET KUNIVERSE:TIMEWARP:WARP to 2. | |
WAIT UNTIL SHIP:ALTITUDE < BODY:ATM:HEIGHT * 1.2. | |
KUNIVERSE:TIMEWARP:CANCELWARP(). | |
wait until kuniverse:timewarp:issettled. | |
uiBanner("Deorbit","Going butt first"). | |
SAS ON. | |
WAIT 3. | |
SET SASMODE TO "RETROGRADE". | |
WAIT 2. | |
SET NAVMODE TO "SURFACE". | |
WAIT 2. | |
uiBanner("Deorbit","Retrograde"). | |
PANELS OFF. | |
RADIATORS OFF. | |
LEGS OFF. | |
} | |
// Try to land | |
if ship:status = "SUB_ORBITAL" or ship:status = "FLYING" { | |
local TouchdownSpeed is 2. | |
local BurnStarted is false. | |
//PID Throttle | |
SET ThrottlePID to PIDLOOP(0.06,0.03,0.02). // Kp, Ki, Kd | |
SET ThrottlePID:MAXOUTPUT TO 1. | |
SET ThrottlePID:MINOUTPUT TO 0. | |
SET ThrottlePID:SETPOINT TO 0. | |
//Fuel Burning Time | |
DECLARE function AverageISP { | |
LIST ENGINES IN myVariable. | |
LOCAL N is 0. | |
LOCAL TIsp is 0. | |
FOR eng IN myVariable { | |
SET TIsp to TIsp + eng:ISP. | |
SET N TO N + 1. | |
} | |
return TIsp/N. | |
} | |
DECLARE function FuelTime { | |
Local FuelMass IS SHIP:MASS - SHIP:DRYMASS. | |
If FuelMass > 0 and Ship:AvailableThrust > 0 { | |
return FuelMass / (Ship:AvailableThrust / (AverageISP() * Constant:g0)). | |
} | |
Else { | |
Return 0. | |
} | |
} | |
// Math and parameters | |
Lock fTime to FuelTime(). | |
local g is body:mu / ((body:radius)^2). | |
lock ShipVelocity to SHIP:velocity:surface. | |
lock ShipWeight to (Ship:Mass * g). | |
lock accl to (Ship:AvailableThrust - ShipWeight) / Ship:Mass. | |
lock dTime to ShipVelocity:MAG / accl. | |
lock BurnDist to (ShipVelocity:MAG * dTime) - (0.5*accl*(dTime^2)). | |
lock BurnAlt to BurnDist + FinalBurnHeight. | |
local NeedKillHV is False. | |
//Check Stages | |
until Ship:AvailableThrust > ShipWeight or stage:number = 0 { | |
uiBanner("Suicide burn","Staging rocket for landing."). | |
if stage:ready Stage. | |
wait 1. | |
} | |
If stage:number = 0 and Ship:AvailableThrust < ShipWeight { | |
uiBanner("Suicide burn","This ship can't do a propulsive landing. Good luck."). | |
chutes on. | |
wait until chutes and landRadarAltimeter() < 1000. | |
} | |
Until dTime < fTime { | |
if DrawDebugVectors { | |
PRINT "Needed burn time " + dTime + " " at (0,0). | |
Print "Available burn time " + fTime + " " at (0,1). | |
} | |
} | |
uiBanner("Suicide burn","Steering and waiting for burn."). | |
SAS OFF. | |
LIGHTS ON. //We want the Kerbals to see where they are going right? | |
LEGS OFF. | |
// Throttle and Steering | |
local tVal is 0. | |
lock Throttle to tVal. | |
local sDir is ship:up. | |
lock steering to sDir. | |
// Main landing loop | |
UNTIL SHIP:STATUS = "LANDED" OR SHIP:STATUS = "SPLASHED" { | |
WAIT 0. | |
//****************** | |
// Steer the rocket | |
//****************** | |
SET ShipVelocity TO SHIP:velocity:surface. | |
SET ShipHVelocity to vxcl(SHIP:UP:VECTOR,ShipVelocity). | |
// Default scenario, try to compensate for horizontal velocity while brake | |
SET SteerVector to -ShipVelocity - ShipHVelocity. | |
If NeedKillHV and landRadarAltimeter() > FinalBurnHeight { // Take care of excessive horizontal velocity | |
SET SteerVector to - ShipHVelocity. | |
IF ShipHVelocity:MAG < MaxHVel OR ShipHVelocity:MAG < ShipVelocity:MAG * 0.4 NeedKillHV Off. | |
} | |
Else { | |
// If the horizontal velocity is low enough, just compensate for ship velocity. | |
IF ShipHVelocity:MAG < MaxHVel { | |
SET SteerVector to -ShipVelocity. | |
NeedKillHV Off. | |
} | |
// Near touchdown make sure the ship is pointed straight up. | |
ELSE IF landRadarAltimeter() < FinalBurnHeight SET SteerVector to SHIP:UP:VECTOR. | |
// When the horizontal velocity is the major part of ship velocity, try to kill it first | |
ELSE IF ShipHVelocity:MAG > ShipVelocity:MAG * 0.5 { | |
SET SteerVector to - ShipHVelocity. | |
NeedKillHV On. | |
} | |
// If ship is going upwards, steer prograde. This should not happen usually. | |
ELSE IF Ship:Verticalspeed > 0 SET SteerVector to ShipVelocity. | |
} | |
set sDir TO SteerVector:Direction. | |
//********************* | |
// Throttle the rocket | |
//********************* | |
set TargetVSpeed to min(landRadarAltimeter() / TouchdownSpeed,TouchdownSpeed*accl). | |
IF Not BurnStarted and landRadarAltimeter() < BurnAlt { | |
uiBanner("Suicide burn","Burning!"). | |
BurnStarted On. | |
} | |
ELSE IF BurnStarted | |
{ | |
set tVal TO ThrottlePID:UPDATE(TIME:seconds,(SHIP:VERTICALSPEED + TargetVSpeed)). | |
} | |
// Use RCS to help remove horizontal velocity | |
if BurnStarted AND ShipHVelocity:mag > MaxHVel { | |
RCS ON. | |
local sense is ship:facing. | |
local dirV is V( | |
vdot(ShipHVelocity, sense:starvector), | |
vdot(ShipHVelocity, sense:upvector), | |
vdot(ShipHVelocity, sense:vector) | |
). | |
set ship:control:translation to -dirV:normalized. | |
} | |
else { | |
set ship:control:translation to v(0,0,0). | |
} | |
// Check for fuel | |
if BurnStarted and fTime < 1 { | |
chutes on. | |
} | |
// Deploy Legs | |
IF BurnStarted AND dTime < 5 OR landRadarAltimeter() < FinalBurnHeight LEGS ON. | |
if DrawDebugVectors { | |
SET DRAWSV TO VECDRAW(v(0,0,0),SteerVector, red, "", 1, true, 1). // Steering | |
SET DRAWV TO VECDRAW(v(0,0,0),ShipVelocity, green, "", 1, true, 1). // Velocity | |
SET DRAWHV TO VECDRAW(v(0,0,0),ShipHVelocity, YELLOW, "", 1, true, 1). //Horizontal Velocity | |
//SET DRAWTV TO VECDRAW(v(0,0,0),TargetVector, Magenta, "Target", 1, true, 1). | |
PRINT "Vertical speed " + abs(Ship:VERTICALSPEED) + " " at (0,0). | |
Print "Target Vspeed " + TargetVSpeed + " " at (0,1). | |
print "Throttle " + tVal + " " at (0,2). | |
print "Ship Velocity " + ShipVelocity:MAG + " " at (0,3). | |
print "Ship height " + landRadarAltimeter() + " " at (0,4). | |
print " " at (0,5). | |
Print "Burn Alt " + BurnAlt + " " at (0,6). | |
Print "Burn Time " + dTime + " " at (0,7). | |
Print "accl " + accl + " " at (0,8). | |
Print "Fuel Time " + FTime + " " at (0,9). | |
} | |
} | |
// Release controls | |
UNLOCK THROTTLE. UNLOCK STEERING. | |
SET SHIP:CONTROL:NEUTRALIZE TO TRUE. | |
SET SHIP:CONTROL:PILOTMAINTHROTTLE TO 0. | |
clearvecdraws(). | |
LADDERS ON. | |
SAS ON. // Helps to don't tumble after landing | |
} | |
else if ship:status = "ORBITING" uiError("Land","This ship is still in orbit!?"). | |
else if ship:status = "LANDED" or ship:status = "SPLASHED" uiError("Land","We are already landed, nothing to do here, move along"). | |
else uiError("Land","Can't land from " + ship:status). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment