Vallado et al. Rev 3 (2006/2013): Final SGP4 Modernization
Why This Matters for SGP4
Section titled “Why This Matters for SGP4”Rev 3 is the authoritative modern SGP4 reference. Every actively maintained implementation — satellite.js, python-sgp4, Skyfield, and pg_orrery — derives from or validates against the C++ source in this revision.
The revision chain:
2006 AIAA 2006-6753 Original conference paper |2008 Rev 1 First revision (our primary archive transcription) | ~ Rev 2 Intermediate revision (undated) |2011 Rev 3 Final public revision. Code stamped 2011-12-30.Where the original 2006 paper cataloged 25 years of SGP4 divergence and Rev 1 provided the corrected algorithms and 518 test vectors, Rev 3 addresses the questions Rev 1 left open: which mode is “official”? What happens when propagation fails? How fast can this run at catalog scale?
This page focuses on what changed. The theory, bug history, TEME definition, and test case design documented in Rev 1 are not repeated here.
The Dual Operation Mode
Section titled “The Dual Operation Mode”The single most important addition in Rev 3 is the operationmode parameter, threaded
through the entire call chain from sgp4init() down to dpper():
| Mode | Character | GHA Calculation | Intrinsic Function Handling |
|---|---|---|---|
| AFSPC | 'a' | Jan 0, 1970 epoch formula (THGR70, C1, FK5R) | Original FORTRAN MOD/ATAN semantics |
| Improved | 'i' | Julian-century GMST1982 (Eq. 2 in paper) | Modern fmod/atan2 semantics |
What the Modes Affect
Section titled “What the Modes Affect”GHA (Greenwich Hour Angle) calculation. In AFSPC mode, initl() computes the Greenwich
sidereal time at epoch using the older 1970-epoch formula with combined constants. In improved
mode, it uses the GMST1982 equation:
The difference is on the order of arcseconds — small, but enough to cause disagreement in verification against AFSPC-generated test vectors.
Intrinsic function handling. In dpper(), when nodep (the perturbed node) goes
negative, AFSPC mode adds to match the behavior of FORTRAN’s MOD function.
Improved mode relies on C/C++ fmod and atan2 to handle the quadrant correctly. The
difference matters for deep-space objects where lunar-solar perturbations push orbital
elements through zero crossings.
Code-Level Changes from Rev 1
Section titled “Code-Level Changes from Rev 1”Rev 3’s source code changelog documents seven categories of changes accumulated between August 2006 and December 2011:
| Date | Change | Impact |
|---|---|---|
| Nov 2008 | Early return false on error codes | Prevents silent garbage output after errors 1-4 and 6 |
| Sep 2008 | DSPACE atime optimization | Faster deep-space propagation for epochs far from propagation time |
| Sep 2008 | operationmode parameter added | Dual AFSPC/improved behavior (see above) |
| Jun 2008 | Small eccentricity threshold update | cc3 only computed when , prevents division issues |
| Aug 2010 | pow() replaced with direct multiplies | x*x and x*x*x instead of pow(x, 2.0) and pow(x, 3.0) |
| Aug 2010 | Unused variable cleanup in initl() | Eliminates compiler warnings |
| Rev 3 | Sub-orbital initialization check removed | TLEs with perigee below Earth’s surface no longer rejected at init |
Error Handling
Section titled “Error Handling”Rev 1’s sgp4() function set error codes but continued processing, meaning the caller
received a position/velocity vector that could be numerical noise. Rev 3 adds explicit
return false; after each error condition:
- Error 1: or e < -0.001
- Error 2: mean motion
- Error 3: perturbed eccentricity outside
- Error 4: semi-latus rectum p < 0
- Error 6: distance from Earth center m_{rt} < 1.0 (satellite has decayed)
Sub-Orbital Check Removal
Section titled “Sub-Orbital Check Removal”Rev 1 set error 5 and returned early when the initial perigee radius was below 1.0 Earth
radii (r_p < 1.0). Rev 3 removes this check entirely. The rationale: a satellite whose
TLE-epoch perigee is below the surface may still be above the surface at the requested
propagation time. The runtime check (m_{rt} < 1.0 in sgp4()) catches actual decay.
This matters for decaying satellites near end-of-life, when tracking accuracy is most
critical.
Performance: pow() to Multiply
Section titled “Performance: pow() to Multiply”Replacing pow(x, 2.0) with x*x throughout the propagator is not cosmetic. The general
pow() function computes — a transcendental function evaluation for what
amounts to a single multiply. For catalog-scale batch processing (the whats_up query
pattern), this accumulates to a measurable speedup across thousands of TLEs per call.
TEME-to-ECEF Improvements
Section titled “TEME-to-ECEF Improvements”Appendix C is restructured to lead with the operational conversion path (TEME to Earth-fixed via GMST) rather than the academic path (TEME to J2000). The worked example is updated to use 6 April 2004 as the reference date with specific UT1-UTC, polar motion, and EOP values.
The transformation matrices (Eqs. C-1 through C-4) and the 23.6-meter “of date” vs. “of epoch” demonstration using the Vanguard 1 TLE are numerically identical to Rev 1.
A terminology update throughout: “CMOC” (Cheyenne Mountain Operations Center) becomes “JSPOC” (Joint Space Operations Center), reflecting the 2005 organizational name change. A new footnote references Koskela (1967) for the historical basis of the 4-term nutation approximation that defines the TEME frame.
Test Case Updates
Section titled “Test Case Updates”The test suite retains the same 29 satellites as Rev 1. Two additions appear in the Rev 3 table descriptions:
| Satellite | Category | Purpose |
|---|---|---|
| 09998 | Synchronous | High-eccentricity GEO (), exercises secular integrator |
| 28129 | Deep Space | GPS satellite, 12-hour non-resonant (e < 0.5) |
The appendix header notes that satellite 23599 results differ from Rev 1 “as a result of
the consistency with AFSPC option.” All Rev 3 test vectors are explicitly generated with
'a', '72' (AFSPC mode, WGS-72 constants), eliminating the mode ambiguity present in Rev 1.