Harden SP-GiST orbital trie after Hamilton review
Fix M2: clamp picksplit nBins to nTuples to prevent out-of-bounds read on the entries array when called with a single tuple. Fix H2: use WGS72_AE as effective bin_low for the first bin in L0 inner_consistent, preventing false negatives when objects with lower SMA than the first label are inserted after index creation. Fix H3: reject degenerate TLEs (mean_motion <= 0) early in the visibility filter rather than propagating nonsensical values. Fix L1: extract shared tle_passes_visibility_filter() to eliminate duplicated 3-stage filter logic between leaf_consistent and the standalone tle_visibility_possible operator. Add boundary tests: degenerate TLE, polar observer, zero-duration window, and post-insert index-vs-seqscan consistency check.
This commit is contained in:
parent
6f9be428f9
commit
2a7240e739
214
src/spgist_tle.c
214
src/spgist_tle.c
@ -116,8 +116,13 @@ max_visible_altitude_km(double min_el_deg)
|
|||||||
double el_rad, sin_el;
|
double el_rad, sin_el;
|
||||||
double rho, h_max;
|
double rho, h_max;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Below 5 deg elevation the horizon geometry allows visibility to
|
||||||
|
* GEO+ altitudes. Disable the altitude filter rather than compute
|
||||||
|
* an impractically large bound. 50000 km exceeds GEO (35786 km).
|
||||||
|
*/
|
||||||
if (min_el_deg < 5.0)
|
if (min_el_deg < 5.0)
|
||||||
return 50000.0; /* effectively no altitude filter */
|
return 50000.0;
|
||||||
|
|
||||||
el_rad = min_el_deg * DEG_TO_RAD;
|
el_rad = min_el_deg * DEG_TO_RAD;
|
||||||
sin_el = sin(el_rad);
|
sin_el = sin(el_rad);
|
||||||
@ -131,7 +136,7 @@ max_visible_altitude_km(double min_el_deg)
|
|||||||
*
|
*
|
||||||
* Simpler conservative bound: h_max = rho_max / sin(el) for el > 0.
|
* Simpler conservative bound: h_max = rho_max / sin(el) for el > 0.
|
||||||
*/
|
*/
|
||||||
rho = 5000.0;
|
rho = 5000.0; /* max practical slant range; well beyond LEO/MEO */
|
||||||
h_max = sqrt(rho * rho + 2.0 * WGS72_AE * rho * sin_el
|
h_max = sqrt(rho * rho + 2.0 * WGS72_AE * rho * sin_el
|
||||||
+ WGS72_AE * WGS72_AE) - WGS72_AE;
|
+ WGS72_AE * WGS72_AE) - WGS72_AE;
|
||||||
|
|
||||||
@ -176,6 +181,12 @@ ground_footprint_deg(double sma_km, double min_el_deg)
|
|||||||
*
|
*
|
||||||
* where n is mean motion in rad/s, J2 and Re are WGS-72.
|
* where n is mean motion in rad/s, J2 and Re are WGS-72.
|
||||||
* Result in rad/day (multiply rad/s by 86400).
|
* Result in rad/day (multiply rad/s by 86400).
|
||||||
|
*
|
||||||
|
* Uses only the J2 zonal harmonic (no J3/J4 short-period terms).
|
||||||
|
* For LEO this can accumulate ~0.5 deg/day error from J3 short-
|
||||||
|
* period oscillations. Acceptable because (a) the RAAN window
|
||||||
|
* includes footprint + Earth rotation padding, and (b) any query
|
||||||
|
* window >= ~12 hours bypasses the RAAN filter entirely.
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
static inline double
|
static inline double
|
||||||
@ -465,6 +476,9 @@ spgist_tle_picksplit(PG_FUNCTION_ARGS)
|
|||||||
nBins = 2;
|
nBins = 2;
|
||||||
if (nBins > 16)
|
if (nBins > 16)
|
||||||
nBins = 16;
|
nBins = 16;
|
||||||
|
/* Prevent over-read: never more bins than tuples */
|
||||||
|
if (nBins > nTuples)
|
||||||
|
nBins = nTuples;
|
||||||
|
|
||||||
perBin = nTuples / nBins;
|
perBin = nTuples / nBins;
|
||||||
remainder = nTuples % nBins;
|
remainder = nTuples % nBins;
|
||||||
@ -571,17 +585,17 @@ spgist_tle_inner_consistent(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* L0: SMA pruning.
|
* L0: SMA pruning.
|
||||||
* bin_low is the SMA of the lowest object in this bin.
|
* bin_low is the SMA of the lowest object in this bin at
|
||||||
* A satellite in this bin has perigee_alt >= bin_low - AE
|
* index build time. Later inserts with lower SMA route to
|
||||||
* (approximately, for near-circular orbits in the bin).
|
* bin 0, so for the first bin we use AE (physical minimum
|
||||||
* If the lowest possible altitude exceeds max_visible, skip.
|
* SMA) to avoid false negatives.
|
||||||
*
|
*
|
||||||
* Conservative: use bin_low as a lower bound on SMA,
|
* Conservative: assume e=0 (circular, worst case for
|
||||||
* compute perigee assuming e=0 (circular, worst case for
|
|
||||||
* "too high" pruning -- circular has highest perigee for
|
* "too high" pruning -- circular has highest perigee for
|
||||||
* a given SMA).
|
* a given SMA).
|
||||||
*/
|
*/
|
||||||
double perigee_alt = bin_low - WGS72_AE;
|
double effective_low = (i == 0) ? WGS72_AE : bin_low;
|
||||||
|
double perigee_alt = effective_low - WGS72_AE;
|
||||||
double max_alt = max_visible_altitude_km(win.min_el_deg);
|
double max_alt = max_visible_altitude_km(win.min_el_deg);
|
||||||
|
|
||||||
if (perigee_alt > max_alt)
|
if (perigee_alt > max_alt)
|
||||||
@ -657,14 +671,78 @@ spgist_tle_inner_consistent(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/* ----------------------------------------------------------------
|
||||||
* spgist_tle_leaf_consistent -- final check on a leaf TLE
|
* Shared filter: three-stage visibility check on a single TLE.
|
||||||
*
|
*
|
||||||
* Applies three filters:
|
|
||||||
* 1. Perigee altitude check (with eccentricity)
|
* 1. Perigee altitude check (with eccentricity)
|
||||||
* 2. Inclination + ground footprint vs observer latitude
|
* 2. Inclination + ground footprint vs observer latitude
|
||||||
* 3. RAAN query-time filter (J2 precession to query midpoint)
|
* 3. RAAN query-time filter (J2 precession to query midpoint)
|
||||||
*
|
*
|
||||||
|
* Called from both leaf_consistent (index scan) and
|
||||||
|
* tle_visibility_possible (sequential scan / standalone operator).
|
||||||
|
* ----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
tle_passes_visibility_filter(const pg_tle *tle, const ObserverWindow *win)
|
||||||
|
{
|
||||||
|
double sma, perigee_alt, max_alt;
|
||||||
|
double obs_lat_abs, footprint_rad;
|
||||||
|
double dt_days, raan_projected, lst;
|
||||||
|
double earth_rot_rad, raan_window_half, raan_diff;
|
||||||
|
|
||||||
|
/* Reject degenerate TLEs (decay, error data) */
|
||||||
|
if (tle->mean_motion <= 0.0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
sma = tle_sma_km(tle);
|
||||||
|
|
||||||
|
/* Filter 1: perigee altitude */
|
||||||
|
perigee_alt = sma * (1.0 - tle->eccentricity) - WGS72_AE;
|
||||||
|
max_alt = max_visible_altitude_km(win->min_el_deg);
|
||||||
|
if (perigee_alt > max_alt)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Filter 2: inclination + footprint vs observer latitude */
|
||||||
|
obs_lat_abs = fabs(win->obs.lat);
|
||||||
|
footprint_rad = ground_footprint_deg(sma, win->min_el_deg) * DEG_TO_RAD;
|
||||||
|
|
||||||
|
if (tle->inclination + footprint_rad < obs_lat_abs)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Filter 3: RAAN alignment via J2 secular precession */
|
||||||
|
dt_days = win->jd_mid - tle->epoch;
|
||||||
|
raan_projected = tle->raan
|
||||||
|
+ j2_raan_rate(sma, tle->inclination) * dt_days;
|
||||||
|
raan_projected = fmod(raan_projected, 2.0 * M_PI);
|
||||||
|
if (raan_projected < 0.0)
|
||||||
|
raan_projected += 2.0 * M_PI;
|
||||||
|
|
||||||
|
/* Observer LST at query midpoint */
|
||||||
|
lst = gmst_from_jd(win->jd_mid) + win->obs.lon;
|
||||||
|
lst = fmod(lst, 2.0 * M_PI);
|
||||||
|
if (lst < 0.0)
|
||||||
|
lst += 2.0 * M_PI;
|
||||||
|
|
||||||
|
/* RAAN window: Earth rotation during query + footprint pad */
|
||||||
|
earth_rot_rad = (win->jd_end - win->jd_start) * EARTH_ROT_RAD_PER_DAY;
|
||||||
|
raan_window_half = earth_rot_rad / 2.0
|
||||||
|
+ ground_footprint_deg(sma, win->min_el_deg) * DEG_TO_RAD;
|
||||||
|
|
||||||
|
if (raan_window_half >= M_PI)
|
||||||
|
return true; /* full rotation -- pass everything */
|
||||||
|
|
||||||
|
raan_diff = fabs(raan_projected - lst);
|
||||||
|
if (raan_diff > M_PI)
|
||||||
|
raan_diff = 2.0 * M_PI - raan_diff;
|
||||||
|
|
||||||
|
return (raan_diff <= raan_window_half);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* spgist_tle_leaf_consistent -- final check on a leaf TLE
|
||||||
|
*
|
||||||
|
* Delegates to tle_passes_visibility_filter() for the &? operator.
|
||||||
* recheck = false: the &? operator IS the superset filter.
|
* recheck = false: the &? operator IS the superset filter.
|
||||||
* The user runs predict_passes() on survivors for SGP4 ground truth.
|
* The user runs predict_passes() on survivors for SGP4 ground truth.
|
||||||
*/
|
*/
|
||||||
@ -688,76 +766,11 @@ spgist_tle_leaf_consistent(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
ObserverWindow win;
|
ObserverWindow win;
|
||||||
HeapTupleHeader composite;
|
HeapTupleHeader composite;
|
||||||
double sma, perigee_alt, max_alt;
|
|
||||||
double obs_lat_abs, footprint_rad;
|
|
||||||
double dt_days, raan_projected, lst;
|
|
||||||
double earth_rot_rad, raan_window_half, raan_diff;
|
|
||||||
|
|
||||||
composite = DatumGetHeapTupleHeader(in->scankeys[i].sk_argument);
|
composite = DatumGetHeapTupleHeader(in->scankeys[i].sk_argument);
|
||||||
extract_observer_window(composite, &win);
|
extract_observer_window(composite, &win);
|
||||||
|
|
||||||
sma = tle_sma_km(tle);
|
if (!tle_passes_visibility_filter(tle, &win))
|
||||||
|
|
||||||
/* Filter 1: perigee altitude check */
|
|
||||||
perigee_alt = sma * (1.0 - tle->eccentricity) - WGS72_AE;
|
|
||||||
max_alt = max_visible_altitude_km(win.min_el_deg);
|
|
||||||
if (perigee_alt > max_alt)
|
|
||||||
{
|
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Filter 2: inclination + footprint vs observer latitude */
|
|
||||||
obs_lat_abs = fabs(win.obs.lat);
|
|
||||||
footprint_rad = ground_footprint_deg(sma, win.min_el_deg)
|
|
||||||
* DEG_TO_RAD;
|
|
||||||
|
|
||||||
if (tle->inclination + footprint_rad < obs_lat_abs)
|
|
||||||
{
|
|
||||||
result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Filter 3: RAAN query-time filter.
|
|
||||||
*
|
|
||||||
* Project RAAN to query midpoint via J2 precession rate.
|
|
||||||
* Check alignment with observer's local sidereal time.
|
|
||||||
* Earth rotates 360 deg/day, so the RAAN window widens
|
|
||||||
* with query duration.
|
|
||||||
*/
|
|
||||||
dt_days = win.jd_mid - tle->epoch;
|
|
||||||
raan_projected = tle->raan
|
|
||||||
+ j2_raan_rate(sma, tle->inclination) * dt_days;
|
|
||||||
raan_projected = fmod(raan_projected, 2.0 * M_PI);
|
|
||||||
if (raan_projected < 0.0)
|
|
||||||
raan_projected += 2.0 * M_PI;
|
|
||||||
|
|
||||||
/* Observer LST at query midpoint */
|
|
||||||
lst = gmst_from_jd(win.jd_mid) + win.obs.lon;
|
|
||||||
lst = fmod(lst, 2.0 * M_PI);
|
|
||||||
if (lst < 0.0)
|
|
||||||
lst += 2.0 * M_PI;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* RAAN window: Earth rotation during query + footprint.
|
|
||||||
* Full rotation (24h+) means pass everything.
|
|
||||||
*/
|
|
||||||
earth_rot_rad = (win.jd_end - win.jd_start) * EARTH_ROT_RAD_PER_DAY;
|
|
||||||
raan_window_half = earth_rot_rad / 2.0
|
|
||||||
+ ground_footprint_deg(sma, win.min_el_deg) * DEG_TO_RAD;
|
|
||||||
|
|
||||||
if (raan_window_half >= M_PI)
|
|
||||||
{
|
|
||||||
/* Full rotation or close: no RAAN filter for this key */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
raan_diff = fabs(raan_projected - lst);
|
|
||||||
if (raan_diff > M_PI)
|
|
||||||
raan_diff = 2.0 * M_PI - raan_diff;
|
|
||||||
|
|
||||||
if (raan_diff > raan_window_half)
|
|
||||||
{
|
{
|
||||||
result = false;
|
result = false;
|
||||||
break;
|
break;
|
||||||
@ -791,51 +804,8 @@ tle_visibility_possible(PG_FUNCTION_ARGS)
|
|||||||
HeapTupleHeader composite = PG_GETARG_HEAPTUPLEHEADER(0);
|
HeapTupleHeader composite = PG_GETARG_HEAPTUPLEHEADER(0);
|
||||||
pg_tle *tle = (pg_tle *) PG_GETARG_POINTER(1);
|
pg_tle *tle = (pg_tle *) PG_GETARG_POINTER(1);
|
||||||
ObserverWindow win;
|
ObserverWindow win;
|
||||||
double sma, perigee_alt, max_alt;
|
|
||||||
double obs_lat_abs, footprint_rad;
|
|
||||||
double dt_days, raan_projected, lst;
|
|
||||||
double earth_rot_rad, raan_window_half, raan_diff;
|
|
||||||
|
|
||||||
extract_observer_window(composite, &win);
|
extract_observer_window(composite, &win);
|
||||||
|
|
||||||
sma = tle_sma_km(tle);
|
PG_RETURN_BOOL(tle_passes_visibility_filter(tle, &win));
|
||||||
|
|
||||||
/* Filter 1: perigee altitude */
|
|
||||||
perigee_alt = sma * (1.0 - tle->eccentricity) - WGS72_AE;
|
|
||||||
max_alt = max_visible_altitude_km(win.min_el_deg);
|
|
||||||
if (perigee_alt > max_alt)
|
|
||||||
PG_RETURN_BOOL(false);
|
|
||||||
|
|
||||||
/* Filter 2: inclination + footprint */
|
|
||||||
obs_lat_abs = fabs(win.obs.lat);
|
|
||||||
footprint_rad = ground_footprint_deg(sma, win.min_el_deg) * DEG_TO_RAD;
|
|
||||||
|
|
||||||
if (tle->inclination + footprint_rad < obs_lat_abs)
|
|
||||||
PG_RETURN_BOOL(false);
|
|
||||||
|
|
||||||
/* Filter 3: RAAN alignment */
|
|
||||||
dt_days = win.jd_mid - tle->epoch;
|
|
||||||
raan_projected = tle->raan
|
|
||||||
+ j2_raan_rate(sma, tle->inclination) * dt_days;
|
|
||||||
raan_projected = fmod(raan_projected, 2.0 * M_PI);
|
|
||||||
if (raan_projected < 0.0)
|
|
||||||
raan_projected += 2.0 * M_PI;
|
|
||||||
|
|
||||||
lst = gmst_from_jd(win.jd_mid) + win.obs.lon;
|
|
||||||
lst = fmod(lst, 2.0 * M_PI);
|
|
||||||
if (lst < 0.0)
|
|
||||||
lst += 2.0 * M_PI;
|
|
||||||
|
|
||||||
earth_rot_rad = (win.jd_end - win.jd_start) * EARTH_ROT_RAD_PER_DAY;
|
|
||||||
raan_window_half = earth_rot_rad / 2.0
|
|
||||||
+ ground_footprint_deg(sma, win.min_el_deg) * DEG_TO_RAD;
|
|
||||||
|
|
||||||
if (raan_window_half >= M_PI)
|
|
||||||
PG_RETURN_BOOL(true); /* full rotation -- pass everything */
|
|
||||||
|
|
||||||
raan_diff = fabs(raan_projected - lst);
|
|
||||||
if (raan_diff > M_PI)
|
|
||||||
raan_diff = 2.0 * M_PI - raan_diff;
|
|
||||||
|
|
||||||
PG_RETURN_BOOL(raan_diff <= raan_window_half);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -207,6 +207,103 @@ ORDER BY name;
|
|||||||
SSO-800
|
SSO-800
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 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,
|
||||||
|
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 &? tle AS visible
|
||||||
|
FROM test_spgist
|
||||||
|
WHERE name = 'DECAYED';
|
||||||
|
name | visible
|
||||||
|
---------+---------
|
||||||
|
DECAYED | f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 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 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 &? tle
|
||||||
|
ORDER BY name;
|
||||||
|
name
|
||||||
|
---------
|
||||||
|
SSO-800
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- Test 11: Zero-duration window — sees only what is directly
|
||||||
|
-- overhead at the instant. RAAN window = footprint only.
|
||||||
|
-- ============================================================
|
||||||
|
SELECT name,
|
||||||
|
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 &? tle AS visible
|
||||||
|
FROM test_spgist
|
||||||
|
WHERE name = 'ISS';
|
||||||
|
name | visible
|
||||||
|
------+---------
|
||||||
|
ISS | f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 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 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 &? tle
|
||||||
|
ORDER BY name;
|
||||||
|
name
|
||||||
|
---------
|
||||||
|
ISS
|
||||||
|
SSO-800
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
RESET enable_seqscan;
|
||||||
|
SET enable_indexscan = off;
|
||||||
|
SET enable_bitmapscan = off;
|
||||||
|
SELECT name
|
||||||
|
FROM test_spgist
|
||||||
|
WHERE 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 &? tle
|
||||||
|
ORDER BY name;
|
||||||
|
name
|
||||||
|
---------
|
||||||
|
ISS
|
||||||
|
SSO-800
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
RESET enable_indexscan;
|
||||||
|
RESET enable_bitmapscan;
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- Cleanup
|
-- Cleanup
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
|
|||||||
@ -191,6 +191,86 @@ WHERE ROW(
|
|||||||
ORDER BY name;
|
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,
|
||||||
|
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 &? tle 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 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 &? tle
|
||||||
|
ORDER BY name;
|
||||||
|
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- Test 11: Zero-duration window — sees only what is directly
|
||||||
|
-- overhead at the instant. RAAN window = footprint only.
|
||||||
|
-- ============================================================
|
||||||
|
SELECT name,
|
||||||
|
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 &? tle 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 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 &? tle
|
||||||
|
ORDER BY name;
|
||||||
|
RESET enable_seqscan;
|
||||||
|
|
||||||
|
SET enable_indexscan = off;
|
||||||
|
SET enable_bitmapscan = off;
|
||||||
|
SELECT name
|
||||||
|
FROM test_spgist
|
||||||
|
WHERE 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 &? tle
|
||||||
|
ORDER BY name;
|
||||||
|
RESET enable_indexscan;
|
||||||
|
RESET enable_bitmapscan;
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- Cleanup
|
-- Cleanup
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user