Integrate IAU 2000B nutation (~9 arcsec) into the solar system observation pipeline via precess_and_nutate_j2000_to_date(). Affects all planet, star, moon, and small body RA/Dec and az/el values. Satellite SGP4/TEME pipeline unchanged. Add make_equatorial(ra_hours, dec_deg, distance_km) constructor to replace error-prone text literal casts. Add 8 rise/set prediction functions (planet_next_rise/set, sun_next_rise/set, moon_next_rise/set, sun_next_rise/set_refracted) using bisection algorithm adapted from satellite pass prediction. Returns NULL for circumpolar and polar night edge cases. Fix DE fallback test fragility: replace exact float equality with tolerance comparisons to handle GCC LTO inlining divergence across translation units. 132 -> 141 SQL objects. 22 -> 24 regression suites. All 24 passing.
312 lines
13 KiB
Plaintext
312 lines
13 KiB
Plaintext
-- v0.11.0 feature tests: make_orbital_elements constructors + moon equatorial
|
|
-- ============================================================
|
|
-- Test 1: make_orbital_elements() — radians input
|
|
-- Round-trip: construct from radians, read back via accessors
|
|
-- ============================================================
|
|
SELECT 'make_oe_rad' AS test,
|
|
round(oe_epoch(oe)::numeric, 1) AS epoch,
|
|
round(oe_perihelion(oe)::numeric, 6) AS q_au,
|
|
round(oe_eccentricity(oe)::numeric, 6) AS ecc,
|
|
round(oe_inclination(oe)::numeric, 4) AS inc_deg,
|
|
round(oe_arg_perihelion(oe)::numeric, 4) AS omega_deg,
|
|
round(oe_raan(oe)::numeric, 4) AS node_deg,
|
|
round(oe_h_mag(oe)::numeric, 1) AS h_mag
|
|
FROM (SELECT make_orbital_elements(
|
|
2460400.5, -- epoch JD
|
|
0.587100, -- q AU
|
|
0.967277, -- e
|
|
radians(162.2269), -- inc
|
|
radians(111.8657), -- omega
|
|
radians(58.1455), -- Omega
|
|
2460450.123, -- tp JD
|
|
5.5, -- H
|
|
4.0 -- G
|
|
) AS oe) sub;
|
|
test | epoch | q_au | ecc | inc_deg | omega_deg | node_deg | h_mag
|
|
-------------+-----------+----------+----------+----------+-----------+----------+-------
|
|
make_oe_rad | 2460400.5 | 0.587100 | 0.967277 | 162.2269 | 111.8657 | 58.1455 | 5.5
|
|
(1 row)
|
|
|
|
-- ============================================================
|
|
-- Test 2: make_orbital_elements_deg() — degree input
|
|
-- Same elements but angles in degrees; should produce identical output
|
|
-- ============================================================
|
|
SELECT 'make_oe_deg' AS test,
|
|
round(oe_epoch(oe)::numeric, 1) AS epoch,
|
|
round(oe_perihelion(oe)::numeric, 6) AS q_au,
|
|
round(oe_eccentricity(oe)::numeric, 6) AS ecc,
|
|
round(oe_inclination(oe)::numeric, 4) AS inc_deg,
|
|
round(oe_arg_perihelion(oe)::numeric, 4) AS omega_deg,
|
|
round(oe_raan(oe)::numeric, 4) AS node_deg,
|
|
round(oe_h_mag(oe)::numeric, 1) AS h_mag
|
|
FROM (SELECT make_orbital_elements_deg(
|
|
2460400.5, -- epoch JD
|
|
0.587100, -- q AU
|
|
0.967277, -- e
|
|
162.2269, -- inc degrees
|
|
111.8657, -- omega degrees
|
|
58.1455, -- Omega degrees
|
|
2460450.123, -- tp JD
|
|
5.5, -- H
|
|
4.0 -- G
|
|
) AS oe) sub;
|
|
test | epoch | q_au | ecc | inc_deg | omega_deg | node_deg | h_mag
|
|
-------------+-----------+----------+----------+----------+-----------+----------+-------
|
|
make_oe_deg | 2460400.5 | 0.587100 | 0.967277 | 162.2269 | 111.8657 | 58.1455 | 5.5
|
|
(1 row)
|
|
|
|
-- ============================================================
|
|
-- Test 3: constructors produce identical results to text I/O
|
|
-- The text format uses degrees, so make_orbital_elements_deg should match
|
|
-- ============================================================
|
|
SELECT 'oe_roundtrip' AS test,
|
|
make_orbital_elements_deg(
|
|
2460400.5, 0.587100, 0.967277,
|
|
162.2269, 111.8657, 58.1455,
|
|
2460450.123, 5.5, 4.0
|
|
)::text
|
|
=
|
|
'(2460400.500000,0.5871000000,0.9672770000,162.226900,111.865700,58.145500,2460450.123000,5.50,4.00)'::orbital_elements::text
|
|
AS matches;
|
|
test | matches
|
|
--------------+---------
|
|
oe_roundtrip | t
|
|
(1 row)
|
|
|
|
-- ============================================================
|
|
-- Test 4: make_orbital_elements() validation — negative q
|
|
-- ============================================================
|
|
DO $$
|
|
BEGIN
|
|
PERFORM make_orbital_elements(2460400.5, -0.1, 0.5, 0, 0, 0, 2460400.5, 0, 0);
|
|
RAISE EXCEPTION 'should have failed';
|
|
EXCEPTION WHEN numeric_value_out_of_range THEN
|
|
RAISE NOTICE 'make_oe_neg_q: correctly rejected';
|
|
END;
|
|
$$;
|
|
NOTICE: make_oe_neg_q: correctly rejected
|
|
-- ============================================================
|
|
-- Test 5: make_orbital_elements() validation — negative eccentricity
|
|
-- ============================================================
|
|
DO $$
|
|
BEGIN
|
|
PERFORM make_orbital_elements(2460400.5, 1.0, -0.1, 0, 0, 0, 2460400.5, 0, 0);
|
|
RAISE EXCEPTION 'should have failed';
|
|
EXCEPTION WHEN numeric_value_out_of_range THEN
|
|
RAISE NOTICE 'make_oe_neg_e: correctly rejected';
|
|
END;
|
|
$$;
|
|
NOTICE: make_oe_neg_e: correctly rejected
|
|
-- ============================================================
|
|
-- Test 6: make_orbital_elements used in small_body_equatorial()
|
|
-- Verify the constructor output works in the observation pipeline
|
|
-- ============================================================
|
|
SELECT 'oe_pipeline' AS test,
|
|
round(eq_ra(eq)::numeric, 2) AS ra_hours,
|
|
round(eq_dec(eq)::numeric, 2) AS dec_deg,
|
|
eq_ra(eq) BETWEEN 0 AND 24 AS ra_valid,
|
|
eq_dec(eq) BETWEEN -90 AND 90 AS dec_valid
|
|
FROM (SELECT small_body_equatorial(
|
|
make_orbital_elements_deg(
|
|
2460400.5, 0.587100, 0.967277,
|
|
162.2269, 111.8657, 58.1455,
|
|
2460450.123, 5.5, 4.0
|
|
),
|
|
'2024-06-15 12:00:00+00'::timestamptz
|
|
) AS eq) sub;
|
|
test | ra_hours | dec_deg | ra_valid | dec_valid
|
|
-------------+----------+---------+----------+-----------
|
|
oe_pipeline | 9.27 | 17.78 | t | t
|
|
(1 row)
|
|
|
|
-- ============================================================
|
|
-- Test 7: galilean_equatorial — all 4 moons
|
|
-- Io, Europa, Ganymede, Callisto should all return valid RA/Dec
|
|
-- near Jupiter's position
|
|
-- ============================================================
|
|
SELECT 'galilean_eq' AS test,
|
|
moon_id,
|
|
round(eq_ra(eq)::numeric, 4) AS ra_hours,
|
|
round(eq_dec(eq)::numeric, 4) AS dec_deg,
|
|
eq_ra(eq) BETWEEN 0 AND 24 AS ra_valid,
|
|
eq_dec(eq) BETWEEN -90 AND 90 AS dec_valid
|
|
FROM generate_series(0, 3) AS moon_id,
|
|
LATERAL galilean_equatorial(moon_id, '2024-06-15 12:00:00+00'::timestamptz) AS eq
|
|
ORDER BY moon_id;
|
|
test | moon_id | ra_hours | dec_deg | ra_valid | dec_valid
|
|
-------------+---------+----------+---------+----------+-----------
|
|
galilean_eq | 0 | 4.1956 | 20.3924 | t | t
|
|
galilean_eq | 1 | 4.1949 | 20.3901 | t | t
|
|
galilean_eq | 2 | 4.1936 | 20.3904 | t | t
|
|
galilean_eq | 3 | 4.2056 | 20.4196 | t | t
|
|
(4 rows)
|
|
|
|
-- ============================================================
|
|
-- Test 8: galilean moons should be near Jupiter
|
|
-- All 4 Galilean moons within 0.5 degrees of Jupiter
|
|
-- ============================================================
|
|
SELECT 'galilean_near_jupiter' AS test,
|
|
moon_id,
|
|
round((galilean_equatorial(moon_id, '2024-06-15 12:00:00+00') <->
|
|
planet_equatorial_apparent(5, '2024-06-15 12:00:00+00'))::numeric, 4)
|
|
AS sep_deg,
|
|
(galilean_equatorial(moon_id, '2024-06-15 12:00:00+00') <->
|
|
planet_equatorial_apparent(5, '2024-06-15 12:00:00+00')) < 0.5
|
|
AS within_half_deg
|
|
FROM generate_series(0, 3) AS moon_id
|
|
ORDER BY moon_id;
|
|
test | moon_id | sep_deg | within_half_deg
|
|
-----------------------+---------+---------+-----------------
|
|
galilean_near_jupiter | 0 | 0.0149 | t
|
|
galilean_near_jupiter | 1 | 0.0241 | t
|
|
galilean_near_jupiter | 2 | 0.0426 | t
|
|
galilean_near_jupiter | 3 | 0.1293 | t
|
|
(4 rows)
|
|
|
|
-- ============================================================
|
|
-- Test 9: saturn_moon_equatorial — Titan (id=5)
|
|
-- Should be near Saturn, within ~0.5 degrees
|
|
-- ============================================================
|
|
SELECT 'saturn_titan_eq' AS test,
|
|
round(eq_ra(eq)::numeric, 4) AS ra_hours,
|
|
round(eq_dec(eq)::numeric, 4) AS dec_deg,
|
|
round((eq <-> planet_equatorial_apparent(6, '2024-06-15 12:00:00+00'))::numeric, 4) AS sep_from_saturn,
|
|
(eq <-> planet_equatorial_apparent(6, '2024-06-15 12:00:00+00')) < 0.5 AS near_saturn
|
|
FROM saturn_moon_equatorial(5, '2024-06-15 12:00:00+00'::timestamptz) AS eq;
|
|
test | ra_hours | dec_deg | sep_from_saturn | near_saturn
|
|
-----------------+----------+---------+-----------------+-------------
|
|
saturn_titan_eq | 23.3909 | -6.0146 | 0.0187 | t
|
|
(1 row)
|
|
|
|
-- ============================================================
|
|
-- Test 10: uranus_moon_equatorial — Titania (id=3)
|
|
-- ============================================================
|
|
SELECT 'uranus_titania_eq' AS test,
|
|
round(eq_ra(eq)::numeric, 4) AS ra_hours,
|
|
round(eq_dec(eq)::numeric, 4) AS dec_deg,
|
|
eq_ra(eq) BETWEEN 0 AND 24 AS ra_valid,
|
|
eq_dec(eq) BETWEEN -90 AND 90 AS dec_valid
|
|
FROM uranus_moon_equatorial(3, '2024-06-15 12:00:00+00'::timestamptz) AS eq;
|
|
test | ra_hours | dec_deg | ra_valid | dec_valid
|
|
-------------------+----------+---------+----------+-----------
|
|
uranus_titania_eq | 3.5123 | 18.7466 | t | t
|
|
(1 row)
|
|
|
|
-- ============================================================
|
|
-- Test 11: mars_moon_equatorial — Phobos and Deimos
|
|
-- Both should be near Mars, within ~0.02 degrees (very close moons)
|
|
-- ============================================================
|
|
SELECT 'mars_moons_eq' AS test,
|
|
moon_id,
|
|
round(eq_ra(eq)::numeric, 4) AS ra_hours,
|
|
round(eq_dec(eq)::numeric, 4) AS dec_deg,
|
|
round((eq <-> planet_equatorial_apparent(4, '2024-06-15 12:00:00+00'))::numeric, 4) AS sep_from_mars
|
|
FROM generate_series(0, 1) AS moon_id,
|
|
LATERAL mars_moon_equatorial(moon_id, '2024-06-15 12:00:00+00'::timestamptz) AS eq
|
|
ORDER BY moon_id;
|
|
test | moon_id | ra_hours | dec_deg | sep_from_mars
|
|
---------------+---------+----------+---------+---------------
|
|
mars_moons_eq | 0 | 2.1850 | 12.0611 | 0.0075
|
|
mars_moons_eq | 1 | 2.1850 | 12.0581 | 0.0059
|
|
(2 rows)
|
|
|
|
-- ============================================================
|
|
-- Test 12: NaN rejection in constructors
|
|
-- NaN passes IEEE 754 comparison guards silently; must be caught explicitly
|
|
-- ============================================================
|
|
DO $$
|
|
BEGIN
|
|
PERFORM make_orbital_elements(2460400.5, 'NaN'::float8, 0.5, 0, 0, 0, 2460400.5, 0, 0);
|
|
RAISE EXCEPTION 'should have failed';
|
|
EXCEPTION WHEN numeric_value_out_of_range THEN
|
|
RAISE NOTICE 'make_oe_nan_q: correctly rejected';
|
|
END;
|
|
$$;
|
|
NOTICE: make_oe_nan_q: correctly rejected
|
|
DO $$
|
|
BEGIN
|
|
PERFORM make_orbital_elements_deg(2460400.5, 1.0, 'NaN'::float8, 0, 0, 0, 2460400.5, 0, 0);
|
|
RAISE EXCEPTION 'should have failed';
|
|
EXCEPTION WHEN numeric_value_out_of_range THEN
|
|
RAISE NOTICE 'make_oe_nan_e: correctly rejected';
|
|
END;
|
|
$$;
|
|
NOTICE: make_oe_nan_e: correctly rejected
|
|
DO $$
|
|
BEGIN
|
|
PERFORM make_orbital_elements('NaN'::float8, 1.0, 0.5, 0, 0, 0, 2460400.5, 0, 0);
|
|
RAISE EXCEPTION 'should have failed';
|
|
EXCEPTION WHEN numeric_value_out_of_range THEN
|
|
RAISE NOTICE 'make_oe_nan_epoch: correctly rejected';
|
|
END;
|
|
$$;
|
|
NOTICE: make_oe_nan_epoch: correctly rejected
|
|
DO $$
|
|
BEGIN
|
|
PERFORM make_orbital_elements(2460400.5, 1.0, 0.5, 'Infinity'::float8, 0, 0, 2460400.5, 0, 0);
|
|
RAISE EXCEPTION 'should have failed';
|
|
EXCEPTION WHEN numeric_value_out_of_range THEN
|
|
RAISE NOTICE 'make_oe_inf_inc: correctly rejected';
|
|
END;
|
|
$$;
|
|
NOTICE: make_oe_inf_inc: correctly rejected
|
|
-- ============================================================
|
|
-- Test 13: NaN in H/G is allowed (sentinel for "unknown")
|
|
-- ============================================================
|
|
SELECT 'nan_h_g_ok' AS test,
|
|
oe_h_mag(make_orbital_elements(2460400.5, 1.0, 0.5, 0, 0, 0, 2460400.5,
|
|
'NaN'::float8, 'NaN'::float8)) AS h_mag_is_nan;
|
|
test | h_mag_is_nan
|
|
------------+--------------
|
|
nan_h_g_ok | NaN
|
|
(1 row)
|
|
|
|
-- ============================================================
|
|
-- Test 14: error paths for all four moon families + negative body_id
|
|
-- ============================================================
|
|
DO $$
|
|
BEGIN
|
|
PERFORM galilean_equatorial(5, '2024-06-15 12:00:00+00');
|
|
RAISE EXCEPTION 'should have failed';
|
|
EXCEPTION WHEN numeric_value_out_of_range THEN
|
|
RAISE NOTICE 'galilean_eq_invalid: correctly rejected';
|
|
END;
|
|
$$;
|
|
NOTICE: galilean_eq_invalid: correctly rejected
|
|
DO $$
|
|
BEGIN
|
|
PERFORM galilean_equatorial(-1, '2024-06-15 12:00:00+00');
|
|
RAISE EXCEPTION 'should have failed';
|
|
EXCEPTION WHEN numeric_value_out_of_range THEN
|
|
RAISE NOTICE 'galilean_eq_negative: correctly rejected';
|
|
END;
|
|
$$;
|
|
NOTICE: galilean_eq_negative: correctly rejected
|
|
DO $$
|
|
BEGIN
|
|
PERFORM saturn_moon_equatorial(8, '2024-06-15 12:00:00+00');
|
|
RAISE EXCEPTION 'should have failed';
|
|
EXCEPTION WHEN numeric_value_out_of_range THEN
|
|
RAISE NOTICE 'saturn_eq_invalid: correctly rejected';
|
|
END;
|
|
$$;
|
|
NOTICE: saturn_eq_invalid: correctly rejected
|
|
DO $$
|
|
BEGIN
|
|
PERFORM uranus_moon_equatorial(5, '2024-06-15 12:00:00+00');
|
|
RAISE EXCEPTION 'should have failed';
|
|
EXCEPTION WHEN numeric_value_out_of_range THEN
|
|
RAISE NOTICE 'uranus_eq_invalid: correctly rejected';
|
|
END;
|
|
$$;
|
|
NOTICE: uranus_eq_invalid: correctly rejected
|
|
DO $$
|
|
BEGIN
|
|
PERFORM mars_moon_equatorial(2, '2024-06-15 12:00:00+00');
|
|
RAISE EXCEPTION 'should have failed';
|
|
EXCEPTION WHEN numeric_value_out_of_range THEN
|
|
RAISE NOTICE 'mars_eq_invalid: correctly rejected';
|
|
END;
|
|
$$;
|
|
NOTICE: mars_eq_invalid: correctly rejected
|