-- 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; -- ============================================================ -- 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 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 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 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 ); -- ============================================================ -- 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; -- ============================================================ -- 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 $$; -- ============================================================ -- 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 ); -- ============================================================ -- 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 ); -- ============================================================ -- 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 ); -- ============================================================ -- 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 $$; -- ============================================================ -- 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 ); -- ============================================================ -- 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: 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; -- ============================================================ -- 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; -- ============================================================ -- 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; -- ============================================================ -- 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 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 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 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; -- ============================================================ -- 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;