BIRDS-RPM satellite ADCS (Attitude Determination and Control System) ground station software — receives, decodes, visualizes in real time, and records telemetry packets. Works with real hardware (COM Port), the built-in simulated data (Demo), or replaying an existing log file (Replay).
The UI is a local web page (Flask + Plotly.js); open it in a native window, or just connect with a browser.
📖 Documentation site — installation, usage, configuration, and FAQ.
About the packet format: the
C_packet_def.pyshipped with this repo is a generic demo format (13-byte / 9-byte), letting the program run out of the box and show the full UI behavior, while also serving as a template for "how to write your own field definitions when hooking up your own hardware." To hook up your own hardware, onlyC_packet_def.pyneeds to change (see "Hook Up Your Own Hardware" below).
pip install -r requirements.txtNative window (optional): additionally
pip install pywebview, requiring Python 3.11 or 3.12. Python 3.13+ currently has no available pythonnet wheel; the app still runs without it, falling back to the default browser automatically.
The time-series charts use Plotly.js, embedded offline to avoid a network
dependency. Place plotly.min.js in the same directory as main.py:
curl -o plotly.min.js https://cdn.plot.ly/plotly-2.27.0.min.jsWithout this file, the chart area shows "plotly.min.js not loaded"; everything else still works.
# Standard mode (opens a native window if pywebview is installed; otherwise opens a browser automatically)
python main.py
# Demo mode: no COM Port needed, view simulated data directly
python main.py --demo
# No-window mode: Flask only, connect manually via browser at http://127.0.0.1:5000/
python main.py --no-window
# The two flags can be combined
python main.py --no-window --demoFor a first try, running python main.py --demo is recommended — the full UI is visible without any hardware.
- For the full environment requirements, install steps, and common install issues, see Installation
| Feature | Description |
|---|---|
| Serial connection | pick a COM Port + baud rate, click Connect |
| Demo mode | built-in sin + noise fake packets, no hardware needed |
| Replay mode | replay an existing log file; auto-detects a pure binary packet file / a text log with trigger lines / the legacy line-by-line hex format |
| Replay speed | the interval between packets is adjustable, can be changed live during playback (0 = full speed) |
| Multiple packet formats | supports defining several lengths of format at once; [TM v] can auto-detect or lock to one |
| Live charts | Plotly.js category tabs; X axis shows HH:MM:SS, zoom can be locked, choose to display 50/100/200/500 points |
| Parameter list | lists every field on the left, with both the decoded value and the raw HEX value; updates every second |
| HEX / Raw Text | bottom tab switch: HEX dump (16 bytes per line) or the source's raw text lines |
| CSV export | written in real time, the output path can be switched anytime without interrupting writes; utf-8-sig, opens in Excel without garbled text |
| Save Raw TXT | separately saves the undecoded raw text line by line into a .txt file, complementing the CSV |
| Clear DB | clears the in-memory history and counters (does not delete the CSV file) |
- For detailed usage of every feature (interface overview, status bar, chart controls, etc.), see Usage
The program expects packets shaped like this:
- Starts with a Header byte (default
0xEC) - Ends with a Footer byte (default
0xCE), always the last byte of the packet - In between are each field's data, whose length and position are defined by
C_packet_def.py
The Header/Footer values are set in C_config.py. Multiple lengths of
format can be defined at once; the program automatically selects the
matching field table based on the length actually received (or one can be
locked in the UI).
The demo format included (C_packet_def.py):
| byte | field | type |
|---|---|---|
| 0 | Header | 0xEC |
| 1-2 | Counter | unsigned 16-bit (LE) |
| 3-4 | Temperature | signed 16-bit (LE), unit 0.01 degC |
| 5 | Status flags | unsigned 8-bit |
| 6-7 | Voltage | unsigned 16-bit (LE), unit mV |
| 8-11 | Uptime | unsigned 32-bit (LE), unit ms (only present in the 13-byte format) |
| 12 | Footer | 0xCE |
The 9-byte short format's first 8 bytes are identical; it simply has no
"Uptime" field, with the footer directly following byte 8. This is
deliberate, demonstrating how the CSV and UI fields stay stable when
"different formats have a different number of fields" (a field missing from
the shorter format is filled with None; the column set never grows or shrinks).
The program automatically recognizes these sources:
| Source | Description |
|---|---|
| Pure binary packet file | the whole file is a run of raw packet bytes, back-to-back |
| Text log with trigger lines | e.g. Telemetry sent (13 bytes): followed by HEX, or the ==== TM dump (N bytes) ==== format with an offset prefix |
| Line-by-line hex text | one packet's HEX string per line |
The trigger-line matching rules are the regular expressions in
C_sources.py; edit them there to match your own hardware's output format.
- Want to hook up your own hardware and edit the packet fields? The full guide is at Configuration