-
Notifications
You must be signed in to change notification settings - Fork 1
Core Program Architecture
Pi-Sat uses a simple split:
-
frontend/: browser UI -
pi_sat_controller/backend/: API, tracking logic, device control, orbital calculations, and data refresh
The browser never talks directly to radios, SDRs, or rotators. All hardware interaction is centralized in the backend.
The project is built this way for a few reasons:
- hardware access belongs on the Pi, not in the browser
- tracking logic should exist in one authoritative place
- the frontend should stay stateless enough to recover cleanly after refreshes
- radio, SDR, and rotator control need shared coordination rules rather than scattered UI logic
This keeps the control path predictable and easier to troubleshoot.
The frontend is intentionally small:
-
frontend/index.html- page shell
- major dashboard/page layout
- footer and global containers
-
frontend/style.css- application styling
- layout, sizing, tables, cards, controls, and map styling
-
frontend/app.js- API polling
- state management for selected satellite/profile/pass
- rendering for dashboard, satellites page, map page, monitor page, and settings
- autotrack UI interactions
- map and pass arc drawing
The frontend is responsible for presentation and operator actions. It does not own orbital truth or device state.
-
backend/app.py- FastAPI app
- startup/shutdown
- scheduler threads
- shared runtime caches
- API endpoints
- orchestration between the other modules
-
backend/config.py- loads and saves the main config file
- loads tracked satellites
- preserves the settings page schema
- handles multi-URL TLE source storage
-
backend/orbital/skyfield_engine.py- satellite position lookup
- ground track generation
- pass prediction
-
backend/orbital/doppler.py- raw Doppler math
This layer turns TLE data into positions, velocities, pass windows, and tracking geometry.
-
backend/controller/rx_tracking.py- live RX/TX tracking loop
- user offset preservation
- Doppler application
- rotator coordination during active passes
-
backend/controller/frequency_planner.py- uplink/downlink offset mapping rules
-
backend/controller/safety.py- TX inhibit checks
This is the behavioral core of the app.
-
backend/radio/- Hamlib radio control wrappers
- local
rigctldmanagement - network
rigctldclient support
-
backend/sdr/- polled SDR readback and write path
This layer isolates device communication from tracking logic.
-
backend/rotator/- network
rotctldsupport - local
rotctldsupport - rotator model loading
- pass-active/manual-control policy
- network
The rotator path is intentionally separate from radio/SDR control because the command rules are different. The goal was to break these things out to allow them to be independent.
-
backend/satellites/tle_manager.py- TLE download, merge, cache, and parse
-
backend/satellites/transponder_source_client.py- pulls transponder data
-
backend/satellites/satellite_profiles.py- reads/writes satellite profile data
- TLE data is loaded or refreshed
- The Skyfield engine builds orbital objects from the merged TLE cache
- Tracked satellites are loaded from config
- Passes are generated and cached
- The frontend reads the cached pass list through the API
- The selected satellite/profile is established
- The backend tracking loop reads current satellite position
- Doppler is calculated from range rate
- RX target is built from center frequency plus Doppler plus user offset
- TX target is built if the profile is not RX-only
- SDR and radios are updated through the backend managers
- Rotator targets are sent only when pass logic allows it
- The frontend polls and renders the current state
The browser does not calculate Doppler or pass state. The backend does. This avoids divergent logic between UI and hardware control.
User offset is preserved as an explicit operator input. Doppler changes move around that offset instead of resetting it every update.
When multiple TLE sources contain the same NORAD ID, Pi-Sat keeps the newest entry and ignores older duplicates. This avoids an issue where one TLE data set is pulled after another, contains older data, but someone gets prioritized at the front of the line.
Pi-Sat supports both:
- direct network access to existing
rigctld/rotctld - managed local Hamlib daemon startup for directly attached serial devices
That keeps the UI and tracking logic the same regardless of how hardware is attached.