pg_orrery/test/sql/v017_features.sql
Ryan Malloy 22b272fd0c Implement v0.17.0: solar elongation, planet phase, satellite eclipse, observing night quality, lunar libration
162 → 174 SQL objects, 27 → 28 test suites, 3 new C source files.

Features:
- solar_elongation(body_id, ts): Sun-Earth-Planet angle [0,180] degrees
- planet_phase(body_id, ts): illuminated disk fraction [0,1]
- satellite_is_eclipsed/next_eclipse_entry/exit/eclipse_fraction:
  cylindrical shadow model (Vallado §5.3) for Earth shadow prediction
- observing_night_quality(observer, ts): composite PL/pgSQL scoring
  based on astronomical darkness duration and Moon interference
- moon_libration_longitude/latitude/position_angle/libration/subsolar_longitude:
  optical libration from Meeus (1998) Ch. 53

Refactored magnitude_funcs.c to extract shared compute_planet_geometry()
used by magnitude, elongation, and phase — single VSOP87 evaluation per call.

All 28 regression suites pass. Zero compiler warnings.
2026-02-26 18:47:30 -07:00

205 lines
9.1 KiB
SQL

-- v017_features.sql -- Tests for v0.17.0: solar elongation, planet phase,
-- satellite eclipse, observing night quality, lunar libration
--
-- Verifies all 12 new functions added in v0.17.0.
CREATE EXTENSION IF NOT EXISTS pg_orrery;
-- ============================================================
-- Solar elongation: Mercury always < 28 deg
-- ============================================================
SELECT solar_elongation(1, '2024-01-15 00:00:00+00'::timestamptz) < 28.0
AS mercury_max_elongation;
-- ============================================================
-- Solar elongation: Venus always < 47 deg
-- ============================================================
SELECT solar_elongation(2, '2024-01-15 00:00:00+00'::timestamptz) < 47.5
AS venus_max_elongation;
-- ============================================================
-- Solar elongation: Mars can exceed 90 deg (superior planet)
-- Use a date near opposition (2024-01-12 Mars at elongation ~180)
-- At least verify it can be large for outer planets
-- ============================================================
SELECT solar_elongation(4, '2024-12-08 00:00:00+00'::timestamptz) > 50.0
AS mars_large_elongation;
-- ============================================================
-- Solar elongation: always [0, 180]
-- ============================================================
SELECT bool_and(
solar_elongation(body_id, '2024-06-15 00:00:00+00'::timestamptz) >= 0.0
AND solar_elongation(body_id, '2024-06-15 00:00:00+00'::timestamptz) <= 180.0
) AS elongation_in_range
FROM (VALUES (1),(2),(4),(5),(6),(7),(8)) AS t(body_id);
-- ============================================================
-- Solar elongation: error on body_id 0, 3, 9
-- ============================================================
DO $$ BEGIN PERFORM solar_elongation(0, '2024-01-15 00:00:00+00'::timestamptz); EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'elong body_id=0: %', SQLERRM; END $$;
DO $$ BEGIN PERFORM solar_elongation(3, '2024-01-15 00:00:00+00'::timestamptz); EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'elong body_id=3: %', SQLERRM; END $$;
DO $$ BEGIN PERFORM solar_elongation(9, '2024-01-15 00:00:00+00'::timestamptz); EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'elong body_id=9: %', SQLERRM; END $$;
-- ============================================================
-- Planet phase: Jupiter always near 1.0 (outer planet)
-- ============================================================
SELECT planet_phase(5, '2024-01-15 00:00:00+00'::timestamptz) > 0.95
AS jupiter_nearly_full;
-- ============================================================
-- Planet phase: Neptune always near 1.0
-- ============================================================
SELECT planet_phase(8, '2024-06-15 00:00:00+00'::timestamptz) > 0.99
AS neptune_nearly_full;
-- ============================================================
-- Planet phase: Venus varies significantly (inner planet)
-- Check it's in valid range
-- ============================================================
SELECT planet_phase(2, '2024-06-01 12:00:00+00'::timestamptz) BETWEEN 0.0 AND 1.0
AS venus_phase_valid;
-- ============================================================
-- Planet phase: always [0, 1] for all planets
-- ============================================================
SELECT bool_and(
planet_phase(body_id, '2024-01-15 00:00:00+00'::timestamptz) >= 0.0
AND planet_phase(body_id, '2024-01-15 00:00:00+00'::timestamptz) <= 1.0
) AS phase_in_range
FROM (VALUES (1),(2),(4),(5),(6),(7),(8)) AS t(body_id);
-- ============================================================
-- Planet phase: error cases match elongation
-- ============================================================
DO $$ BEGIN PERFORM planet_phase(3, '2024-01-15 00:00:00+00'::timestamptz); EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'phase body_id=3: %', SQLERRM; END $$;
-- ============================================================
-- Satellite eclipse: ISS point-in-time test
-- (At night the ISS can be eclipsed; just verify function returns bool)
-- ============================================================
SELECT satellite_is_eclipsed(
E'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025\n2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle,
'2024-01-01 12:00:00+00'::timestamptz
) IS NOT NULL
AS eclipse_returns_bool;
-- ============================================================
-- Satellite eclipse: next entry/exit return timestamps or NULL
-- ============================================================
SELECT satellite_next_eclipse_entry(
E'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025\n2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle,
'2024-01-01 12:00:00+00'::timestamptz
) > '2024-01-01 12:00:00+00'::timestamptz
AS entry_in_future;
SELECT satellite_next_eclipse_exit(
E'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025\n2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle,
'2024-01-01 12:00:00+00'::timestamptz
) > '2024-01-01 12:00:00+00'::timestamptz
AS exit_in_future;
-- ============================================================
-- Satellite eclipse: fraction in [0, 1] for a 2-hour window
-- ============================================================
SELECT satellite_eclipse_fraction(
E'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025\n2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle,
'2024-01-01 12:00:00+00'::timestamptz,
'2024-01-01 14:00:00+00'::timestamptz
) BETWEEN 0.0 AND 1.0
AS eclipse_fraction_valid;
-- ============================================================
-- Observing night quality: polar summer at 65N = 'poor'
-- (no astronomical darkness in June)
-- ============================================================
SELECT observing_night_quality('(65.0,25.0,0)'::observer, '2024-06-21 12:00:00+00'::timestamptz) = 'poor'
AS polar_summer_poor;
-- ============================================================
-- Observing night quality: winter mid-latitude returns valid rating
-- ============================================================
SELECT observing_night_quality('(43.7,-116.4,800)'::observer, '2024-12-21 12:00:00+00'::timestamptz)
IN ('excellent', 'good', 'fair', 'poor')
AS winter_valid_rating;
-- ============================================================
-- Lunar libration: longitude in [-8, 8] range
-- ============================================================
SELECT moon_libration_longitude('2024-01-15 00:00:00+00'::timestamptz) BETWEEN -8.5 AND 8.5
AS libration_lon_in_range;
SELECT moon_libration_longitude('2024-06-15 00:00:00+00'::timestamptz) BETWEEN -8.5 AND 8.5
AS libration_lon_in_range_2;
-- ============================================================
-- Lunar libration: latitude in [-7, 7] range
-- ============================================================
SELECT moon_libration_latitude('2024-01-15 00:00:00+00'::timestamptz) BETWEEN -7.5 AND 7.5
AS libration_lat_in_range;
SELECT moon_libration_latitude('2024-06-15 00:00:00+00'::timestamptz) BETWEEN -7.5 AND 7.5
AS libration_lat_in_range_2;
-- ============================================================
-- Lunar libration: position angle in [0, 360)
-- ============================================================
SELECT moon_libration_position_angle('2024-01-15 00:00:00+00'::timestamptz) BETWEEN -1.0 AND 361.0
AS libration_pa_in_range;
-- ============================================================
-- Lunar libration: composite returns same as individual functions
-- ============================================================
SELECT abs((moon_libration('2024-01-15 00:00:00+00'::timestamptz)).l
- moon_libration_longitude('2024-01-15 00:00:00+00'::timestamptz)) < 0.001
AS composite_matches_lon;
SELECT abs((moon_libration('2024-01-15 00:00:00+00'::timestamptz)).b
- moon_libration_latitude('2024-01-15 00:00:00+00'::timestamptz)) < 0.001
AS composite_matches_lat;
-- ============================================================
-- Lunar libration: changes over time (not constant)
-- ============================================================
SELECT moon_libration_longitude('2024-01-01 00:00:00+00'::timestamptz)
!= moon_libration_longitude('2024-01-15 00:00:00+00'::timestamptz)
AS libration_varies;
-- ============================================================
-- Subsolar longitude: in [0, 360) range
-- ============================================================
SELECT moon_subsolar_longitude('2024-01-15 00:00:00+00'::timestamptz) BETWEEN 0.0 AND 360.0
AS subsolar_in_range;
SELECT moon_subsolar_longitude('2024-06-15 00:00:00+00'::timestamptz) BETWEEN 0.0 AND 360.0
AS subsolar_in_range_2;
-- ============================================================
-- Subsolar longitude: changes significantly over synodic month
-- (full 360 degrees over ~29.5 days)
-- ============================================================
SELECT abs(moon_subsolar_longitude('2024-01-01 00:00:00+00'::timestamptz)
- moon_subsolar_longitude('2024-01-15 00:00:00+00'::timestamptz)) > 10.0
AS subsolar_moves;