Phase 17 — Space & Satellite Domain¶
Status: COMPLETE Date: 2026-03-04 Tests: 149 new (4,763 total)
Summary¶
Full Space & Satellite domain covering orbital mechanics, GPS constellation management, space-based ISR, missile early warning, SATCOM dependency, and anti-satellite warfare. New space/ package with 9 source modules. Space assets modulate existing navigation, detection, communication, and missile defense systems through their existing parameter interfaces — no parallel resolution systems. All effects backward-compatible via enable_space config flag defaulting to False and default parameter values preserving existing behavior.
Deliverables¶
17a: Orbital Mechanics & Constellation Management (tests)¶
space/__init__.py— Package initspace/events.py— Space domain events (SatelliteOverpassEvent, GPSDegradedEvent, SATCOMWindowEvent, ASATEngagementEvent, ConstellationDegradedEvent)space/orbits.py— Simplified Keplerian orbital mechanics: periodT = 2pi*sqrt(a^3/mu), ground track computation, J2 nodal precession for sun-synchronous orbits, Kepler equation solver (Newton-Raphson)space/constellations.py— Constellation manager: satellite group definitions, coverage windows over theater bounding box, constellation health tracking, EventBus integration
17b: GPS Dependency & Navigation Warfare (tests)¶
space/gps.py— GPS accuracy model: visible satellite count over theater, DOP (dilution of precision) computation, position errorsigma = DOP * sigma_range. INS drift model for GPS-denied:sigma(t) = sigma_0 + drift_rate * t. CEP scaling for GPS-guided weapons (JDAM-class: ~13m CEP GPS, ~30m+ INS-only)
17c: Space-Based ISR & Early Warning (tests)¶
space/isr.py— Space-based ISR: imaging satellites generate detection events during overpass windows. Resolution determines minimum detectable unit size. Revisit time from orbital period + ground track drift. Cloud cover blocks optical satellites (not SAR)space/early_warning.py— Missile early warning: GEO/HEO IR satellites detect missile launches (IR bloom). Detection time = coverage check + processing delay (30-90s). Wired intocombat/missile_defense.pyearly warning time parameter. No coverage = no early warning (fall back to ground radar)
17d: SATCOM Dependency & Anti-Satellite Warfare (tests)¶
space/satcom.py— SATCOM dependency model: satellite coverage windows determine SATCOM availability for beyond-LOS communications. Bandwidth capacity limits per theater. Degradation feeds intoc2/communications.pyreliability for SATCOM-type equipmentspace/asat.py— Anti-satellite warfare: direct-ascent kinetic kill vehicle (Pk from intercept geometry), ground-based laser dazzle (temporary blinding) and laser destruct (permanent). Poisson debris generation with cascade model (Kessler syndrome risk). Satellite loss cascades to constellation degradation
17e: Integration (tests)¶
core/types.py— AddedModuleId.SPACEenvironment/electromagnetic.py— Addedconstellation_accuracy_mfor dynamic GPS accuracy from orbital modelcombat/missile_defense.py— Addedearly_warning_time_sparameter for space-based early warning integrationcombat/missiles.py— Addedgps_accuracy_mparameter for GPS-guided weapon CEP scalingc2/communications.py— Addedsatcom_reliability_factorfor SATCOM availability modulationsimulation/scenario.py— Addedspace_enginefield to SimulationContextsimulation/engine.py— Addedspace_engine.update()call in tick loop
17f: YAML Data & Validation (tests)¶
- 9 constellation YAMLs: GPS NAVSTAR (24-slot MEO), GLONASS (24-slot MEO), Milstar SATCOM, WGS SATCOM, Keyhole optical, Lacrosse SAR, SBIRS early warning, Molniya early warning, SIGINT LEO
- 3 ASAT weapon YAMLs: SM-3 Block IIA, Nudol ASAT, ground-based laser
- 3 validation scenarios: space_gps_denial (PGM accuracy comparison), space_isr_gap (exploit satellite overpass gap), space_asat_escalation (kinetic ASAT cascading DOP increase)
Key Design Decisions¶
- Simplified Keplerian, not SGP4: Campaign-scale simulation needs "when does satellite see theater?" — full SGP4/TLE propagation is unnecessary complexity. J2 precession captures the most important perturbation (sun-synchronous orbit drift).
- DOP-based GPS accuracy: Visible satellite count drives geometric DOP, which scales position error. This captures constellation degradation effects naturally — losing satellites increases DOP.
- INS drift as linear model: GPS-denied navigation degrades linearly with time. Simple but captures the key dynamic — longer GPS denial means worse navigation.
- Space→detection via parameter injection:
constellation_accuracy_m,early_warning_time_s,satcom_reliability_factor,gps_accuracy_mparameters added to existing modules — zero-impact defaults mean all existing callers unaffected. - Poisson debris with cascade: Each kinetic ASAT kill generates Poisson-distributed fragments. Each fragment has per-orbit collision probability for satellites at similar altitude. Captures Kessler cascade risk without full orbital debris simulation.
Backward Compatibility¶
SpaceConfig.enable_space = False— space engines not instantiated unless enabledSimulationContext.space_engine = None— consuming code checks None before queryingconstellation_accuracy_mdefaults preserve existing GPS accuracy when no space engine activeearly_warning_time_sdefault preserves existing missile defense behaviorsatcom_reliability_factor = 1.0— comms reliability unchanged when no space enginegps_accuracy_mdefault preserves existing weapon delivery behavior
Files Changed¶
- 9 new source files in
space/ - 7 modified existing files (types, electromagnetic, missile_defense, missiles, communications, scenario, engine)
- 12 YAML data files (9 constellations, 3 ASAT weapons)
- 3 validation scenarios
- 1 existing test file modified (test_types.py — added SPACE to ModuleId set)
No New Dependencies¶
All orbital mechanics and space domain physics implemented with existing numpy/scipy. No new package requirements.
Known Limitations / Future Work¶
- Simplified Keplerian orbits (no SGP4/TLE, no atmospheric drag for LEO decay)
- No detailed satellite bus modeling (power, thermal, attitude control)
- No space-based SIGINT integration with Phase 16 SIGINT engine
- Debris cascade model is statistical (no individual fragment tracking)
- No satellite maneuvering or station-keeping fuel limits
- No space weather effects (solar flares, radiation belt variations)
Lessons Learned¶
- J/S ratio lesson from Phase 16 applies here too: parameter injection through existing interfaces keeps integration clean
- Space engine wired into engine tick loop (unlike Phase 16 EW which deferred this)
- Constellation YAML data follows same pydantic-loader pattern established in Phase 2
- Per-side GPS accuracy needs worst-case aggregation when feeding a shared (non-per-side) EM environment — caught in postmortem
- Geometry-dependent visibility tests need careful fixture design — equatorial LEO is visible from surprising distances due to orbital altitude
Postmortem¶
Scope: On target¶
- Plan: ~150 tests across 6 sub-phases. Delivered: 149.
- Plan: 9 source + 7 modified + ~14 YAML + 3 scenarios. Delivered: 9 + 7 + 12 + 3.
- All planned items delivered. Nothing dropped or deferred beyond the documented known limitations.
- One unplanned change:
test_types.pymodification to add SPACE to expected ModuleId set.
Quality: High¶
- All 7 non-init modules use
get_logger(__name__). No bareprint(). - No PRNG violations. No TODOs/FIXMEs. No
set()for determinism-sensitive iteration. - Type hints on all public functions. DI pattern throughout.
- All engines implement
get_state()/set_state()with roundtrip tests. - All 7 event types are frozen dataclasses inheriting from Event base class.
- Test distribution well-balanced: 35/25/25/30/14/20 across 6 files.
- No
@pytest.mark.slowneeded — all 149 tests run in 0.49s. - Some geometry-dependent tests use weak assertions (
isinstance(list)) with comments explaining why — orbital visibility is hard to guarantee without fixing exact positions.
Integration: Mostly wired, 2 gaps fixed/documented¶
- Fixed: Per-side GPS accuracy overwrite in
SpaceEngine.update()— was callingset_constellation_accuracy()in a loop where red's value overwrote blue's. Fixed to usemax()(worst-case) since EMEnvironment is shared state. - Documented gap: ScenarioLoader doesn't auto-wire SpaceEngine from scenario YAML. Same pattern as Phase 16 EW. Both need a future wiring pass.
- By design: No event subscribers outside
space/and tests. Space events are captured by SimulationRecorder via Event MRO dispatch. Effects flow via parameter injection, not event reaction. - By design:
except Exception: passwrappingspace_engine.update()in engine.py. Consistent with existing EW pattern — prevents space domain errors from crashing the sim.
Deficits: 2 new (+ 6 already documented)¶
- EMEnvironment GPS accuracy is not per-side — single
_constellation_accuracy_mvalue. Mitigated by using worst-case (max) aggregation. Per-side EM would require architectural changes. - ScenarioLoader doesn't auto-wire SpaceEngine or EWEngine — both Phase 16 and Phase 17 engines require manual wiring. Future integration pass needed.
(6 pre-existing limitations already in devlog index: simplified Keplerian, no satellite bus, no SIGINT integration, statistical debris, no maneuvering, no space weather)
Performance: No impact¶
- Full suite: 4,763 tests in ~90s (consistent with Phase 16 baseline).
- Phase 17 tests alone: 0.49s — negligible.
Action items completed¶
- Fixed per-side GPS accuracy overwrite bug.
- Added 2 new deficits to devlog index and development-phases-post-mvp.md.
- Wrote this postmortem.