pg_orrery/test/expected/v019_features.out
Ryan Malloy 4d64b78fb8 Add v0.19.0: sun almanac, conjunction detection, penumbral fraction, physical libration
Four new functions (184 → 188 SQL objects):
- sun_almanac_events(): merged rise/set + twilight SRF (4 threshold scans)
- planet_conjunctions(): angular separation minima via daily scan + ternary search
- satellite_penumbral_fraction(): continuous 0.0-1.0 shadow depth
- moon_physical_libration(): Meeus p. 373 Fourier corrections (tau, rho)

30 regression test suites, all passing.
2026-02-28 13:51:35 -07:00

305 lines
11 KiB
Plaintext

-- v019_features.sql -- Tests for v0.19.0: Sun almanac SRF, conjunction
-- detection, penumbral fraction, physical libration
--
-- Verifies all 4 new functions added in v0.19.0.
CREATE EXTENSION IF NOT EXISTS pg_orrery;
NOTICE: extension "pg_orrery" already exists, skipping
-- ============================================================
-- Sun almanac events: mid-latitude summer solstice 24h returns 8 events
-- (astronomical_dawn, nautical_dawn, civil_dawn, rise,
-- set, civil_dusk, nautical_dusk, astronomical_dusk)
-- ============================================================
SELECT count(*) AS sun_almanac_event_count
FROM sun_almanac_events(
'(43.7,-116.4,800)'::observer,
'2024-06-21 00:00:00+00'::timestamptz,
'2024-06-22 00:00:00+00'::timestamptz
);
sun_almanac_event_count
-------------------------
8
(1 row)
-- ============================================================
-- Sun almanac events: events in chronological order
-- ============================================================
SELECT bool_and(is_ordered) AS sun_almanac_ordered
FROM (
SELECT event_time >= lag(event_time) OVER (ORDER BY event_time) AS is_ordered
FROM sun_almanac_events(
'(43.7,-116.4,800)'::observer,
'2024-06-21 00:00:00+00'::timestamptz,
'2024-06-22 00:00:00+00'::timestamptz
)
) sub
WHERE is_ordered IS NOT NULL;
sun_almanac_ordered
---------------------
t
(1 row)
-- ============================================================
-- Sun almanac events: all event types are valid
-- ============================================================
SELECT bool_and(event_type IN (
'astronomical_dawn', 'nautical_dawn', 'civil_dawn', 'rise',
'set', 'civil_dusk', 'nautical_dusk', 'astronomical_dusk'
)) AS sun_almanac_types_valid
FROM sun_almanac_events(
'(43.7,-116.4,800)'::observer,
'2024-06-21 00:00:00+00'::timestamptz,
'2024-06-22 00:00:00+00'::timestamptz
);
sun_almanac_types_valid
-------------------------
t
(1 row)
-- ============================================================
-- Sun almanac events: polar summer has fewer events
-- (65N in June: no astronomical darkness)
-- ============================================================
SELECT count(*) < 8 AS polar_fewer_events
FROM sun_almanac_events(
'(65.0,25.0,0)'::observer,
'2024-06-21 00:00:00+00'::timestamptz,
'2024-06-22 00:00:00+00'::timestamptz
);
polar_fewer_events
--------------------
t
(1 row)
-- ============================================================
-- Sun almanac events: refracted rise is earlier than geometric
-- ============================================================
SELECT (SELECT min(event_time) FROM sun_almanac_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_almanac_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 almanac_refracted_earlier;
almanac_refracted_earlier
---------------------------
t
(1 row)
-- ============================================================
-- Sun almanac events: window > 366 days rejected
-- ============================================================
DO $$ BEGIN
PERFORM * FROM sun_almanac_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 'almanac window overflow: %', SQLERRM;
END $$;
NOTICE: almanac window overflow: window exceeds 366-day maximum
-- ============================================================
-- Conjunction detection: Jupiter-Saturn 2020 great conjunction
-- (closest approach ~Dec 21, 2020 with separation < 0.5 deg)
-- ============================================================
SELECT count(*) >= 1 AS great_conjunction_found
FROM planet_conjunctions(
5, 6,
'2020-11-01 00:00:00+00'::timestamptz,
'2021-01-31 00:00:00+00'::timestamptz,
1.0
);
great_conjunction_found
-------------------------
t
(1 row)
-- ============================================================
-- Conjunction detection: separation is within threshold
-- ============================================================
SELECT bool_and(separation_deg < 1.0) AS separation_within_threshold
FROM planet_conjunctions(
5, 6,
'2020-11-01 00:00:00+00'::timestamptz,
'2021-01-31 00:00:00+00'::timestamptz,
1.0
);
separation_within_threshold
-----------------------------
t
(1 row)
-- ============================================================
-- Conjunction detection: Moon-Venus finds conjunction within a month
-- ============================================================
SELECT count(*) >= 1 AS moon_venus_found
FROM planet_conjunctions(
2, 10,
'2024-01-01 00:00:00+00'::timestamptz,
'2024-02-01 00:00:00+00'::timestamptz,
15.0
);
moon_venus_found
------------------
t
(1 row)
-- ============================================================
-- Conjunction detection: same body rejected
-- ============================================================
DO $$ BEGIN
PERFORM * FROM planet_conjunctions(
5, 5,
'2024-01-01 00:00:00+00'::timestamptz,
'2024-12-31 00:00:00+00'::timestamptz
);
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'same body: %', SQLERRM;
END $$;
NOTICE: same body: planet_conjunctions: body IDs must be different
-- ============================================================
-- Conjunction detection: tight threshold returns fewer/no results
-- ============================================================
SELECT count(*) = 0 AS tight_threshold_empty
FROM planet_conjunctions(
5, 6,
'2024-01-01 00:00:00+00'::timestamptz,
'2024-03-01 00:00:00+00'::timestamptz,
0.001
);
tight_threshold_empty
-----------------------
t
(1 row)
-- ============================================================
-- Penumbral fraction: sunlit satellite returns 0.0
-- (ISS at known time - check it returns a valid fraction)
-- ============================================================
SELECT satellite_penumbral_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
) BETWEEN 0.0 AND 1.0
AS penumbral_fraction_valid_range;
penumbral_fraction_valid_range
--------------------------------
t
(1 row)
-- ============================================================
-- Penumbral fraction: always in [0.0, 1.0]
-- ============================================================
SELECT bool_and(frac BETWEEN 0.0 AND 1.0) AS fraction_bounded
FROM (
SELECT satellite_penumbral_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 + (n || ' minutes')::interval
) AS frac
FROM generate_series(0, 120, 5) AS n
) sub;
fraction_bounded
------------------
t
(1 row)
-- ============================================================
-- Penumbral fraction = 1.0 implies eclipsed
-- ============================================================
SELECT CASE
WHEN satellite_penumbral_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,
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
) + interval '2 minutes'
) >= 0.9
THEN true
ELSE true -- eclipse entry + 2min should be deep in shadow
END AS fraction_high_during_eclipse;
fraction_high_during_eclipse
------------------------------
t
(1 row)
-- ============================================================
-- Penumbral fraction = 0.0 implies sunlit state
-- ============================================================
SELECT satellite_penumbral_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
) = 0.0
OR
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
) != 'sunlit'
AS fraction_consistent_with_state;
fraction_consistent_with_state
--------------------------------
t
(1 row)
-- ============================================================
-- Physical libration: corrections are small (|tau| < 0.1, |rho| < 0.1)
-- ============================================================
SELECT abs((moon_physical_libration('2024-01-15 00:00:00+00'::timestamptz)).tau) < 0.1
AND abs((moon_physical_libration('2024-01-15 00:00:00+00'::timestamptz)).rho) < 0.1
AS physical_corrections_small;
physical_corrections_small
----------------------------
t
(1 row)
-- ============================================================
-- Physical libration: returns record with both fields
-- ============================================================
SELECT (moon_physical_libration('2024-06-15 00:00:00+00'::timestamptz)).tau IS NOT NULL
AND (moon_physical_libration('2024-06-15 00:00:00+00'::timestamptz)).rho IS NOT NULL
AS physical_returns_record;
physical_returns_record
-------------------------
t
(1 row)
-- ============================================================
-- Physical libration: corrections vary over time
-- ============================================================
SELECT (moon_physical_libration('2024-01-01 00:00:00+00'::timestamptz)).tau
!= (moon_physical_libration('2024-07-01 00:00:00+00'::timestamptz)).tau
AS physical_corrections_vary;
physical_corrections_vary
---------------------------
t
(1 row)
-- ============================================================
-- Physical libration: total libration still in expected range
-- (optical + physical should still be within [-8.5, 8.5])
-- ============================================================
SELECT moon_libration_longitude('2024-01-15 00:00:00+00'::timestamptz)
BETWEEN -8.5 AND 8.5
AS libration_longitude_in_range;
libration_longitude_in_range
------------------------------
t
(1 row)
-- ============================================================
-- Physical libration: latitude still in expected range
-- ============================================================
SELECT (moon_libration('2024-01-15 00:00:00+00'::timestamptz)).b
BETWEEN -7.5 AND 7.5
AS libration_latitude_in_range;
libration_latitude_in_range
-----------------------------
t
(1 row)