I recently started using Skyfield (python) to find (x, y, z) coordinates of things in space. Now I'd like to transform between Earth-Centered, Earth-Fixed (rotating) and Earth-Centered Inertial (non-rotating) coordinates, since "rocket speeds" may sometimes be webcasted in earth-fixed (rotating) frames . See this answer and this answer

This is what I have so far. The barycentric positions are straight from Skyfield, but I'm just subtracting the earth position from the moon's position to get earth-centered inertial position - I think this is OK for non-precision results, I don't know if it is really that easy or if there is something subtle I'm missing.

However, I'm wondering if there is an "official" Earth-fixed (rotating) frame, and if Skyfield has a method to convert to it, or to any rotating frame in general?


from skyfield.api import load, JulianDate

de421 = load('de421.bsp')

earth = de421['earth']
moon  = de421['moon']
slc40 = earth.topos('28.562 N', '80.577 W')

jd = JulianDate(utc=(2015,12, 22, 1, 48, 0))

epos     = earth.at(jd).position.km # barycentric (ICRS) position of earth
mpos     = moon.at(jd).position.km  # barycentric (ICRS) position of moon
slc40pos = slc40.at(jd).position.km # barycentric (ICRS) position of SLC-40

mpos_ec     = mpos     - epos # earth centered (inertial) position of moon
slc40pos_ec = slc40pos - epos # earth centered (inertial) position of SLC-40

1 Answer 1


Skyfield ≥1.34

The most recent versions of Skyfield introduce an explicit itrs reference frame that rotates with the Earth. You can learn about it starting here:

Skyfield <1.34

First: the main problem with your technique of subtracting the two positions is that it does not account for light-time travel. In the case of the Moon observation that you set up in your code, the error is only about 38 km, which might be well within your tolerances. The way to ask for a relative position that is properly light-time backdated is through the observe() method:

mpos_ec = earth.at(jd).observe(moon).position.km

Second: You are right that Skyfield currently has no built-in first-class support for a reference frame that rotates with the Earth. I should probably add one. A workaround that should get you up and running immediately, though, is to tell Skyfield that you want to observe:

  • From an Earth location, so the Skyfield does the rotation calculation that you want.
  • But from an Earth location whose latitude and longitude are zero, so that Skyfield applies no further rotations beyond moving into the Earth's rotating reference frame.
  • And from a location whose elevation is negative enough to place it exactly at the center of the Earth, so that you are not applying an offset to the position of the Moon or whatever.

The one trick is that we have to do the rotation ourselves because, as you note, the way Skyfield is currently written it's only willing to apply the rotation if it's about to reduce the coordinates to polar alt/az for you. So try out the following:

# Create an observer at the center of the Earth,
# rotated zero degrees from the Earth's reference frame:

gcrs = earth.topos(

# Ask where the observer was at a given time:

g = gcrs.at(jd)

# Then ask for a relative position as usual:

p = g.observe(moon).position.km
print p

# Now the tricky part: each observer.at() has
# an attribute `altaz_rotation` which is the rotation
# matrix you want to apply:

print einsum('ij...,j...->i...', g.altaz_rotation, p)

Let me know if the numbers you get back seem to work! And once I have the time-system rewrite finished up and can release Skyfield 1.0, I'll turn my attention to make this computation a bit more convenient.

  • $\begingroup$ Great! Thank you for the answer plus detailed explanation. I'll report back. On-line docs do a great job explaining the effects of finite speed of light on observations, but this can't be repeated often enough - it's so easy to forget! Right now at this moment I'm drawing and animating orbits of space vehicles in 3D with matplotlib and Blender (see this and this)` from different points of view. Have to remember that these are different frames and the moon will appear in a different place! $\endgroup$
    – uhoh
    Commented Jan 24, 2016 at 3:29
  • $\begingroup$ I know np.einsum is very popular for those who know how to use it. For those unfamiliar (like myself), the four answers to this question are helpful. $\endgroup$
    – uhoh
    Commented Jan 24, 2016 at 3:37
  • $\begingroup$ I just cut-and-pasted the einsum example from my own source code, I don't try each time to remember how it works from the ground up. :) $\endgroup$ Commented Jan 24, 2016 at 4:16
  • $\begingroup$ If you are drawing a view of the solar system from “outside”, then maybe observe() does not make sense — people watching an animation of the planets moving, maybe in which the camera is moving too, generally seem to expect to see “where things really are” without any light-time adjustment. $\endgroup$ Commented Jan 24, 2016 at 4:16
  • $\begingroup$ It's a very interesting question/issue to think about and I'm glad you brought it up! I mentioned the moon because it was visible from the SpaceX 2nd stage as it was deploying the eleven Orbcomm-2 satellites (see this question and this GIF). For that "scene" including the light-time would be correct, and using g.observe() would hopefully be close enough for the "Astronomy Police" :) $\endgroup$
    – uhoh
    Commented Jan 24, 2016 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.