Full-year rise / transit / set almanac for one topocentric observer.
For each body and each crossing it computes, to one-second accuracy:
- rise / set — the apparent altitude of the body's centre crossing the USNO horizon threshold (going up / down).
- transit — upper meridian crossing (local hour angle = 0), recording the apparent altitude and azimuth.
Conventions (match Skyfield's almanac)
Apparent altitude/azimuth and hour angle come from
EphCore.Corrections.ApparentPlace (light-time + annual aberration + topocentric
parallax on top of the validated IAU 2006/2000A precession-nutation frame).
The horizon thresholds encode refraction + semidiameter rather than modelling
them (Skyfield's build_horizon_function):
| Body | Centre altitude at rise/set |
|---|---|
| Sun | -50' (-0.8333°) — 34' refraction + 16' limb |
| Moon | -34' - asin-free (R_moon / range) (distance-dep) |
| Planets | -34' (-0.5667°) |
Method (analytic-seed root-find)
For each UTC day the algorithm:
- Computes one
ApparentPlace.frameat noon UTC, capturing the slow-varying nutation, Earth velocity, and sidereal-time context. - Evaluates
ApparentPlace.lookfor every body at that frame to obtain the body's apparent RA/Dec. - Uses the Meeus closed-form hour-angle formula
(
cos H₀ = −tan φ tan δ) to predict rise, set, and transit UTC times analytically from the noon RA/Dec and local sidereal time via the standard Meeus hour-angle formula. - Places a narrow bracket (±
@bracket_minutes) around each predicted time and confirms a sign change (altitude crossing threshold, or HA crossing zero) with two preciseApparentPlace.lookevaluations. - Refines within the confirmed bracket with a bracketed interpolation
solver to ≤
@refine_tolerance_us(250 ms; the inverse-interpolation step converges well inside that, so the residual error stays ~0.5 s against Skyfield — comfortably inside the 1 s contract and the second-granularity UTC output), reusing the day's nutation and Earth velocity throughout.
A full year for eight bodies therefore costs ~365 noon frames plus ~10 evaluations per event, instead of a dense hourly grid, while staying in sub-second agreement with reference almanac implementations.
Summary
Functions
Example observer at Chicago (41.8781°N, 87.6298°W, 181 m elevation).
Compute every rise/transit/set event for bodies over year, flat and sorted
by tt_jd. See module doc for the conventions and method.
Convenience wrapper returning %{meta: ..., events: ...} matching SPEC §6.
USNO horizon threshold (centre altitude, degrees) for body at topocentric
range_km. Only the Moon is range-dependent.
Body atoms for a named preset (:classic7 / :classic8 / :modern10).
All presets as a map.
The half-open UTC span [YEAR-01-01, (YEAR+1)-01-01) for year.
Types
Functions
@spec chicago() :: observer()
Example observer at Chicago (41.8781°N, 87.6298°W, 181 m elevation).
Compute every rise/transit/set event for bodies over year, flat and sorted
by tt_jd. See module doc for the conventions and method.
Options
:step_seconds— ignored (kept for API compat with the old dense-grid path).
Convenience wrapper returning %{meta: ..., events: ...} matching SPEC §6.
Options
:year(default2026),:bodies(atoms or a preset atom, default:classic8),:observer(default Chicago at 181 m),:kernel(label only, default"de440s.bsp"), plus anyevents/4options.
USNO horizon threshold (centre altitude, degrees) for body at topocentric
range_km. Only the Moon is range-dependent.
Body atoms for a named preset (:classic7 / :classic8 / :modern10).
All presets as a map.
@spec span(integer()) :: {DateTime.t(), DateTime.t()}
The half-open UTC span [YEAR-01-01, (YEAR+1)-01-01) for year.