pg_orrery/test/sql/moon_observe.sql
Ryan Malloy ad7209d0db Phase 3: Planetary moons and Jupiter radio burst prediction
Add observation functions for 19 planetary moons across four systems:
- Galilean moons (Io, Europa, Ganymede, Callisto) via clean-room L1.2 theory
- Saturn moons (Mimas through Hyperion) via TASS 1.7
- Uranus moons (Miranda through Oberon) via GUST86
- Mars moons (Phobos, Deimos) via MarsSat

Add Jupiter decametric radio burst prediction for Radio JOVE operators:
- io_phase_angle() — Io orbital phase from superior conjunction
- jupiter_cml() — System III Central Meridian Longitude with light-time correction
- jupiter_burst_probability() — Carr et al. (1983) source regions A, B, C, D

L1.2 Galilean theory is a clean-room MIT implementation from the published
IMCCE FORTRAN coefficients. All other ephemeris libraries are MIT-licensed
extractions from Stellarium with static caching removed for PARALLEL SAFE.

All 10 regression tests pass. Extension .so grows from 2.4MB to 2.5MB.
2026-02-16 01:55:13 -07:00

153 lines
7.4 KiB
SQL

-- moon_observe regression tests
--
-- Tests planetary moon observation (Galilean, Saturn, Uranus, Mars)
-- and Jupiter decametric radio burst prediction functions.
-- Reference distances from JPL/NASA fact sheets.
\set boulder '''40.015N 105.270W 1655m'''::observer
-- ============================================================
-- Test 1: Galilean moon observation - all four from Boulder
-- Io, Europa, Ganymede, Callisto should return valid topocentric.
-- ============================================================
SELECT 'galilean_io' AS test,
round(topo_azimuth(galilean_observe(0, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(galilean_observe(0, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
SELECT 'galilean_europa' AS test,
round(topo_azimuth(galilean_observe(1, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(galilean_observe(1, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
SELECT 'galilean_ganymede' AS test,
round(topo_azimuth(galilean_observe(2, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(galilean_observe(2, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
SELECT 'galilean_callisto' AS test,
round(topo_azimuth(galilean_observe(3, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(galilean_observe(3, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
-- ============================================================
-- Test 2: Galilean moons should be very close to Jupiter
-- All four should have ranges within ~0.05 AU of Jupiter's range.
-- Jupiter is ~4-6 AU from Earth; moons orbit within 0.013 AU.
-- ============================================================
SELECT 'galilean_near_jupiter' AS test,
round(topo_range(planet_observe(5, :boulder,
'2024-03-15 03:00:00+00'))::numeric, -4) AS jupiter_km,
round(topo_range(galilean_observe(0, :boulder,
'2024-03-15 03:00:00+00'))::numeric, -4) AS io_km,
round(topo_range(galilean_observe(3, :boulder,
'2024-03-15 03:00:00+00'))::numeric, -4) AS callisto_km;
-- ============================================================
-- Test 3: Saturn moon observation - Titan (body_id=5)
-- Titan is Saturn's largest moon, should be near Saturn.
-- ============================================================
SELECT 'saturn_titan' AS test,
round(topo_azimuth(saturn_moon_observe(5, :boulder,
'2024-06-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(saturn_moon_observe(5, :boulder,
'2024-06-15 03:00:00+00'))::numeric, 0) AS el_deg;
-- ============================================================
-- Test 4: Saturn moons - Mimas through Iapetus all return results
-- ============================================================
SELECT 'saturn_moons' AS test,
body_id,
round(topo_range(saturn_moon_observe(body_id, :boulder,
'2024-06-15 03:00:00+00'))::numeric, -4) AS range_km
FROM generate_series(0, 7) AS body_id;
-- ============================================================
-- Test 5: Uranus moon observation - Titania (body_id=3)
-- ============================================================
SELECT 'uranus_titania' AS test,
round(topo_azimuth(uranus_moon_observe(3, :boulder,
'2024-06-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(uranus_moon_observe(3, :boulder,
'2024-06-15 03:00:00+00'))::numeric, 0) AS el_deg;
-- ============================================================
-- Test 6: All Uranus moons return valid results
-- ============================================================
SELECT 'uranus_moons' AS test,
body_id,
round(topo_range(uranus_moon_observe(body_id, :boulder,
'2024-06-15 03:00:00+00'))::numeric, -4) AS range_km
FROM generate_series(0, 4) AS body_id;
-- ============================================================
-- Test 7: Mars moons - Phobos and Deimos
-- ============================================================
SELECT 'mars_phobos' AS test,
round(topo_azimuth(mars_moon_observe(0, :boulder,
'2024-01-15 06:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(mars_moon_observe(0, :boulder,
'2024-01-15 06:00:00+00'))::numeric, 0) AS el_deg;
SELECT 'mars_deimos' AS test,
round(topo_azimuth(mars_moon_observe(1, :boulder,
'2024-01-15 06:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(mars_moon_observe(1, :boulder,
'2024-01-15 06:00:00+00'))::numeric, 0) AS el_deg;
-- ============================================================
-- Test 8: Io phase angle - should be [0, 360)
-- ============================================================
SELECT 'io_phase' AS test,
round(io_phase_angle('2024-03-15 03:00:00+00')::numeric, 1) AS phase_deg,
io_phase_angle('2024-03-15 03:00:00+00') >= 0.0
AND io_phase_angle('2024-03-15 03:00:00+00') < 360.0 AS in_range;
-- ============================================================
-- Test 9: Jupiter CML (System III) - should be [0, 360)
-- ============================================================
SELECT 'jupiter_cml' AS test,
round(jupiter_cml(:boulder, '2024-03-15 03:00:00+00')::numeric, 1) AS cml_deg,
jupiter_cml(:boulder, '2024-03-15 03:00:00+00') >= 0.0
AND jupiter_cml(:boulder, '2024-03-15 03:00:00+00') < 360.0 AS in_range;
-- ============================================================
-- Test 10: Jupiter burst probability - known high probability zone
-- Source A: CML 200-260, Io phase 195-265 => p=0.8
-- ============================================================
SELECT 'burst_source_a' AS test,
jupiter_burst_probability(230.0, 230.0) AS p_source_a;
-- ============================================================
-- Test 11: Jupiter burst probability - known zones
-- ============================================================
SELECT 'burst_zones' AS test,
jupiter_burst_probability(90.0, 150.0) AS p_source_b,
jupiter_burst_probability(240.0, 350.0) AS p_source_c,
jupiter_burst_probability(100.0, 25.0) AS p_source_d,
jupiter_burst_probability(0.0, 130.0) AS p_quiet;
-- ============================================================
-- Test 12: Io phase changes over time (Io orbits in ~1.77 days)
-- Two times 12 hours apart should show significant phase change.
-- ============================================================
SELECT 'io_phase_changes' AS test,
round(io_phase_angle('2024-03-15 00:00:00+00')::numeric, 1) AS phase_0h,
round(io_phase_angle('2024-03-15 12:00:00+00')::numeric, 1) AS phase_12h,
abs(io_phase_angle('2024-03-15 00:00:00+00') -
io_phase_angle('2024-03-15 12:00:00+00')) > 10.0 AS changed;
-- ============================================================
-- Test 13: Error handling - invalid Galilean moon body_id
-- ============================================================
SELECT 'invalid_galilean' AS test, galilean_observe(4, :boulder, now());
-- ============================================================
-- Test 14: Error handling - invalid Saturn moon body_id
-- ============================================================
SELECT 'invalid_saturn' AS test, saturn_moon_observe(8, :boulder, now());