Skip to content

Overview & Extraction Notes

This page documents the extraction methodology and technical context for the 1980 Spacetrack Report No. 3 — the canonical reference for the SGP4, SDP4, SGP, SGP8, and SDP8 satellite propagation models. The report contains both the mathematical theory and the FORTRAN IV source code that implements it.

Vision-based transcription from 300 DPI page images (91 pages, approximately 25 MB of source imagery). All equations were transcribed to LaTeX, all tables to markdown.

FileContent
00-title.mdTitle page, authors, distribution
01-introduction.mdPurpose and scope
02-propagation-models.mdModel overview and selection criteria
03-compatibility.mdElement set compatibility requirements
04-general-description.mdMathematical framework
05-sgp-model.mdSimplified General Perturbations
06-sgp4-model.mdSGP4 (Brouwer mean elements)
07-sdp4-model.mdSDP4 (deep-space extension)
08-sgp8-model.mdSGP8 (Hoots drag model)
09-sdp8-model.mdSDP8 (deep-space with SGP8 drag)
10-deep-space.mdDEEP subroutine theory
11-driver-functions.mdDriver and I/O routines
12-users-guide.mdUsage instructions
13-test-cases.md25 reference test vectors
14-sample-implementation.mdComplete FORTRAN source listing
15-acknowledgements.mdContributors
16-references.mdBibliography
17-form.mdStandard form (DD 1473)
FileLinesDescription
sgp.f109SGP model (Simplified General Perturbations)
sgp4.f215SGP4 model with Brouwer mean elements
sdp4.f187SDP4 deep-space model (calls DEEP)
sgp8.f185SGP8 model with Hoots drag
sdp8.f226SDP8 deep-space model (column 72 fix applied)
deep.f451DEEP subroutine — lunar-solar perturbations
driver.f113Main driver program with WGS-72 constants
actan.f22Four-quadrant arctangent
fmod2p.f11Modulo 2π2\pi
thetag.f22Greenwich sidereal angle
MakefileBuild configuration for gfortran
FileFormatContent
test_cases.csvCSV25 reference vectors in tabular form
test_cases.jsonJSONSame vectors with structured metadata
constants.jsonJSONWGS-72 constants as used in the code
symbols.jsonJSONVariable name to symbol mapping
tle_format.mdMarkdownTLE format specification
tle_format.jsonJSONSub-field breakdowns including implied-decimal-with-exponent encoding

Both the internal G-card format (used within NORAD) and the public two-line element format were captured. The JSON specification includes sub-field breakdowns for the implied-decimal-with-exponent encoding scheme — the format where 66816-4 means 0.66816×1040.66816 \times 10^{-4}.

FORTRAN IV uses fixed-format source inherited from the 80-column punch card:

ColumnsPurpose
1—5Statement labels (numeric)
6Continuation character (any non-blank, non-zero)
7—72Source code — the only columns the compiler reads
73—80Sequence numbers (silently ignored by compiler)

The column 72 boundary is the root of the problem. Anything past column 72 is silently discarded. A single extra space of indentation pushes a character from column 72 to column 73, and the compiler throws it away without warning. In monospaced text without column rulers, this boundary is completely invisible.

Consider three organizations (A, B, C) that independently transcribed the printed source in the 1980s:

  • Organization A introduces a column-shift error in the drag calculation
  • Organization B makes a different transcription error in the Kepler solver
  • Organization C gets a clean copy but applies a “fix” to compensate for a bug they found (which was actually a transcription artifact from a copy they received from A)

Twenty-five years later, all three have implementations that claim to be “SGP4” but produce different results. Each has accumulated compensating modifications built on top of their particular transcription errors.

The Vallado Rev-1 paper was the reconciliation effort that documented this divergence. Our own 2026 extraction reproduced the same class of error: a vision-based LLM transcription pushed the I in *COSI to column 73 in sdp8.f, turning a multiplication by cosine of inclination into a bare *COS function call with no arguments. The compiler accepted it silently.

FORTRAN IV’s implicit typing convention: variables beginning with I through N are INTEGER, all others are REAL. The DEEP subroutine’s DPSEC entry point receives a parameter T that must be DOUBLE PRECISION for adequate accuracy, but the implicit rules make it REAL. An explicit DOUBLE PRECISION T declaration is required.

The original source uses DECODE, a DEC/VAX extension for reading from in-memory character buffers. This was replaced with standard FORTRAN 77 READ with an internal unit for portability.

The original CHARACTER ABUF*80(2) places the length specifier between the variable name and the dimension — a non-standard syntax. Modern compilers require CHARACTER*80 ABUF(2).

Hollerith constants (e.g., 6HSGP4 ) are a pre-CHARACTER*N technique for storing text data in numeric variables. The gfortran compiler still supports them when the -w flag suppresses warnings.

FORTRAN IV allocated all local variables statically — they persist across subroutine calls. Without the -fno-automatic compiler flag, modern Fortran places locals on the stack, and carefully computed coefficients in the initialization path are destroyed when the subroutine returns and is called again for propagation.

The DEEP subroutine uses three ENTRY points (DPINIT, DPSEC, DPPER) to provide three distinct interfaces into a single routine that shares state through local variables. This was an efficient design in 1980 — today it would be a module or object with methods.

In FORTRAN IV, all entry points in a subroutine shared a single argument area. Calling DPINIT would set up arguments that DPSEC could later read from the same memory locations. Modern gfortran gives each entry point its own calling convention, breaking this assumption. The fix: save cross-entry arguments to local variables (SIQSAV, CIQSAV, OGDSAV, TSAVE) during initialization and read from them during secular and periodic updates.

The original code uses ASSIGNED GOTO for computed dispatch — a statement that stores a label in an integer variable and later branches to it. This feature was removed from Fortran 95 but remains supported by gfortran with warnings suppressed.

The code deliberately hardcodes WGS-72 geodetic constants because TLE element sets are fitted using WGS-72. Key values:

ConstantSymbolValue
Earth radiusaea_e6378.135 km
Gravitational parameterμ\mu398600.8 km3^3/s2^2
J2J_2XJ21.082616×1031.082616 \times 10^{-3}
J3J_3XJ32.53881×106-2.53881 \times 10^{-6}
J4J_4XJ41.65597×106-1.65597 \times 10^{-6}
kek_eXKE0.07436691610.0743669161

The near-earth/deep-space boundary is an orbital period of 225 minutes. The threshold constant .15625 in the code is 225/1440225 / 1440 (minutes per day). Satellites with period 225\geq 225 min are routed to SDP4/SDP8; those below go to SGP4/SGP8.

The iterative solver uses Newton-Raphson with a convergence tolerance of E6A = 1.0E-6 and a hard limit of 10 iterations. For nearly circular orbits this converges in 2—3 iterations. The Rev-1 paper documents a convergence failure for high-eccentricity orbits (e>0.9e > 0.9) where the iteration limit was insufficient.

For complete compilation instructions, compiler flags, and detailed discussion of each compatibility fix, see the FORTRAN source overview.

All 25 reference vectors from Chapter 13 were verified against the compiled binary:

ModelTypeMax Position ErrorMax Velocity Error
SGPNear-earth0.011 km0.000012 km/s
SGP4Near-earth0.001 km0.000001 km/s
SGP8Near-earth0.000 km0.000000 km/s
SDP4Deep-space1.769 km0.001366 km/s
SDP8Deep-space1.463 km0.000680 km/s

Near-earth models achieve sub-meter agreement with the printed reference values. Deep-space models show 1—2 km drift at large propagation times (t=1440t = 1440 min) due to the precision difference between our full double-precision compilation (-fdefault-real-8) and the original mixed single/double precision arithmetic.