Skip to content

fix(libastro): use apparent sidereal time in EquatorialToHorizontal; add accuracy tests#2383

Merged
knro merged 1 commit into
indilib:masterfrom
ckemper67:test/libnova-coverage
May 2, 2026
Merged

fix(libastro): use apparent sidereal time in EquatorialToHorizontal; add accuracy tests#2383
knro merged 1 commit into
indilib:masterfrom
ckemper67:test/libnova-coverage

Conversation

@ckemper67
Copy link
Copy Markdown
Contributor

@ckemper67 ckemper67 commented Apr 30, 2026

Summary

Bug fix: EquatorialToHorizontal called ln_get_hrz_from_equ which uses mean sidereal time internally, while HorizontalToEquatorial's ln_get_equ_from_hrz uses apparent sidereal time. The mismatch introduced a fixed ~17" RA offset (equation of the equinoxes) in every round-trip. Fixed by calling ln_get_hrz_from_equ_sidereal_time with ln_get_apparent_sidereal_time(JD) explicitly, matching the inverse path.

New tests (test/core/test_libastro.cpp): five tests documenting the accuracy of the libnova coordinate pipeline against external truth (IMCCE Miriade, INPOP19) and internal round-trip consistency.

Accuracy results

Reciprocity (J2000 -> JNow -> J2000, Deneb): < 0.003"
Round-trip (Equatorial -> Horizontal -> Equatorial): sub-picosecond (was ~11" before fix)
vs IMCCE truth, Vega: az 2-17", alt 1-8"
vs IMCCE truth, Polaris: az 0.4-0.6", alt 0.6-1.0"

Polaris (polar distance 0.74°) is essentially at the celestial pole so its
position is dominated by precession and nutation in RA, which are well-corrected.
Vega's residuals reflect the IAU 1980 nutation accuracy floor of libnova.

Files changed

  • libs/indicore/libastro.cpp - sidereal time fix
  • test/core/test_libastro.cpp - new tests
  • test/core/CMakeLists.txt - wire up new test executable
  • test/data/horizontal_golden.json - IMCCE truth data (8 cases)
  • tools/generate_golden_files.py - script to regenerate golden data

Test plan

  • All 5 new tests pass locally
  • Existing tests unaffected

@ckemper67 ckemper67 force-pushed the test/libnova-coverage branch 2 times, most recently from 07d776e to 45d6cac Compare April 30, 2026 14:32
…add accuracy tests

EquatorialToHorizontal called ln_get_hrz_from_equ which uses mean sidereal
time internally, while HorizontalToEquatorial's ln_get_equ_from_hrz uses
apparent sidereal time. The mismatch introduced a fixed ~17" RA offset
(equation of the equinoxes) in every round-trip. Fix: call
ln_get_hrz_from_equ_sidereal_time with ln_get_apparent_sidereal_time(JD)
explicitly, matching the inverse path.

Five tests document the accuracy of the libnova coordinate pipeline:

Reciprocity: J2000toObserved -> ObservedToJ2000 closes to 0.003" for Deneb
at two epochs (2020, 2026).

HorizontalAccuracy_vs_IMCCE: measures accuracy against IMCCE Miriade (INPOP19)
for Vega and Polaris at four sites (Greenwich, Mitaka, Mauna Kea, Siding Spring).
Observed errors:
  Vega:    az 2-17",  alt 1-8"
  Polaris: az 0.4-0.6", alt 0.6-1.0"
Polaris is nearly on the celestial pole (polar distance 0.74°) so its position
is dominated by precession and nutation in RA; those are well-corrected by the
libnova nutation fix. Vega's residual errors reflect the IAU 1980 nutation
accuracy floor. Per-case errors are logged; the assertion catches gross
regressions which cause degree-level errors.

ObserverLongitudeMatters: Vega altitude spans > 30° across sites covering 316°
of longitude.

ObserverLatitudeMatters: Polaris is above the horizon at Greenwich (51.5 N) and
below it at Siding Spring (31.3 S).

RoundTrip_HorizontalToEquatorial: after the sidereal time fix, the round-trip
closes to sub-picosecond (was ~11" RA before the fix).

Golden data generated by tools/generate_golden_files.py (IMCCE Miriade, INPOP19).
@ckemper67 ckemper67 force-pushed the test/libnova-coverage branch from 45d6cac to eea107e Compare April 30, 2026 14:53
@knro knro self-assigned this May 1, 2026
@knro
Copy link
Copy Markdown
Contributor

knro commented May 1, 2026

Thank you! This offset was never caught in many years! Btw, how would ERFA perform in comparison with libnova for such calculations?

@ckemper67
Copy link
Copy Markdown
Contributor Author

ckemper67 commented May 1, 2026

Thank you! This offset was never caught in many years! Btw, how would ERFA perform in comparison with libnova for such calculations?

I was actually looking at ERFA this week. I could not explain some of the errors I was getting compared to IMCCE which I use as the external source of truth. So I started to look into the source of the errors and found the two issues (#2382 and #2383). I am still doing the investigation and these are my current numbers:


  Star accuracy vs IMCCE (PM-corrected input, arcsec):
  ------------------------------------------------------------
  Star             libnova  ERFA-2000B  ERFA-2000A  B vs libnova    B vs A
  ------------------------------------------------------------
  Deneb             0.0943      0.0189      0.0192        0.0754    0.0002
  Polaris           0.5459      0.3880      0.3877        0.1579    0.0003
  Canopus           0.3122      0.2412      0.2413        0.0710    0.0001
  Sirius            0.3195      0.2678      0.2681        0.0516    0.0002
  Vega              0.2507      0.1514      0.1512        0.0993    0.0002
  Rigel             0.0357      0.0221      0.0219        0.0136    0.0002
  Betelgeuse        0.1198      0.1220      0.1220        0.0022    0.0000
  Arcturus          0.1958      0.0805      0.0805        0.1153    0.0000
  ------------------------------------------------------------

ERFA is a bit better because it has a different nutation model. ERFA200A is the full model with the highest accuracy. I was concerned that it may be too expensive for smaller embedded systems, so I also looked into a simpler model ERFA 2000B. Both are closer to the IMCCE data, but the difference to libnova is in tenth of an arcsecond with my fixes. So from an accuracy perspective this may not matter.

My bigger worry about libnova is that the maintenance story is unclear. i.e. can I upstream fixes somewhere? In this case I could work around that because we have the libastro abstraction sitting on top of libnova, but in other cases that is harder. e.g. I started looking into the accuracy of the planetary models (which ERFA does not cover btw) and the moon model used in libnova has this error because it is using an older model:
ELP2000-82B (Moon): 42.6" at J2000, 54.9" in 2020 -- theory accuracy floor vs DE440 (ELP2000-82B was fitted to DE200).

Btw the size of the moon is 1800", so we will still find it even with the "large" error of nearly an arcminute.

@knro
Copy link
Copy Markdown
Contributor

knro commented May 2, 2026

Thank you for the detailed investigation. I'm not entirely sure about the maintenance status of libnova. Since the accuracy is not a big issue yet, we may consider switching to ERFA later. We can technically fork libnova and apply the fixes there as well if the upstream is no longer maintained.

@knro knro merged commit 8841397 into indilib:master May 2, 2026
10 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.

2 participants