-- 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.1957 | 20.3905 | t | t galilean_eq | 1 | 4.1950 | 20.3883 | t | t galilean_eq | 2 | 4.1937 | 20.3885 | t | t galilean_eq | 3 | 4.2057 | 20.4177 | 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.0138 | 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.5124 | 18.7450 | 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.1851 | 12.0602 | 0.0075 mars_moons_eq | 1 | 2.1851 | 12.0572 | 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