T+0.0. ALT 10. VEL 0. GAM 90.0. Vertical launch. Cape Canaveral, Pad 39. Computer: not a UNIVAC 1100.

“I have not been able to get the test case from the report working yet.”

Ralph Carmichael, author of PDAS – the largest public archive of aerospace software. Program MSC-13914 is listed on his site as “work in progress.” Since 2009. Seventeen years.

The program was written in 1970. In FORTRAN IV. For a UNIVAC 1100 computer that hasn’t existed for half a century. Documentation – four volumes, 700 pages, test data preserved only in illegible dot-matrix printer scans. Nobody has been able to run it.

The Archive

In 2024, NASA published the COSMIC archive – a catalog of software the agency distributed through the University of Georgia from the 1960s through the 1990s. Hundreds of programs for computing orbits, aerodynamics, structural loads. Most in FORTRAN, targeting long-vanished machines.

Among them – MSC-13914. Space Shuttle Synthesis Program. Developed by General Dynamics in 1970 under contract NAS9-11193 for the Manned Spacecraft Center in Houston. 7,811 lines of FORTRAN IV. Automates trajectory, weight, and performance calculations for a two-stage Space Shuttle system.

  1. The Shuttle hadn’t flown yet – Columbia’s first flight was still 11 years away. The program was built for predesign studies: “will this configuration work?” Not for controlling a real flight – for evaluating concepts.

I found it as a single text file. 608 kilobytes. ELT format – a punch card image for the UNIVAC 1100 loader. No Makefile, no readable documentation. Just code.

Autopsy

Remarkably, after nearly sixty years, getting the program to run on a modern computer required no major surgery. Ten fixes total. None touching logic – only half-century-old syntax. IFIX() instead of INT(). ALOG() instead of LOG(). A PUNCH statement for card output. A typo in the original: HARTBL87) instead of HARTBL(7). UNIVAC loader directives that have nothing to do with FORTRAN.

gfortran -ffixed-form -std=legacy -fno-automatic -o sssp sssp_fixed.f

It compiles. 2026, Apple Silicon, macOS.

But -fno-automatic isn’t cosmetic. It’s the key to everything. Original FORTRAN IV on UNIVAC stored local variables statically. Subroutine STORE saved orbiter data through entry point ORBSTO and restored it through ORBCAL – a different entry point in the same subroutine. Without -fno-automatic, gfortran puts local variables on the stack, and data evaporates between calls. The program compiles, runs, and produces garbage. A silent, insidious bug that Ralph Carmichael probably never found.

Dead End

First quest cleared. The program builds. But running it requires input data – 300 weight coefficients, aerodynamic tables, a pitch schedule.

The original test case exists – in Appendix VII of NASA CR-114985. I found the PDF. Opened it. Scanned pages. Dot-matrix printouts. Every page marked: “REPRODUCIBILITY OF THE ORIGINAL PAGE IS POOR.” Digits blur together. 3 is indistinguishable from 8.

But even if the scans were legible – the 1970 test case doesn’t describe the real Shuttle. It’s a General Dynamics concept: a fully reusable two-stage system, both stages winged. Nothing in common with what actually flew in 1981.

If we can’t read the old data – we’ll create new data. For the real Shuttle.

Wikipedia as a Data Source

We have a program that can calculate weights and trajectories of spacecraft. But it has 300 tuning coefficients and not a single known value.

What we do have are answers. Not the coefficients – but what the right coefficients produce.

Orbiter dry weight: 151,000 pounds. Wikipedia. Wing weight: 16,200 pounds. NASA weight statements. Thermal protection: 18,895 pounds. Wikipedia. RS-25 engines: 7,004 pounds each, 512,300 pounds vacuum thrust, ISP 452.3 seconds. Wikipedia. Solid rocket boosters: 1,100,000 pounds of propellant, 123 seconds burn time. Wikipedia. Launch pad LC-39 coordinates: 28.608 N, 80.604 W. Google Maps.

Data from an encyclopedia and fact sheets. Rounded. Averaged across missions. But sufficient.

Program-in-the-Loop

The classic approach to reviving such code: read the documentation, understand the meaning of each of the 300 coefficients, tune them by hand. For a program with illegible documentation and a half-century gap in context – a dead end.

A different approach: reverse engineering. We know the answer – wing weight equals 16,200 pounds. We know the formula inside SSSP: WWING = C(1) * f(geometry) + C(2) * area + C(3). We need to find C(1), C(2), C(3) such that the formula gives the right answer.

Not for one component – for all of them simultaneously. 14 target categories: wing, fuselage, thermal protection, landing gear, engines, hydraulics, electrical, avionics, orientation control, crew, payload. Each category – a number from Wikipedia. Each depends on several C() coefficients. The coefficients are coupled: changing C(15) (fuselage structure) affects total weight, which affects required propellant, which affects tank volume, which affects body surface area, which affects thermal protection.

A nonlinear system with cross-dependencies. Manual tuning is impossible – changing one coefficient shifts ten others.

Solution: scipy.optimize.minimize, Nelder-Mead method. The objective function is the sum of squared relative errors across all 14 categories. At each iteration of the optimizer:

  1. Generate an SSSP input file with current C() coefficients
  2. Run the Fortran program as a subprocess
  3. Parse stdout – find “WEIGHT BREAKDOWN – ORBITAL STAGE”
  4. Extract 14 numbers, compare against Wikipedia targets
  5. Return the error to the optimizer

SSSP-in-the-loop. A 1970 Fortran program as a black box inside a 2026 Python optimizer. Each call – 2 seconds. 429 calls. Five minutes.

The same method – for the trajectory. Different target points: altitude, velocity, and flight path angle at T+60, T+90, T+120, T+200, T+300 seconds – from STS mission data. Different parameters: average SRB thrust, specific impulse, liftoff weight. Same loop: generate input, run SSSP, parse trajectory, compare against targets, minimize error.

And for aerodynamics – the same method. Drag coefficient scale as a parameter, target points – altitude and flight path angle from real flight reconstructions.

Three optimizers. One principle: don’t guess parameters – fit them to known answers.

Not Overfitting

A critical question: did we simply rig the numbers to produce a pretty result? Did we create an unphysical vehicle that exists only in the optimizer’s parameter space?

Every coefficient found maps to real components.

C(15) = 39,448 pounds – “basic fuselage structure.” This isn’t a magic number. It’s forward fuselage (10,800) + mid fuselage (8,000) + aft fuselage (8,000) + payload bay doors (4,600) + secondary structure (8,048). A sum of real components, each known from NASA weight statements.

C(180) = 1.44 pounds per square foot – thermal protection insulation unit weight. Real HRSI tiles: 1.5 lb/ft2. LRSI: 0.8. RCC on the nose and leading edges: 4.0. Our value is a weighted average. An engineer designing thermal protection would arrive at a similar number.

Average SRB thrust: 2,814,000 pounds. Real peak: 3,300,000, at liftoff: 2,700,000. Our value falls between them. For a constant-thrust model, this is the only physically correct approximation.

Aerodynamics: the optimizer returned CD_SCALE = 1.0. It didn’t touch the drag coefficients. Open-source data used as-is. Transonic drag rise of 2.2x – within the 2.0-2.5x range typical of launch vehicles.

The optimizer didn’t create an unphysical vehicle. It found the Shuttle in parameter space. Every coefficient is explainable. Every one falls within real engineering ranges. An aerospace engineer looking at these numbers would recognize the configuration.

This isn’t overfitting. It’s calibration.

The Flight

The weight model is statics. Dynamics is where it gets interesting.

SSSP contains a complete trajectory module: three-dimensional equations of motion over a rotating oblate Earth with J2-J4 gravity harmonics. A fifth-order Runge-Kutta-Butcher integrator with adaptive step control – 245 lines, double precision for intermediate values (a luxury in 1970). Bilinear interpolation of aero coefficients by Mach number and angle of attack. Cape Kennedy Reference Atmosphere 1963 – 193 data points from sea level to 2.3 million feet. A propulsion model with ten operating modes.

All of it – original 1970 code. Not a single formula changed.

Getting it to work was its own adventure: dozens of undocumented flags, each silently disabling an entire module if not set correctly. Defaults in subroutine TRAJC that overwrite input parameters after they’re read. A singularity at zero velocity. But that’s engineering archaeology, of interest mainly to specialists. Details are in the EXPERIMENT_LOG on GitHub.

The result: aerodynamics from open data (11 points of axial force coefficient vs Mach), atmosphere from BLOCK DATA, pitch schedule from published I-load profiles of the real Shuttle – alpha=-2 degrees at max-Q, alpha=-4 degrees post max-Q, alpha=+3.5 degrees during second stage. All loaded through standard $DATA1 input blocks, using the original ALPC=22 mode – one of 31 attitude control modes built into the program.

T+0. Vertical launch. Three SSMEs and two SRBs. Liftoff mass 4,575,000 pounds.

T+60. Max-Q. Altitude 13.5 km. Velocity 549 m/s. Flight path angle 53 degrees. Gravity turn.

T+123. SRB separation. Altitude 50 km. Velocity 1,895 m/s. Boosters jettisoned – mass drops by 400,000 pounds in an instant.

T+300. Altitude 115 km. Velocity 5,205 m/s. Flight path angle 0 degrees. Horizontal flight. No atmosphere left – drag is zero.

T+389. MECO. Main Engine Cut-Off. Velocity 7,803 m/s. Orbital. Engines silent.

T+500. Altitude 109 km. Velocity 7,803 m/s. Flight path angle 0.4 degrees. Orbital coast. Stable.

SSSP has no built-in MECO – the 1970 program assumed engines burn until propellant depletion. Without MECO, the rocket accelerates to twice orbital velocity, and the program reports “VEHICLE ATTEMPTED SUBTERRANEAN FLIGHT.” Five lines in subroutine PRPSN: if velocity exceeds orbital – thrust equals zero.

Accuracy and Its Limits

Gravity turn: GAM=75.6 degrees at T+30s versus 75.0 real. Half a percent error.

Velocity at T+200s: 9,843 m/s versus 10,000 real. 1.6% error.

MECO altitude: 109 km versus 115 km real. 5% error.

The discrepancies are explainable from two sides.

On our side – SSSP doesn’t model SSME throttle-down through max-Q (the real Shuttle reduces thrust to 67%; we hold 100%). Doesn’t model closed-loop PEG guidance – the real Shuttle computes thrust direction in real time; we use a fixed alpha(Mach) table. Doesn’t model wind loads, roll program, yaw steering.

But there’s another side. Our “real data” are numbers from Wikipedia and NASA press kits. Rounded. Averaged across 135 missions, each flying with its own payload mass, in its own weather, to its own orbit. SRB separation altitude varied from 150,000 to 174,000 feet between missions – a 15% spread. Some of our target points (T+200s, T+300s) are interpolations, not measurements from a specific flight. We couldn’t find detailed per-second telemetry from real missions in the public domain.

We’re comparing an approximate model against approximate data. The 5-15% error isn’t just a limitation of SSSP. It’s also the uncertainty of the targets themselves.

For a predesign tool – that’s enough. It answers the question “will this configuration work?” And it answers correctly.


The experiment started as curiosity – code from a NASA archive, illegible scans, a forgotten tool. It ended with a working simulation of a real spacecraft’s flight.

The Space Shuttle program ended in 2011. 135 missions. The technology that defined an era of crewed spaceflight. The Artemis program is preparing a return to the Moon – SLS uses the same modified RS-25 engines and 5-segment SRBs.

And program MSC-13914, written 11 years before Columbia’s first flight, can still calculate its trajectory. On a laptop. In 10 seconds.

In subroutine SOLVE, line 6439, there’s a construct IF(0) 5005,10,5005 – an arithmetic IF that always branches to label 10. The developer’s comment: “THIS CARD NECESSARY TO PROGRAM AROUND COMPILER OPTIMIZATION BUG ON UNIVAC 1108 EXEC II.” A workaround for a 1970 compiler bug. Still works in gfortran 2026. We didn’t touch it.

7,811 lines. 44 subroutines. 10 fixes to compile. Three optimizers for calibration. One make run to launch.

Video: youtu.be/aavo1POfrZg

Code: github.com/bes-dev/sssp