Technical documentation

Numerical Ephemeris Calculation

Mathematical method, constants, series and cross-language validation

Version 0.5.3 - July 25, 2026

Abstract

This numerical core computes apparent topocentric ephemerides for asteroids, comets and interstellar objects from osculating orbital elements. Elliptic, parabolic and hyperbolic initial states are handled without an external orbit service. Numerical integration includes solar gravity, the first relativistic correction, planets, Earth and Moon, Earth J2, optionally 16 massive asteroids, and Marsden A1/A2/A3. Planetary and asteroid states are obtained from compact tables by cubic Hermite interpolation.

The observed direction follows UTC-to-TDB conversion, light time, Earth rotation, nutation and precession. Six extreme objects and 20 frozen JPL Horizons rows form the common acceptance test for all 49 independent target-language programs.

1. Terms and symbols

Every astronomical and numerical term required by the calculation is defined here. Important concepts are also explained briefly at their first use in the main text.

Glossary of astronomical and numerical terms.

Term Meaning in this article
Ephemeris Predicted sequence of sky positions and related quantities for specified epochs and an observer.
apparent Direction after light time, Earth rotation, nutation and precession; atmospheric refraction is outside this core.
topocentric Referred to the actual observer on or near Earth's surface, rather than Earth's centre.
geocentric / heliocentric Referred respectively to the centre of Earth or the centre of the Sun.
barycentre Common centre of mass of several bodies; EMB denotes the Earth-Moon barycentre.
osculating orbital elements Six elements of the instantaneous Kepler orbit matching position and velocity at an epoch.
epoch Instant for which orbital elements or a state vector are valid.
conic Ellipse, parabola or hyperbola; eccentricity e distinguishes the three orbit forms.
eccentricity e Dimensionless shape parameter: e<1 ellipse, e=1 parabola, e>1 hyperbola.
q, Tp, i, Omega, omega Perihelion distance, perihelion epoch, inclination, ascending-node longitude and perihelion argument.
M, E, H, nu Mean, eccentric, hyperbolic and true anomaly; angle or auxiliary value locating the body on its orbit.
J2000 The fixed equatorial reference orientation at standard epoch JD 2451545.0 used by this core.
AU Astronomical unit, exactly 149597870.7 kilometres.
JD Julian date, the continuous astronomical day count.
UTC / TAI / TDB Coordinated Universal Time, International Atomic Time and barycentric dynamical time.
leap second Integer correction tying UTC to irregular Earth rotation while TAI remains uniform.
gravitational parameter mu Product G*M of gravitational constant and body mass; mu_sun is the solar value.
mass ratio eta_b Ratio M_b/M_sun of a perturbing body to the Sun, equal to mu_b/mu_sun.
perturbing body Body whose gravity adds acceleration beyond the pure two-body orbit.
direct and indirect term The body acts directly on the target; the simultaneous acceleration of the heliocentric origin is subtracted.
N16 Optional set of the 16 most massive asteroids integrated as additional perturbers.
cubic Hermite interpolation Third-degree interpolation reproducing position and velocity at both table nodes.
little-endian float64 Binary format storing the least significant byte first and IEEE-754 64-bit floating-point values.
Runge-Kutta method One-step method combining several derivative evaluations to advance the state.
embedded method Two Runge-Kutta solutions of different order share stages; their difference estimates local error.
Dormand-Prince 5(4) Embedded fifth- and fourth-order Runge-Kutta pair with seven stages.
Butcher tableau Tabular arrangement of Runge-Kutta coefficients c_i, a_ij and b_i defining stages and final weights.
local error Estimated error of one integration step, here the difference between fifth- and fourth-order solutions.
adaptive step size Automatic reduction or enlargement of the time step based on the normalized error estimate.
RMS Root mean square; here the square root of the mean of six squared, scaled component errors.
clamp Restriction of a number to a closed minimum-maximum interval.
light time Time required for light to travel from target to observer, shifting the evaluated emission epoch.
precession / nutation Slow secular and shorter-period changes in Earth's axis orientation.
GMST Greenwich mean sidereal time, the Earth rotation angle relative to the mean celestial frame.
Earth J2 Leading coefficient of Earth's gravity-field oblateness relative to an ideal point mass.
Marsden A1/A2/A3 Empirical comet acceleration in radial, transverse and normal directions.
RA / declination Right ascension and declination, the equatorial coordinate pair of the apparent direction.
MPC rho factors Dimensionless Minor Planet Center site factors for distance from Earth's axis and equatorial plane.
arcsecond 1/3600 degree, denoted by the double-prime symbol.
reference ephemeris / residual JPL Horizons comparison position and the remaining spherical angular separation.
covariance propagation Propagation of statistical orbit uncertainties and correlations; outside this nominal core.
acceptance test Fixed comparison whose limit a port must meet before release.

Solar and terrestrial gravitational parameters are kept unambiguous.

Symbol Meaning Value or unit
mu_sun = G*M_sun Heliocentric gravitational parameter of the Sun 0.0002959122082855911 AU cubed/day squared
mu_E = G*M_E Geocentric gravitational parameter of Earth 8.887692445125148326e-10 AU cubed/day squared
mu_Moon = G*M_Moon Gravitational parameter of the Moon 1.09318946230584718e-11 AU cubed/day squared
eta_b = mu_b/mu_sun Mass ratio of perturbing body b to the Sun dimensionless
r, v, a Position, velocity and acceleration vector AU; AU/day; AU/day squared
t0, Tp, Delta Element epoch, perihelion epoch and t0-Tp JD or days
q, a, p, e Perihelion distance, semimajor axis, semilatus rectum and eccentricity AU or dimensionless
M, E, H, nu Mean, eccentric, hyperbolic and true anomaly radians
Omega, omega, i Ascending node, argument of perihelion and inclination input degrees; internal radians
c Speed of light 173.144632674240 AU/day
rho Topocentric target vector, or Earth distance in the J2 section AU
J2, R_E Earth oblateness coefficient and equatorial radius dimensionless and AU
A1, A2, A3 Marsden non-gravitational parameters AU/day squared
T Julian centuries since J2000 dimensionless

2. Conventions, gravitational parameters and base quantities

The dynamical state y=[x,y,z,vx,vy,vz] is heliocentric, right-handed and referred to the equatorial J2000 frame. Position is in AU, velocity in AU/day and acceleration in AU/day squared. Internal angles are radians; public orbital elements, geographic inputs and result angles are degrees.

The symbol mu denotes a gravitational parameter mu=G*M, the product of gravitational constant G and body mass M. To avoid ambiguity this article always writes mu_sun=G*M_sun for the Sun and mu_E=G*M_E for Earth. For a perturbing body b, eta_b=mu_b/mu_sun=M_b/M_sun.

Quantity Exact value Unit
pi 3.141592653589793238462643383279502884 dimensionless
AU 149597870.7 km
mu_sun = G*M_sun 0.0002959122082855911 AU cubed/day squared
mu_E = G*M_E 8.887692445125148326e-10 AU cubed/day squared
mu_Moon = G*M_Moon 1.09318946230584718e-11 AU cubed/day squared
speed of light 173.144632674240 AU/day
light time 0.0057755183 day/AU
Earth/Sun mass 3.0034896149156e-6 dimensionless
Moon/Sun mass 3.6943033497651e-8 dimensionless
Earth radius 6378.1363 / 149597870.7 AU
Earth J2 1.08262668e-3 dimensionless
J2000 obliquity 84381.448 arcseconds

3. Initial state from osculating elements

Inputs are epoch t0, eccentricity e, perihelion distance q, perihelion epoch Tp, ascending node Omega, argument of perihelion omega and inclination i. Osculating means that these elements reproduce the position and velocity of the perturbed orbit at their epoch. With mu_sun=G*M_sun and Delta=t0-Tp, the true anomaly nu is determined first.

3.1 Ellipse

Validity: e < 1 - 1e-8
a = q / (1 - e)
n = sqrt(mu_sun / a^3)
M = normalize(n Delta, -pi, +pi)
E0 = M                    when e < 0.8
E0 = sign(M) pi           otherwise
E(k+1) = Ek - (Ek - e sin Ek - M) / (1 - e cos Ek)
Stop: |correction| < 2e-14 or after 80 iterations
nu = 2 atan2(sqrt(1+e) sin(E/2), sqrt(1-e) cos(E/2))

3.2 Hyperbola

Validity: e > 1 + 1e-8
a = q / (e - 1)                    (positive magnitude convention)
M = sqrt(mu_sun / a^3) Delta
H0 = asinh(M / e)
H(k+1) = Hk - (e sinh Hk - Hk - M) / (e cosh Hk - 1)
Stop: |correction| < 2e-14 or after 80 iterations
nu = 2 atan2(sqrt(e+1) sinh(H/2), sqrt(e-1) cosh(H/2))

3.3 Parabola and the near-parabolic boundary

Validity: |e - 1| <= 1e-8
s = sqrt(2 q^3 / mu_sun)
B = Delta / s
D = 2 sinh(asinh(1.5 B) / 3)
nu = 2 atan D

Barker's form avoids the ill-conditioned division by 1-e or e-1 in the immediate parabolic boundary. The following equations then apply to all three conics:

p = q (1 + e)
r = p / (1 + e cos nu)
v0 = sqrt(mu_sun / p)
r_orb = [r cos nu, r sin nu, 0]
v_orb = [-v0 sin nu, v0 (e + cos nu), 0]

Rotation from the orbital plane uses Rz(Omega) Rx(i) Rz(omega). For a two-dimensional input [x,y], the expanded form is:

X = (cosOmega cosomega - sinOmega sinomega cosi) x
    + (-cosOmega sinomega - sinOmega cosomega cosi) y
Y = (sinOmega cosomega + cosOmega sinomega cosi) x
    + (-sinOmega sinomega + cosOmega cosomega cosi) y
Z = (sinomega sini) x + (cosomega sini) y

A fixed x-axis rotation by 84381.448 arcseconds then transforms J2000 ecliptic position and velocity to the equatorial frame.

4. Perturber tables and Hermite interpolation

Each table node stores position r0 and velocity v0. For two nodes separated by h and u=(t-t0)/h, the core uses the following complete cubic Hermite basis and exact numerical coefficients:

h00(u) =  2 u^3 - 3 u^2 + 1
h10(u) =    u^3 - 2 u^2 + u
h01(u) = -2 u^3 + 3 u^2
h11(u) =    u^3 -   u^2

r(u) = h00 r0 + h10 h v0 + h01 r1 + h11 h v1

dh00/dt = ( 6 u^2 - 6 u) / h
dh10/dt =   3 u^2 - 4 u + 1
dh01/dt = (-6 u^2 + 6 u) / h
dh11/dt =   3 u^2 - 2 u
v(u) = (dh00/dt) r0 + dh10 v0 + (dh01/dt) r1 + dh11 v1
Perturbing body Mass/Sun mass
Mercury 1.660114153054348e-7
Venus 2.447838287784771e-6
Mars 3.227156037554996e-7
Jupiter 9.547919101886966e-4
Saturn 2.858856727222416e-4
Uranus 4.366249662744965e-5
Neptune 5.151383772628673e-5
Pluto 7.350487833457740e-9
N16 body Mass/Sun mass
Ceres 4.719154706681575e-10
Pallas 1.0297360324108826e-10
Juno 1.4471670474939703e-11
Vesta 1.3026875547328177e-10
Iris 8.589038999387399e-12
Hygiea 4.238598614896659e-11
Eunomia 1.5243642468487352e-11
Psyche 1.1978215785019767e-11
Euphrosyne 8.133159614634648e-12
Europa 2.0216913527078825e-11
Cybele 7.06871003275745e-12
Sylvia 1.6337820878172953e-11
Thisbe 8.965306556356798e-12
Camilla 1.0878696848144257e-11
Davida 2.9345275747768696e-11
Interamnia 2.1327387533901138e-11

5. Equation of motion

d/dt [r, v] = [v, a(r,v,t)]

The solar base acceleration is a_s=-mu_sun r/|r|^3. A perturbing body at heliocentric position r_b and with mass ratio eta_b contributes the direct and indirect terms together:

a_b = mu_sun eta_b [(r_b - r)/|r_b - r|^3 - r_b/|r_b|^3]

Earth and Moon are treated separately inside 0.2 AU and as the Earth-Moon barycentre outside it. Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune and Pluto are always active. N16 is added only when the switch is set and all data are present.

5.1 Relativistic solar correction

a_rel = mu_sun / (c^2 |r|^3)
        * [(4 mu_sun/|r| - |v|^2) r + 4 (r dot v) v]

5.2 Earth J2

Inside 0.02 AU from Earth, the usual J2 acceleration is added as a direct-minus-indirect term in this port's approximate Earth-axis frame:

mu_E = G*M_E = 8.887692445125148326e-10 AU^3/day^2
k = 1.5 J2 mu_E R_E^2 / rho^5
a_J2,x = k x (5 z^2/rho^2 - 1)
a_J2,y = k y (5 z^2/rho^2 - 1)
a_J2,z = k z (5 z^2/rho^2 - 3)

5.3 Non-gravitational acceleration

g(r) = alpha (r/r0)^(-m) [1 + (r/r0)^n]^(-k)
Defaults: r0=2.808, alpha=0.1112620426, m=2.15, n=5.093, k=4.6142
a_ng = g(r) [A1 e_r + A2 e_t + A3 e_n]

e_r is radial, e_n is normal to the orbital plane along r cross v, and e_t=e_n cross e_r is transverse. When A1=A2=A3=0 the exact zero vector is returned.

6. Dormand-Prince 5(4)

An embedded Runge-Kutta pair produces fifth- and fourth-order solutions from shared stages; their difference estimates the local error. A Butcher tableau is the tabular collection of stage abscissae c_i, intermediate weights a_ij and final weights b_i. It therefore defines the complete integration step. All coefficients used by the core are:

c2=1/5;       a21=1/5
c3=3/10;      a31=3/40; a32=9/40
c4=4/5;       a41=44/45; a42=-56/15; a43=32/9
c5=8/9;       a51=19372/6561; a52=-25360/2187;
              a53=64448/6561; a54=-212/729
c6=1;         a61=9017/3168; a62=-355/33; a63=46732/5247;
              a64=49/176; a65=-5103/18656
c7=1;         a71=35/384; a72=0; a73=500/1113;
              a74=125/192; a75=-2187/6784; a76=11/84

Order 5: b = [35/384, 0, 500/1113, 125/192,
              -2187/6784, 11/84, 0]
Order 4: b*= [5179/57600, 0, 7571/16695, 393/640,
              -92097/339200, 187/2100, 1/40]

For every state component, scale=2e-15+2e-13*max(|y_old|,|y_new|). The normalized RMS error is sqrt(sum((error/scale)^2)/6). A step is accepted for error <=1 or |h|<=1e-8 day. The next factor is clamp(0.9*error^(-0.2),0.1,5), or 5 for zero error. Step size stays between 1e-8 and 0.25 day, and one propagation may take at most 1,000,000 steps.

7. Time scales and observer

The public time scale is UTC. Dynamics use the approximation TDB=UTC+(TAI-UTC+32.184 s)/86400. The implemented leap-second steps are:

JD >= 2457754.5 : TAI-UTC = 37 s
JD >= 2457204.5 : TAI-UTC = 36 s
JD >= 2456109.5 : TAI-UTC = 35 s
JD >= 2454832.5 : TAI-UTC = 34 s
JD >= 2453736.5 : TAI-UTC = 33 s
otherwise       : TAI-UTC = 32 s

With T=(JD_UTC-2451545)/36525, GMST and the complete short nutation and precession series used by the port are:

GMST = 280.46061837 + 360.98564736629 (JD-2451545)
       + 0.000387933 T^2 - T^3/38710000

L_sun  = 280.4665  + 36000.7698 T              [degrees]
L_moon = 218.3165  + 481267.8813 T             [degrees]
Omega_moon = 125.04452 - 1934.136261 T         [degrees]

Delta_psi = -17.20 sin Omega_moon -1.32 sin(2 L_sun)
            -0.23 sin(2 L_moon) +0.21 sin(2 Omega_moon) [arcsec]
Delta_eps =  9.20 cos Omega_moon +0.57 cos(2 L_sun)
            +0.10 cos(2 L_moon) -0.09 cos(2 Omega_moon) [arcsec]

eps0 = 23 + 26/60 + 21.448/3600 degrees
       -(46.8150 T + 0.00059 T^2 - 0.001813 T^3) arcsec

zeta = 2306.2181 T + 0.30188 T^2 + 0.017998 T^3 arcsec
z = 2306.2181 T + 1.09468 T^2 + 0.018203 T^3 arcsec
theta = 2004.3109 T - 0.42665 T^2 - 0.041833 T^3 arcsec

The geocentric site vector is formed from MPC rho factors or geodetic longitude, latitude and height, rotated by apparent sidereal time, then returned to J2000 through nutation and precession.

8. Light time and apparent direction

Earth and observer states remain at reception time. Emission time is iterated at most five times:

t_emit,0 = t_TDB
rho_k = r_object(t_emit,k) - r_earth(t_TDB) - r_observer(t_UTC)
t_emit,k+1 = t_TDB - |rho_k| * 0.0057755183
Stop: |t_emit,k+1 - t_emit,k| < 1e-12 day

RA  = normalize(atan2(rho_y,rho_x), 0, 2 pi)
Dec = atan2(rho_z, sqrt(rho_x^2+rho_y^2))

9. Binary tables

Planet chunks cover 2000 through 2040 in nine overlapping intervals at 0.5-day spacing. N16 also uses nine chunks at exactly 1.9999817474948436 days. Overlap permits safe Hermite interpolation at boundaries. Every value is little-endian float64.

Dataset Interval Step Content
Planets 2000-2040 0.5 day 11 bodies: EMB, Earth, Moon, Mercury through Pluto
N16 2000-2040 1.9999817474948436 days 16 massive asteroids

10. Extreme examples

The following tables list UTC, frozen JPL Horizons reference values, the C-port result and the approximately spherical angular separation. Station A68 and identical nominal elements were used. Angles are degrees.

Orbit form and perihelion distance of the six validation cases.

Object Epoch JD e q AU
433 Eros 2453311.5 0.2228078944584026 1.133355399799004
C/2023 A3 2460448.5 1.000095309222603 0.3914300307809727
2P Encke 2459847.5 0.8477496967533629 0.3379482792219925
2I Borisov 2458853.5 3.356475782676596 2.006520878500843
99942 Apophis 2459215.5 0.19152168935010219 0.74582704784665232
367943 Duende 2456158.5 0.10812843436824709 0.893491631652941

Perihelion epoch and orbital-plane orientation.

Object Tp TDB Omega deg omega deg i deg
433 Eros 2453371.5859943051 304.40102733795362 178.665326776373 10.8291838260782
C/2023 A3 2460581.24084083 21.55947863619833 308.4917712569641 139.1121095087364
2P Encke 2460239.6495185494 334.1935846036774 187.1342463695676 11.41227811179314
2I Borisov 2458826.052845906 308.1477292269942 209.1236864378081 44.05264247909138
99942 Apophis 2459101.0394224627 204.0389272089208 126.65205183685531 3.3367513200667558
367943 Duende 2456261.554400227 147.2625010881184 271.08746874977783 10.33728752192523

Non-gravitational parameters; zero omits that component.

Object A1 / A2 / A3
433 Eros 0 / 0 / 0
C/2023 A3 1.4218744044776211e-8 / 1.3486232284695711e-9 / 0
2P Encke 2.34758107815004e-10 / -6.540690743894436e-13 / 0
2I Borisov 4.8595882640274848e-8 / 1.8798986909513609e-8 / 2.8355032201284751e-9
99942 Apophis 5e-13 / -2.9017666371531647e-14 / 0
367943 Duende 0 / 0 / 0

Frozen JPL Horizons reference positions.

Object UTC JPL RA deg JPL Dec deg
433 Eros 2026-07-21 22:00:00 188.352878680 -15.485731924
433 Eros 2026-07-22 22:00:00 189.033073621 -15.673602566
433 Eros 2026-07-23 22:00:00 189.712493431 -15.860306533
C/2023 A3 2026-07-21 22:00:00 270.131469713 20.765253967
C/2023 A3 2026-07-22 22:00:00 269.997011490 20.710979861
C/2023 A3 2026-07-23 22:00:00 269.864375552 20.655473422
2P Encke 2026-07-21 22:00:00 22.086326480 16.151079557
2P Encke 2026-07-22 22:00:00 22.204227929 16.258348832
2P Encke 2026-07-23 22:00:00 22.318311808 16.365096989
2I Borisov 2019-12-08 00:00:00 171.852537543 -17.895950115
2I Borisov 2019-12-09 00:00:00 172.269466247 -18.665188729
2I Borisov 2019-12-10 00:00:00 172.685173642 -19.435471809
99942 Apophis 2029-04-13 20:30:00 162.187610650 -1.853259832
99942 Apophis 2029-04-13 20:40:00 158.279644424 0.774049246
99942 Apophis 2029-04-13 20:50:00 153.882081064 3.710242552
99942 Apophis 2029-04-13 21:00:00 148.942949743 6.944475706
367943 Duende 2013-02-15 19:15:00 180.339825182 -21.468034850
367943 Duende 2013-02-15 19:20:00 180.569814701 -17.847154119
367943 Duende 2013-02-15 19:25:00 180.799653390 -14.126076156
367943 Duende 2013-02-15 19:30:00 181.029779486 -10.326525675

Calculated positions and spherical separation from the reference.

Object UTC Core RA deg Core Dec deg Delta arcsec
433 Eros 2026-07-21 22:00:00 188.352878496 -15.485732361 0.001696
433 Eros 2026-07-22 22:00:00 189.033073460 -15.673603008 0.001687
433 Eros 2026-07-23 22:00:00 189.712493295 -15.860306981 0.001680
C/2023 A3 2026-07-21 22:00:00 270.131471956 20.765254400 0.007710
C/2023 A3 2026-07-22 22:00:00 269.997013729 20.710980297 0.007701
C/2023 A3 2026-07-23 22:00:00 269.864377787 20.655473860 0.007693
2P Encke 2026-07-21 22:00:00 22.086326254 16.151079161 0.001626
2P Encke 2026-07-22 22:00:00 22.204227701 16.258348433 0.001638
2P Encke 2026-07-23 22:00:00 22.318311578 16.365096587 0.001650
2I Borisov 2019-12-08 00:00:00 171.852535053 -17.895966055 0.058015
2I Borisov 2019-12-09 00:00:00 172.269463755 -18.665203001 0.052076
2I Borisov 2019-12-10 00:00:00 172.685171171 -19.435484512 0.046495
99942 Apophis 2029-04-13 20:30:00 162.187667572 -1.853286529 0.226242
99942 Apophis 2029-04-13 20:40:00 158.279702838 0.774021995 0.232031
99942 Apophis 2029-04-13 20:50:00 153.882138225 3.710216547 0.225683
99942 Apophis 2029-04-13 21:00:00 148.943005706 6.944451473 0.218189
367943 Duende 2013-02-15 19:15:00 180.339837945 -21.468014339 0.085326
367943 Duende 2013-02-15 19:20:00 180.569829577 -17.847141796 0.067577
367943 Duende 2013-02-15 19:25:00 180.799668504 -14.126072752 0.054168
367943 Duende 2013-02-15 19:30:00 181.029797039 -10.326531491 0.065598

433 Eros checks an ordinary ellipse. C/2023 A3 is near-parabolic and contains A1/A2. 2P/Encke is a periodic comet with non-gravitational acceleration. 2I/Borisov enforces e=3.356475782676596 with A1/A2/A3. Apophis and Duende test rapid apparent motion during close Earth passages.

11. Reproducibility and limitations

Appendix A. Comparison of programming languages

Every successful edition passed the same 20 reference rows. Accuracy is sorted alphabetically by language. The separate timing table is sorted by ascending median time, fastest first. Each language calculated six cases seven times in an alternating cross-language order. These numbers show only relative performance on this test computer.

A.1 Accuracy, alphabetical

Maximum spherical angular separation over all 20 reference rows.

Language Max. Delta arcsec Result Source B Package B Toolchain
Ada 0.223931 PASS 65,773 520,661 GNAT/GCC 15.2.0 (MSYS2)
B4X 0.232382 PASS 63,915 550,906 B4J 10.50
Ballerina 0.238317 PASS 54,944 546,455 Ballerina Swan Lake 2201.13.4
BlitzMax 1.203843 PASS 91,196 549,418 BlitzMax NG 0.154.3.58
C 0.232031 PASS 55,659 565,802 GCC 16.1.0
C# 0.232031 PASS 50,345 545,734 .NET SDK 8.0.423
C++ 0.238317 PASS 56,461 546,717 Microsoft Visual C++ Community
Clojure 0.235614 PASS 45,101 486,240 Clojure 1.12.5 / OpenJDK
Common Lisp 0.223203 PASS 45,225 496,773 SBCL 2.6.6
D 0.250215 PASS 38,210 514,240 DMD 2.112.0
Dart 0.228185 PASS 42,668 538,284 Dart 3.12.2
Delphi/Object Pascal 0.232316 PASS 56,636 524,660 Free Pascal 3.2.2
Elixir 0.243787 PASS 53,273 575,919 Elixir 1.20.2 / Erlang OTP 29
Erlang 0.243787 PASS 52,475 583,700 Erlang/OTP 29.0.3
F# 0.216606 PASS 121,927 758,208 .NET SDK 8.0.423
Factor 51.049773 PASS 43,312 493,140 Factor 0.101
Fantom 0.238317 PASS 54,847 549,148 Fantom 1.0.83
Fortran 0.226948 PASS 51,037 495,006 GNU Fortran 16.1.0
FreeBASIC 0.252308 PASS 66,512 580,763 FreeBASIC 1.10.1
GNU Octave 38.034360 PASS 35,427 503,786 GNU Octave 11.3.0
Go 0.239381 PASS 39,667 526,531 Go 1.26.5
Groovy 2.810988 PASS 47,731 586,421 Apache Groovy 5.0.2 / OpenJDK
Haskell 0.259614 PASS 44,580 498,045 GHC 9.10.3
Haxe 0.228185 PASS 46,913 533,441 Haxe 4.3.7
Java 0.238317 PASS 46,825 548,599 Microsoft OpenJDK 25.0.3
JavaScript 0.236219 PASS 107,767 592,039 Node.js 24.15.0 / Browser ES Modules
Julia 0.229764 PASS 30,958 477,282 Julia 1.12.6
Kotlin 0.238317 PASS 39,887 505,280 Kotlin 2.4.10 / OpenJDK
Lua 0.216606 PASS 41,719 521,245 Lua 5.4
Nim 0.228185 PASS 38,024 520,143 Nim 2.2.10
OCaml 0.232382 PASS 119,362 724,314 OCaml 5.4.0
Perl 0.236719 PASS 43,107 469,668 Perl 5
PHP 0.232031 PASS 33,017 516,442 PHP 8.3.30 CLI
PowerShell 37.997961 PASS 49,973 470,165 PowerShell 7.6.4
Prolog 38.032226 PASS 55,733 632,243 SWI-Prolog 10.0.2
Python 0.082129 PASS 218,903 787,360 Python 3.11.9
R 0.231526 PASS 33,980 506,812 R 4.6.1
Racket 0.218560 PASS 44,652 482,854 Racket 9.2
Ruby 0.238317 PASS 37,165 506,816 Ruby 3.4
Rust 0.228185 PASS 49,635 541,242 rustc 1.94.1
Scala 0.238317 PASS 43,107 499,504 Scala CLI 1.15.0 / OpenJDK
Smalltalk 42.792737 PASS 34,747 422,800 Pharo 12
Squirrel 0.228185 PASS 44,919 530,616 Squirrel 3 with float64 build
Tcl 0.232382 PASS 47,620 468,744 Tcl 8.6
TypeScript + WebAssembly 0.239511 PASS 57,170 555,531 TypeScript 7.0.2, Rust WASI, Node.js 24.15.0
V 0.236584 PASS 40,341 507,133 V 0.4
Vala/Genie 0.228185 PASS 74,030 589,922 Vala 0.56
Visual Basic .NET 0.232031 PASS 102,111 650,099 .NET SDK 8.0.423
Zig 0.239824 PASS 44,555 524,110 Zig 0.16.0

A.2 Performance, fastest first

Factor 1.000 denotes the fastest median in the validation run.

Rank Language Minimum ms Median ms Maximum ms Factor
1 TypeScript + WebAssembly 597.377 700.999 1047.449 1.000
2 Fortran 596.688 750.360 1158.344 1.070
3 Rust 523.705 823.898 1294.582 1.175
4 B4X 775.000 971.000 2194.000 1.385
5 Haxe 39.747 1077.578 2013.806 1.537
6 Delphi/Object Pascal 937.000 1078.000 1672.000 1.538
7 Zig 1035.454 1160.447 2320.055 1.655
8 C++ 1223.592 1441.870 2671.024 2.057
9 Java 1284.711 1458.187 3353.160 2.080
10 C 1343.938 1486.065 3056.110 2.120
11 Kotlin 1176.612 1593.443 5530.074 2.273
12 Ada 1460.014 1640.974 3057.029 2.341
13 V 1575.736 1878.741 2571.347 2.680
14 D 1546.526 1890.757 4220.870 2.697
15 Go 1468.523 2141.171 9256.961 3.054
16 Visual Basic .NET 1696.939 2216.958 3742.810 3.163
17 F# 1783.725 2231.113 3564.241 3.183
18 Nim 2119.599 2326.564 5060.611 3.319
19 C# 2374.419 2552.401 6731.288 3.641
20 Dart 2190.595 2733.704 5027.791 3.900
21 Scala 2073.900 3242.849 7436.041 4.626
22 OCaml 2669.212 3467.746 8892.401 4.947
23 FreeBASIC 2767.911 3570.477 5810.864 5.093
24 Julia 3434.719 4304.078 8335.834 6.140
25 Fantom 4241.671 5794.304 8545.390 8.266
26 Vala/Genie 4412.042 5820.308 8666.353 8.303
27 Haskell 6047.352 8690.853 14693.710 12.398
28 BlitzMax 7899.000 8742.000 12931.000 12.471
29 JavaScript 7724.900 10157.720 23217.210 14.490
30 Common Lisp 11056.269 14392.095 22933.360 20.531
31 Ballerina 12341.616 16894.464 33663.612 24.101
32 Racket 16694.584 21229.729 44060.672 30.285
33 Erlang 19354.829 24802.406 45274.726 35.381
34 Elixir 23434.035 29699.584 53299.712 42.367
35 PHP 41451.985 49798.621 104949.488 71.039
36 Prolog 49043.000 69317.000 255244.000 98.883
37 Lua 53249.000 69977.000 130427.000 99.825
38 Squirrel 55843.000 71891.000 119351.000 102.555
39 Clojure 55241.707 72206.563 171065.047 103.005
40 Ruby 66049.112 90263.925 168102.051 128.765
41 Factor 70004.415 94462.332 151220.285 134.754
42 Smalltalk 65113.000 95089.000 157550.000 135.648
43 Groovy 68129.578 95282.139 143061.528 135.923
44 Python 74058.281 138890.584 308314.450 198.132
45 Perl 108115.457 154930.755 276448.215 221.014
46 PowerShell 206315.845 250942.054 717706.460 357.978
47 Tcl 202078.602 265143.768 494701.078 378.237
48 R 168780.000 343730.000 910730.000 490.343
49 GNU Octave 315324.607 419255.178 922894.954 598.082

Appendix B. Licence boundary

The project-written numerical core, generators and documentation covered by LICENSE-SCOPE.md are under Apache License 2.0. Data tables, external ephemerides, JPL kernels and third-party programs require separate assessment. Redistribution must include LICENSE and NOTICE, preserve copyright and licence notices, and identify modified files. The patent grant and termination rule are stated in section 3 of the Apache licence.

Project website: https://www.jostjahn.de/amrum-software/ephemeris/