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.
54 lines
1.9 KiB
Python
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}
|