module

Kozai::SGP4

The SGP4/SDP4 orbital propagator.

Takes an element set and a time, returns a position and velocity. Nothing else: no IO, no logging, no shared mutable state, no clock of its own. The same inputs give the same outputs forever, which is what makes the verification suite meaningful.

Correctness before anything else

This model is easy to implement almost correctly, and an almost-correct orbital propagator produces numbers that look entirely reasonable and are wrong by hundreds of kilometres. There is no way to tell the two apart by inspection. The only defence is the published verification set, which this implementation is checked against on every run of crystal spec — see spec/sgp4_verification_spec.cr and spec/fixtures/PROVENANCE.md.

Three things that are load-bearing

  • The constants are WGS-72, not WGS-84. SGP4 was defined on WGS-72 and the element sets are fitted to it. Substituting the newer and more accurate WGS-84 makes the results worse, not better. Observer geodesy uses WGS-84; see Observer. The two never mix.
  • The deep-space branch is not optional. Above a 225-minute period the near-earth branch omits lunar and solar perturbations and resonance entirely. Geostationary satellites, Molniyas, GPS and everything else high would be grossly wrong without it.
  • The output frame is TEME, not J2000 and definitely not an earth-fixed frame. Handing these vectors to something expecting ECEF puts the satellite hundreds of kilometres from where it is. Frames does the conversion.

Usage

elements = Kozai::TLE.parse(line1, line2)
satellite = Kozai::SGP4::Satellite.new(elements)

state = satellite.propagate_at(Time.utc)
if state.ok?
  puts state.position # TEME, kilometres
  puts state.velocity # TEME, kilometres per second
end

Constants

DEEP_SPACE_PERIOD_MINUTES = 225.0

Orbital period, in minutes, at which the deep-space branch takes over.

TWO_PI = Math::PI * 2.0

Two pi.

XPDOTP = 1440.0 / TWO_PI

Minutes in a day divided by two pi, converting revolutions per day to radians per minute.

Nested types