-- v016_features.sql -- Tests for v0.16.0: twilight, lunar phase, planet magnitude -- -- Verifies twilight dawn/dusk, lunar phase calculations, -- and planet apparent magnitude. CREATE EXTENSION IF NOT EXISTS pg_orrery; NOTICE: extension "pg_orrery" already exists, skipping -- ============================================================ -- Twilight: ordering (astronomical < nautical < civil < sunrise) -- Eagle, Idaho on the 2024 summer solstice -- ============================================================ -- Dawn ordering: astronomical dawn < nautical dawn < civil dawn < sunrise -- Use midnight MDT (07:00 UTC) so all "next" events land on the same morning SELECT sun_astronomical_dawn('(43.7,-116.4,800)'::observer, '2024-06-21 07:00:00+00'::timestamptz) < sun_nautical_dawn('(43.7,-116.4,800)'::observer, '2024-06-21 07:00:00+00'::timestamptz) AS astro_before_nautical; astro_before_nautical ----------------------- t (1 row) SELECT sun_nautical_dawn('(43.7,-116.4,800)'::observer, '2024-06-21 07:00:00+00'::timestamptz) < sun_civil_dawn('(43.7,-116.4,800)'::observer, '2024-06-21 07:00:00+00'::timestamptz) AS nautical_before_civil; nautical_before_civil ----------------------- t (1 row) SELECT sun_civil_dawn('(43.7,-116.4,800)'::observer, '2024-06-21 07:00:00+00'::timestamptz) < sun_next_rise('(43.7,-116.4,800)'::observer, '2024-06-21 07:00:00+00'::timestamptz) AS civil_before_sunrise; civil_before_sunrise ---------------------- t (1 row) -- Dusk ordering: sunset < civil dusk < nautical dusk < astronomical dusk -- Noon MDT (18:00 UTC) ensures all dusk events are still ahead SELECT sun_next_set('(43.7,-116.4,800)'::observer, '2024-06-21 18:00:00+00'::timestamptz) < sun_civil_dusk('(43.7,-116.4,800)'::observer, '2024-06-21 18:00:00+00'::timestamptz) AS sunset_before_civil; sunset_before_civil --------------------- t (1 row) SELECT sun_civil_dusk('(43.7,-116.4,800)'::observer, '2024-06-21 18:00:00+00'::timestamptz) < sun_nautical_dusk('(43.7,-116.4,800)'::observer, '2024-06-21 18:00:00+00'::timestamptz) AS civil_before_nautical; civil_before_nautical ----------------------- t (1 row) SELECT sun_nautical_dusk('(43.7,-116.4,800)'::observer, '2024-06-21 18:00:00+00'::timestamptz) < sun_astronomical_dusk('(43.7,-116.4,800)'::observer, '2024-06-21 18:00:00+00'::timestamptz) AS nautical_before_astro; nautical_before_astro ----------------------- t (1 row) -- ============================================================ -- Twilight: civil dawn ~30 min before sunrise at mid-latitude -- ============================================================ SELECT extract(epoch FROM sun_next_rise('(43.7,-116.4,800)'::observer, '2024-06-21 07:00:00+00'::timestamptz) - sun_civil_dawn('(43.7,-116.4,800)'::observer, '2024-06-21 07:00:00+00'::timestamptz) ) BETWEEN 1200 AND 3600 AS civil_dawn_reasonable_offset; civil_dawn_reasonable_offset ------------------------------ t (1 row) -- ============================================================ -- Twilight: high latitude summer -- no astronomical darkness -- At 60N in June, astronomical dusk should be NULL (never gets dark enough) -- ============================================================ SELECT sun_astronomical_dusk('(60.0,25.0,0)'::observer, '2024-06-21 00:00:00+00'::timestamptz) IS NULL AS no_astro_dark_60n_summer; no_astro_dark_60n_summer -------------------------- t (1 row) -- ============================================================ -- Lunar phase: known full moon (2024-01-25 ~17:54 UTC) -- Phase angle should be near 180 deg, illumination near 1.0 -- ============================================================ SELECT round(moon_phase_angle('2024-01-25 18:00:00+00'::timestamptz)::numeric, 0) BETWEEN 170 AND 190 AS full_moon_angle_near_180; full_moon_angle_near_180 -------------------------- t (1 row) SELECT round(moon_illumination('2024-01-25 18:00:00+00'::timestamptz)::numeric, 2) >= 0.95 AS full_moon_high_illumination; full_moon_high_illumination ----------------------------- t (1 row) SELECT moon_phase_name('2024-01-25 18:00:00+00'::timestamptz) = 'full_moon' AS full_moon_named; full_moon_named ----------------- t (1 row) -- ============================================================ -- Lunar phase: known new moon (2024-01-11 ~11:57 UTC) -- Phase angle should be near 0 or 360, illumination near 0 -- ============================================================ SELECT moon_illumination('2024-01-11 12:00:00+00'::timestamptz) < 0.05 AS new_moon_low_illumination; new_moon_low_illumination --------------------------- t (1 row) SELECT moon_phase_name('2024-01-11 12:00:00+00'::timestamptz) = 'new_moon' AS new_moon_named; new_moon_named ---------------- t (1 row) -- ============================================================ -- Lunar phase: first quarter (2024-01-18 ~03:53 UTC) -- Phase angle near 90, illumination near 0.5 -- ============================================================ SELECT round(moon_phase_angle('2024-01-18 04:00:00+00'::timestamptz)::numeric, 0) BETWEEN 80 AND 100 AS first_quarter_angle_near_90; first_quarter_angle_near_90 ----------------------------- t (1 row) SELECT moon_illumination('2024-01-18 04:00:00+00'::timestamptz) BETWEEN 0.4 AND 0.6 AS first_quarter_half_illuminated; first_quarter_half_illuminated -------------------------------- t (1 row) SELECT moon_phase_name('2024-01-18 04:00:00+00'::timestamptz) = 'first_quarter' AS first_quarter_named; first_quarter_named --------------------- t (1 row) -- ============================================================ -- Moon age: new moon has age near 0, full moon near 14.7 -- ============================================================ SELECT moon_age('2024-01-11 12:00:00+00'::timestamptz) < 2.0 AS new_moon_young; new_moon_young ---------------- t (1 row) SELECT moon_age('2024-01-25 18:00:00+00'::timestamptz) BETWEEN 12.0 AND 17.0 AS full_moon_age_midcycle; full_moon_age_midcycle ------------------------ t (1 row) -- ============================================================ -- Illumination range: always [0, 1] -- ============================================================ SELECT moon_illumination('2024-06-01 00:00:00+00'::timestamptz) BETWEEN 0.0 AND 1.0 AND moon_illumination('2024-09-01 00:00:00+00'::timestamptz) BETWEEN 0.0 AND 1.0 AND moon_illumination('2024-12-01 00:00:00+00'::timestamptz) BETWEEN 0.0 AND 1.0 AS illumination_always_valid; illumination_always_valid --------------------------- t (1 row) -- ============================================================ -- Planet magnitude: Jupiter should be bright (negative mag) -- ============================================================ SELECT planet_magnitude(5, '2024-01-15 00:00:00+00'::timestamptz) < 0.0 AS jupiter_is_bright; jupiter_is_bright ------------------- t (1 row) -- ============================================================ -- Planet magnitude: Venus is the brightest planet -- ============================================================ SELECT planet_magnitude(2, '2024-06-01 12:00:00+00'::timestamptz) < planet_magnitude(4, '2024-06-01 12:00:00+00'::timestamptz) AS venus_brighter_than_mars; venus_brighter_than_mars -------------------------- t (1 row) -- ============================================================ -- Planet magnitude: Neptune is faint (~+7-8) -- ============================================================ SELECT planet_magnitude(8, '2024-01-15 00:00:00+00'::timestamptz) > 7.0 AS neptune_is_faint; neptune_is_faint ------------------ t (1 row) -- ============================================================ -- Planet magnitude: all planets return finite values -- ============================================================ SELECT bool_and( planet_magnitude(body_id, '2024-01-15 00:00:00+00'::timestamptz) IS NOT NULL AND planet_magnitude(body_id, '2024-01-15 00:00:00+00'::timestamptz) > -30 AND planet_magnitude(body_id, '2024-01-15 00:00:00+00'::timestamptz) < 30 ) AS all_magnitudes_finite FROM (VALUES (1),(2),(4),(5),(6),(7),(8)) AS t(body_id); all_magnitudes_finite ----------------------- t (1 row) -- ============================================================ -- Planet magnitude: error cases -- ============================================================ DO $$ BEGIN PERFORM planet_magnitude(0, '2024-01-15 00:00:00+00'::timestamptz); EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'body_id=0(Sun): %', SQLERRM; END $$; NOTICE: body_id=0(Sun): planet_magnitude: body_id 0 must be 1-8 (Mercury-Neptune) DO $$ BEGIN PERFORM planet_magnitude(3, '2024-01-15 00:00:00+00'::timestamptz); EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'body_id=3(Earth): %', SQLERRM; END $$; NOTICE: body_id=3(Earth): cannot compute magnitude for Earth from Earth DO $$ BEGIN PERFORM planet_magnitude(9, '2024-01-15 00:00:00+00'::timestamptz); EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'body_id=9: %', SQLERRM; END $$; NOTICE: body_id=9: planet_magnitude: body_id 9 must be 1-8 (Mercury-Neptune)