On the use of TEME

Hi everyone,

First, a disclaimer: I know that TEME is not a recommended frame (lack of common definition, pointed out by CCSDS for example and well traced in Orekit’s documentation) and should be avoided, but unfortunately some data e.g. 18SDS’s SP ephemerides do come in that frame.

I have made a simple test which is the following (code follows):

  1. I create an instance of EquinoctialOrbit from a GCRF position-velocity vector.

  2. I propagate it for 3600s as is with a KeplerianPropagator and store the result

  3. I take the original orbit, convert it into TEME, propagate in to the same date in Keplerian motion again and convert back to GCRF. I get differences almost of the order of a meter!

// Initial date in UTC time scale
final TimeScale utc = TimeScalesFactory.getUTC();
final AbsoluteDate initialDate = new AbsoluteDate(2021, 10, 2, 0, 0, 0.000, utc);

// Frames
final Frame inertialFrame = FramesFactory.getGCRF();
final Frame teme = FramesFactory.getTEME();

// Initial conditions
final double r0 = Constants.WGS84_EARTH_EQUATORIAL_RADIUS + 500e3;
final double v0 = FastMath.sqrt(Constants.WGS84_EARTH_MU / r0);
final double mass = 10.;
final Vector3D pos = new Vector3D(r0, 0., 0.);
final Vector3D vel = new Vector3D(0., v0, 0.);
final PVCoordinates pv = new PVCoordinates(pos, vel);
final double mu = Constants.EGM96_EARTH_MU;
final EquinoctialOrbit orbit = new EquinoctialOrbit(pv, inertialFrame, initialDate, mu);
final SpacecraftState state = new SpacecraftState(orbit, mass);

// Propagate in original frame
final double dt = 3600.;
final AbsoluteDate date = initialDate.shiftedBy(dt);
final KeplerianPropagator prop1 = new KeplerianPropagator(orbit, mu);
final PVCoordinates pvProp = prop1.propagate(date).getPVCoordinates(inertialFrame);

// Convert in TEME, propagate and convert back 
final EquinoctialOrbit orbitTransfo = new EquinoctialOrbit(state.getPVCoordinates(teme), teme, initialDate, mu);
final KeplerianPropagator prop2 = new KeplerianPropagator(orbitTransfo, mu);
final PVCoordinates pvProp2 = prop2.propagate(date).getPVCoordinates(inertialFrame);

final double[] diffPos = (pvProp.getPosition().subtract(pvProp2.getPosition())).toArray();
final double[] diffVel = (pvProp.getVelocity().subtract(pvProp2.getVelocity())).toArray();

I am a bit surprised, TEME is still supposed to be pseudo-inertial no? How can I have such big differences in such short time? Please point out any mistakes I have made.


Hi @Serrof

Could you try with a date away from midnight, in order to see if it is related to EOP interpolation, which changes at midnight?

Hi Luc,

I do not see any changes if I set a time away from midnight.

Thank you,

Hi Romain, Luc,

I’ve looked a bit into it and I’m wondering if it’s not coming from the initial point error.

If you use a step handler every second you’ll see the error slowly diverging with an exponential harmonic at orbital period.

I’ve tried some conversions at initial point:

        // Convert to TEME and back
        PVCoordinates gcrfPv0 = orbit.getPVCoordinates();
        PVCoordinates temePv1 = orbit.getPVCoordinates(teme);
        Orbit temeOrbit = new CartesianOrbit(temePv1, teme, initialDate, mu);
        PVCoordinates gcrfPv1 = temeOrbit.getPVCoordinates(inertialFrame);
        PVCoordinates diff1 = new PVCoordinates(gcrfPv0, gcrfPv1);

Or more directly

        Transform inertialToTeme = inertialFrame.getTransformTo(teme, initialDate);
        PVCoordinates temePv2 = inertialToTeme.transformPVCoordinates(gcrfPv0);
        PVCoordinates gcrfPv2 = inertialToTeme.getInverse().transformPVCoordinates(temePv2);
        PVCoordinates diff2 = new PVCoordinates(gcrfPv0, gcrfPv2);

Which gives this:
P(2.0489096641540527E-8, -7.275957614183426E-12, -3.637978807091713E-12), V(7.105427360593102E-15, 2.1827872842550278E-11, -8.97600814205382E-18), A(-2.4868995751603507E-14, -1.5635720444679136E-17, -3.976243264630839E-18)}

So, in my opinion, transforming back and forth between GCRF and TEME leads to a nanometer error that builds up with time afterwards.
@luc, does this level of error looks normal to you when doing the transform and inverse transform successively.
I find it a bit high but I’m not sure why it is so.


I don’t know about the evolution of errors in almost but not exactly inertial frames.
One way to check this would be to use InertialForces to check how this behaves between EME2000 and TEME.

FYI here is a visualization of how the errors grow over time in QSW frame: