pg_orrery/test/sql/planet_observe.sql
Ryan Malloy 0544a78276 pg_orbit 0.2.0: Full solar system computation at the SQL layer
Phase 1 — Stars, comets, Keplerian propagation:
- star_observe() / star_observe_safe(): fixed star alt/az via IAU 1976
  precession, equatorial-to-horizontal transform
- kepler_propagate(): two-body Keplerian orbit propagation for
  elliptic, parabolic, and hyperbolic orbits
- comet_observe(): observe comets/asteroids from orbital elements
- heliocentric type: ecliptic J2000 position (x, y, z in AU)

Phase 2 — VSOP87 planets, ELP82B Moon, Sun:
- planet_heliocentric(): VSOP87 heliocentric ecliptic J2000 positions
  for Mercury through Neptune (Bretagnon & Francou, MIT)
- planet_observe(): full observation pipeline for any planet
- sun_observe(): Sun position from negated Earth VSOP87
- moon_observe(): ELP2000-82B lunar position (Chapront-Touzé, MIT)
- Clean-room precession (IAU 2006) and sidereal time (IERS 2010)
- elliptic_to_rectangular utility (Stellarium, MIT)

All Stellarium extractions are MIT-licensed, thread-safe (static
caching removed for PARALLEL SAFE), zero external data files.

All 9 regression tests pass (90ms total).
2026-02-16 01:36:27 -07:00

117 lines
5.8 KiB
SQL

-- planet_observe regression tests
--
-- Tests VSOP87 planetary positions, Sun and Moon observation,
-- and the planet_heliocentric function.
-- Reference values cross-checked against JPL Horizons.
\set boulder '''40.015N 105.270W 1655m'''::observer
-- ============================================================
-- Test 1: Sun at heliocentric origin
-- planet_heliocentric(0, t) should always return (0, 0, 0)
-- because the Sun IS the origin of heliocentric coordinates.
-- ============================================================
SELECT 'sun_origin' AS test,
round(helio_x(planet_heliocentric(0, '2024-06-21 12:00:00+00'))::numeric, 10) AS x,
round(helio_y(planet_heliocentric(0, '2024-06-21 12:00:00+00'))::numeric, 10) AS y,
round(helio_z(planet_heliocentric(0, '2024-06-21 12:00:00+00'))::numeric, 10) AS z;
-- ============================================================
-- Test 2: Earth heliocentric distance ~ 1 AU
-- Earth (body_id=3) should be ~0.983-1.017 AU from the Sun
-- throughout the year (eccentricity ~0.017).
-- ============================================================
SELECT 'earth_distance' AS test,
round(helio_distance(planet_heliocentric(3, '2024-01-03 12:00:00+00'))::numeric, 3) AS perihelion,
round(helio_distance(planet_heliocentric(3, '2024-07-05 12:00:00+00'))::numeric, 3) AS aphelion;
-- ============================================================
-- Test 3: Mars heliocentric distance ~ 1.38-1.67 AU
-- Mars (body_id=4) average distance is ~1.524 AU.
-- ============================================================
SELECT 'mars_distance' AS test,
round(helio_distance(planet_heliocentric(4, '2024-06-21 12:00:00+00'))::numeric, 2) AS dist_au;
-- ============================================================
-- Test 4: Jupiter heliocentric distance ~ 4.95-5.46 AU
-- Jupiter (body_id=5) average distance is ~5.203 AU.
-- ============================================================
SELECT 'jupiter_distance' AS test,
round(helio_distance(planet_heliocentric(5, '2024-06-21 12:00:00+00'))::numeric, 1) AS dist_au;
-- ============================================================
-- Test 5: planet_observe for Jupiter from Boulder
-- Jupiter should be observable (check we get a valid result,
-- not an error). Elevation will vary by time.
-- ============================================================
SELECT 'jupiter_observe' AS test,
round(topo_azimuth(planet_observe(5, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(planet_observe(5, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
-- ============================================================
-- Test 6: planet_observe for Venus from Boulder
-- Venus (body_id=2) should return valid az/el.
-- ============================================================
SELECT 'venus_observe' AS test,
round(topo_azimuth(planet_observe(2, :boulder,
'2024-06-15 02:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(planet_observe(2, :boulder,
'2024-06-15 02:00:00+00'))::numeric, 0) AS el_deg;
-- ============================================================
-- Test 7: sun_observe from Boulder at local noon (~18:00 UTC)
-- At summer solstice noon in Boulder (MDT = UTC-6),
-- Sun should be high in the south (az ~180, el ~73 deg).
-- ============================================================
SELECT 'sun_noon' AS test,
round(topo_azimuth(sun_observe(:boulder,
'2024-06-21 18:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(sun_observe(:boulder,
'2024-06-21 18:00:00+00'))::numeric, 0) AS el_deg;
-- ============================================================
-- Test 8: sun_observe range should be ~1 AU = 149,597,871 km
-- ============================================================
SELECT 'sun_range' AS test,
round(topo_range(sun_observe(:boulder,
'2024-06-21 18:00:00+00'))::numeric, -4) AS range_km;
-- ============================================================
-- Test 9: moon_observe from Boulder
-- Should return valid az/el with range ~ 356,000-407,000 km.
-- ============================================================
SELECT 'moon_observe' AS test,
round(topo_azimuth(moon_observe(:boulder,
'2024-06-21 18:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(moon_observe(:boulder,
'2024-06-21 18:00:00+00'))::numeric, 0) AS el_deg,
round(topo_range(moon_observe(:boulder,
'2024-06-21 18:00:00+00'))::numeric, -3) AS range_km;
-- ============================================================
-- Test 10: Moon range sanity (should be 356k-407k km)
-- ============================================================
SELECT 'moon_range_check' AS test,
topo_range(moon_observe(:boulder, '2024-06-21 18:00:00+00')) BETWEEN 350000 AND 410000 AS in_range;
-- ============================================================
-- Test 11: Error handling - cannot observe Earth from Earth
-- ============================================================
SELECT 'earth_error' AS test, planet_observe(3, :boulder, now());
-- ============================================================
-- Test 12: Error handling - invalid body_id
-- ============================================================
SELECT 'invalid_body' AS test, planet_heliocentric(9, now());
-- ============================================================
-- Test 13: All planets return valid heliocentric positions
-- Mercury through Neptune, none should error.
-- ============================================================
SELECT 'all_planets' AS test,
body_id,
round(helio_distance(planet_heliocentric(body_id, '2024-06-21 12:00:00+00'))::numeric, 2) AS dist_au
FROM generate_series(1, 8) AS body_id;