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.
Framesdoes 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
Orbital period, in minutes, at which the deep-space branch takes over.
Two pi.
Minutes in a day divided by two pi, converting revolutions per day to radians per minute.