6 custom types (tle, eci_position, geodetic, topocentric, observer, pass_event), 67 SQL functions, 2 operators (&&, <->), and a GiST operator class for altitude-band indexing. Wraps Bill Gray's sat_code for SGP4/SDP4 propagation with WGS-72 constants for propagation and WGS-84 for coordinate output. All 5 regression tests pass on PG 18.
75 lines
3.0 KiB
SQL
75 lines
3.0 KiB
SQL
-- Test TLE type: parsing, round-trip, accessors
|
|
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
|
|
|
-- Parse a valid ISS TLE
|
|
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
|
2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle IS NOT NULL AS parse_ok;
|
|
|
|
-- Accessor functions
|
|
WITH iss AS (
|
|
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
|
2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle AS t
|
|
)
|
|
SELECT
|
|
tle_norad_id(t) AS norad_id,
|
|
round(tle_inclination(t)::numeric, 4) AS inc_deg,
|
|
round(tle_eccentricity(t)::numeric, 7) AS ecc,
|
|
round(tle_mean_motion(t)::numeric, 8) AS mm_rev_day,
|
|
round(tle_period(t)::numeric, 2) AS period_min,
|
|
round(tle_perigee(t)::numeric, 1) AS perigee_km,
|
|
round(tle_apogee(t)::numeric, 1) AS apogee_km,
|
|
tle_intl_desig(t) AS cospar
|
|
FROM iss;
|
|
|
|
-- Observer type parsing
|
|
SELECT '40.0N 105.3W 1655m'::observer IS NOT NULL AS observer_ok;
|
|
SELECT observer_lat('40.0N 105.3W 1655m'::observer) AS lat,
|
|
observer_lon('40.0N 105.3W 1655m'::observer) AS lon;
|
|
|
|
-- ECI type parsing
|
|
SELECT '(1000.0,2000.0,3000.0,1.0,2.0,3.0)'::eci_position IS NOT NULL AS eci_ok;
|
|
SELECT eci_x('(1000.0,2000.0,3000.0,1.0,2.0,3.0)'::eci_position) AS x;
|
|
|
|
-- SGP4 propagation at epoch
|
|
WITH iss AS (
|
|
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
|
2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle AS t
|
|
)
|
|
SELECT
|
|
round(eci_speed(sgp4_propagate(t, '2024-01-01 12:00:00+00'))::numeric, 2) AS speed_kms,
|
|
round(eci_altitude(sgp4_propagate(t, '2024-01-01 12:00:00+00'))::numeric, 0) AS alt_km
|
|
FROM iss;
|
|
|
|
-- Altitude overlap operator
|
|
WITH sats AS (
|
|
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
|
2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle AS iss,
|
|
'1 28874U 05038A 24001.50000000 .00000012 00000+0 00000+0 0 9993
|
|
2 28874 55.4408 300.3467 0117034 51.6543 309.5420 2.00557079 00006'::tle AS gps
|
|
)
|
|
SELECT
|
|
iss && iss AS same_overlap,
|
|
iss && gps AS different_no_overlap
|
|
FROM sats;
|
|
|
|
-- Subsatellite point
|
|
WITH iss AS (
|
|
SELECT '1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
|
2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle AS t
|
|
)
|
|
SELECT
|
|
round(geodetic_lat(subsatellite_point(t, '2024-01-01 12:00:00+00'))::numeric, 2) AS lat,
|
|
round(geodetic_alt(subsatellite_point(t, '2024-01-01 12:00:00+00'))::numeric, 0) AS alt_km
|
|
FROM iss;
|
|
|
|
-- GiST index creation
|
|
CREATE TABLE test_sats (id serial, tle tle);
|
|
INSERT INTO test_sats (tle) VALUES (
|
|
'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
|
2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001');
|
|
CREATE INDEX test_sats_gist ON test_sats USING gist (tle);
|
|
SELECT count(*) FROM test_sats WHERE tle && (
|
|
'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9025
|
|
2 25544 51.6400 208.9163 0006703 30.1694 61.7520 15.50100486 00001'::tle);
|
|
DROP TABLE test_sats;
|