├── README.md └── launchtoCirc.ks /README.md: -------------------------------------------------------------------------------- 1 | launchToCirc code written for the kOS mod for Kerbal Space Program 2 | 3 | Launches most vessel configurations to the circular orbit of your choice without too much fuss on your part. 4 | -------------------------------------------------------------------------------- /launchtoCirc.ks: -------------------------------------------------------------------------------- 1 | // Auto-launch program for achieving circular orbits of desired altitude and inclination 2 | // Written by /u/only_to_downvote 3 | 4 | // USAGE: 5 | // run launchToCirc(, // in km (only required input) 6 | // [desired inclination], // in degrees, default value 0. (>0 for Northward, <0 for S) 7 | // [auto ascent T/F], // 'True' or 'False', default True 8 | // [turn end altitude], // in meters, default 35000 9 | // [turn exponent], // unitless, recommend 0.25-1.5 range, default 0.7. 10 | // [turn start altitude] ). // in meters, default is 2x starting altitude above terrain 11 | // 12 | 13 | // PREFERRED STAGING SETUP: 14 | // - Initial launch engines ignite with first staging action 15 | // - Second staging action releases launch clamps (and ignites solid boosters if applicable) 16 | // - Separation and next stage ignition can be either grouped as one stage or separate states 17 | // - Ullage motors (if used) assumed to be solid motors whose fuel mass is <5% of the total current 18 | // mass of the rocket and can be staged with or one stage before the main stage engines 19 | 20 | // ACTION GROUPS: 21 | // - Use action group 8 ONLY for fairing and/or launch escape system separation action (or 22 | // not at all), script triggers AG8 during ascent. Fairing separation can also be part of 23 | // standard staging, but should be combined with another separation event or engine ignition. 24 | // - Abort action group can be automatically triggered under certain (probably catastrophic) 25 | // circumstances. If desired assign launch escape systems accordingly 26 | 27 | // DEPENDANTS: 28 | // - none 29 | 30 | /////////////////////////////////////////////////// 31 | /////// BEGIN VARIABLES FOR TWEAKING LAUNCH ////// 32 | /////////////////////////////////////////////////// 33 | 34 | // Trajectory program variables 35 | PARAMETER targetApoapsis. 36 | PARAMETER targetInclination IS 0. 37 | PARAMETER autoAscent IS True. // Use if you want automatic "gravity turn" based on characteristics of the body you're launching from and the TWR of your rocket. Not gaurnteed to be the most efficient launch, but should be pretty good. 38 | 39 | PARAMETER turnEnd IS 35000. // Use if you want to set your own "gravity turn", ignored if above = True 40 | PARAMETER turnExponent IS 0.7. 41 | PARAMETER turnStart IS ALTITUDE + (ALTITUDE - SHIP:GEOPOSITION:TERRAINHEIGHT). 42 | 43 | // Staging delays (time after detection of engine shutdown before staging or between staging actions) 44 | SET boosterStageDelay TO 2. 45 | SET stageDelay TO 3. // Should be greater than 1 to prevent problems. 46 | 47 | // PID controller value & overrides 48 | SET throttleP TO 0.004. 49 | SET throttleD TO 0.004. 50 | SET STEERINGMANAGER:ROLLPID:KP TO 0. // Only enforce roll rate, not specific roll angle 51 | SET STEERINGMANAGER:ROLLPID:KI TO 0. 52 | 53 | // Miscellaneous 54 | SET allowAbort TO True. // whether or not to allow code to automatically trigger abort 55 | SET useWarp TO True. // whether or not to use timewarp to Apoapsis burn. Not recommended if using persistent rotation mod. 56 | SET logTimeIncrement TO 30. // base increment in seconds between periodic log entries; doubles while coasting, quadruples while timewarping) 57 | SET logVerboseData TO True. // turn verbose data log on or off 58 | SET verboseLogIncrement TO 1.0. // time increment for verbose log 59 | SET orbitErrorThreshold TO 5. // percent error allowed in orbital apoapsis or periapsis 60 | SET maxNumReboost TO 3. // maximum number of times re-boost burn is allowed before aborting if apoapsis if falling 61 | SET limitToTermV TO False. // works for any atmosphere model, terminal V based on ship's forces, but generally not all that useful. Need very high TWR to break terminal velocity. 62 | 63 | //////////////////////////////////////////////// 64 | /////// END VARIABLES FOR TWEAKING LAUNCH ////// 65 | //////////////////////////////////////////////// 66 | 67 | // Initial required variables 68 | SET targetApoapsis TO targetApoapsis*1000. 69 | IF SHIP:BODY:ATM:EXISTS{SET atmoHeight TO SHIP:BODY:ATM:HEIGHT.} ELSE SET atmoHeight TO 0. 70 | 71 | // Bad input checks 72 | IF targetApoapsis*(1-orbitErrorThreshold/100) < atmoHeight { 73 | PRINT "Minimum target apoapsis of "+targetApoapsis*(1-orbitErrorThreshold/100)+"m based on". 74 | PRINT "orbit error threshold is below atmosphere height". 75 | PRINT "of "+atmoHeight+"m". 76 | PRINT " ". 77 | PRINT "Use ctrl+c to cancel program and try again". 78 | PRINT " ". 79 | WAIT UNTIL False. 80 | }. 81 | IF ABS(targetInclination) < FLOOR(ABS(LATITUDE)) OR ABS(targetInclination) > (180 - CEILING(ABS(LATITUDE))) { 82 | PRINT "Desired inclination impossible. ". 83 | PRINT "Magnitude must be larger than or equal to the ". 84 | PRINT "current latitude of "+ROUND(LATITUDE,1)+" deg". 85 | PRINT " ". 86 | PRINT "Use ctrl+c to cancel program and try again". 87 | PRINT " ". 88 | WAIT UNTIL False. 89 | }. 90 | 91 | // Make sure all launch clamps are on a single stage 92 | SET launchClampStage TO 999. 93 | FOR p in SHIP:PARTS { 94 | IF p:MODULES:CONTAINS("LaunchClamp") { 95 | IF launchClampStage = 999 SET launchClampStage TO p:STAGE. 96 | ELSE IF p:STAGE <> launchClampStage { 97 | PRINT "All launch clamps not on one stage". 98 | PRINT " ". 99 | PRINT "Use ctrl+c to cancel program and try again". 100 | PRINT " ". 101 | WAIT UNTIL False. 102 | }. 103 | }. 104 | }. 105 | 106 | // Initiate settings 107 | clearscreen. unlock all. 108 | SET SHIP:CONTROL:PILOTMAINTHROTTLE TO 0. 109 | SET TERMINAL:WIDTH TO 50. 110 | SET TERMINAL:HEIGHT TO 40. 111 | SET CONFIG:IPU TO 500. 112 | SET maxLinesToPrint TO 24. // Max # of lines in scrolling list 113 | SET listLineStart TO 16. // First line for print scrolling list 114 | IF logVerboseData { 115 | LOG 1 TO launchDataLog.csv. DELETEPATH("launchDataLog.csv"). 116 | // Verbose data log header 117 | LOG "M.E.T. [s], Sea Level Altitude [m], Radar Altitude [m], Latitude [deg], Longitude [deg], Surface Velocity [m/s], Orbital Velocity [m/s], Vertical Speed [m/s], Ground Speed [m/s], Apoapsis [m], Time to Apoapsis [s], Periapsis [m], Time to Periapsis [s], Inclination [deg], Mass [t], Max Thrust [kN], Current Thrust [kN], T.W.R., % Terminal Velocity, Trajectory Preferred Pitch [deg], Pitch Command [deg], Vessel Pitch [deg], Heading Command [deg], Vessel Heading [deg], dv Spent [m/s], Dynamic Pressure [kPa]" TO launchDataLog.csv. 118 | }. 119 | // Define functions to be used in main execution loop 120 | // Scrolling print 121 | SET printList TO LIST(). 122 | FUNCTION scrollPrint { 123 | DECLARE PARAMETER nextPrint. 124 | printList:ADD(nextPrint). 125 | UNTIL printList:LENGTH <= maxLinesToPrint {printList:REMOVE(0).}. 126 | LOCAL currentLine IS listLineStart. 127 | FOR printLine in printList { 128 | PRINT " " AT (0,currentLine). 129 | PRINT printLine AT (0,currentLine). 130 | SET currentLine TO currentLine+1. 131 | }. 132 | }. 133 | // solid motor info for current stage (used for ullage staging operations) 134 | FUNCTION stageSolidInfo { 135 | LOCAL solidMinBurnTime IS 99999. 136 | LOCAL solidTotalThrust IS 0. 137 | LOCAL engList IS LIST(). 138 | LIST ENGINES IN engList. 139 | FOR e in engList { 140 | IF e:IGNITION AND e:RESOURCES:LENGTH <> 0 { 141 | FOR r in e:RESOURCES { 142 | IF r:NAME = "SolidFuel" { 143 | LOCAL isp IS e:ISP. IF isp = 0 SET isp TO 0.001. 144 | LOCAL thrust IS e:THRUST. 145 | SET solidTotalThrust TO solidTotalThrust + thrust. 146 | LOCAL mdot IS thrust/(isp*9.81). IF mdot = 0 SET mdot TO 0.001. 147 | LOCAL burnTime IS (r:AMOUNT*0.0075)/mdot. 148 | IF burnTime < solidMinBurnTime SET solidMinBurnTime TO burnTime. 149 | }. 150 | }. 151 | }. 152 | }. 153 | RETURN LIST(solidMinBurnTime, solidTotalThrust). 154 | }. 155 | FUNCTION activeEngineInfo { 156 | // ## should technically be updated to account for engines not pointing directly aft 157 | LIST ENGINES IN engList. 158 | LOCAL currentT IS 0. 159 | LOCAL maxT IS 0. 160 | LOCAL mDot IS 0. 161 | FOR eng IN engList { 162 | IF eng:IGNITION { 163 | SET maxT TO maxT + eng:AVAILABLETHRUST. 164 | SET currentT TO currentT + eng:THRUST. 165 | IF NOT eng:ISP = 0 SET mDot TO mDot + currentT / eng:ISP. 166 | }. 167 | }. 168 | IF mDot = 0 LOCAL avgIsp IS 0. 169 | ELSE LOCAL avgIsp IS currentT / mDot. 170 | RETURN LIST(currentT, maxT, avgIsp, mDot). 171 | }. 172 | // Function for estimating stage deltaV, doesn't correctly account for boosters in current form but generally only needs to be accurate near circularization burn and boosters are unlikely to be present then. 173 | FUNCTION deltaVstage { 174 | FOR p in SHIP:PARTS { 175 | IF p:MODULES:CONTAINS("CModuleFuelLine") { 176 | RETURN -1. // Unable to calculate deltaV if fuel lines complicate fuel flow pattern. 177 | }. 178 | }. 179 | 180 | // calculate total fuel masses (STAGE:RESOURCES doesn't work, returns fuel from non-active stages) 181 | LOCAL fuelMass IS STAGE:LIQUIDFUEL*0.005 + STAGE:OXIDIZER*0.005 + STAGE:SOLIDFUEL*0.0075. 182 | 183 | // thrust weighted average isp 184 | LOCAL thrustTotal IS 0. 185 | LOCAL mDotTotal IS 0. 186 | LOCAL engList IS LIST(). LIST ENGINES IN engList. 187 | FOR eng in engList { 188 | IF eng:IGNITION { 189 | LOCAL t IS eng:AVAILABLETHRUST. 190 | SET thrustTotal TO thrustTotal + t. 191 | IF eng:ISP <> 0 SET mDotTotal TO mDotTotal + (t / eng:ISP). 192 | }. 193 | }. 194 | IF mDotTotal <> 0 LOCAL avgIsp IS thrustTotal/mDotTotal. 195 | ELSE LOCAL avgIsp IS 0. 196 | 197 | // deltaV calculation as Isp*g*ln(m0/m1). 198 | LOCAL deltaV IS avgIsp*9.81*ln(SHIP:MASS / (SHIP:MASS-fuelMass)). 199 | 200 | RETURN deltaV. 201 | }. 202 | // required kOS lib functions included here for keeping program in a single file 203 | function compass_for { 204 | parameter ves. 205 | 206 | local pointing is ves:facing:forevector. 207 | local east is vcrs(ves:up:vector, ves:north:vector). 208 | 209 | local trig_x is vdot(ves:north:vector, pointing). 210 | local trig_y is vdot(east, pointing). 211 | 212 | local result is arctan2(trig_y, trig_x). 213 | 214 | if result < 0 { 215 | return 360 + result. 216 | } 217 | else { 218 | return result. 219 | } 220 | }. 221 | function pitch_for { 222 | parameter ves. 223 | 224 | return 90 - vang(ves:up:vector, ves:facing:forevector). 225 | }. 226 | 227 | // Loop variables initialize 228 | SET WARPMODE TO "PHYSICS". 229 | SET throt TO 0. 230 | LOCK THROTTLE to throt. // required for odd kOS bug with locking throttle multiple times in a script 231 | SET steerTo TO UP. 232 | SET launchTime TO TIME:SECONDS+6. 233 | SET logTime TO launchTime+logTimeIncrement. 234 | SET verboseLogTime TO launchTime. 235 | LOCK MET TO TIME:SECONDS-launchTime. 236 | SET launchLoc TO SHIP:GEOPOSITION. 237 | SET launchAlt TO ALTITUDE. 238 | SET currentStageNum TO 1. 239 | SET stagingInProgress TO False. 240 | SET launchComplete TO False. 241 | SET runMode TO -1. 242 | SET numParts TO SHIP:PARTS:LENGTH. 243 | SET boostBurn TO FALSE. 244 | SET trajectoryPitch TO 90. 245 | SET steerPitch TO 90. 246 | SET triggerStage TO False. 247 | SET numReboost TO 0. 248 | SET throttleSetting TO 1. 249 | SET boostStageTime TO TIME:SECONDS+100000. 250 | SET stageTime TO TIME:SECONDS+100000. 251 | SET ullageTime TO TIME:SECONDS+100000. 252 | SET ullageDetect TO False. 253 | SET ullageStageNeeded TO False. 254 | SET ullageThrust TO 0. 255 | SET ullageShutdown TO False. 256 | SET engIgnoreList TO LIST(). 257 | SET flameoutDetect TO False. 258 | SET dropTanksEmpty TO False. 259 | SET tMin5 TO TRUE. SET tMin4 TO TRUE. SET tMin3 TO TRUE. 260 | SET tMin2 TO TRUE. SET tMin1 TO TRUE. SET tMin0 TO TRUE. 261 | SET tMinHold TO False. 262 | SET timeStamp TO TIME:SECONDS. 263 | SET accelVector TO V(0,0,0). 264 | SET aeroForceLIST TO LIST(0,0,0). 265 | SET broke30s TO False. 266 | SET firstStageAllSolid TO True. 267 | SET pctTerminalVel TO "N/A". 268 | SET lastDVTime TO TIME:SECONDS. 269 | SET dVSpent TO 0. 270 | SET finalBurnTime TO 0. 271 | SET runOnce TO True. SET runOnce2 TO True. 272 | // ## detect whether using realfuels or stock fuels to set list 273 | SET liquidFuelResources TO LIST("LiquidFuel","Oxidizer"). 274 | 275 | PRINT " Current Mode =" AT (1,0). 276 | PRINT "==================================================" AT (0,2). 277 | PRINT "Sea Lvl. | Ground | Orbit" AT (0,3). 278 | PRINT " Alt. | Dist. | Incl." AT (0,4). 279 | PRINT " [km] | [km] | [deg]" AT (0,5). 280 | PRINT "----------------+----------------+----------------" AT (0,6). 281 | PRINT "Apoap. |Periap. | TWR" AT (0,7). 282 | PRINT " [km] | [km] |Max TWR" AT (0,8). 283 | PRINT " (ETA) | (ETA) |% Term V" AT (0,9). 284 | PRINT "----------------+----------------+----------------" AT (0,10). 285 | PRINT " Total | Stage | Spent" AT (0,11). 286 | PRINT "Vac. dV | dV | dV" AT (0,12). 287 | PRINT " [m/s] | [m/s] | [m/s]" AT (0,13). 288 | PRINT "==================================================" AT (0,14). 289 | 290 | // Fairing separation when above 95% of atmosphere height 291 | IF SHIP:BODY:ATM:EXISTS { 292 | WHEN ALTITUDE > atmoHeight*0.95 THEN { 293 | TOGGLE AG8. 294 | SET numParts TO SHIP:PARTS:LENGTH. 295 | printList:ADD("T+"+ROUND(MET,1)+" Fairing/LES separation"). 296 | LOG ("T+"+ROUND(MET,1)+" Fairing separation") TO launchLog.txt. 297 | }. 298 | }. 299 | ON ABORT SET runMode TO 666. 300 | 301 | // Calculate launch azimuth 302 | SET inertialAzimuth TO ARCSIN(MAX(MIN(COS(targetInclination) / COS(launchLoc:LAT),1),-1)). 303 | SET targetOrbitSpeed TO SQRT(SHIP:BODY:MU / (targetApoapsis+SHIP:BODY:RADIUS)). 304 | SET rotVelX TO targetOrbitSpeed*SIN(inertialAzimuth) - (6.2832*SHIP:BODY:RADIUS/SHIP:BODY:ROTATIONPERIOD). 305 | SET rotVelY TO targetOrbitSpeed*COS(inertialAzimuth). 306 | SET launchAzimuth TO ARCTAN(rotVelX / rotVelY). 307 | IF targetInclination < 0 {SET launchAzimuth TO 180-launchAzimuth.}. 308 | SET steerHeading TO launchAzimuth. 309 | 310 | // Main loop begin 311 | UNTIL launchComplete { 312 | // Countdown 313 | IF runMode = -1 { 314 | PRINT "Countdown" AT (17,0). 315 | 316 | IF MET >= -5 AND tMin5 { 317 | PRINT "55555" AT (43,35). 318 | PRINT "5 " AT (43,36). 319 | PRINT "5555 " AT (43,37). 320 | PRINT " 55" AT (43,38). 321 | PRINT "5555 " AT (43,39). 322 | SAS ON. 323 | scrollPrint("T-5.0 Launch stability assist system activated"). 324 | SET tMin5 TO FALSE. 325 | }. 326 | 327 | IF MET >= -4 AND tMin4 { 328 | PRINT "4 4 " AT (43,35). 329 | PRINT "4 4 " AT (43,36). 330 | PRINT "444444" AT (43,37). 331 | PRINT " 4 " AT (43,38). 332 | PRINT " 4 " AT (43,39). 333 | SET tMin4 TO FALSE. 334 | }. 335 | 336 | IF NOT firstStageAllSolid { 337 | IF engInfo[0] < 0.95*engInfo[1] { // require current thrust > 95% max thrust to launch 338 | SET tMinHold TO True. 339 | } 340 | ELSE { 341 | IF runOnce2 { 342 | scrollPrint("T"+ROUND(MET,1)+" Main engine at full thrust"). 343 | SET runOnce2 TO False. 344 | }. 345 | SET tMinHold TO False. 346 | }. 347 | }. 348 | 349 | IF MET >= -3 AND tMin3 { 350 | PRINT "33333 " AT (43,35). 351 | PRINT " 33" AT (43,36). 352 | PRINT " 333 " AT (43,37). 353 | PRINT " 33" AT (43,38). 354 | PRINT "33333 " AT (43,39). 355 | LOCAL stageNumber IS STAGE:NUMBER. 356 | FOR p in SHIP:PARTS { 357 | IF p:STAGE >= launchClampStage-1 { 358 | FOR resource IN p:RESOURCES { 359 | IF liquidFuelResources:CONTAINS(resource:NAME) { 360 | SET firstStageAllSolid TO False. 361 | }. 362 | }. 363 | }. 364 | }. 365 | IF firstStageAllSolid { 366 | SET tMin3 TO FALSE. 367 | } 368 | ELSE { 369 | STAGE. 370 | SET numParts TO SHIP:PARTS:LENGTH. 371 | SET ignitionTime to TIME:SECONDS. 372 | scrollPrint("T-3.0 Main engine ignition sequence begin"). 373 | LOCK throt TO (TIME:SECONDS-ignitionTime)/2. 374 | SET tMin3 TO FALSE. 375 | }. 376 | }. 377 | 378 | IF MET >= -2 AND tMin2 { 379 | PRINT " 2222 " AT (43,35). 380 | PRINT "2 22" AT (43,36). 381 | PRINT " 22 " AT (43,37). 382 | PRINT "22 " AT (43,38). 383 | PRINT "222222" AT (43,39). 384 | SET tMin2 TO FALSE. 385 | }. 386 | 387 | IF MET >= -1 AND tMin1 { 388 | PRINT " 11 " AT (43,35). 389 | PRINT " 1 1 " AT (43,36). 390 | PRINT " 1 " AT (43,37). 391 | PRINT " 1 " AT (43,38). 392 | PRINT " 11111" AT (43,39). 393 | SET tMin1 TO FALSE. 394 | }. 395 | 396 | IF MET >= -0.1 AND tMinHold { 397 | PRINT " HOLD " AT (43,35). 398 | PRINT " HOLD " AT (43,36). 399 | PRINT " HOLD " AT (43,37). 400 | PRINT " HOLD " AT (43,38). 401 | PRINT " HOLD " AT (43,39). 402 | IF runOnce { 403 | SET runOnce TO False. 404 | scrollPrint("T-X.X HOLD - Waiting for engines to spool up"). 405 | }. 406 | SET launchTime TO TIME:SECONDS+0.1. 407 | SET throt to 1. 408 | }. 409 | 410 | IF MET >= 0 AND tMin0 { 411 | SET throt to 1. 412 | STAGE. 413 | SET numParts TO SHIP:PARTS:LENGTH. 414 | IF STAGE:SOLIDFUEL > 0 { 415 | scrollPrint("T-0.0 SRB Ignition"). 416 | }. 417 | scrollPrint("T+0.0 Liftoff!"). 418 | SET runMode to 0. 419 | SET tMin0 TO FALSE. 420 | }. 421 | 422 | // Attempts at dealing with improper staging sequence 423 | IF VERTICALSPEED > 1 OR ALTITUDE < launchAlt-2 { 424 | SET runMode TO 0. 425 | SET throt TO 1. 426 | scrollPrint("T"+ROUND(MET,1)+" EARLY RELEASE! Attempting to launch now"). 427 | IF MAXTHRUST < 0.1 { 428 | scrollPrint("T"+ROUND(MET,1)+" No active engines, staging"). 429 | WAIT UNTIL STAGE:READY. // required delay between staging actions 430 | STAGE. 431 | SET numParts TO SHIP:PARTS:LENGTH. 432 | }. 433 | } 434 | ELSE IF ALTITUDE < launchAlt+2 AND STAGE:SOLIDFUEL > 0 AND tMin0 { 435 | SET runMode TO 0. 436 | SET throt TO 1. 437 | scrollPrint("T"+ROUND(MET,1)+" Early SRB Ignition! Launching faster"). 438 | FOR p IN SHIP:PARTS { 439 | IF p:MODULES:CONTAINS("LaunchClamp") { 440 | WAIT UNTIL STAGE:READY. // required delay between staging actions 441 | STAGE. 442 | SET numParts TO SHIP:PARTS:LENGTH. 443 | BREAK. 444 | }. 445 | }. 446 | }. 447 | 448 | // One-time actions before initiating vertical ascent 449 | IF runMode = 0 { 450 | WAIT 0. 451 | SET launchTime TO TIME:SECONDS. 452 | LOCK MET TO TIME:SECONDS-launchTime. 453 | SET logTime TO launchTime+logTimeIncrement. 454 | SET engInfo TO activeEngineInfo(). 455 | SET launchTWR TO engInfo[1]/(SHIP:MASS*BODY:MU/(ALTITUDE+BODY:RADIUS)^2). 456 | PRINT " " AT (43,35). 457 | PRINT " " AT (43,36). 458 | PRINT " " AT (43,37). 459 | PRINT " " AT (43,38). 460 | PRINT " " AT (43,39). 461 | SET speedErrT0 TO 0. 462 | SET timeStamp TO TIME:SECONDS. 463 | IF autoAscent { // Auto adjust pitch based on TWR 464 | SET turnEnd TO 0.128*atmoHeight*launchTWR + 0.5*atmoHeight. // Based on testing 465 | SET turnExponent TO MAX(1/(2.5*launchTWR - 1.7), 0.25). // Based on testing 466 | printList:ADD("T+"+ROUND(MET,1)+" Using auto ascent trajectory with paramaters:"). 467 | printList:ADD(" Turn End Alt. = "+ROUND(turnEnd)). 468 | scrollPrint(" Turn Exponent = "+ROUND(turnExponent,3)). 469 | }. 470 | }. 471 | }. 472 | 473 | // Initial vertical ascent to clear support structures 474 | IF runMode = 0 { 475 | PRINT "Initial vertical ascent" AT (17,0). 476 | IF WARP > 1 SET WARP TO 1. // limit physwarp to 2x for code stability 477 | IF ALTITUDE > turnStart { 478 | SET runMode TO 1. 479 | scrollPrint("T+"+ROUND(MET,1)+" Tower cleared, starting ascent guidance"). 480 | SAS OFF. 481 | LOCK STEERING TO steerTo. 482 | }. 483 | }. 484 | 485 | // Ascent trajectory program until reach desired apoapsis 486 | IF runMode = 1 { 487 | PRINT "Ascent trajectory program" AT (17,0). 488 | IF WARP > 1 SET WARP TO 1. // limit physwarp to 2x for code stability 489 | 490 | IF stagingInProgress { 491 | SET ascentSteer TO SHIP:SRFPROGRADE. //Steer to surface prograde while staging 492 | } 493 | ELSE { 494 | // Ship pitch control 495 | SET trajectoryPitch TO max(90-(((ALTITUDE-turnStart)/(turnEnd-turnStart))^turnExponent*90),0). 496 | SET steerPitch TO trajectoryPitch. 497 | 498 | //Keep time to apoapsis > 30s during ascent once it is above 30s 499 | IF broke30s AND ETA:APOAPSIS < 30 SET steerPitch TO steerPitch+(30-ETA:APOAPSIS). 500 | ELSE IF ETA:APOAPSIS > 30 AND NOT broke30s SET broke30s TO True. 501 | 502 | // Ship compass heading control 503 | IF ABS(SHIP:OBT:INCLINATION - ABS(targetInclination)) > 2 { 504 | SET steerHeading TO launchAzimuth. 505 | } 506 | ELSE { // Feedback loop once close to desired inclination 507 | IF targetInclination >= 0 { 508 | IF VANG(VXCL(SHIP:UP:VECTOR, SHIP:FACING:VECTOR), SHIP:NORTH:VECTOR) <= 90 { 509 | SET steerHeading TO (90-targetInclination) - 2*(ABS(targetInclination) - SHIP:OBT:INCLINATION). 510 | } 511 | ELSE { 512 | SET steerHeading TO (90-targetInclination) + 2*(ABS(targetInclination) - SHIP:OBT:INCLINATION). 513 | }. 514 | } 515 | ELSE IF targetInclination < 0 { 516 | SET steerHeading TO (90-targetInclination) + 2*(ABS(targetInclination) - SHIP:OBT:INCLINATION). 517 | }. 518 | }. 519 | 520 | SET ascentSteer TO HEADING(steerHeading, steerPitch). 521 | 522 | // Don't pitch too far off surface prograde while under high dynamic pressrue 523 | IF SHIP:Q > 0 SET angleLimit TO MAX(3, MIN(90, 5*LN(0.9/SHIP:Q))). 524 | ELSE SET angleLimit TO 90. 525 | SET angleToPrograde TO VANG(SHIP:SRFPROGRADE:VECTOR,ascentSteer:VECTOR). 526 | IF angleToPrograde > angleLimit { 527 | SET ascentSteerLimited TO (angleLimit/angleToPrograde * (ascentSteer:VECTOR:NORMALIZED - SHIP:SRFPROGRADE:VECTOR:NORMALIZED)) + SHIP:SRFPROGRADE:VECTOR:NORMALIZED. 528 | SET ascentSteer TO ascentSteerLimited:DIRECTION. 529 | }. 530 | }. 531 | SET steerTo TO ascentSteer. 532 | 533 | // Throttle 534 | IF SHIP:BODY:ATM:EXISTS AND ALTITUDE<0.9*atmoHeight { 535 | IF TIME:SECONDS > timeStamp { 536 | LIST SENSORS IN sensorList. 537 | SET haveAccel TO False. 538 | FOR sens IN sensorList { 539 | IF sens:TYPE = "ACC" SET haveAccel TO True. 540 | }. 541 | // calculate terminal velocity based on gravity force = aerodynamics forces 542 | IF haveAccel { // ## no longer working? 543 | SET accelVec TO SHIP:SENSORS:ACC. 544 | SET thrustVec TO SHIP:FACING:VECTOR:NORMALIZED * engInfo[0]. 545 | SET gravVec TO -1 * UP:VECTOR:NORMALIZED * (BODY:MU * SHIP:MASS / (BODY:RADIUS + ALTITUDE)^2). 546 | SET aeroVec TO (accelVec * SHIP:MASS) - thrustVec - gravVec. 547 | aeroForceList:ADD(aeroVec:MAG * COS(VANG(-1 * UP:VECTOR, aeroVec))). // only vertical speed matters 548 | aeroForceList:REMOVE(0). 549 | SET aeroForce TO (aeroForceList[0] + aeroForceList[1] + aeroForceList[2]) / 3. // smooth out with moving avg 550 | SET speedErrT1 TO gravVec:MAG - aeroForce. 551 | SET pctTerminalVel TO 100 * SQRT(ABS(aeroForce / gravVec:MAG)). 552 | SET dSpeedErr TO (speedErrT1 - speedErrT0)/(TIME:SECONDS - timeStamp). 553 | SET throttleSetting TO throttleSetting + (throttleP*speedErrT1 + throttleD*dSpeedErr). 554 | IF limitToTermV AND NOT stagingInProgress SET throt TO MIN(MAX(throttleSetting,0),1). 555 | ELSE IF NOT stagingInProgress SET throt TO 1. 556 | SET timeStamp TO TIME:SECONDS. 557 | SET speedErrT0 TO speedErrT1. 558 | } 559 | ELSE IF NOT stagingInProgress { 560 | SET pctTerminalVel TO "NoAcc". 561 | SET throt TO 1. 562 | }. 563 | }. 564 | } 565 | ELSE IF NOT stagingInProgress { 566 | SET throt TO 1. 567 | SET pctTerminalVel TO "N/A". 568 | }. 569 | 570 | // Ascent mode end conditions 571 | IF APOAPSIS >= targetApoapsis { 572 | SET throt TO 0. 573 | SET trajectoryPitch TO 0. 574 | SET steerPitch TO 0. 575 | scrollPrint("T+"+ROUND(MET,1)+" Desired apoapsis reached"). 576 | SET pctTerminalVel TO "N/A". 577 | IF ALTITUDE < atmoHeight { 578 | SET runMode TO 2. 579 | scrollPrint("T+"+ROUND(MET,1)+" Steering prograde until out of atmosphere"). 580 | } 581 | ELSE { 582 | SET runMode TO 3. 583 | }. 584 | }. 585 | }. 586 | 587 | // Coast out of atmosphere 588 | IF runMode = 2 { 589 | PRINT "Coast out of atmosphere " AT (17,0). 590 | IF WARP > 1 SET WARP TO 1. // limit physwarp to 2x for code stability 591 | SET steerTo TO SHIP:SRFPROGRADE. 592 | IF ALTITUDE > atmoHeight { 593 | SET runMode TO 3. 594 | }. 595 | IF APOAPSIS < (1-orbitErrorThreshold/100)*targetApoapsis AND NOT(boostBurn) { 596 | SET boostBurn TO True. 597 | SET numReboost TO numReboost+1. 598 | scrollPrint("T+"+ROUND(MET,1)+" Apoapsis dropping, reigniting engines"). 599 | SET throt TO 1. 600 | }. 601 | IF APOAPSIS > targetApoapsis AND boostBurn { 602 | SET throt TO 0. 603 | scrollPrint("T+"+ROUND(MET,1)+" Desired apoapsis re-reached"). 604 | SET boostBurn to False. 605 | }. 606 | }. 607 | 608 | // Circularization burn maneuver node setup 609 | IF runMode = 3 { 610 | SET throt TO 0. 611 | printList:ADD("T+"+ROUND(MET,1)+" Generating circularization maneuver node"). 612 | SET rPeriapsis TO PERIAPSIS + SHIP:BODY:RADIUS. 613 | SET rApoapsis TO APOAPSIS + SHIP:BODY:RADIUS. 614 | SET nodeDeltaV TO SQRT(SHIP:BODY:MU/(rApoapsis))*(1-SQRT(2*rPeriapsis/(rPeriapsis+rApoapsis))). 615 | SET burnNode TO node(TIME:SECONDS+ETA:APOAPSIS, 0, 0, nodeDeltaV). ADD burnNode. 616 | printList:ADD("T+"+ROUND(MET,1)+" Circ. burn = "+ROUND(nodeDeltaV,1)+" m/s in "+ROUND(ETA:APOAPSIS)+" s"). 617 | SET runMode TO 4. 618 | scrollPrint("T+"+ROUND(MET,1)+" Steering to maneuver node"). 619 | }. 620 | 621 | // One time check to decouple stage if nearly depleted 622 | IF runMode = 4 { 623 | SET throt TO 0. 624 | SET runMode TO 4.5. 625 | IF stageDeltaV >= 0 { // dV alculator returns -1 if unable to calculate 626 | IF (stageDeltaV < nodeDeltaV*0.5 AND nodeDeltaV > 200) OR stageDeltaV < 100 { 627 | SET triggerStage TO True. 628 | scrollPrint("T+"+ROUND(MET,1)+" Low fuel in stage, separating"). 629 | }. 630 | }. 631 | }. 632 | 633 | // Potential waiting on staging action if triggered 634 | IF runMode = 4.5 { 635 | SET throt TO 0. 636 | IF NOT (triggerStage OR stagingInProgress OR SHIP:MAXTHRUST < 0.01) { 637 | SET runMode TO 5. 638 | SET burnTime TO nodeDeltaV/(SHIP:AVAILABLETHRUST/SHIP:MASS). 639 | SET leadTime to 0.5*burnTime. 640 | }. 641 | }. 642 | 643 | // Steer to maneuver node 644 | IF runMode = 5 { 645 | SET throt TO 0. 646 | PRINT "Steer to burn node " AT (17,0). 647 | IF WARP > 0 {SET WARP TO 0.}. // enforce warp 0 in case user previously set physwarp so runmode 6 will be timewarp not physwarp 648 | SET steerTo TO burnNode. 649 | SET stErrX TO burnNode:BURNVECTOR:NORMALIZED:X - FACING:VECTOR:NORMALIZED:X. 650 | SET stErrY TO burnNode:BURNVECTOR:NORMALIZED:Y - FACING:VECTOR:NORMALIZED:Y. 651 | SET stErrZ TO burnNode:BURNVECTOR:NORMALIZED:Z - FACING:VECTOR:NORMALIZED:Z. 652 | SET stErr TO sqrt(stErrX^2+stErrY^2+stErrZ^2). 653 | IF useWarp { 654 | IF stErr < 0.01 { 655 | SET runMode TO 6. 656 | scrollPrint("T+"+ROUND(MET,1)+" Warping to ignition time"). 657 | SET failedToSteer TO False. 658 | } 659 | ELSE IF burnNode:ETA <=leadtime { 660 | SET runMode TO 8. 661 | printList:ADD("T+"+ROUND(MET,1)+" Failed to steer to node before burn start"). 662 | scrollPrint(" Igniting engines and hoping for the best"). 663 | SET failedToSteer TO True. 664 | SET throt TO 1. 665 | }. 666 | } 667 | ELSE IF NOT useWarp { 668 | IF burnNode:ETA <= leadtime { 669 | SET runMode TO 8. 670 | IF stErr < 0.1 {SET failedToSteer TO False.}. 671 | ELSE {SET failedToSteer TO True.}. 672 | }. 673 | }. 674 | }. 675 | 676 | // Warp to node 677 | IF runMode = 6 { 678 | SET throt TO 0. 679 | PRINT "Warping to ignition time " AT (17,0). 680 | SET WARPMODE TO "RAILS". 681 | WARPTO(TIME:SECONDS + burnNode:ETA - leadTime - 10). // 10 second buffer 682 | SET runMode TO 7. 683 | }. 684 | // Wait for warp 685 | IF runMode = 7 { 686 | SET throt TO 0. 687 | IF burnNode:ETA <= leadTime { 688 | SET throt TO 1. 689 | SET runMode TO 8. 690 | scrollPrint("T+"+ROUND(MET,1)+" Executing circularization maneuver"). 691 | }. 692 | ELSE SET steerTo TO burnNode. 693 | }. 694 | 695 | // Apoapsis circularization maneuver execution 696 | IF runMode = 8 { 697 | PRINT "Executing circularization burn" AT (17,0). 698 | LOCK burnTimeRemaining TO burnNode:DELTAV:MAG/MAX(AVAILABLETHRUST/SHIP:MASS,0.001). 699 | // Stage if staging required before burn end and would put debris in orbit ### TODO 700 | IF burnTimeRemaining > 2 { 701 | SET throt TO 1. 702 | SET steerTo TO burnNode. 703 | IF failedToSteer AND APOAPSIS > (1+orbitErrorThreshold/100)*targetApoapsis AND PERIAPSIS > atmoHeight { 704 | printList:ADD("T+"+ROUND(MET,1)+" Overshot apoapsis, but in stable orbit"). 705 | scrollPrint(" Stopping circularization burn now"). 706 | SET throt TO 0. 707 | SET launchComplete TO True. 708 | }. 709 | } 710 | ELSE { 711 | UNLOCK STEERING. 712 | SAS ON. 713 | scrollPrint("T+"+ROUND(MET,1)+" Burn nearly complete, timing remainder"). 714 | wait 0.001. 715 | SET finalBurnTime TO burnTimeRemaining - 0.1. // -0.1s time adjustment based on testing. 716 | SET burnEndTime TO TIME:SECONDS + finalBurnTime. 717 | WAIT UNTIL TIME:SECONDS > burnEndTime. 718 | SET throt TO 0. 719 | UNLOCK burnTimeRemaining. 720 | scrollPrint("T+"+ROUND(MET,1)+" Circularization burn complete"). 721 | SET launchComplete TO True. 722 | }. 723 | 724 | }. 725 | 726 | // Perform abort if determined necessary 727 | IF runMode = 666 { 728 | SET throt TO 0. 729 | SET SHIP:CONTROL:NEUTRALIZE TO True. 730 | SAS ON. 731 | TOGGLE ABORT. 732 | scrollPrint("T+"+ROUND(MET,1)+" ~~~~~Launch aborted!~~~~~"). 733 | HUDTEXT("Launch Aborted!",5,2,100,RED,False). 734 | BREAK. 735 | }. 736 | 737 | //Continuous staging check logic 738 | IF runMode > 0 { 739 | // Staging triggers 740 | IF (runMode = 1 OR runMode = 8) AND NOT stagingInProgress { 741 | // Engine flameout detection 742 | LIST ENGINES IN engList. 743 | FOR eng IN engList { 744 | IF NOT engIgnoreList:CONTAINS(eng:UID) { 745 | // If flameout is just ullage motors stopping 746 | IF eng:FLAMEOUT AND ullageDetect { 747 | SET ullageShutdown TO True. 748 | engIgnoreList:ADD(eng:UID). 749 | } 750 | // If flameout is due to a booster shutdown only 751 | ELSE IF eng:FLAMEOUT AND MAXTHRUST >= 0.1 { 752 | SET flameoutDetect TO True. 753 | SET stagingInProgress TO True. 754 | scrollPrint("T+"+ROUND(MET,1)+" Booster shutdown detected"). 755 | SET boostStageTime TO TIME:SECONDS+boosterStageDelay. 756 | BREAK. 757 | } 758 | // If flameout is entire stage engine shutdown 759 | ELSE IF eng:FLAMEOUT AND MAXTHRUST < 0.1 { 760 | SET flameoutDetect TO True. 761 | SET stagingInProgress TO True. 762 | SET throt TO 0. 763 | scrollPrint("T+"+ROUND(MET,1)+" Stage "+currentStageNum+" shutdown detected"). 764 | SET stageTime TO TIME:SECONDS+stageDelay. 765 | BREAK. 766 | }. 767 | }. 768 | }. 769 | IF ullageShutdown { 770 | SET ullageDetect TO False. 771 | SET ullageShutdown TO False. 772 | scrollPrint("T+"+ROUND(MET,1)+" Ullage shutdown"). 773 | }. 774 | // Drop tanks empty detection 775 | // IF NOT(flameoutDetect) { 776 | // ## TODO 777 | // SET dropTanksEmpty TO True. 778 | // }. 779 | }. 780 | 781 | // Staging triggered elsewhere in code 782 | IF triggerStage { 783 | SET throt TO 0. 784 | scrollPrint("T+"+ROUND(MET,1)+" Staging triggered"). 785 | SET stageTime TO TIME:SECONDS+stageDelay. 786 | SET triggerStage TO False. 787 | SET stagingInProgress TO True. 788 | }. 789 | 790 | // Booster staging (after specified delay) 791 | IF TIME:SECONDS >= boostStageTime { 792 | STAGE. 793 | SET numParts TO SHIP:PARTS:LENGTH. 794 | scrollPrint("T+"+ROUND(MET,1)+" Booster separation"). 795 | SET boostStageTime TO TIME:SECONDS+100000. 796 | SET stagingInProgress TO False. 797 | }. 798 | 799 | // Full staging 800 | IF TIME:SECONDS >= stageTime { 801 | STAGE. 802 | SET numParts TO SHIP:PARTS:LENGTH. 803 | // drop tank release 804 | IF dropTanksEmpty { 805 | scrollPrint("T+"+ROUND(MET,1)+" Drop tanks released"). 806 | SET stageTime TO TIME:SECONDS+100000. 807 | SET dropTanksEmpty TO False. 808 | }. 809 | // Detect ullage motor ignition 810 | SET stageSolidFuelMass TO 0.0075*STAGE:SOLIDFUEL. 811 | IF stageSolidFuelMass < 0.05*SHIP:MASS AND stageSolidFuelMass > 0 { 812 | SET ullageDetect TO True. 813 | scrollPrint("T+"+ROUND(MET,1)+" Ullage motor ignition detected"). 814 | LOCAL temp IS stageSolidInfo(). 815 | SET ullageTime TO temp[0] + TIME:SECONDS. 816 | IF temp[0] = 99999 SET ullageDetect TO False. //should never happen, but just in case 817 | SET ullageThrust TO temp[1]. 818 | LOCAL temp IS activeEngineInfo(). 819 | IF temp[1] < ullageThrust*1.1 SET ullageStageNeeded TO True. 820 | SET stageTime TO TIME:SECONDS+100000. 821 | } 822 | // Detect separation only (ignition on the next stage) 823 | ELSE IF SHIP:MAXTHRUST < 0.01 { 824 | scrollPrint("T+"+ROUND(MET,1)+" Stage "+currentStageNum+" separation"). 825 | SET stageTime TO TIME:SECONDS+stageDelay. 826 | } 827 | // Ignite next stage if already primed by separation action 828 | ELSE IF SHIP:MAXTHRUST >= 0.01 AND NOT ullageDetect { 829 | IF runMode = 1 OR runMode = 8 SET throt TO 1. // don't ignite if coasting 830 | SET currentStageNum TO currentStageNum+1. 831 | scrollPrint("T+"+ROUND(MET,1)+" Stage "+currentStageNum+" ignition"). 832 | SET stageTime TO TIME:SECONDS+100000. 833 | SET stagingInProgress TO False. 834 | }. 835 | }. 836 | 837 | // Ullage staging 838 | IF TIME:SECONDS >= (ullageTime - 1) { //start engines 1s before ullage engines stop 839 | IF ullageStageNeeded { 840 | WAIT UNTIL STAGE:READY. 841 | STAGE. 842 | SET ullageStageNeeded TO False. 843 | }. 844 | IF runMode = 1 OR runMode = 8 SET throt TO 1. 845 | SET currentStageNum TO currentStageNum+1. 846 | scrollPrint("T+"+ROUND(MET,1)+" Stage "+currentStageNum+" ignition"). 847 | SET stageTime TO TIME:SECONDS+100000. 848 | SET ullageTime TO TIME:SECONDS+100000. 849 | SET stagingInProgress TO False. 850 | }. 851 | }. 852 | 853 | //Continuous abort detection logic 854 | 855 | IF allowAbort { 856 | // Angle to desired steering > 45deg (i.e. steering control loss) 857 | IF runMode = 1 { 858 | IF VANG(SHIP:FACING:VECTOR, steerTo:VECTOR) > 45 AND MET > 5 { 859 | SET runMode TO 666. 860 | scrollPrint("T+"+ROUND(MET,1)+" Ship lost steering control!"). 861 | }. 862 | }. 863 | 864 | // Abort if falling back toward surface (i.e. insufficient thrust) 865 | IF runMode < 3 AND runMode >= 0 AND VERTICALSPEED < -1.0 { 866 | SET runMode TO 666. 867 | scrollPrint("T+"+ROUND(MET,1)+" Insufficient vertical velocity!"). 868 | }. 869 | 870 | // Abort if # parts less than expected (i.e. ship breaking up) 871 | IF SHIP:PARTS:LENGTH < numParts AND STAGE:READY { 872 | SET runMode TO 666. 873 | scrollPrint("T+"+ROUND(MET,1)+" Ship breaking apart!"). 874 | }. 875 | 876 | // Abort if number of re-boost are too many (i.e. too shallow trajectory) 877 | IF numReboost > maxNumReboost { 878 | SET runMode TO 666. 879 | scrollPrint("T+"+ROUND(MET,1)+" Too many re-boosts, trajectory poor"). 880 | }. 881 | 882 | // Perform abort if insufficient total deltaV vs deltaV to orbit ###TODO 883 | }. 884 | 885 | // Continuous informational printouts 886 | PRINT ROUND(ALTITUDE/1000,2)+" " AT (8,4). 887 | SET downRangeDist TO SQRT(launchLoc:Distance^2 - (ALTITUDE-launchAlt)^2). // #@ should update to use curvature 888 | PRINT ROUND(downRangeDist/1000,2)+" " AT (25,4). 889 | PRINT ROUND(SHIP:OBT:INCLINATION,1)+" " AT (44,4). 890 | PRINT ROUND(APOAPSIS/1000,2)+" " AT (8,8). 891 | PRINT ROUND(ETA:APOAPSIS) + "s " AT (9,9). 892 | PRINT ROUND(PERIAPSIS/1000,2)+" " AT (24,8). 893 | PRINT ROUND(ETA:PERIAPSIS) + "s " AT (26,9). 894 | SET engInfo TO activeEngineInfo(). 895 | SET currentTWR TO engInfo[0]/(SHIP:MASS*BODY:MU/(ALTITUDE+BODY:RADIUS)^2). 896 | SET maxTWR TO engInfo[1]/(SHIP:MASS*BODY:MU/(ALTITUDE+BODY:RADIUS)^2). 897 | PRINT ROUND(currentTWR,2)+" " AT (44,7). 898 | PRINT ROUND(maxTWR,2) + " " AT (44,8). 899 | IF pctTerminalVel = "N/A" OR pctTerminalVel = "NoAcc" { 900 | PRINT pctTerminalVel + " " AT (44,9). 901 | }. 902 | ELSE { 903 | PRINT ROUND(pctTerminalVel,0) + " " AT (44,9). 904 | }. 905 | SET shipDeltaV TO "TBD". // ## TODO 906 | PRINT shipDeltaV AT (9,12). 907 | SET stageDeltaV TO deltaVStage(). 908 | PRINT ROUND(stageDeltaV)+" " AT (26,12). 909 | IF lastDVTime < TIME:SECONDS AND finalBurnTime = 0 { 910 | SET dVSpent TO dVSpent + ((engInfo[0]/SHIP:MASS) * (TIME:SECONDS - lastDVTime)). 911 | SET lastDVTime TO TIME:SECONDS. 912 | } 913 | ELSE IF finalBurnTime > 0 { 914 | SET dVSpent TO dVSpent + ((engInfo[1]/SHIP:MASS) * finalBurnTime). 915 | }. 916 | PRINT ROUND(dVSpent,0) + " " AT (44,12). 917 | 918 | //Periodic logging of progress 919 | IF TIME:SECONDS > logTime { 920 | printList:ADD("T+"+ROUND(MET,0)+" Velocity = "+ROUND(VELOCITY:ORBIT:MAG,2)+" m/s"). 921 | printList:ADD(" Altitude = "+ROUND(ALTITUDE/1000,2)+" km"). 922 | scrollPrint(" Downrange distance = "+ROUND(downRangeDist/1000,2)+" km"). 923 | IF runMode < 3 {SET logTime TO logTime + logTimeIncrement.}. 924 | ELSE IF runMode < 6 {SET logTime TO logTime + 2*logTimeIncrement.}. 925 | ELSE SET logTime TO logTime + 4*logTimeIncrement. 926 | }. 927 | 928 | // Verbose data logging if requested 929 | IF logVerboseData and TIME:SECONDS >= verboseLogTime { 930 | LOG MET+", "+ALTITUDE+", "+ALT:RADAR+", "+LATITUDE+", "+LONGITUDE+", "+SHIP:AIRSPEED+", "+VELOCITY:ORBIT:MAG+", "+VERTICALSPEED+", "+GROUNDSPEED+", "+APOAPSIS+", "+ETA:APOAPSIS+", "+PERIAPSIS+", "+ETA:PERIAPSIS+", "+SHIP:OBT:INCLINATION+", "+SHIP:MASS+", "+engInfo[1]+", "+engInfo[0]+", "+currentTWR+", "+pctTerminalVel+", "+trajectoryPitch+", "+steerPitch+", "+pitch_for(SHIP)+", "+steerHeading+", "+compass_for(SHIP)+", "+dVSpent+", "+SHIP:Q TO launchDataLog.csv. 931 | SET verboseLogTime TO TIME:SECONDS + verboseLogIncrement. 932 | }. 933 | }. 934 | // Main loop end 935 | 936 | SET throt TO 0. 937 | UNLOCK STEERING. 938 | 939 | IF launchComplete { 940 | remove burnNode. 941 | printList:ADD("T+"+ROUND(MET,1)+" Orbit achieved"). 942 | printList:ADD("-------------------------------------------"). 943 | printList:ADD(" Final apoapsis = "+ROUND(APOAPSIS/1000,2)+ 944 | " km, Error = "+ROUND(ABS(targetApoapsis-APOAPSIS)/1000,2)+" km"). 945 | printList:ADD(" Final periapsis = "+ROUND(PERIAPSIS/1000,2)+ 946 | " km, Error = "+ROUND(ABS(targetApoapsis-PERIAPSIS)/1000,2)+" km"). 947 | printList:ADD(" Final inclination = "+ROUND(SHIP:OBT:INCLINATION,1)+ 948 | " deg, Error = "+ROUND(ABS(ABS(targetInclination)-SHIP:OBT:INCLINATION),1)+" deg"). 949 | printList:ADD(" Total dV spent = "+ROUND(dVSpent)+" m/s"). 950 | printList:ADD("-------------------------------------------"). 951 | scrollPrint("Program ended successfully"). 952 | } 953 | ELSE { 954 | scrollPrint("Program terminating"). 955 | }. 956 | --------------------------------------------------------------------------------