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.
111 lines
4.5 KiB
Plaintext
111 lines
4.5 KiB
Plaintext
-- Test coordinate transform functions
|
|
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
|
NOTICE: extension "pg_orbit" already exists, skipping
|
|
-- Subsatellite point 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(geodetic_lat(subsatellite_point(t, '2024-01-01 12:00:00+00'))::numeric, 2) AS lat,
|
|
round(geodetic_lon(subsatellite_point(t, '2024-01-01 12:00:00+00'))::numeric, 2) AS lon,
|
|
round(geodetic_alt(subsatellite_point(t, '2024-01-01 12:00:00+00'))::numeric, 0) AS alt_km
|
|
FROM iss;
|
|
lat | lon | alt_km
|
|
-------+-------+--------
|
|
51.75 | 21.48 | 420
|
|
(1 row)
|
|
|
|
-- Subsatellite point 30 minutes later
|
|
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:30:00+00'))::numeric, 2) AS lat_30m,
|
|
round(geodetic_lon(subsatellite_point(t, '2024-01-01 12:30:00+00'))::numeric, 2) AS lon_30m,
|
|
round(geodetic_alt(subsatellite_point(t, '2024-01-01 12:30:00+00'))::numeric, 0) AS alt_30m
|
|
FROM iss;
|
|
lat_30m | lon_30m | alt_30m
|
|
---------+---------+---------
|
|
-21.98 | 119.17 | 427
|
|
(1 row)
|
|
|
|
-- eci_to_geodetic: propagate ISS then convert
|
|
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(eci_to_geodetic(sgp4_propagate(t, '2024-01-01 12:00:00+00'), '2024-01-01 12:00:00+00'))::numeric, 2) AS eci_lat,
|
|
round(geodetic_lon(eci_to_geodetic(sgp4_propagate(t, '2024-01-01 12:00:00+00'), '2024-01-01 12:00:00+00'))::numeric, 2) AS eci_lon,
|
|
round(geodetic_alt(eci_to_geodetic(sgp4_propagate(t, '2024-01-01 12:00:00+00'), '2024-01-01 12:00:00+00'))::numeric, 0) AS eci_alt
|
|
FROM iss;
|
|
eci_lat | eci_lon | eci_alt
|
|
---------+---------+---------
|
|
51.75 | 21.48 | 420
|
|
(1 row)
|
|
|
|
-- Topocentric from Boulder, CO
|
|
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(topo_azimuth(eci_to_topocentric(sgp4_propagate(t, '2024-01-01 12:00:00+00'), '40.0N 105.3W 1655m'::observer, '2024-01-01 12:00:00+00'))::numeric, 1) AS az_deg,
|
|
round(topo_elevation(eci_to_topocentric(sgp4_propagate(t, '2024-01-01 12:00:00+00'), '40.0N 105.3W 1655m'::observer, '2024-01-01 12:00:00+00'))::numeric, 1) AS el_deg,
|
|
round(topo_range(eci_to_topocentric(sgp4_propagate(t, '2024-01-01 12:00:00+00'), '40.0N 105.3W 1655m'::observer, '2024-01-01 12:00:00+00'))::numeric, 0) AS range_km
|
|
FROM iss;
|
|
az_deg | el_deg | range_km
|
|
--------+--------+----------
|
|
30.6 | -36.4 | 8245
|
|
(1 row)
|
|
|
|
-- Ground track: 5 points over 40 minutes
|
|
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
|
|
count(*) AS track_points,
|
|
round(min(lat)::numeric, 0) AS min_lat,
|
|
round(max(lat)::numeric, 0) AS max_lat
|
|
FROM iss, ground_track(t, '2024-01-01 12:00:00+00', '2024-01-01 12:40:00+00', '10 minutes');
|
|
track_points | min_lat | max_lat
|
|
--------------+---------+---------
|
|
5 | -46 | 52
|
|
(1 row)
|
|
|
|
-- Geodetic type I/O round-trip
|
|
SELECT geodetic_lat('(51.75, 21.48, 420.0)'::geodetic) AS lat,
|
|
geodetic_lon('(51.75, 21.48, 420.0)'::geodetic) AS lon,
|
|
geodetic_alt('(51.75, 21.48, 420.0)'::geodetic) AS alt;
|
|
lat | lon | alt
|
|
-------+-------+-----
|
|
51.75 | 21.48 | 420
|
|
(1 row)
|
|
|
|
-- Topocentric type I/O round-trip
|
|
SELECT round(topo_azimuth('(45.0, 30.0, 500.0, -2.5)'::topocentric)::numeric, 1) AS az,
|
|
round(topo_elevation('(45.0, 30.0, 500.0, -2.5)'::topocentric)::numeric, 1) AS el,
|
|
round(topo_range('(45.0, 30.0, 500.0, -2.5)'::topocentric)::numeric, 1) AS rng,
|
|
round(topo_range_rate('(45.0, 30.0, 500.0, -2.5)'::topocentric)::numeric, 1) AS rr;
|
|
az | el | rng | rr
|
|
------+------+-------+------
|
|
45.0 | 30.0 | 500.0 | -2.5
|
|
(1 row)
|
|
|
|
-- ISS latitude should stay within inclination bounds (51.64 deg)
|
|
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
|
|
abs(geodetic_lat(subsatellite_point(t, '2024-01-01 12:00:00+00'))) <= 52.0 AS within_inc
|
|
FROM iss;
|
|
within_inc
|
|
------------
|
|
t
|
|
(1 row)
|
|
|