I am starting to get more into orbital dynamics. As a result, Julia is a new programming language that is rising in adaptation. I decided to port a Python version of an orbital dynamics library into Julia. Over the next few weeks, I would like to write some explanations of what all the functions in the Python library are, how they work and write the port. Orbital dynamic angles seem like the easiest place to start.

The three anomalies

The confusing part for anyone coming to this fresh is that there are three different angles all describing where a body is in its orbit, and they only agree at two points.

True anomaly f is the honest one: the actual angle at the focus between perihelion and the body. It’s what you’d measure if you were standing at the Sun with a protractor. The problem is that it doesn’t advance evenly in time, because the body moves fast at perihelion and slowly at aphelion.

Mean anomaly M is the opposite: a fictional angle that advances perfectly evenly, M = n(t - t₀), where n = sqrt(μ/a³). It’s the angle a body would have if the orbit were a circle traversed at constant rate. It is easy to compute from a timestamp and corresponds to nothing physical.

Eccentric anomaly E is the bridge between them, defined by projecting the orbit onto its circumscribing circle. Kepler’s equation ties it to the mean anomaly:

M = E - e * sin(E)

That equation is transcendental. There is no closed form for E given M, which is the whole reason this corner of the field exists. Newton’s method converges in a handful of iterations for anything not close to parabolic:

function kepler_E(M, e; tol=1e-12, maxiter=50)
    E = e < 0.8 ? M : pi            # a decent starting guess
    for _ in 1:maxiter
        dE = (E - e*sin(E) - M) / (1 - e*cos(E))
        E -= dE
        abs(dE) < tol && break
    end
    return E
end

# E to true anomaly, quadrant-safe
true_anomaly(E, e) = 2 * atan(sqrt(1 + e) * sin(E/2), sqrt(1 - e) * cos(E/2))

Note the two-argument atan in that last line. The half-angle formula is usually written with a single tan, which silently collapses two quadrants into one; feeding the numerator and denominator in separately keeps the sign information and saves you a class of bug that only shows up on the far half of the orbit.

There are some resources I have found that are very relevant and help explain some of these functions. The Kepler angles are the easiest to understand and explained at Kepler Equation

The other angles presented in the below Julia code can be explained from Session Orbits