Clean-room DE binary reader (~400 lines C) with Chebyshev/Clenshaw evaluation — no GPL dependency on jpl_eph. Per-backend lazy initialization preserves PARALLEL SAFE. Existing VSOP87/ELP82B functions stay IMMUTABLE; new _de() variants are STABLE with automatic fallback to compiled-in ephemerides on any DE failure. Implementation: - de_reader.c: header parse, record seek, Clenshaw recurrence - eph_provider.c: GUC (pg_orbit.ephemeris_path), lazy init, ICRS-to-ecliptic frame rotation, on_proc_exit cleanup - de_funcs.c: 11 new SQL functions (_de variants + diagnostics) - Constant chain of custody rules 6-8 (frame rotation, same-provider, AU consistency) Extract observe_from_geocentric() to astro_math.h for shared use by planet_funcs.c, moon_funcs.c, and de_funcs.c. 57 → 68 functions, 11 → 12 regression test suites, all passing.
129 lines
7.5 KiB
SQL
129 lines
7.5 KiB
SQL
-- de_ephemeris regression tests
|
|
--
|
|
-- Tests the _de() function variants and VSOP87 fallback behavior.
|
|
-- Since DE ephemeris files are not available in CI, these tests
|
|
-- verify that fallback behavior is correct and produces identical
|
|
-- results to the VSOP87 variants.
|
|
|
|
\set boulder '''40.015N 105.270W 1655m'''::observer
|
|
|
|
-- ============================================================
|
|
-- Test 1: pg_orbit_ephemeris_info() returns VSOP87 when no DE file
|
|
-- The provider should be 'VSOP87' since no ephemeris_path is set.
|
|
-- ============================================================
|
|
SELECT 'eph_info_vsop87' AS test,
|
|
(pg_orbit_ephemeris_info()).provider AS provider;
|
|
|
|
-- ============================================================
|
|
-- Test 2: planet_heliocentric_de falls back to VSOP87
|
|
-- Should produce identical results to planet_heliocentric()
|
|
-- when DE is unavailable.
|
|
-- ============================================================
|
|
SELECT 'helio_fallback' AS test,
|
|
round(helio_x(planet_heliocentric(3, '2024-06-21 12:00:00+00'))::numeric, 8) =
|
|
round(helio_x(planet_heliocentric_de(3, '2024-06-21 12:00:00+00'))::numeric, 8) AS x_match,
|
|
round(helio_y(planet_heliocentric(3, '2024-06-21 12:00:00+00'))::numeric, 8) =
|
|
round(helio_y(planet_heliocentric_de(3, '2024-06-21 12:00:00+00'))::numeric, 8) AS y_match,
|
|
round(helio_z(planet_heliocentric(3, '2024-06-21 12:00:00+00'))::numeric, 8) =
|
|
round(helio_z(planet_heliocentric_de(3, '2024-06-21 12:00:00+00'))::numeric, 8) AS z_match;
|
|
|
|
-- ============================================================
|
|
-- Test 3: planet_heliocentric_de Sun at origin
|
|
-- ============================================================
|
|
SELECT 'sun_origin_de' AS test,
|
|
round(helio_x(planet_heliocentric_de(0, '2024-06-21 12:00:00+00'))::numeric, 10) AS x,
|
|
round(helio_y(planet_heliocentric_de(0, '2024-06-21 12:00:00+00'))::numeric, 10) AS y,
|
|
round(helio_z(planet_heliocentric_de(0, '2024-06-21 12:00:00+00'))::numeric, 10) AS z;
|
|
|
|
-- ============================================================
|
|
-- Test 4: planet_observe_de falls back to VSOP87
|
|
-- Elevation and azimuth should match planet_observe().
|
|
-- ============================================================
|
|
SELECT 'observe_fallback' AS test,
|
|
round(topo_azimuth(planet_observe(5, :boulder, '2024-03-15 03:00:00+00'))::numeric, 4) =
|
|
round(topo_azimuth(planet_observe_de(5, :boulder, '2024-03-15 03:00:00+00'))::numeric, 4) AS az_match,
|
|
round(topo_elevation(planet_observe(5, :boulder, '2024-03-15 03:00:00+00'))::numeric, 4) =
|
|
round(topo_elevation(planet_observe_de(5, :boulder, '2024-03-15 03:00:00+00'))::numeric, 4) AS el_match;
|
|
|
|
-- ============================================================
|
|
-- Test 5: sun_observe_de falls back to VSOP87
|
|
-- ============================================================
|
|
SELECT 'sun_fallback' AS test,
|
|
round(topo_azimuth(sun_observe(:boulder, '2024-06-21 18:00:00+00'))::numeric, 4) =
|
|
round(topo_azimuth(sun_observe_de(:boulder, '2024-06-21 18:00:00+00'))::numeric, 4) AS az_match,
|
|
round(topo_elevation(sun_observe(:boulder, '2024-06-21 18:00:00+00'))::numeric, 4) =
|
|
round(topo_elevation(sun_observe_de(:boulder, '2024-06-21 18:00:00+00'))::numeric, 4) AS el_match;
|
|
|
|
-- ============================================================
|
|
-- Test 6: moon_observe_de falls back to ELP2000-82B
|
|
-- ============================================================
|
|
SELECT 'moon_fallback' AS test,
|
|
round(topo_azimuth(moon_observe(:boulder, '2024-06-21 18:00:00+00'))::numeric, 4) =
|
|
round(topo_azimuth(moon_observe_de(:boulder, '2024-06-21 18:00:00+00'))::numeric, 4) AS az_match,
|
|
round(topo_range(moon_observe(:boulder, '2024-06-21 18:00:00+00'))::numeric, 0) =
|
|
round(topo_range(moon_observe_de(:boulder, '2024-06-21 18:00:00+00'))::numeric, 0) AS range_match;
|
|
|
|
-- ============================================================
|
|
-- Test 7: lambert_c3_de falls back to VSOP87
|
|
-- Earth-Mars 2024 window should match lambert_c3().
|
|
-- ============================================================
|
|
SELECT 'lambert_fallback' AS test,
|
|
round(lambert_c3(3, 4, '2024-05-01 00:00:00+00', '2025-02-01 00:00:00+00')::numeric, 2) =
|
|
round(lambert_c3_de(3, 4, '2024-05-01 00:00:00+00', '2025-02-01 00:00:00+00')::numeric, 2) AS c3_match;
|
|
|
|
-- ============================================================
|
|
-- Test 8: lambert_transfer_de falls back to VSOP87
|
|
-- Should produce identical departure C3.
|
|
-- ============================================================
|
|
SELECT 'transfer_fallback' AS test,
|
|
round((lambert_transfer(3, 4, '2024-05-01 00:00:00+00', '2025-02-01 00:00:00+00')).c3_departure::numeric, 2) =
|
|
round((lambert_transfer_de(3, 4, '2024-05-01 00:00:00+00', '2025-02-01 00:00:00+00')).c3_departure::numeric, 2) AS c3_match;
|
|
|
|
-- ============================================================
|
|
-- Test 9: galilean_observe_de falls back to VSOP87
|
|
-- ============================================================
|
|
SELECT 'galilean_fallback' AS test,
|
|
round(topo_elevation(galilean_observe(0, :boulder, '2024-03-15 03:00:00+00'))::numeric, 4) =
|
|
round(topo_elevation(galilean_observe_de(0, :boulder, '2024-03-15 03:00:00+00'))::numeric, 4) AS el_match;
|
|
|
|
-- ============================================================
|
|
-- Test 10: saturn_moon_observe_de falls back to VSOP87
|
|
-- ============================================================
|
|
SELECT 'saturn_moon_fallback' AS test,
|
|
round(topo_elevation(saturn_moon_observe(5, :boulder, '2024-06-15 04:00:00+00'))::numeric, 4) =
|
|
round(topo_elevation(saturn_moon_observe_de(5, :boulder, '2024-06-15 04:00:00+00'))::numeric, 4) AS el_match;
|
|
|
|
-- ============================================================
|
|
-- Test 11: uranus_moon_observe_de falls back to VSOP87
|
|
-- ============================================================
|
|
SELECT 'uranus_moon_fallback' AS test,
|
|
round(topo_elevation(uranus_moon_observe(3, :boulder, '2024-06-15 04:00:00+00'))::numeric, 4) =
|
|
round(topo_elevation(uranus_moon_observe_de(3, :boulder, '2024-06-15 04:00:00+00'))::numeric, 4) AS el_match;
|
|
|
|
-- ============================================================
|
|
-- Test 12: mars_moon_observe_de falls back to VSOP87
|
|
-- ============================================================
|
|
SELECT 'mars_moon_fallback' AS test,
|
|
round(topo_elevation(mars_moon_observe(0, :boulder, '2024-06-15 04:00:00+00'))::numeric, 4) =
|
|
round(topo_elevation(mars_moon_observe_de(0, :boulder, '2024-06-15 04:00:00+00'))::numeric, 4) AS el_match;
|
|
|
|
-- ============================================================
|
|
-- Test 13: All DE planet functions work (fallback mode)
|
|
-- Mercury through Neptune, all should return valid positions
|
|
-- matching their VSOP87 counterparts.
|
|
-- ============================================================
|
|
SELECT 'all_planets_de' AS test,
|
|
body_id,
|
|
round(helio_distance(planet_heliocentric_de(body_id, '2024-06-21 12:00:00+00'))::numeric, 2) AS dist_au
|
|
FROM generate_series(1, 8) AS body_id;
|
|
|
|
-- ============================================================
|
|
-- Test 14: Error handling - invalid body_id
|
|
-- ============================================================
|
|
SELECT 'invalid_body_de' AS test, planet_heliocentric_de(9, now());
|
|
|
|
-- ============================================================
|
|
-- Test 15: Error handling - cannot observe Earth from Earth
|
|
-- ============================================================
|
|
SELECT 'earth_error_de' AS test, planet_observe_de(3, :boulder, now());
|