A Zig library for astronomical calculations, starting with the Moon.
  • Zig 96.2%
  • Shell 3.4%
  • Nix 0.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Jeffrey C. Ollie 1f216fee40
All checks were successful
test / test (push) Successful in 7m38s
test / docs (push) Successful in 7m38s
Add the Moon's topocentric libration
moon.libration.topocentric is Meeus's direct calculation (p. 375): the
libration and the position angle of the axis found as for the Earth's
center, from the Moon's topocentric ecliptic coordinates and right
ascension. The geocentric libration and it now share one function.

Against Horizons from Palomar, Tromso and Cerro Paranal, up to the last
measured Delta T: 0.0082 deg in longitude and 0.0081 deg in the position
angle; in latitude the same 0.0224 deg lunar frame tilt as from the
center, and 0.0049 deg with it taken out. The topocentric libration is
up to 0.989 deg from the geocentric at these sites.

Closes #15

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014UMaGEwDffMoTp1d9BKsaz
2026-10-23 21:51:31 -05:00
.forgejo/workflows Add the project scaffolding 2026-10-22 22:04:50 -05:00
docs Settle the vocabulary for observers on the Earth's surface 2026-10-23 17:55:11 -05:00
LICENSES Add the principal phases and lunar age 2026-10-23 00:07:10 -05:00
src Add the Moon's topocentric libration 2026-10-23 21:51:31 -05:00
tests Add the Moon's topocentric libration 2026-10-23 21:51:31 -05:00
tools Add the Moon's topocentric libration 2026-10-23 21:51:31 -05:00
.gitignore Add the project scaffolding 2026-10-22 22:04:50 -05:00
AGENTS.md Add the project scaffolding 2026-10-22 22:04:50 -05:00
build.zig Add instants, time scales and ΔT 2026-10-22 23:14:14 -05:00
build.zig.zon Add the project scaffolding 2026-10-22 22:04:50 -05:00
CONTRIBUTING.md Add atmospheric refraction 2026-10-23 21:45:51 -05:00
flake.lock Add the project scaffolding 2026-10-22 22:04:50 -05:00
flake.nix Add the project scaffolding 2026-10-22 22:04:50 -05:00
GLOSSARY.md Settle the vocabulary for observers on the Earth's surface 2026-10-23 17:55:11 -05:00
README.md Add the Moon's topocentric libration 2026-10-23 21:51:31 -05:00
REUSE.toml Add atmospheric refraction 2026-10-23 21:45:51 -05:00

zig-astro

Astronomical calculations for Zig 0.17.

The Earth's Moon is the first target. The Sun, the planets, and other natural and artificial satellites are to follow.

What is here so far is the foundation everything else stands on:

astro.time Instants, the time scales they are counted in (TT and UT), Julian and Gregorian calendar dates, Unix time, and ΔT
astro.Angle Angles, read and written in radians, degrees, arcseconds, hours, and sexagesimal, so that one is never mistaken for another
astro.coord Ecliptic, equatorial and horizontal coordinates, and the conversions between them
astro.earth Nutation, the mean and true obliquity of the ecliptic, and mean and apparent sidereal time at Greenwich
astro.observer An observer on the Earth's surface, by geodetic latitude, east longitude and height on the WGS 84 ellipsoid, and the topocentric position — right ascension and declination, ecliptic coordinates, hour angle, azimuth and altitude, and distance — of any body whose geocentric position is known; and atmospheric refraction, from airless to observed altitude and back
astro.sun The Sun's apparent position and distance, to about 0.01°, from the Earth's center or from an observer
astro.moon The Moon's apparent position and distance, to about 10″ in longitude and 4″ in latitude, from the Earth's center or from an observer, from whom it can be a degree away
astro.moon.phase The Moon's phase at an instant — elongation, phase angle, illuminated fraction, the bright limb, waxing or waning, and its named phase — and the instants of new Moon, first quarter, full Moon and last quarter, to within seconds, with the lunar age
astro.moon.libration The Moon's optical, physical and total libration and the position angle of its axis, from the Earth's center or from an observer, the subsolar point, and the colongitude that tells which craters the Sun is rising on
const astro = @import("astro");

// 1957 October 4, 19:26:24 UT, the launch of Sputnik 1.
const launch = astro.time.Instant.fromCalendar(
    .gregorian,
    .init(1957, .october, 4, 19, 26, 24),
    .ut,
);
const tt = launch.to(.tt, .model); // about 32 s later, by the ΔT model

Where the Moon is in the sky, seen from Palomar Observatory:

const palomar: astro.observer.Observer = .{
    .latitude = astro.Angle.fromDegrees(33.3561),
    .longitude = astro.Angle.fromDegrees(-116.8625), // east is positive
    .height_m = 1706,
};
const now = astro.time.Instant.fromCalendar(
    .gregorian,
    .init(2026, .october, 23, 4, 0, 0),
    .ut,
);
const moon = astro.moon.topocentric(now, palomar);
// moon.horizontal.azimuth.degrees() is about 153.0, a little east of
// south, and moon.horizontal.altitude.degrees() about 51.1.

Longitude counts positive east of Greenwich, and azimuth from the north through the east, as almost everyone counts them; Meeus counts both differently, and the conversions from his conventions are made inside the library. Altitudes are airless; astro.observer.observedAltitude adds the refraction that lifts a body to where it is seen, about half a degree at the horizon, and airlessAltitude takes it away again.

Every calculation takes an Instant, which says whether it is counted in TT, the uniform scale the theories use, or UT, which civil time follows. UTC is counted as UT. ΔT, the difference, comes from the Espenak–Meeus polynomials for the past, the U.S. Naval Observatory's measurements since 2005, and a prediction joining them to the long-term trend, unless a caller supplies a value of their own. Calendar dates say which calendar they are in: there is no automatic switch at 1582, because countries changed calendars at different times.

The API documentation is generated from the doc comments and rebuilt from main on every push.

Where this lives

The Forgejo instance at https://git.jcollie.dev/jeff/zig-astro is the main home of the repository:

$ git clone https://git.jcollie.dev/jeff/zig-astro.git

and it is mirrored on Tangled at https://tangled.org/jcollie.dev/zig-astro.

Adding it to a project

$ zig fetch --save git+https://git.jcollie.dev/jeff/zig-astro.git

then, in build.zig:

const astro = b.dependency("astro", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("astro", astro.module("astro"));

Building

The Nix flake's development shell has everything, Zig 0.17.0 included:

$ nix develop
$ zig build test --summary all    # the tests
$ zig build check                 # compile everything without running it
$ zig build fuzz --fuzz           # fuzz every calculation with any f64 at all
$ zig build docs-serve            # the API documentation, at http://127.0.0.1:8000/

Without Nix, Zig 0.17.0 is all that is needed; the library has no dependencies.

The tests check every calculation two ways: against the worked examples in Meeus's Astronomical Algorithms, which shows the code does what the book says, and against ephemerides from JPL Horizons over 1900–2100, which shows the book agrees with the sky. The Horizons tables are kept in tests/reference/ exactly as Horizons sent them, and tools/horizons.sh is the query that fetches them again. The instants of the phases are checked against all 9,895 in NASA's table of the phases of 1901–2100, which tools/nasa-phases.sh fetches.

The documentation has to be served rather than opened: what Zig emits is a WebAssembly viewer that fetches the source it shows from a tar file beside it, and a browser refuses to fetch anything from a file:// page. tools/docs_server.zig is that server, for one person on the loopback interface.

Reporting issues and contributing

Report issues and send pull requests to the Tangled mirror, https://tangled.org/jcollie.dev/zig-astro. CONTRIBUTING.md covers setting up, the checks a change has to pass, and how the code is laid out.

References cited

License

MIT. See LICENSES/MIT.txt. This project follows the REUSE specification.