mirror of
https://github.com/smittix/intercept.git
synced 2026-04-26 07:40:01 -07:00
Add ground station automation with 6-phase implementation
Phase 1 - Automated observation engine: - utils/ground_station/scheduler.py: GroundStationScheduler fires at AOS/LOS, claims SDR, manages IQBus lifecycle, emits SSE events - utils/ground_station/observation_profile.py: ObservationProfile dataclass + DB CRUD - routes/ground_station.py: REST API for profiles, scheduler, observations, recordings, rotator; SSE stream; /ws/satellite_waterfall WebSocket - DB tables: observation_profiles, ground_station_observations, ground_station_events, sigmf_recordings (added to utils/database.py init_db) - app.py: ground_station_queue, WebSocket init, scheduler startup in _deferred_init - routes/__init__.py: register ground_station_bp Phase 2 - Doppler correction: - utils/doppler.py: generalized DopplerTracker extracted from sstv_decoder.py; accepts satellite name or raw TLE tuple; thread-safe; update_tle() method - utils/sstv/sstv_decoder.py: replace inline DopplerTracker with import from utils.doppler - Scheduler runs 5s retune loop; calls rotator.point_to() if enabled Phase 3 - IQ recording (SigMF): - utils/sigmf.py: SigMFWriter writes .sigmf-data + .sigmf-meta; disk-free guard (500MB) - utils/ground_station/consumers/sigmf_writer.py: SigMFConsumer wraps SigMFWriter Phase 4 - Multi-decoder IQ broadcast pipeline: - utils/ground_station/iq_bus.py: IQBus single-producer fan-out; IQConsumer Protocol - utils/ground_station/consumers/waterfall.py: CU8→FFT→binary frames - utils/ground_station/consumers/fm_demod.py: CU8→FM demod (numpy)→decoder subprocess - utils/ground_station/consumers/gr_satellites.py: CU8→cf32→gr_satellites (optional) Phase 5 - Live spectrum waterfall: - static/js/modes/ground_station_waterfall.js: /ws/satellite_waterfall canvas renderer - Waterfall panel in satellite dashboard sidebar, auto-shown on iq_bus_started SSE event Phase 6 - Antenna rotator control (optional): - utils/rotator.py: RotatorController TCP client for rotctld (Hamlib line protocol) - Rotator panel in satellite dashboard; silently disabled if rotctld unreachable Also fixes pre-existing test_weather_sat_predict.py breakage: - utils/weather_sat_predict.py: rewritten with self-contained skyfield implementation using find_discrete (matching what committed tests expected); adds _format_utc_iso - tests/test_weather_sat_predict.py: add _MOCK_WEATHER_SATS and @patch decorators for tests that assumed NOAA-18 active (decommissioned Jun 2025, now active=False) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -636,6 +636,82 @@ def init_db() -> None:
|
||||
VALUES ('40069', 'METEOR-M2', NULL, NULL, 1, 1)
|
||||
''')
|
||||
|
||||
# =====================================================================
|
||||
# Ground Station Tables (automated observations, IQ recordings)
|
||||
# =====================================================================
|
||||
|
||||
# Observation profiles — per-satellite capture configuration
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS observation_profiles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
norad_id INTEGER UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
frequency_mhz REAL NOT NULL,
|
||||
decoder_type TEXT NOT NULL DEFAULT 'fm',
|
||||
gain REAL DEFAULT 40.0,
|
||||
bandwidth_hz INTEGER DEFAULT 200000,
|
||||
min_elevation REAL DEFAULT 10.0,
|
||||
enabled BOOLEAN DEFAULT 1,
|
||||
record_iq BOOLEAN DEFAULT 0,
|
||||
iq_sample_rate INTEGER DEFAULT 2400000,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
|
||||
# Observation history — one row per captured pass
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS ground_station_observations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
profile_id INTEGER,
|
||||
norad_id INTEGER NOT NULL,
|
||||
satellite TEXT NOT NULL,
|
||||
aos_time TEXT,
|
||||
los_time TEXT,
|
||||
status TEXT DEFAULT 'scheduled',
|
||||
output_path TEXT,
|
||||
packets_decoded INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (profile_id) REFERENCES observation_profiles(id) ON DELETE SET NULL
|
||||
)
|
||||
''')
|
||||
|
||||
# Per-observation events (packets decoded, Doppler updates, etc.)
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS ground_station_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
observation_id INTEGER,
|
||||
event_type TEXT NOT NULL,
|
||||
payload_json TEXT,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (observation_id) REFERENCES ground_station_observations(id) ON DELETE CASCADE
|
||||
)
|
||||
''')
|
||||
|
||||
# SigMF recordings — one row per IQ recording file pair
|
||||
conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS sigmf_recordings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
observation_id INTEGER,
|
||||
sigmf_data_path TEXT NOT NULL,
|
||||
sigmf_meta_path TEXT NOT NULL,
|
||||
size_bytes INTEGER DEFAULT 0,
|
||||
sample_rate INTEGER,
|
||||
center_freq_hz INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (observation_id) REFERENCES ground_station_observations(id) ON DELETE SET NULL
|
||||
)
|
||||
''')
|
||||
|
||||
conn.execute('''
|
||||
CREATE INDEX IF NOT EXISTS idx_gs_observations_norad
|
||||
ON ground_station_observations(norad_id, created_at)
|
||||
''')
|
||||
|
||||
conn.execute('''
|
||||
CREATE INDEX IF NOT EXISTS idx_gs_events_observation
|
||||
ON ground_station_events(observation_id, timestamp)
|
||||
''')
|
||||
|
||||
logger.info("Database initialized successfully")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user