-- v020_features: Lagrange point support -- Tests Sun-planet, Earth-Moon, planetary moon Lagrange points, -- Hill radius, zone radius, DE fallback, and input validation. -- Reference observer: Greenwich, UK \set obs '''(51.4769,-0.0005,0)''' -- Reference time: J2000 epoch (2000-01-01 12:00:00 UTC) \set t '''2000-01-01 12:00:00+00''' -- ============================================================ -- Sun-Earth L1/L2: should be ~0.01 AU from Earth (~1.5 million km) -- SOHO is at L1, JWST at L2. -- ============================================================ -- L1 heliocentric: should be close to Earth's heliocentric (~1 AU from Sun) SELECT round(helio_distance(lagrange_heliocentric(3, 1, :t ::timestamptz))::numeric, 2) AS sun_dist_au; -- L2 heliocentric: also ~1 AU from Sun, slightly further than L1 SELECT round(helio_distance(lagrange_heliocentric(3, 2, :t ::timestamptz))::numeric, 2) AS sun_dist_au; -- L1 between Sun and Earth (closer to Sun than L2) SELECT helio_distance(lagrange_heliocentric(3, 1, :t ::timestamptz)) < helio_distance(lagrange_heliocentric(3, 2, :t ::timestamptz)) AS l1_closer_than_l2; -- ============================================================ -- Sun-Jupiter L4/L5: ~60 degrees from Jupiter, ~5.2 AU from Sun -- These are the Trojan asteroid zones. -- ============================================================ -- L4/L5 should be ~5.2 AU from Sun SELECT round(helio_distance(lagrange_heliocentric(5, 4, :t ::timestamptz))::numeric, 1) AS l4_sun_dist; SELECT round(helio_distance(lagrange_heliocentric(5, 5, :t ::timestamptz))::numeric, 1) AS l5_sun_dist; -- L4 and L5 equidistant from Sun (within 0.001 AU) SELECT abs( helio_distance(lagrange_heliocentric(5, 4, :t ::timestamptz)) - helio_distance(lagrange_heliocentric(5, 5, :t ::timestamptz)) ) < 0.001 AS l4_l5_equidistant; -- ============================================================ -- Earth-Moon L1: ~326,000 km from Earth -- ============================================================ -- lunar_lagrange_equatorial returns distance in km SELECT round(eq_distance(lunar_lagrange_equatorial(1, :t ::timestamptz))::numeric, -3) BETWEEN 300000 AND 360000 AS em_l1_in_range; -- ============================================================ -- lagrange_observe returns valid az/el -- ============================================================ SELECT topo_elevation(lagrange_observe(3, 2, :obs ::observer, :t ::timestamptz)) BETWEEN -90 AND 90 AS valid_elevation; -- lagrange_equatorial returns valid RA/Dec SELECT eq_ra(lagrange_equatorial(3, 1, :t ::timestamptz)) BETWEEN 0 AND 24 AS valid_ra, eq_dec(lagrange_equatorial(3, 1, :t ::timestamptz)) BETWEEN -90 AND 90 AS valid_dec; -- ============================================================ -- lagrange_distance self-test: L-point distance to itself ≈ 0 -- ============================================================ SELECT round(lagrange_distance( 5, 4, lagrange_heliocentric(5, 4, :t ::timestamptz), :t ::timestamptz )::numeric, 10) AS self_distance; -- ============================================================ -- Hill radius -- ============================================================ -- Jupiter Hill radius ~0.35 AU SELECT round(hill_radius(5, :t ::timestamptz)::numeric, 2) BETWEEN 0.30 AND 0.40 AS jupiter_hill_ok; -- Earth Hill radius ~0.01 AU SELECT round(hill_radius(3, :t ::timestamptz)::numeric, 3) BETWEEN 0.008 AND 0.012 AS earth_hill_ok; -- Lunar Hill radius (much smaller, AU) SELECT hill_radius_lunar(:t ::timestamptz) > 0 AS lunar_hill_positive; -- ============================================================ -- Zone radius -- ============================================================ SELECT lagrange_zone_radius(5, 4, :t ::timestamptz) > 0 AS jup_l4_zone_positive; SELECT lagrange_zone_radius(5, 1, :t ::timestamptz) > 0 AS jup_l1_zone_positive; -- ============================================================ -- Convenience functions -- ============================================================ -- lagrange_mass_ratio returns small positive number SELECT lagrange_mass_ratio(5) > 0 AND lagrange_mass_ratio(5) < 0.01 AS jupiter_mu_ok; SELECT lagrange_mass_ratio(3) > 0 AND lagrange_mass_ratio(3) < 0.001 AS earth_mu_ok; -- lagrange_point_name SELECT lagrange_point_name(1) AS l1_name; SELECT lagrange_point_name(5) AS l5_name; -- ============================================================ -- All planets produce valid results -- ============================================================ SELECT body_id, round(helio_distance(lagrange_heliocentric(body_id, 1, :t ::timestamptz))::numeric, 2) AS sun_dist_au FROM generate_series(1, 8) AS body_id ORDER BY body_id; -- ============================================================ -- Planetary moon Lagrange points -- ============================================================ -- Galilean: Io L4 (body=0, point=4) SELECT eq_ra(galilean_lagrange_equatorial(0, 4, :t ::timestamptz)) BETWEEN 0 AND 24 AS io_l4_valid_ra; -- Saturn: Titan L1 (body=5, point=1) SELECT eq_ra(saturn_moon_lagrange_equatorial(5, 1, :t ::timestamptz)) BETWEEN 0 AND 24 AS titan_l1_valid_ra; -- Uranus: Titania L2 (body=3, point=2) SELECT eq_ra(uranus_moon_lagrange_equatorial(3, 2, :t ::timestamptz)) BETWEEN 0 AND 24 AS titania_l2_valid_ra; -- Mars: Phobos L5 (body=0, point=5) SELECT eq_ra(mars_moon_lagrange_equatorial(0, 5, :t ::timestamptz)) BETWEEN 0 AND 24 AS phobos_l5_valid_ra; -- Galilean observe returns valid topocentric SELECT topo_elevation(galilean_lagrange_observe(2, 3, :obs ::observer, :t ::timestamptz)) BETWEEN -90 AND 90 AS ganymede_l3_valid_el; -- ============================================================ -- DE fallback (no DE loaded, should produce same results as VSOP87) -- ============================================================ SELECT round(helio_distance(lagrange_heliocentric_de(3, 1, :t ::timestamptz))::numeric, 4) AS de_l1_dist; SELECT round(helio_distance(lagrange_heliocentric(3, 1, :t ::timestamptz))::numeric, 4) AS vsop_l1_dist; -- DE hill_radius fallback SELECT round(hill_radius_de(5, :t ::timestamptz)::numeric, 4) = round(hill_radius(5, :t ::timestamptz)::numeric, 4) AS hill_de_matches_vsop; -- ============================================================ -- Input validation -- ============================================================ -- Bad body_id SELECT lagrange_heliocentric(0, 1, :t ::timestamptz); -- Sun not valid SELECT lagrange_heliocentric(9, 1, :t ::timestamptz); -- body 9 invalid -- Bad point_id SELECT lagrange_heliocentric(3, 0, :t ::timestamptz); -- point 0 invalid SELECT lagrange_heliocentric(3, 6, :t ::timestamptz); -- point 6 invalid -- Bad lunar point_id SELECT lunar_lagrange_equatorial(0, :t ::timestamptz); -- point 0 invalid SELECT lunar_lagrange_equatorial(6, :t ::timestamptz); -- point 6 invalid -- Bad planetary moon body_id SELECT galilean_lagrange_equatorial(4, 1, :t ::timestamptz); -- Galilean 4 invalid SELECT saturn_moon_lagrange_equatorial(8, 1, :t ::timestamptz); -- Saturn 8 invalid SELECT uranus_moon_lagrange_equatorial(5, 1, :t ::timestamptz); -- Uranus 5 invalid SELECT mars_moon_lagrange_equatorial(2, 1, :t ::timestamptz); -- Mars 2 invalid -- lagrange_mass_ratio bad body SELECT lagrange_mass_ratio(0); SELECT lagrange_mass_ratio(9); -- lagrange_point_name bad id SELECT lagrange_point_name(0); SELECT lagrange_point_name(6);