pg_orrery/test/sql/v018_features.sql
Ryan Malloy b309980003 Add v0.18.0: Saturn ring tilt, penumbral eclipse, rise/set windows, angular rate
Four features, 10 new SQL functions (174 → 184 objects), 29 test suites:

Saturn ring tilt: saturn_ring_tilt() exposes sub-observer latitude B'.
planet_magnitude() for Saturn now includes Mallama & Hilton Eq. 10
ring correction (-2.60|sin B'| + 1.25 sin²B'), removing the ~1.5 mag
globe-only caveat. IAU 2000 pole direction, ecliptic J2000 projection.

Conical shadow model: Replaces cylindrical shadow with umbra/penumbra
cones using Sun's finite angular size. Four new functions:
satellite_in_penumbra(), satellite_shadow_state(),
satellite_next_penumbra_entry/exit(). Existing eclipse functions are
backward compatible via narrower (more accurate) umbra boundary.

Rise/set event windows: Three SRFs returning TABLE(event_time, event_type)
for all rise/set events within a time window — planet_rise_set_events(),
sun_rise_set_events(), moon_rise_set_events(). Follows predict_passes()
SRF pattern. Optional refracted parameter, 366-day window limit.

Angular separation rate: Vincenty formula extracted to reusable helper.
eq_angular_rate() for generic finite-difference rate, planet_angular_rate()
for solar system body convenience (1-minute dt, handles Sun/planets/Moon).
2026-02-27 23:52:06 -07:00

260 lines
9.9 KiB
SQL

-- v018_features.sql -- Tests for v0.18.0: Saturn ring tilt, penumbral eclipse,
-- rise/set event windows, angular separation rate
--
-- Verifies all 10 new functions added in v0.18.0.
CREATE EXTENSION IF NOT EXISTS pg_orrery;
-- ============================================================
-- Saturn ring tilt: in [-27, +27] range
-- ============================================================
SELECT saturn_ring_tilt('2024-01-15 00:00:00+00'::timestamptz) BETWEEN -27.0 AND 27.0
AS ring_tilt_in_range;
-- ============================================================
-- Saturn ring tilt: near zero around 2025 ring crossing
-- (rings edge-on to Earth around March 2025)
-- ============================================================
SELECT abs(saturn_ring_tilt('2025-03-23 00:00:00+00'::timestamptz)) < 5.0
AS ring_tilt_near_edge_on;
-- ============================================================
-- Saturn ring tilt: varies over time (not constant)
-- ============================================================
SELECT saturn_ring_tilt('2024-01-01 00:00:00+00'::timestamptz)
!= saturn_ring_tilt('2024-07-01 00:00:00+00'::timestamptz)
AS ring_tilt_varies;
-- ============================================================
-- Saturn ring tilt: sign changes across ring plane crossing
-- (2017 was fully open, 2025 is edge-on, tilt changes sign)
-- ============================================================
SELECT abs(saturn_ring_tilt('2017-06-15 00:00:00+00'::timestamptz)) > 10.0
AS ring_tilt_open_2017;
-- ============================================================
-- Planet magnitude: Saturn now includes ring correction
-- (ring-corrected magnitude should differ from globe-only)
-- Saturn magnitude should be roughly between -0.5 and +1.5
-- ============================================================
SELECT planet_magnitude(6, '2024-01-15 00:00:00+00'::timestamptz) BETWEEN -1.0 AND 2.0
AS saturn_mag_valid_range;
-- ============================================================
-- Satellite shadow state: returns valid text values
-- ============================================================
SELECT satellite_shadow_state(
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
) IN ('sunlit', 'penumbra', 'umbra')
AS shadow_state_valid;
-- ============================================================
-- Satellite in penumbra: returns bool
-- ============================================================
SELECT satellite_in_penumbra(
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 penumbra_returns_bool;
-- ============================================================
-- Backward compatibility: satellite_is_eclipsed still works
-- (cone model upgrade is internal-only)
-- ============================================================
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_backward_compat;
-- ============================================================
-- Penumbra entry precedes umbra entry (penumbra is outer zone)
-- ============================================================
SELECT satellite_next_penumbra_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
) <= 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
) AS penumbra_precedes_umbra;
-- ============================================================
-- Penumbra exit is after penumbra entry
-- ============================================================
SELECT satellite_next_penumbra_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 penumbra_exit_in_future;
-- ============================================================
-- Eclipse fraction still valid after cone upgrade
-- ============================================================
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_still_valid;
-- ============================================================
-- Sun rise/set events: mid-latitude 24h window returns events
-- ============================================================
SELECT count(*) >= 1 AS sun_events_exist
FROM sun_rise_set_events(
'(43.7,-116.4,800)'::observer,
'2024-06-21 00:00:00+00'::timestamptz,
'2024-06-22 00:00:00+00'::timestamptz
);
-- ============================================================
-- Sun rise/set events: events alternate rise/set
-- ============================================================
SELECT bool_and(event_type IN ('rise', 'set')) AS sun_event_types_valid
FROM sun_rise_set_events(
'(43.7,-116.4,800)'::observer,
'2024-06-21 00:00:00+00'::timestamptz,
'2024-06-22 00:00:00+00'::timestamptz
);
-- ============================================================
-- Sun rise/set events: refracted vs geometric
-- (refracted rise is earlier than geometric rise)
-- ============================================================
SELECT (SELECT min(event_time) FROM sun_rise_set_events(
'(43.7,-116.4,800)'::observer,
'2024-06-21 00:00:00+00'::timestamptz,
'2024-06-22 00:00:00+00'::timestamptz,
true
) WHERE event_type = 'rise')
<=
(SELECT min(event_time) FROM sun_rise_set_events(
'(43.7,-116.4,800)'::observer,
'2024-06-21 00:00:00+00'::timestamptz,
'2024-06-22 00:00:00+00'::timestamptz,
false
) WHERE event_type = 'rise')
AS refracted_rise_earlier;
-- ============================================================
-- Moon rise/set events: returns valid event types
-- ============================================================
SELECT bool_and(event_type IN ('rise', 'set')) AS moon_event_types_valid
FROM moon_rise_set_events(
'(43.7,-116.4,800)'::observer,
'2024-01-15 00:00:00+00'::timestamptz,
'2024-01-16 00:00:00+00'::timestamptz
);
-- ============================================================
-- Planet rise/set events: Jupiter over 24h
-- ============================================================
SELECT count(*) >= 1 AS jupiter_events_exist
FROM planet_rise_set_events(
5,
'(43.7,-116.4,800)'::observer,
'2024-01-15 00:00:00+00'::timestamptz,
'2024-01-16 00:00:00+00'::timestamptz
);
-- ============================================================
-- Rise/set events: window > 366 days rejected
-- ============================================================
DO $$ BEGIN
PERFORM * FROM sun_rise_set_events(
'(43.7,-116.4,800)'::observer,
'2024-01-01 00:00:00+00'::timestamptz,
'2025-03-01 00:00:00+00'::timestamptz
);
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'window overflow: %', SQLERRM;
END $$;
-- ============================================================
-- Rise/set events: stop before start rejected
-- ============================================================
DO $$ BEGIN
PERFORM * FROM sun_rise_set_events(
'(43.7,-116.4,800)'::observer,
'2024-06-22 00:00:00+00'::timestamptz,
'2024-06-21 00:00:00+00'::timestamptz
);
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'stop before start: %', SQLERRM;
END $$;
-- ============================================================
-- eq_angular_rate: generic rate computation
-- Two positions that are 10 deg apart, then 9 deg apart after 1 hour
-- should give rate = -1.0 deg/hr (approaching)
-- ============================================================
SELECT abs(eq_angular_rate(
'(6.0, 45.0, 1.0)'::equatorial,
'(6.667, 45.0, 1.0)'::equatorial,
'(6.0, 45.0, 1.0)'::equatorial,
'(6.6, 45.0, 1.0)'::equatorial,
3600.0
)) > 0.0
AS angular_rate_nonzero;
-- ============================================================
-- eq_angular_rate: dt_seconds <= 0 rejected
-- ============================================================
DO $$ BEGIN
PERFORM eq_angular_rate(
'(6.0, 45.0, 1.0)'::equatorial,
'(7.0, 45.0, 1.0)'::equatorial,
'(6.0, 45.0, 1.0)'::equatorial,
'(7.0, 45.0, 1.0)'::equatorial,
0.0
);
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'dt_seconds=0: %', SQLERRM;
END $$;
-- ============================================================
-- planet_angular_rate: Moon rate ~0.5 deg/hr relative to Sun
-- ============================================================
SELECT abs(planet_angular_rate(0, 10, '2024-01-15 00:00:00+00'::timestamptz)) > 0.1
AS moon_sun_rate_nonzero;
-- ============================================================
-- planet_angular_rate: same body rejected
-- ============================================================
DO $$ BEGIN
PERFORM planet_angular_rate(5, 5, '2024-01-15 00:00:00+00'::timestamptz);
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'same body: %', SQLERRM;
END $$;
-- ============================================================
-- planet_angular_rate: Jupiter-Saturn rate is small
-- (outer planets move slowly)
-- ============================================================
SELECT abs(planet_angular_rate(5, 6, '2024-01-15 00:00:00+00'::timestamptz)) < 1.0
AS outer_planet_rate_slow;