v0.13.0: sat_orbits adapter (forward-orbit-track per satellite) + antimeridian splitter

New SourceAdapter publishes one forward-orbit-track LineString per tracked
satellite per poll (5min cadence, 90min horizon, 60s vertex resolution).
Drives the "each sat's path" map view Matt asked for after enabling the
satellite family and seeing overlapping orange visibility-footprint
circles + a polar-orbit ground track wrapping the wrong way across the
antimeridian.

Companion to v0.12.0 sat_positions: one publishes the current sub-sat POINT
per minute, sat_orbits publishes the LINE of where it's going. Complement,
not replacement.

data_class=telemetry (continuous trajectory state, surfaces on /telemetry).
Geo carries both centroid (current sub-sat point for the "here it is" dot)
and geometry (the forward track LineString or MultiLineString).

Antimeridian splitter is the key new sat_common primitive: walks the
vertex list, splits at +/-180 crossings, interpolates lat at the crossing
point for crisp dateline termination. ALSO fixes the v0.11.2
satpass_predict "wrong-way wrap" bug by rewiring _build_pass_geometry's
ground_track through the same splitter (sibling concern, documented in
the PR body as intentional scope-coupling).

CENTRAL_SAT stream STREAM_CATEGORY_DOMAINS extends from
('tle', 'pass', 'position') to ('tle', 'pass', 'position', 'orbit'). No
max_bytes bump needed; 6 sats x 12 polls/hour x 24 hours x ~5KB = ~8.5
MB/day, negligible against the 5 GiB cap.

GUI events_list.html adds a small per-NORAD-ID color helper using
golden-angle HSL hue distribution. sat_orbits events render with
per-satellite colors; other adapters keep their existing per-adapter
palette color (additive).

Phase A sanity verified: ISS TLE at 2026-06-09T07:00 UTC propagates to
91 vertices over 90min, first vertex matches v0.11.1's known sub-sat
point (170.66 lon, -17.15 lat, 417.4 km alt), one antimeridian crossing
splits the track into a 2-segment MultiLineString.

44/44 satpass_predict regression-guard tests pass after the
_build_pass_geometry rewire. One new test specifically exercises the
splitter inside _build_pass_geometry for a synthesized polar-orbit
ground_track.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
malice 2026-06-09 18:44:14 -06:00
commit 03602c02f8
15 changed files with 838 additions and 9 deletions

View file

@ -1979,6 +1979,47 @@ at parameter `00060`, gage height (ft) at `00065`, water temperature (°C) at
the key via GUI `/api-keys` then the adapter picks it up on the next
config-change notification.
### sat_orbits — forward orbital track per satellite (v0.13.0)
- **Source:** same as `sat_positions` and `satpass_predict` — reads the
latest TLE per `norad_id` from `celestrak_tle`'s events. Empty TLE table
= zero events yielded, no exception.
- **Data class:** `telemetry`. Surfaces on `/telemetry`, not `/events`;
these are continuous-state trajectories, not alerts.
- **Stream:** `CENTRAL_SAT` (existing; v0.13.0 extends
`STREAM_CATEGORY_DOMAINS["CENTRAL_SAT"]` to
`("tle", "pass", "position", "orbit")`).
- **Subject:** `central.sat.orbit.<norad_id>` — one subject per satellite.
Consumers can subscribe to `central.sat.orbit.>` for every tracked
satellite's forward track, or pin to a single satellite.
- **Dedup key shape:** `<norad_id>:<propagation_iso>` where
`propagation_iso` is the propagation start time truncated to whole
seconds. Matches the `sat_positions` convention.
- **Severity:** always 1 (informational telemetry).
- **Geo:**
- `centroid = (current_lon_deg, current_lat_deg)` — first vertex of the
track, anchoring the "here's where it IS" dot on the map.
- `geometry` = GeoJSON `LineString` for the common case (no
antimeridian crossing), or `MultiLineString` when the forward track
crosses ±180° (polar orbits, mid-Pacific orbits). Antimeridian
splitting lives in `sat_common.split_antimeridian` and is shared
with `satpass_predict`'s ground-track render — v0.13.0 also fixes
the v0.11.2 satpass_predict "wrong-way wrap" bug as a sibling concern.
- **Event.data fields:** `norad_id`, `satellite_name`,
`propagation_start_iso`, `forward_minutes`, `sample_seconds`,
`vertex_count`, `current_lon_deg`, `current_lat_deg`, `current_alt_km`,
`tle_epoch`.
- **Cadence:** 300s (5 min) default. Lower than `sat_positions`' 60s
because a forward LineString covers ~90 minutes of orbit; the
trajectory doesn't change meaningfully tick-to-tick at that horizon.
- **Settings:** `track_only_norad_ids` (empty = all fresh TLEs;
non-empty pins to those IDs), `forward_minutes = 90` (~1 LEO orbit),
`sample_seconds = 60` (~90 vertices per event), `max_tle_age_days = 14`.
- **GUI rendering:** sat_orbits events are colored **per-NORAD-ID** in
the events map (golden-angle HSL hue distribution off `norad_id`) so
multiple tracked satellites render as distinguishable lines. Other
adapters keep their existing per-adapter palette color.
\
---