pg-orrery-catalog/tests/test_regime.py
Ryan Malloy 1d36729bed Implement pg-orrery-catalog: TLE catalog builder for pg_orrery
Core modules:
- tle.py: NORAD decoding (Alpha-5 + Super-5, matching get_el.c),
  3LE/2LE parsing, TLERecord dataclass with epoch-based dedup
- config.py: TOML config + env var overlay (XDG-compliant paths)
- cache.py: File-based cache with staleness checking
- catalog.py: Multi-source merge with MergeStats tracking
- regime.py: LEO/MEO/GEO/HEO classification by mean motion

Source downloaders (httpx):
- celestrak.py: Active catalog + supplemental GP groups
- satnogs.py: JSON API with 3LE conversion
- spacetrack.py: POST auth flow, bulk GP download

Output formatters:
- sql.py: pg_orrery-compatible INSERT generation (E'' strings)
- tle_file.py: Standard 3LE text output
- json_out.py: JSON with orbital metadata and regime

CLI (Click + Rich):
- download: Cache TLEs from all sources
- build: Merge + output SQL/3LE/JSON (pipes to psql)
- load: Direct DB load via psycopg (optional [pg] extra)
- info: Cache stats and configuration display

58 tests covering NORAD decoding (all 4 encoding cases),
parsing, merge/dedup, SQL escaping, regime classification.
2026-02-18 00:31:46 -07:00

54 lines
1.9 KiB
Python

"""Tests for orbital regime classification."""
from pg_orrery_catalog.regime import classify_regime, regime_summary
from pg_orrery_catalog.tle import TLERecord
def _make_record_with_mm(norad_id: int, mean_motion: float) -> TLERecord:
"""Create a TLERecord with a specific mean motion in line2."""
line1 = f"1 {norad_id:05d}U 98067A 24001.50000000 .00000000 00000-0 00000-0 0 9990"
mm_str = f"{mean_motion:011.8f}"
line2 = f"2 {norad_id:05d} 51.6400 100.0000 0007417 30.0000 330.1234 {mm_str}999990"
return TLERecord(
line1=line1, line2=line2, name=f"SAT-{norad_id}",
norad_id=norad_id, epoch=24001.0,
)
class TestClassifyRegime:
def test_leo(self):
assert classify_regime(15.5) == "LEO" # ISS-like
assert classify_regime(11.26) == "LEO" # boundary
def test_meo(self):
assert classify_regime(2.0) == "MEO" # GPS-like
assert classify_regime(11.25) == "MEO" # just below LEO
def test_geo(self):
assert classify_regime(1.0) == "GEO" # near-synchronous
assert classify_regime(0.91) == "GEO"
def test_heo(self):
assert classify_regime(0.5) == "HEO" # Molniya-like
assert classify_regime(0.9) == "HEO" # boundary
assert classify_regime(0.1) == "HEO" # deep space
class TestRegimeSummary:
def test_mixed(self):
records = {
1: _make_record_with_mm(1, 15.5), # LEO
2: _make_record_with_mm(2, 2.0), # MEO
3: _make_record_with_mm(3, 1.0), # GEO
4: _make_record_with_mm(4, 0.5), # HEO
}
summary = regime_summary(records)
assert summary["LEO"] == 1
assert summary["MEO"] == 1
assert summary["GEO"] == 1
assert summary["HEO"] == 1
def test_empty(self):
summary = regime_summary({})
assert summary == {"LEO": 0, "MEO": 0, "GEO": 0, "HEO": 0}