Hi everyone ![]()
I have recently been using the TLEPropagator class in Python to propagate orbits from TLEs for a project. The issue I encountered is, in my opinion, quite subtle. The documentation is not entirely clear and is open to interpretation.
In the online documentation for TLEPropagator, there is a method named selectExtrapolator which takes the TLE as an argument. It also accepts parameters such as AttitudeProvider, Frame, and the objectâs mass.
When I first read this, I assumed that the propagator would return coordinates in TEME if no reference frame was provided (the default), and that providing a different reference frame would result in PVCoordinates being returned in that specific frame.
However, the following code yielded the same result:
sgp4_propagator = TLEPropagator.selectExtrapolator(best_tle)
pv_teme = sgp4_propagator.getPVCoordinates(date)
print(pv_teme)
{P(3776323.3171252054, -5698119.577950979, 4084162.4275406236), V(4372.036788046353, 4546.960067274173, 2658.373342105812), A(0.0, 0.0, 0.0)}
sgp4_propagator = TLEPropagator.selectExtrapolator(best_tle, FramesFactory.getEME2000())
pv_teme = sgp4_propagator.getPVCoordinates(date)
print(pv_teme)
{P(3776323.3171252054, -5698119.577950979, 4084162.4275406236), V(4372.036788046353, 4546.960067274173, 2658.373342105812), A(0.0, 0.0, 0.0)}
As you can see, regardless of which Frame you pass to the extrapolator, it always returns the PVCoordinates result as the one would obtain in the TEME reference frame.
When I observed this behaviour, I thought, âMaybe providing EME2000 to selectExtrapolator does nothing, and it defaults to TEME internally.â However, when querying the propagator for its reference frame, it returned different values (as expected) depending on the specified frame:
sgp4_propagator = TLEPropagator.selectExtrapolator(best_tle)
print(sgp4_propagator.getFrame())
TEME
sgp4_propagator = TLEPropagator.selectExtrapolator(best_tle, FramesFactory.getEME2000())
print(sgp4_propagator.getFrame())
EME2000
I am unsure if this is the expected behaviour or if I am missing something, but I find it very strange and counterintuitive. Being able to change the reference frame of the TLEPropagator, yet having it always return PVCoordinates results from the TEME frame, is unexpected. Reading the documentation, one would assume that providing a reference frame would alter the output coordinates. However, it seems to merely change the internal frame label of the propagator without affecting the propagation logic. I expected the propagator to handle internal transformations to provide PVCoordinates in the requested frame, but it appears to be doing something completely different (as shown below).
This caused me some headaches. Some real sensor measurements that I was analysing appeared biased, but in reality, they were not. I was able to correct this by manually transforming the coordinates to EME2000, as shown here:
sgp4_propagator = TLEPropagator.selectExtrapolator(best_tle)
pv_teme = sgp4_propagator.getPVCoordinates(date)
from org.orekit.frames import FramesFactory
teme = FramesFactory.getTEME()
tf = teme.getTransformTo(FramesFactory.getEME2000(), date)
pv_eme2000 = tf.transformPVCoordinates(pv_teme)
Iâve also tried other methods, like propagate or propagateOrbit, but they yielded the same result:
sgp4_propagator = TLEPropagator.selectExtrapolator(best_tle)
print(sgp4_propagator.getFrame())
print(sgp4_propagator.propagate(date))
TEME
SpacecraftState{orbit=Cartesian parameters: {P(3776323.3171252054, -5698119.577950979, 4084162.4275406236), V(4372.036788046353, 4546.960067274173, 2658.373342105812)}, attitude=org.orekit.attitudes.Attitude@1d6713dd, mass=1000.0, additional={}, additionalDot={}}
sgp4_propagator = TLEPropagator.selectExtrapolator(best_tle, FramesFactory.getEME2000())
print(sgp4_propagator.getFrame())
print(sgp4_propagator.propagate(date))
EME2000
SpacecraftState{orbit=Cartesian parameters: {P(3776323.3171252054, -5698119.577950979, 4084162.4275406236), V(4372.036788046353, 4546.960067274173, 2658.373342105812)}, attitude=org.orekit.attitudes.Attitude@1b47b7f5, mass=1000.0, additional={}, additionalDot={}}
sgp4_propagator = TLEPropagator.selectExtrapolator(best_tle, FramesFactory.getEME2000())
print(sgp4_propagator.getFrame())
print(sgp4_propagator.propagateOrbit(date))
EME2000
Cartesian parameters: {P(3776323.3171252054, -5698119.577950979, 4084162.4275406236), V(4372.036788046353, 4546.960067274173, 2658.373342105812)}
I also noticed that if I do not provide a reference frame initially, but then use propagate (or propagateOrbit) followed by the method getPVCoordinates(Frame), Orekit correctly transforms the PVCoordinates to the specified frame:
sgp4_propagator = TLEPropagator.selectExtrapolator(best_tle)
print(sgp4_propagator.getFrame())
print(sgp4_propagator.propagateOrbit(date).getPVCoordinates(FramesFactory.getEME2000()))
TEME
{2025-09-11T20:59:01.521055, P(3753732.0058709593, -5719568.394969737, 4074997.0132491607), V(4404.7352584595055, 4521.86388163676, 2647.178586968454), A(-2.9632564982077105, 4.515119384912664, -3.2168682379049436)}
However, if you specify the reference frame you want in selectExtrapolator and then attempt the same operation, Orekit assumes the propagator is already in that frame and does not transform anything. Consequently, it provides the same PVCoordinates that one would get in the TEME frame, effectively treating TEME coordinates as if they were in the user-specified frame:
sgp4_propagator = TLEPropagator.selectExtrapolator(best_tle, FramesFactory.getEME2000())
print(sgp4_propagator.getFrame())
print(sgp4_propagator.propagateOrbit(date).getPVCoordinates(FramesFactory.getEME2000()))
EME2000
{2025-09-11T20:59:01.521055, P(3776323.3171252054, -5698119.577950979, 4084162.4275406236), V(4372.036788046353, 4546.960067274173, 2658.373342105812), A(-2.981090442733755, 4.498187360799161, -3.2241035941229423)}
Therefore, the Frame parameter in selectExtrapolator seems to merely tell the propagator, âYou are now in EME2000â (overriding the TEME default definition but providing the same results it would yield in TEME), rather than instructing it to transform the results from TEME to EME2000.
Thank you so much in advance,
Antonio
