GiST: entryvec->vector[] uses 1-based indexing (FirstOffsetNumber), not 0-based. Reading vector[0] hit uninitialized memory, causing SIGSEGV on large catalogs (14k+ satellites). Fixed in gist_tle_union and gist_tle_picksplit. SP-GiST: PostgreSQL requires the indexed column as the LEFT argument of the operator to form a ScanKey (skey.h:23-26). Flipped &? from (observer_window, tle) to (tle, observer_window) so inner_consistent receives scankeys for tree-level pruning. Removed L0 altitude pruning from inner_consistent — SMA bins don't carry eccentricity, so HEO satellites (e.g. CLUSTER II, e=0.88, SMA ~70000 km, perigee ~2000 km) were falsely pruned. L0 now only narrows SMA range for L1 footprint computation. All 15 regression tests pass. Consistency check on 14,376 satellites confirms 0 false negatives, 0 false positives.
278 lines
9.0 KiB
SQL
278 lines
9.0 KiB
SQL
-- Test SP-GiST orbital trie index and &? visibility cone operator
|
|
SET client_min_messages = warning;
|
|
CREATE EXTENSION IF NOT EXISTS pg_orrery;
|
|
RESET client_min_messages;
|
|
|
|
-- ============================================================
|
|
-- Test table with mixed orbital regimes
|
|
-- ============================================================
|
|
CREATE TABLE test_spgist (
|
|
id serial,
|
|
name text,
|
|
tle tle
|
|
);
|
|
|
|
-- ISS (LEO, ~400km, 51.64 deg)
|
|
INSERT INTO test_spgist (name, tle) VALUES ('ISS',
|
|
'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');
|
|
|
|
-- Hubble (LEO, ~540km, 28.47 deg)
|
|
INSERT INTO test_spgist (name, tle) VALUES ('Hubble',
|
|
'1 20580U 90037B 24001.50000000 .00000790 00000+0 39573-4 0 9992
|
|
2 20580 28.4705 61.4398 0002797 317.3115 42.7577 15.09395228 00008');
|
|
|
|
-- GPS IIR-M (MEO, ~20200km, 55.44 deg)
|
|
INSERT INTO test_spgist (name, tle) VALUES ('GPS-IIR',
|
|
'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');
|
|
|
|
-- Equatorial-LEO (same altitude as ISS, 5 deg inclination)
|
|
INSERT INTO test_spgist (name, tle) VALUES ('Equatorial-LEO',
|
|
'1 99901U 24999A 24001.50000000 .00016717 00000-0 10270-3 0 9990
|
|
2 99901 5.0000 208.9163 0006703 30.1694 61.7520 15.50100486 00001');
|
|
|
|
-- SSO-800 (Sun-synchronous, ~800km, 98.7 deg)
|
|
INSERT INTO test_spgist (name, tle) VALUES ('SSO-800',
|
|
'1 99902U 24999B 24001.50000000 .00000100 00000+0 50000-4 0 9991
|
|
2 99902 98.7000 120.0000 0001000 90.0000 270.0000 14.19553000 00001');
|
|
|
|
-- GEO-SAT (Geostationary, ~35786km, 0.04 deg)
|
|
INSERT INTO test_spgist (name, tle) VALUES ('GEO-SAT',
|
|
'1 99903U 24999C 24001.50000000 .00000000 00000+0 00000+0 0 9992
|
|
2 99903 0.0400 270.0000 0003000 0.0000 180.0000 1.00273791 00001');
|
|
|
|
|
|
-- ============================================================
|
|
-- Test 1: Operator standalone — ISS from Eagle Idaho (2h window)
|
|
-- Eagle Idaho: 43.6977N 116.3535W, 760m elevation
|
|
-- ISS passes altitude and inclination checks, but RAAN filter
|
|
-- rejects it — the orbital plane isn't overhead during this
|
|
-- specific 2-hour window (correct physics, see Test 5 for 24h).
|
|
-- ============================================================
|
|
SELECT name,
|
|
tle &? ROW(
|
|
observer('43.6977N 116.3535W 760m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-01 02:00:00+00'::timestamptz,
|
|
10.0
|
|
)::observer_window AS visible
|
|
FROM test_spgist
|
|
WHERE name = 'ISS';
|
|
|
|
|
|
-- ============================================================
|
|
-- Test 2: Equatorial-LEO NOT visible from Eagle Idaho
|
|
-- 5 deg inc + ~12 deg footprint = 17 deg < 43.7 deg latitude
|
|
-- ============================================================
|
|
SELECT name,
|
|
tle &? ROW(
|
|
observer('43.6977N 116.3535W 760m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-01 02:00:00+00'::timestamptz,
|
|
10.0
|
|
)::observer_window AS visible
|
|
FROM test_spgist
|
|
WHERE name = 'Equatorial-LEO';
|
|
|
|
|
|
-- ============================================================
|
|
-- Test 3: Create SP-GiST index, verify index scan with positive
|
|
-- results. Equatorial observer at 0E — SSO-800 RAAN (120 deg)
|
|
-- aligns with LST near 0E at this epoch, so it passes.
|
|
-- ============================================================
|
|
CREATE INDEX test_spgist_idx ON test_spgist USING spgist (tle tle_spgist_ops);
|
|
|
|
SET enable_seqscan = off;
|
|
|
|
SELECT name
|
|
FROM test_spgist
|
|
WHERE tle &? ROW(
|
|
observer('0.0N 0.0E 0m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-01 02:00:00+00'::timestamptz,
|
|
10.0
|
|
)::observer_window
|
|
ORDER BY name;
|
|
|
|
RESET enable_seqscan;
|
|
|
|
|
|
-- ============================================================
|
|
-- Test 4: Seqscan vs index scan consistency — same query must
|
|
-- return identical results regardless of scan method.
|
|
-- ============================================================
|
|
SET enable_indexscan = off;
|
|
SET enable_bitmapscan = off;
|
|
|
|
SELECT name
|
|
FROM test_spgist
|
|
WHERE tle &? ROW(
|
|
observer('0.0N 0.0E 0m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-01 02:00:00+00'::timestamptz,
|
|
10.0
|
|
)::observer_window
|
|
ORDER BY name;
|
|
|
|
RESET enable_indexscan;
|
|
RESET enable_bitmapscan;
|
|
|
|
|
|
-- ============================================================
|
|
-- Test 5: 24-hour window — RAAN filter bypassed (full Earth
|
|
-- rotation). Only ISS and SSO-800 pass inclination from Eagle
|
|
-- Idaho (43.7 deg). Hubble (28.5+14.8=43.3 deg) barely fails.
|
|
-- GPS-IIR and GEO-SAT filtered by altitude.
|
|
-- ============================================================
|
|
SELECT name
|
|
FROM test_spgist
|
|
WHERE tle &? ROW(
|
|
observer('43.6977N 116.3535W 760m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-02 00:00:00+00'::timestamptz,
|
|
10.0
|
|
)::observer_window
|
|
ORDER BY name;
|
|
|
|
|
|
-- ============================================================
|
|
-- Test 6: High min_el (45 deg) changes footprint — wider
|
|
-- footprint lets more inclinations through. Same 24h window.
|
|
-- ============================================================
|
|
SELECT name
|
|
FROM test_spgist
|
|
WHERE tle &? ROW(
|
|
observer('43.6977N 116.3535W 760m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-02 00:00:00+00'::timestamptz,
|
|
45.0
|
|
)::observer_window
|
|
ORDER BY name;
|
|
|
|
|
|
-- ============================================================
|
|
-- Test 7: GiST coexistence — both index types on same table
|
|
-- ============================================================
|
|
CREATE INDEX test_gist_idx ON test_spgist USING gist (tle);
|
|
|
|
-- GiST overlap query still works
|
|
SELECT a.name AS sat_a, b.name AS sat_b, a.tle && b.tle AS overlaps
|
|
FROM test_spgist a, test_spgist b
|
|
WHERE a.name = 'ISS' AND b.name = 'Hubble';
|
|
|
|
-- SP-GiST query still works alongside GiST
|
|
SET enable_seqscan = off;
|
|
SELECT name
|
|
FROM test_spgist
|
|
WHERE tle &? ROW(
|
|
observer('43.6977N 116.3535W 760m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-02 00:00:00+00'::timestamptz,
|
|
10.0
|
|
)::observer_window
|
|
ORDER BY name;
|
|
RESET enable_seqscan;
|
|
|
|
|
|
-- ============================================================
|
|
-- Test 8: NULL TLE handling — NULLs should be excluded
|
|
-- ============================================================
|
|
INSERT INTO test_spgist (name, tle) VALUES ('NULL-SAT', NULL);
|
|
|
|
SELECT name
|
|
FROM test_spgist
|
|
WHERE tle &? ROW(
|
|
observer('43.6977N 116.3535W 760m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-02 00:00:00+00'::timestamptz,
|
|
10.0
|
|
)::observer_window
|
|
ORDER BY name;
|
|
|
|
|
|
-- ============================================================
|
|
-- Test 9: Degenerate TLE (mean_motion = 0) — rejected by filter
|
|
-- ============================================================
|
|
INSERT INTO test_spgist (name, tle) VALUES ('DECAYED',
|
|
'1 99904U 24999D 24001.50000000 .00000000 00000+0 00000+0 0 9993
|
|
2 99904 0.0000 0.0000 0000000 0.0000 0.0000 0.00000000 00001');
|
|
|
|
SELECT name,
|
|
tle &? ROW(
|
|
observer('0.0N 0.0E 0m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-02 00:00:00+00'::timestamptz,
|
|
10.0
|
|
)::observer_window AS visible
|
|
FROM test_spgist
|
|
WHERE name = 'DECAYED';
|
|
|
|
|
|
-- ============================================================
|
|
-- Test 10: Polar observer (90N) — only ISS and SSO-800 reach
|
|
-- the pole. ISS (51.6 + footprint) < 90, so only SSO-800
|
|
-- (retrograde, 98.7 deg inc > 90 deg) passes. 24h window.
|
|
-- ============================================================
|
|
SELECT name
|
|
FROM test_spgist
|
|
WHERE tle &? ROW(
|
|
observer('90.0N 0.0E 0m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-02 00:00:00+00'::timestamptz,
|
|
10.0
|
|
)::observer_window
|
|
ORDER BY name;
|
|
|
|
|
|
-- ============================================================
|
|
-- Test 11: Zero-duration window — sees only what is directly
|
|
-- overhead at the instant. RAAN window = footprint only.
|
|
-- ============================================================
|
|
SELECT name,
|
|
tle &? ROW(
|
|
observer('0.0N 0.0E 0m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
10.0
|
|
)::observer_window AS visible
|
|
FROM test_spgist
|
|
WHERE name = 'ISS';
|
|
|
|
|
|
-- ============================================================
|
|
-- Test 12: Index-vs-seqscan consistency on 24h Eagle Idaho
|
|
-- (the primary correctness test, now after all inserts)
|
|
-- ============================================================
|
|
SET enable_seqscan = off;
|
|
SELECT name
|
|
FROM test_spgist
|
|
WHERE tle &? ROW(
|
|
observer('43.6977N 116.3535W 760m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-02 00:00:00+00'::timestamptz,
|
|
10.0
|
|
)::observer_window
|
|
ORDER BY name;
|
|
RESET enable_seqscan;
|
|
|
|
SET enable_indexscan = off;
|
|
SET enable_bitmapscan = off;
|
|
SELECT name
|
|
FROM test_spgist
|
|
WHERE tle &? ROW(
|
|
observer('43.6977N 116.3535W 760m'),
|
|
'2024-01-01 00:00:00+00'::timestamptz,
|
|
'2024-01-02 00:00:00+00'::timestamptz,
|
|
10.0
|
|
)::observer_window
|
|
ORDER BY name;
|
|
RESET enable_indexscan;
|
|
RESET enable_bitmapscan;
|
|
|
|
|
|
-- ============================================================
|
|
-- Cleanup
|
|
-- ============================================================
|
|
DROP TABLE test_spgist;
|