TALOS-SatNOGS Integration Analysis¶
Version: 0.3.0 Date: April 2026 Author: Libre Space Foundation / TALOS Project Status: Active Research
1. Current Integration Points¶
TALOS consumes two SatNOGS APIs through a single consolidated client (shared/satnogs_client.py). All SatNOGS interactions across the web backend and the mission director route through the SatNOGSClient class, which provides caching, error handling, and connection pooling via a persistent requests.Session.
SatNOGS DB API (https://db.satnogs.org/api)¶
| Endpoint | Method | Purpose | TALOS Consumer |
|---|---|---|---|
/api/satellites/?status=alive |
get_satellites() |
Full satellite catalog sync | sync_satnogs_data() in core/main.py |
/api/tle/ (full catalog) |
get_all_tles() |
Bulk TLE download for sky scanning | sync_satnogs_data() startup sync |
/api/tle/?sat_id={id} |
get_tle(sat_id) |
Single-satellite TLE fetch | TLEManager.fetch() in director/tle_manager.py |
/api/tle/?norad_cat_id={id} |
Direct httpx call |
Public tracker TLE fetch | _fetch_tle_for_norad() in core/main.py |
/api/transmitters/?satellite__norad_cat_id={id} |
get_transmitters(norad_id) |
Transmitter metadata for campaigns | Campaign creation, legacy mission creation |
SatNOGS Network API (https://network.satnogs.org/api)¶
| Endpoint | Method | Purpose | TALOS Consumer |
|---|---|---|---|
/api/stations/{id}/ |
get_station_info(network_id) |
Station metadata during provisioning | provision_station() in core/main.py |
Health Check¶
The client exposes health_check() which pings GET /api/ on DB API and returns True if the response status is below 500. This is used for readiness probes.
2. Data Flow¶
Satellite Catalog Sync¶
On startup (deferred 30 seconds to avoid blocking the HTTP server), sync_satnogs_data() executes a full two-phase sync:
Phase 1 -- Satellite metadata:
1. Fetches GET /api/satellites/?status=alive&format=json (typically 3000-5000 entries)
2. Upserts each satellite into the satellitecache PostgreSQL table (SatelliteCache model)
3. Fields stored: sat_id (PK), name, norad_id (indexed), status, updated_at
4. Satellites without a norad_cat_id are skipped
Phase 2 -- TLE physics cache:
1. Fetches GET /api/tle/ (the entire TLE catalog, typically 8000+ entries)
2. Compiles EarthSatellite objects in chunks of 500 to bound peak RAM (~30 MB per chunk)
3. Atomically swaps the GLOBAL_SAT_REGISTRY list so readers always see a consistent snapshot
4. This registry powers the "Magic Find" sky-scanning feature
The sync can be triggered manually via POST /api/sync which runs sync_satnogs_data as a background task.
TLE Caching (Mission Director)¶
The TLEManager in director/tle_manager.py handles per-campaign TLE management:
- On first tick for a campaign,
MultiTLEManager.get_or_create()instantiates aTLEManagerand callsfetch(satnogs_id, name) fetch()callsSatNOGSClient.get_tle(sat_id)which hits/api/tle/?sat_id={id}- The response's first entry (most recent TLE) is compiled into an
EarthSatellite - TLE epoch is extracted and age is tracked with two thresholds:
TLE_WARN_AGE: 24 hours (warning log)TLE_CRITICAL_AGE: 72 hours (critical warning log)- On fetch failure, the cached satellite is returned as fallback
MultiTLEManager.cleanup_stale()is called each tick to free TLE managers for deactivated campaigns
Transmitter Metadata¶
When a campaign is created (POST /api/orgs/{slug}/campaigns):
norad_idis resolved fromsatnogs_idvia theSatelliteCachetable if not provided directlySatNOGSClient.get_transmitters(norad_id)fetches active transmitters from DB API- Only transmitters with
status == "active"are persisted - A safety cap of 50 transmitters prevents runaway storage for multi-transmitter satellites
- Each transmitter is stored as a
Transmitterrow linked to the campaign, preserving:uuid,description,type,uplink_low/high,downlink_low/high,mode,baud,status
Station Metadata¶
During station provisioning (POST /api/orgs/{slug}/stations):
- Operator provides a SatNOGS Network station ID
SatNOGSClient.get_station_info(network_id)fetchesGET /api/stations/{id}/from Network API- Station name, latitude, longitude, and altitude are extracted (with fallbacks for field name variations:
lon/lng,altitude/elevation) - A TALOS station record is created with auto-generated
station_idandapi_key - Station info is NOT cached -- it is a one-time fetch during provisioning
Public Satellite Tracker¶
The public tracker (/track route, public_track.html) fetches TLEs independently:
- Uses
httpx.AsyncClientdirectly (not the sharedSatNOGSClient) for async compatibility - Hits
GET /api/tle/?norad_cat_id={id}&format=jsonon each position request - Propagates the TLE to current time using Skyfield's SGP4 implementation
- Returns: lat, lon, altitude_km, velocity_km_s, norad_id, tle_age_hours
- The frontend polls this every 2 seconds and displays a TLE age warning when age exceeds 24 hours
- Quick-select buttons default to PHASMA-LAMARR (66673), PHASMA-DIRAC (66681), PeakSat (98378), and ISS (25544)
3. API Usage Patterns¶
Caching Strategy¶
The SatNOGSClient implements a thread-safe in-memory cache with per-key TTLs:
| Cache Key Pattern | TTL | Scope |
|---|---|---|
satellites:{status} |
15 min (default) | Full satellite catalog |
tle:all |
30 min (explicit) | Entire TLE catalog |
tle:{sat_id} |
15 min (default) | Single-satellite TLE |
transmitters:{norad_id} |
15 min (default) | Transmitter list per satellite |
| Station info | Not cached | One-time provisioning fetch |
The default cache_ttl is 900 seconds (15 minutes). The bulk TLE catalog uses an explicit 1800 seconds (30 minutes) since it is a large payload that changes infrequently.
Stale Data Fallback¶
On any request failure (timeout, connection error, HTTP error, invalid JSON), the client checks for a stale cache entry via _cache_get_stale(). This returns expired data with a warning log, providing graceful degradation when SatNOGS APIs are unreachable. This is critical for satellite tracking continuity -- a stale TLE is far better than no TLE.
Timeouts¶
| Operation | Timeout |
|---|---|
| Catalog fetches (satellites, all TLEs) | 45 seconds |
| Single-item fetches (one TLE, transmitters, station info) | 10 seconds |
| Health check | 10 seconds |
| Public tracker TLE fetch (httpx) | 10 seconds |
Rate Limiting¶
TALOS does not implement explicit rate limiting against SatNOGS APIs. The caching layer provides implicit rate reduction. The SatNOGS DB API is public and does not require authentication for read operations. The Network API station endpoint is also public. There is no API key configured in the client.
Error Handling¶
All requests exceptions are caught individually (Timeout, ConnectionError, HTTPError, RequestException, ValueError for JSON parse). Each is logged at ERROR level with the URL. The method returns None on failure after attempting the stale cache fallback.
4. Coexistence Strategy¶
Architecture: talos-agent Alongside satnogs-client¶
Both daemons can run on the same station host (Raspberry Pi or x86 PC):
Station Host
+-----------------------------------------------+
| |
| satnogs-client talos-agent |
| (GNU Radio, DSP, (MQTT, rotctld, |
| waterfall, upload) rig control) |
| | | |
| +--- rotctld (TCP 4533) -+ |
| +--- rigctld (TCP 4532) -+ |
| +--- SDR device --------+ |
| |
+-----------------------------------------------+
Resource Contention Points¶
- Rotator (rotctld): Both systems send az/el commands via TCP. Only one should control at a time.
- SDR device: USB SDR dongles (RTL-SDR, Airspy) support single-client access. Simultaneous use causes device busy errors.
- Rig control (rigctld): Frequency tuning conflicts if both try to set different frequencies.
Coordination Mechanism (Not Yet Implemented)¶
The recommended approach is a file-lock or MQTT-based mutex:
- When TALOS activates a campaign assignment for the station, it acquires the hardware lock
- satnogs-client checks the lock before starting an observation and defers if locked
- When the TALOS campaign deactivates, the lock is released
- During idle periods, satnogs-client operates normally with SatNOGS-scheduled observations
This coordination layer is identified as a Phase 2 integration item and is not yet built.
5. Feature Comparison¶
| Capability | SatNOGS Network | TALOS |
|---|---|---|
| Scheduling model | Pre-scheduled per-station jobs, polled | Real-time operator-directed, fleet-wide |
| Station coordination | None (stations are independent) | Central director drives all stations simultaneously |
| Doppler correction | Client-side per-station (GNU Radio) | Centralized physics engine, pushed via MQTT |
| Rotator tracking | Client-side via rotctld | Centralized az/el push at ~2 Hz |
| Signal processing | GNU Radio flowgraphs (gr-satnogs) | Not implemented |
| Data products | Waterfall PNG, audio OGG, decoded frames, HDF5 | None |
| Satellite database | SatNOGS DB (authoritative) | Consumes SatNOGS DB |
| Sky scanning | Not available | "Magic Find" -- 8000+ orbits scanned in real-time |
| Communication | HTTP polling (~60s intervals) | MQTT pub/sub (sub-second) |
| Station provisioning | Manual via satnogs-config | One-command deploy from SatNOGS Network ID |
| Multi-frequency coordination | Not possible | Different stations can monitor different transmitters |
| Pass handoff | Not possible | Designed for seamless station-to-station handoff |
What TALOS Adds¶
- Real-time multi-station coordination: SatNOGS treats each station as independent; TALOS orchestrates fleets
- Centralized physics engine: All stations share the same orbital model, eliminating per-station drift
- Sub-second command loop: MQTT-based az/el and Doppler push at 2 Hz
- Live mission control UI: Real-time satellite footprint, rotator telemetry, Doppler display
- Instant sky scanning: "What is overhead right now?" answered in milliseconds across 8000+ objects
6. Integration Gaps¶
Observation Scheduling¶
TALOS has no built-in scheduler. Campaigns are operator-activated, not time-scheduled. There is no integration with SatNOGS auto-scheduler or the Network's job dispatch system (GET /api/jobs/). Stations cannot currently accept SatNOGS-scheduled observations during TALOS idle time.
Waterfall and RF Data Capture¶
TALOS agents control rotators and tune radios but do not capture RF data. There is no IQ recording, waterfall generation, or audio capture. This means TALOS-driven passes produce no permanent data products and contribute nothing back to the SatNOGS observation archive.
Demodulation and Decoding¶
No demodulation pipeline exists. TALOS does not integrate with gr-satnogs flowgraphs or any alternative DSP stack. Decoded telemetry frames are not produced or submitted to the SatNOGS DB SiDS endpoint (POST /api/telemetry/).
Observation Submission¶
There is no mechanism to submit observation results to SatNOGS Network (POST /api/observations/). Even if RF data were captured, the upload pipeline is absent.
TLE Source Redundancy¶
TALOS relies exclusively on SatNOGS DB for TLEs. There is no fallback to CelesTrak, Space-Track, or other TLE sources. If SatNOGS DB is down, tracking degrades to stale cached TLEs.
Satellite Status Notifications¶
TALOS syncs the satellite catalog on startup and via manual trigger but does not detect status changes (new launches, re-entries, status updates) between syncs. There is no webhook or polling mechanism for incremental updates.
7. PHASMA Mission Context¶
The PHASMA mission consists of two CubeSats built by Libre Space Foundation:
- PHASMA-LAMARR (NORAD 66673) -- Default tracked satellite in the TALOS public tracker
- PHASMA-DIRAC (NORAD 66681) -- Second quick-select option
These are the primary operational targets for TALOS, which explains why the public tracker defaults to PHASMA-LAMARR rather than ISS.
How PHASMA Benefits from TALOS + SatNOGS Integration¶
Multi-station pass coverage: PHASMA CubeSats in LEO have short pass windows (typically 5-12 minutes). TALOS can coordinate multiple ground stations to track the same pass simultaneously, increasing total contact time through geographic diversity and enabling handoff tracking as the satellite moves between station footprints.
Centralized Doppler for command uplink: When commanding PHASMA satellites, precise Doppler correction is critical. TALOS computes Doppler centrally and pushes corrections to all stations, ensuring consistent frequency tracking across the fleet.
Transmitter metadata from SatNOGS DB: PHASMA transmitter frequencies, modes, and baud rates are maintained in SatNOGS DB. When a TALOS campaign is created for PHASMA-LAMARR or PHASMA-DIRAC, transmitter data is automatically fetched and linked to the campaign, providing operators with accurate frequency information.
TLE quality monitoring: The TLEManager tracks TLE age and warns at 24h and 72h thresholds. For active PHASMA operations, this alerts operators before orbital predictions degrade significantly.
Public visibility: The public tracker at /track?norad=66673 provides a zero-login satellite tracking page powered by SatNOGS TLEs, useful for outreach and public engagement around the PHASMA mission.
Current Gaps for PHASMA Operations¶
- No telemetry decoding -- PHASMA telemetry frames received via SatNOGS Network stations cannot be correlated with TALOS tracking sessions
- No IQ capture during TALOS-driven passes -- RF data from PHASMA passes is lost
- No integration with PHASMA mission control systems beyond basic tracking
8. Recommendations¶
Priority 1: TLE Source Redundancy (Low effort, high resilience)¶
Add CelesTrak as a fallback TLE source in SatNOGSClient. When SatNOGS DB returns no data for a satellite, fall back to https://celestrak.org/NORAD/elements/gp.php?CATNR={norad_id}&FORMAT=TLE. This eliminates the single-point-of-failure on SatNOGS DB for orbital data.
Priority 2: Public Tracker TLE Caching (Low effort, reduces API load)¶
The public tracker's _fetch_tle_for_norad() bypasses the shared SatNOGSClient and uses raw httpx with no caching. Every 2-second frontend poll triggers a fresh API call. Refactor to use SatNOGSClient.get_tle() with its 15-minute cache, or add a simple in-memory cache to the async path.
Priority 3: Station Hardware Mutex (Medium effort, enables coexistence)¶
Implement a lock file or MQTT-based coordination protocol so talos-agent and satnogs-client can share station hardware without conflicts. This is the critical enabler for Phase 2 coexistence. Suggested approach: talos-agent writes a lock to /tmp/talos-station.lock when active; a wrapper script for satnogs-client checks this lock before starting observations.
Priority 4: Lightweight Waterfall Capture (Medium effort, high value)¶
Add optional IQ recording to talos-agent during active tracking passes using SoapySDR. Post-process with a simple FFT pipeline (numpy/scipy, no GNU Radio) to generate waterfall PNGs. This produces the first TALOS data products without the heavyweight gr-satnogs dependency.
Priority 5: SatNOGS Observation Submission (Medium effort, community value)¶
Build an upload pipeline to submit TALOS-generated waterfalls and metadata to SatNOGS Network via POST /api/observations/. This requires a SatNOGS Network API token per station and proper observation metadata formatting. Every TALOS pass would then contribute to the global open dataset.
Priority 6: Idle-Time SatNOGS Job Acceptance (High effort, full integration)¶
When no TALOS campaign is active, poll GET /api/jobs/?ground_station={id} for pending SatNOGS observations and execute them. This makes TALOS stations useful contributors to the SatNOGS network during idle periods and maximizes station utilization.
Design Principles¶
- Never duplicate SatNOGS DB. It is the authoritative satellite catalog. Always consume, never fork.
- Contribute data back. Every TALOS observation should eventually produce data that flows to the open commons.
- Coexist, do not compete. Stations should serve both TALOS and SatNOGS without conflict.
- Keep the agent lightweight. Run
satnogs-clientalongsidetalos-agentrather than reimplementing GNU Radio integration. - TALOS adds the real-time layer. SatNOGS is batch-oriented; TALOS is real-time. These are complementary, not competing.
Data Model Reference¶
SatelliteCache (PostgreSQL)¶
| Column | Type | Notes |
|---|---|---|
sat_id |
str (PK) |
SatNOGS satellite identifier |
name |
str (indexed) |
Satellite display name |
norad_id |
int (indexed) |
NORAD catalog number |
status |
str |
SatNOGS status (alive, re-entered, etc.) |
updated_at |
datetime |
Last sync timestamp |
Transmitter (PostgreSQL)¶
| Column | Type | Notes |
|---|---|---|
id |
int (PK) |
Auto-increment |
campaign_id |
int (FK, nullable) |
Link to Campaign |
uuid |
str (indexed) |
SatNOGS transmitter UUID |
description |
str |
Transmitter description from SatNOGS |
type |
str |
Transmitter/Transceiver/Transponder |
uplink_low/high |
BigInteger |
Uplink frequency range (Hz) |
downlink_low/high |
BigInteger |
Downlink frequency range (Hz) |
mode |
str |
Modulation mode (FM, BPSK, etc.) |
baud |
float |
Baud rate |
status |
str |
Active/inactive from SatNOGS |
Campaign (PostgreSQL)¶
| Column | Type | Notes |
|---|---|---|
satnogs_id |
str (indexed, nullable) |
SatNOGS satellite ID for TLE resolution |
norad_id |
int |
NORAD catalog number |
transmitter_uuid |
str (nullable) |
Selected transmitter for operations |