Skip to content

Speed up integrator alt/az via pyerfa + rewritten FastAltAz#423

Merged
brickbots merged 2 commits into
mainfrom
altaz_perf
May 19, 2026
Merged

Speed up integrator alt/az via pyerfa + rewritten FastAltAz#423
brickbots merged 2 commits into
mainfrom
altaz_perf

Conversation

@brickbots
Copy link
Copy Markdown
Owner

Summary

The integrator's per-frame altaz/constellation work was dominated by skyfield's observe()/apparent()/altaz() chain, which the integrator called twice per frame (eyepiece + camera center). This PR replaces it with erfa.atco13 (pyerfa) — the same SOFA/ERFA precession/nutation/aberration math, ~19× faster, accurate to ~14″.

  • Skyfield_utils.radec_to_altaz: reimplemented via erfa.atco13. Same (ra, dec, dt, atmos=True) signature. Adds pyerfa==2.0.1.5 to requirements.txt (small C-extension wheel, ARM-friendly).
  • FastAltAz: body rewritten using IAU-1982 GMST + spherical trig + Bennett refraction. Fixes a subtle LST bug (the prior 0.985647 · days_since_j2000 term double-counted the fractional day) and adds the previously-missing refraction. API unchanged: __init__(lat, lon, dt) + radec_to_altaz(ra, dec, alt_only=False). Continues to back catalogs.py and calc_object_altitude.
  • Skyfield_utils.set_location: now no-ops when (lat, lon, altitude) is unchanged, removing the per-tick wgs84.latlon rebuilds for callers like the integrator and chart UI.

Performance

Measured on macOS dev machine; Pi numbers will scale, but ratios should hold.

Path before after speedup
Skyfield_utils.radec_to_altaz ~915 µs (skyfield) ~48 µs (pyerfa) ~19×
Skyfield_utils.set_location (same loc) ~25 µs ~0.25 µs (cached no-op) ~100×
FastAltAz.radec_to_altaz ~3.4 µs ~1.6 µs ~2×
Integrator per-frame altaz block¹ ~1840 µs ~113 µs ~16×

¹ set_location + 2× radec_to_altaz + radec_to_constellation.

Accuracy

vs raw skyfield observe()/apparent()/altaz("standard") reference, median angular separation:

Path median error
Skyfield_utils.radec_to_altaz (pyerfa) 14″ (~arcsec)
FastAltAz (rewritten) ~13′ (~0.3°, precession-dominated, same bar calc_object_altitude already lived with)

Test plan

  • nox -s lint — clean (after auto-fixes of unused imports)
  • nox -s format — clean
  • nox -s type_hints — clean on this branch's changed files (only pre-existing errors elsewhere)
  • nox -s smoke_tests — 2/2 pass
  • nox -s unit_tests — 107/107 pass, including 9 new tests:
    • TestFastAltAz: bounds, alt_only short-circuit, refraction sign, accuracy floor vs skyfield, LST advances with time
    • TestSkyfieldUtilsRadecToAltaz: requires set_location, matches skyfield within 120″ (atmos=True) and 30″ (atmos=False) at well-elevated points, atmos flag lifts altitude
  • Smoke test on a Pi to confirm the pyerfa wheel installs and runs (recommended before merging)

🤖 Generated with Claude Code

Replaces the per-iteration skyfield observe()/apparent()/altaz() chain
inside Skyfield_utils.radec_to_altaz with erfa.atco13 (pyerfa). Same
precession/nutation/aberration math under the hood, ~19x faster, accuracy
holds within ~14 arcsec of the prior skyfield output at well-elevated
points.

Also rewrites FastAltAz around IAU-1982 GMST + spherical trig + Bennett
refraction. Fixes the previous LST formula which double-counted the
fractional day, and adds the missing refraction term. API unchanged
(catalogs.py and calc_object_altitude continue to work). Per-call cost
drops from ~3.4 us to ~1.6 us; total accuracy floor of ~0.3 deg
(precession-dominated) is unchanged.

Skyfield_utils.set_location now no-ops when the location is unchanged,
removing wgs84.latlon rebuilds on every integrator tick.

Net effect on the integrator's per-frame altaz/constellation work
(set_location + 2x radec_to_altaz + constellation): ~1840 us -> ~113 us
(~16x faster).

Adds pyerfa to requirements.txt and unit tests for FastAltAz and the
pyerfa-backed Skyfield_utils.radec_to_altaz.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@brickbots brickbots changed the base branch from release to main May 19, 2026 21:57
@brickbots brickbots merged commit 7b148bb into main May 19, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant