Understanding Frame Conversions and GHA

Hello, I am a relatively novice user of Orekit, and have come across an external API I’ve having trouble making sense of, which is probably more a testament to my layman’s understanding of the intricacies of reference frames than anything else, but I’m hoping someone can help me out. For everything I’ve done so far, I have used an ECEF reference frame for propagation and event detection defined by

Frame ECEF = FramesFactory.getITRF(ITRFVersion.ITRF_2020, IERSConventions.IERS_2010, false);

One of the APIs I’m starting to interact with requests an ephemeris point in ECI-TOD frame, but also includes a parameter for Greenwich Hour Angle.

I am converting my ECEF ephemerris point to ECI-TOD using code along the lines of:

TimeStampedPVCoordinates ecefEph = new TimeStampedPVCoordinates(absDate, posEcef, velEcef, Vector3D.ZERO);

Transform xform = ECEF.getTransformTo(FramesFactory.getTOD(IERSConventions.IERS_2010, false);

TimeStampedPVCoordinates eciEph = xform.transformPVCoordinates(ecefEph);

This seems to work well, and gets me within a few meters accuracy from a reference test case in terms of position and velocity, but the API asks for a Greenwich Heading Angle to go along with the ECI ephemeris point, and I’ve not been able to figure out what they are looking for in this parameter. I have played around with TimeScalarFunctions, e.g.,

TimeScalarFunction f1 = IERSConventions.IERS_2010.getGMSTFunction(TimeScalesFactory.getUTC());

double isThisGha = f1.value(absDate);

I have not been able to figure out what the API is really looking for, though, and the scalar function above seems to give me answers in degrees (172.00555) when documentation says it is in radians. I’m also quite sure I’m missing something in terms of what the API is asking for and in terms of how to find GHA for an ephemeris point. Any nudges in the right direction are appreciated!

EDIT
Through fiddling around, and not through any actual understanding, I’ve arrived at the conclusion that the value the API is looking for in the GHA field is equivalent to the value returned if I do:

double gha = xform.getRotation.getAngle();

in my transformation above. Is this a reasonable conclusion I’ve reached?

The IERSConventions.getGMSTFunction(utc).value(date) really returns radians, but it is continuous, not clipped back to 0 2π, so you can end up with hundreds (or even millions) of radians.

The xform.getRotation.getAngle() is close to what you want, yes.

If you get accuracies at meters levels, it seems you are on the right tracks, but this can probably be improved (Orekit is validated at centimeters levels, or even millimeters when it comes to frame transforms). Here are some possible causes for meters level errors:

  • different Earth Orientation Parameters (make sure you have up-to-date orekit-data, and make sure the API you interact with also uses the same data)
  • different conventions (IERS 1996, IERS 2003, IERS 2010)
  • different choices on applied effects (like tidal effects, but they are at about 20mm level only)

Be aware there are many different ECEF frames and many different ECI frames, these names are just too broad. However, I guess you used the correct ones if you achieved meter level accuracy, so it is really a good start, congratulation!

Well, I appreciate the vote of confidence, even though I’m not really feeling it… It looks like for the GHA angle I’m abiout 6e-6 radians away from what I’m told is the “right” answer, which seems like this approach will get me where I need to be. Is there a good reason for an API to be asking for this value in conjunction with an ECI point?
To your other point about meters accuracy, I appreciate the pointers and am sure the difference comes down to different EOP used. I suppose I also have the option to include nutation values, though. What magnitude of difference can one expect from using different nutation parameters?
Thanks!

6e-6 radians is indeed quite large (about 38m on Earth surface).
I am confused by the API asking for this angle. It could be because the underlying code assumes a single rotation around Z axis is needed somewhere, which is wrong, Earth motion is much more complicated than that, this is why we use full Transform objects in Orekit. I can recall having seen that for three different use cases. One was decades ago on a frame that was already obsolete at that time, and which we provide as Veis frame, sometimes also called Gamma 50 CNES (if you don’t know what it is, stay away from it, it was only included for validation against legacy systems). The second one is used operationally in Orekit, hidden within DSST force models to handle Earth rotating gravity field. The third one was for station-keeping of Earth observation satellites, in order to define the repeating ground trace. In this last case (which you can see in some examples of the Phasing tutorial I think), we had to use some strange Earth frames to deal with that.

As you are using TOD (True Of Date), you already are taking nutation into account. Basically, MOD (Mean Of Date) includes precession,TOD adds nutation, GTOD adds rotation angle irregularity (DUT1) and ITRF adds polhody. All these are using equinox-based paradigm, which are consistent with GMST function.

Perhaps you could try using the CIO paradigm, with CIRF equivalent to TOD (but with a different X axis), and then ERA angle instead of GMST (to be consistent with said X axis).

EOP corrections orders of magnitude at Earth surface are hundreds of meters for DUT1, 15 meters for polhody and a few meters for precession/nutation.