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.
305 lines
11 KiB
Plaintext
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)
|
|
|