Question about Frame conversion between ITRF and EME2000

Hi, Orekit users,

I am a newcomer to Orekit, and now I have a question about the frame conversion between ITRF and EME2000.

Here is the code I’m using:

AbsoluteDate initialDate = new AbsoluteDate(2019, 03, 04, 19, 23, 19.107, utc);
Frame j2000 = FramesFactory.getEME2000();
Vector3D p = new Vector3D(4721243.173, -5304707.381, -29098.763);
Vector3D v = new Vector3D(3061.331, 2707.255, 6280.672);
PVCoordinates pv = new PVCoordinates(p, v);
System.out.println("Orbit: " + j2000.getTransformTo(itrf, initialDate).transformPVCoordinates(pv));

by the way, ITRF = ECEF, EME2000 = J2000, am I right?

The result is: P(-5532934.557015174, -4451643.020879899, -20342.986059240364),
V(2227.74214972784, -2777.2138597461817,6286.218464870787)
But the result I got from STK is: P(-5532965.653, -4451604.404, -20335.815)
V(2227.721, -2227.218, 6286.224)

The differences between the two P’s above are really large(30m~40m). Thus, which result is right? Is there anything wrong in my code? Does anyone have ideas or suggestions or advice?

Thanks a lot!!


by the way, ITRF = ECEF, EME2000 = J2000, am I right?


Can you give us the part of the code where you define the ITRF frame?

The method from FramesFactory to define an ITRF frame takes two arguments. Depending on the parameters you selected, this might explain the difference with STK:,boolean)

  • conventions - IERS conventions to apply
  • simpleEOP - if true, tidal effects are ignored when interpolating EOP


Hi Miaomiao,

AbsoluteDate initialDate = new AbsoluteDate(2019, 03, 04, 19, 23, 19.107, utc);

Here, you should rather use AbsoluteDate(2019, 3, 4, 19, 23, 19.107, utc), i.e. without the leading 0 in the integer arguments because in Java (just like C or C++) an integer starting with 0 is considered in base 8, so you could for example not write 08 or 09. This is weird, I agree.

The result is: P(-5532934.557015174, -4451643.020879899, -20342.986059240364),
V(2227.74214972784, -2777.2138597461817,6286.218464870787)
But the result I got from STK is: P(-5532965.653, -4451604.404, -20335.815)
V(2227.721, -2227.218, 6286.224)

The reason for the discrepancy is most probably due to Earth Orientation Parameters. Orekit typically loads EOP from a directory if you followed the recommended way in I have just updated the default data set with current EOP so transforms from early March 2019 should be very good. With this settings, and assuming the ITRF frame was built as

Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true);

Then I got these results:
Orbit: {P(-5532936.377581564, -4451640.759330523, -20342.717244308886), V(2227.741346228882, -2777.2148030375624, 6286.21837508928), A(-0.43445523497726507, -0.3485702847874696, -4.7502998555017007E-7)}

In order to get results close to STK, I had to do the following:

  1. prevent loading of Earth Orientation Parameter (or rather load truncated data the does NOT cover March 2019
  2. use TIRF as Earth frame rather than ITRF (i.e. ignore both pole wandering and S’ regular rate)

With these two settings, I get these results:
Orbit: {P(-5532965.981090575, -4451603.9940751055, -20336.33870949751), V(2227.721531578508, -2777.218942867515, 6286.223565804276), A(-0.43445755747722115, -0.34856739657047814, 2.5095149630985185E-8)}

which are 75cm away from STK.

So I would say Orekit results are good, and your STK configuration could be improved. I don’t know how to tell STK to use current EOP, and you should make sure your Earth frame is really ITRF, which takes into account many tiny effects.


As an additional information, frames transforms in Orekit have been extensively tested and validated by numerous different institutions. Both legacy models (like IERS conventions 1996) and state of the art models are available (precession, nutation, Earth Orientation Parameters, pole wandering, tidal effects on frames, stations displacements due to tides, station displacements due to ocean loading…). All these geometrical computations are considered to be accurate at centimeter level or better in the Earth vicinity.

1 Like

Thanks for all the replies!I noticed that I didn’t post the definitions of my timescale and itrf. Here is the code I missed last time:
Timescale utc = TimescalesFactory.getUTC();

Frame ITRF = FrameFactory.getITRF(IERSConventions.IERS_2010,true);

My classmates suggested trying UT1 instead of UTC, it seems I can also get a result close to STK (less than 1m) following his suggestion . Is this just a coincidence? Which timescale and frame should I use? I’m really confused now.

No, you should not use UT1 here. UT1 is used only internally to compute the Earth rotation, it is not really a time scale to locate events occurrence in a timeline (at least not in Orekit, I don’t know about STK).
When you initialized your orbit, you just have to make sure the position, velocity and time are consistent with each other, this depends only on the source of your data. Nowadays, many people use UTC so your input seemed reasonable to me.

The difference between UTC and UT1 is precisely one of the Earth Orientation Parameters, so I guess this confirms your Orekit configuration does take EOP into account and your STK configuration does not.
So if you tell Orekit the position is in UT1, it will basically rewind Earth rotation by the difference between UTC and UT1, then add it back when it takes all the EOP into account, which means the UTC/UT1 difference will cancel out. If at the same time you ask STK to compute but do not provide it any EOP, then the UTC/UT1 difference will be zero and results from both computations will be close… and both false.

One way to check in Orekit that you did load EOP correctly is to put at the end of your program (after all computations), the following statements:

        for (String s : DataProvidersManager.getInstance().getLoadedDataNames()) {
        EOPHistory eop = ((EOPBasedTransformProvider) itrf.getTransformProvider()).getEOPHistory();

The first loop will display all the files that have been loaded under the hood during the computation. Among these files, you should see the ones that contain EOP data (most probably finals.all of finals2000A.all).
The last statement will display for the ITRF that you used the final date covered by the underlying EOP (that have been loaded from some of the previous files). This could help you understand if Orekit really did use EOP, and I am pretty sure it did.

I don’t know how to do the same in STK, but I am also sure STK does support EOP. As EOP data are updated all the time (for example bulletinA files are produced once a week), users must update their program of choice by providing these always changing data.

1 Like

Thanks a lot!I got it!:)

@luc I ran this today to verify a unit test I am authoring. I am getting slightly different results than what you posted above in Apr 2019 (2/7). I am using Orekit 10.2 with master data downloaded on 2021-06-10.

My results

ECI coordinates {P(4721243.173,        -5304707.381,       -29098.763),          V(3061.331,            2707.255,           6280.672),          A(0.0, 0.0, 0.0)}
ECF coordinates {P(-5532936.376947298, -4451640.760121941, -20342.71656724535),  V(2227.7413465245772, -2777.214801640454,  6286.218375659901), A(-0.43445523478611486, -0.3485702848606439, -4.750361891564755E-7)}
    Back to ECI {P(4721243.172999996,  -5304707.380999996, -29098.762999999955), V(3061.330999999998,   2707.2549999999987, 6280.67199999999),  A(1.8579966704104353E-16, -1.6132598696281504E-16, -6.634981129410346E-20)}

The results are within 1cm but I wanted to verify that I wasn’t doing something wrong that is resulting in the difference.

The unit test code is below. I reduced to the actual unit test down to a self-contained test that doesn’t rely on any of my own classes for simplification and left out any assertions. I also back-converted for comparison.

  public void testECFtoECI() {

    File orekitData = new File("orekit-data");
    DataProvidersManager manager = DataContext.getDefault().getDataProvidersManager();
    manager.addProvider(new DirectoryCrawler(orekitData));

    TimeScale utc = TimeScalesFactory.getUTC();
    AbsoluteDate epoch =
        new AbsoluteDate(2019, 3, 4, 19, 23, 19.107, utc);

    Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
    Frame eme2000 = FramesFactory.getEME2000();

    Vector3D eciPos = new Vector3D(4721243.173, -5304707.381, -29098.763);
    Vector3D eciVel = new Vector3D(3061.331, 2707.255, 6280.672);
    PVCoordinates pvECI = new PVCoordinates(eciPos, eciVel);
    log.debug("ECI coordinates " + pvECI);

    // Transform ECI to ECI (EME2000 to ITRF).
    Transform eciToEcf = eme2000.getTransformTo(itrf, epoch);
    PVCoordinates pvECF = eciToEcf.transformPVCoordinates(pvECI);
    log.debug("ECF coordinates " + pvECF);

    // Transform back from ECF to ECI for comparison.
    Transform ecfToEci = earth.getItrfFrame().getTransformTo(eme2000, epoch);
    pvECI = ecfToEci.transformPVCoordinates(pvECF);
    log.debug("    Back to ECI " + pvECI);


Hi @creplogle ,

The difference you have between the initial ECI position and the one recalculated after transformations (ECI → ITRF → ECI) is not 1cm but about 6 nanometers :slight_smile:
For the velocity, it is about 10 picometers per second.

Because your initial position is given with 1 mm accuracy (i.e. 3 significant digits), it is easy to assume that this 6 nanometers difference is not a major difference here.

Best regards,

1 Like

Bryan, thanks for the quick reply. I stand corrected on the accuracy :slight_smile:

LOVE Orekit!!!

1 Like