Implements 5 new C functions requested by the Craft (Astrolock) API team: - tle_from_lines(text, text): two-argument TLE constructor - observer_from_geodetic(float8, float8, float8): numeric observer constructor - observe(tle, observer, timestamptz): single-call propagate + topocentric - sgp4_propagate_safe(tle, timestamptz): returns NULL on propagation error - observe_safe(tle, observer, timestamptz): returns NULL on propagation error Refactors do_propagate() into safe/unsafe variants to support NULL returns. Adds regression test (convenience.sql) covering all new functions including an equivalence test verifying observe() matches the manual two-step pipeline. All 6 regression tests pass.
109 lines
3.4 KiB
Plaintext
109 lines
3.4 KiB
Plaintext
-- convenience functions requested by Craft (Astrolock) integration
|
|
CREATE EXTENSION IF NOT EXISTS pg_orbit;
|
|
NOTICE: extension "pg_orbit" already exists, skipping
|
|
-- tle_from_lines: two-argument constructor
|
|
SELECT tle_norad_id(tle_from_lines(
|
|
'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9002',
|
|
'2 25544 51.6400 208.5000 0007417 68.5000 291.5000 15.49560000100001'
|
|
)) AS iss_norad_id;
|
|
iss_norad_id
|
|
--------------
|
|
25544
|
|
(1 row)
|
|
|
|
-- observer_from_geodetic: numeric constructor
|
|
SELECT observer_from_geodetic(40.0, -105.3, 1655);
|
|
observer_from_geodetic
|
|
--------------------------
|
|
40.0000N 105.3000W 1655m
|
|
(1 row)
|
|
|
|
-- observer_from_geodetic: default altitude = 0
|
|
SELECT observer_from_geodetic(40.0, -105.3);
|
|
observer_from_geodetic
|
|
------------------------
|
|
40.0000N 105.3000W 0m
|
|
(1 row)
|
|
|
|
-- sgp4_propagate_safe: normal propagation returns result
|
|
SELECT (sgp4_propagate_safe(
|
|
'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9002
|
|
2 25544 51.6400 208.5000 0007417 68.5000 291.5000 15.49560000100001'::tle,
|
|
'2024-01-01 12:00:00+00'::timestamptz
|
|
) IS NOT NULL) AS safe_returns_result;
|
|
safe_returns_result
|
|
---------------------
|
|
t
|
|
(1 row)
|
|
|
|
-- sgp4_propagate_safe: diverged orbit returns NULL
|
|
SELECT sgp4_propagate_safe(
|
|
'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9002
|
|
2 25544 51.6400 208.5000 0007417 68.5000 291.5000 15.49560000100001'::tle,
|
|
'2124-01-01 12:00:00+00'::timestamptz
|
|
) IS NULL AS safe_null_on_diverge;
|
|
safe_null_on_diverge
|
|
----------------------
|
|
t
|
|
(1 row)
|
|
|
|
-- observe: single-call propagate + topocentric
|
|
SELECT observe(
|
|
'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9002
|
|
2 25544 51.6400 208.5000 0007417 68.5000 291.5000 15.49560000100001'::tle,
|
|
observer_from_geodetic(40.0, -105.3, 1655),
|
|
'2024-01-01 12:00:00+00'::timestamptz
|
|
);
|
|
observe
|
|
----------------------------------------
|
|
(134.5927,-21.2008,5580.842,-2.373498)
|
|
(1 row)
|
|
|
|
-- observe_safe: returns NULL on diverged orbit
|
|
SELECT observe_safe(
|
|
'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9002
|
|
2 25544 51.6400 208.5000 0007417 68.5000 291.5000 15.49560000100001'::tle,
|
|
'40.0N 105.3W 1655m'::observer,
|
|
'2124-01-01 12:00:00+00'::timestamptz
|
|
) IS NULL AS observe_safe_null;
|
|
observe_safe_null
|
|
-------------------
|
|
t
|
|
(1 row)
|
|
|
|
-- observe matches manual eci_to_topocentric pipeline
|
|
-- (verify observe() produces same result as the two-step approach)
|
|
WITH propagated AS (
|
|
SELECT sgp4_propagate(
|
|
'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9002
|
|
2 25544 51.6400 208.5000 0007417 68.5000 291.5000 15.49560000100001'::tle,
|
|
'2024-01-01 12:00:00+00'::timestamptz
|
|
) AS eci
|
|
),
|
|
manual AS (
|
|
SELECT eci_to_topocentric(
|
|
eci,
|
|
'40.0N 105.3W 1655m'::observer,
|
|
'2024-01-01 12:00:00+00'::timestamptz
|
|
) AS topo
|
|
FROM propagated
|
|
),
|
|
single_call AS (
|
|
SELECT observe(
|
|
'1 25544U 98067A 24001.50000000 .00016717 00000-0 10270-3 0 9002
|
|
2 25544 51.6400 208.5000 0007417 68.5000 291.5000 15.49560000100001'::tle,
|
|
'40.0N 105.3W 1655m'::observer,
|
|
'2024-01-01 12:00:00+00'::timestamptz
|
|
) AS topo
|
|
)
|
|
SELECT
|
|
topo_azimuth(m.topo) = topo_azimuth(s.topo) AS az_match,
|
|
topo_elevation(m.topo) = topo_elevation(s.topo) AS el_match,
|
|
topo_range(m.topo) = topo_range(s.topo) AS range_match
|
|
FROM manual m, single_call s;
|
|
az_match | el_match | range_match
|
|
----------+----------+-------------
|
|
t | t | t
|
|
(1 row)
|
|
|