Twilight: 6 functions (civil/nautical/astronomical × dawn/dusk) reusing the existing find_next_crossing() bisection search with Sun depression angle thresholds (-6°, -12°, -18°). Returns NULL for polar regions where the threshold is never reached. Lunar phase: 4 functions computing Sun-Earth-Moon geometry from VSOP87 + ELP2000-82B. Phase angle [0,360) via elongation + cross product z-component for waxing/waning discrimination. 8 named phases in 45° bins. Moon age approximated from phase angle and mean synodic month. Planet magnitude: Mallama & Hilton (2018) polynomial model with VSOP87 heliocentric distances and phase angle via law of cosines. All 7 planets (Mercury-Neptune, excluding Earth). Saturn ring tilt not modeled. 151 → 162 SQL objects. 26 → 27 test suites, all passing.
244 lines
8.9 KiB
Plaintext
244 lines
8.9 KiB
Plaintext
-- 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)
|