Three pages updated: home, getting-started, TUI guide. All now show uvx as the primary install method with tabbed alternatives for pip install and from-source. Documents the camera extra and lists all three PyPI packages.
248 lines
13 KiB
Plaintext
248 lines
13 KiB
Plaintext
---
|
||
title: "The Birdcage TUI"
|
||
description: A six-screen terminal interface for controlling Winegard satellite dishes — from a $75 RV salvage find to something that looks like it belongs in a ground station.
|
||
sidebar:
|
||
order: 8
|
||
badge:
|
||
text: New
|
||
variant: tip
|
||
---
|
||
|
||
import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
|
||
|
||
Mission control for less than dinner for two.
|
||
|
||

|
||
|
||
Somewhere inside every Winegard Carryout G2 is a 2013-vintage NXP Cortex-M4 running firmware 02.02.48,
|
||
driving two Allegro A3981 stepper motors and a Broadcom BCM4515 DVB-S2 tuner through 12 submenus
|
||
and over 100 undocumented commands. In 2026, it takes commands from a Python TUI built on
|
||
[Textual](https://textual.textualize.io/) and doesn't seem to mind.
|
||
|
||
Gabe Emerson ([KL1FI](https://github.com/saveitforparts)) went first — publishing control scripts
|
||
for five different Winegard variants, proving that a $50–200 RV salvage yard dish could replace
|
||
a $500–2000 commercial amateur radio rotator. Chris Davidson ([cdavidson0522](https://github.com/cdavidson0522/winegard-sky-scan))
|
||
figured out the G2's RS-422 wiring and discovered `azscanwxp` — the firmware's built-in radio telescope mode.
|
||
Birdcage is what happens when you take all of that and give it a proper interface.
|
||
|
||
## Quick Start
|
||
|
||
<Tabs>
|
||
<TabItem label="uvx (no clone needed)">
|
||
```bash
|
||
# Demo mode — no hardware, no clone, no install
|
||
uvx birdcage-tui --demo
|
||
|
||
# With camera capture support (Pillow + astropy)
|
||
uvx --with 'birdcage-tui[camera]' birdcage-tui --demo
|
||
|
||
# Connect to a real dish
|
||
uvx birdcage-tui --port /dev/ttyUSB0 --firmware g2
|
||
```
|
||
</TabItem>
|
||
<TabItem label="From source">
|
||
```bash
|
||
# Clone and install
|
||
cd tui/
|
||
uv sync
|
||
|
||
# Demo mode
|
||
uv run birdcage-tui --demo
|
||
|
||
# Connect to a real dish
|
||
uv run birdcage-tui --port /dev/ttyUSB0 --firmware g2
|
||
```
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
### Extras
|
||
|
||
The base install includes everything except image processing. Optional extras add camera capabilities:
|
||
|
||
| Extra | Install | What it adds |
|
||
|-------|---------|-------------|
|
||
| *(base)* | `uvx birdcage-tui` | Full TUI, all 6 screens, demo mode |
|
||
| `camera` | `uvx --with 'birdcage-tui[camera]' birdcage-tui` | JPEG annotation (Pillow) + FITS export for radio astronomy (astropy) |
|
||
|
||
The camera screen (F6) works without the `camera` extra — it falls back to minimal JPEG output. The extra adds annotated frames and FITS file export for integration with DS9, CASA, and other astronomy tools.
|
||
|
||
<Aside type="tip">
|
||
Demo mode simulates a complete Carryout G2 — motor physics at 10°/s with settling noise,
|
||
a Gaussian RSSI signal model, and all 12 firmware submenus. Every screen works, every button
|
||
does something. You don't need a dish on the roof to try it.
|
||
</Aside>
|
||
|
||
## Six Screens
|
||
|
||
Navigate between screens with **F1**–**F4** keys, plus **F5** and **F6** overlays. Click the
|
||
tab bar at the top or use the hotkeys. The device status bar at the bottom persists across
|
||
all screens — connection state, serial port, firmware version, and current menu prompt are
|
||
always visible.
|
||
|
||
### F1 — Dashboard
|
||
|
||

|
||
|
||
The launch pad. Four action cards — **Point Dish**, **Monitor Signal**, **Scan Sky**, and
|
||
**Stow** — each jump to the relevant screen with a single click. It's the screen you see
|
||
on connect, and the one you come back to when you're done adjusting.
|
||
|
||
### F2 — Control
|
||
|
||

|
||
|
||
Where you actually point the dish. Four modes across the top: **Manual**, **Presets**, **Track**, and **Craft**.
|
||
Track mode starts a rotctld server on port 4533 so external controllers — [Gpredict](/guides/satellite-tracking/#configuring-gpredict) or a remote [Craft](https://space.warehack.ing) instance — can drive the dish with live satellite tracking.
|
||
Craft mode talks to the [space.warehack.ing](https://space.warehack.ing) API directly — no rotctld needed.
|
||
|
||
In Manual mode, a compass rose shows current azimuth with a bearing indicator. AZ and EL
|
||
readouts update live — the G2's stepper resolution is 0.009° azimuth (40,000 steps/rev)
|
||
and 0.014° elevation (24,960 steps/rev). Arrow keys nudge the dish, with a step size
|
||
selector (0.1° to 10°) that multiplies each keypress. AZ and EL sparklines give immediate
|
||
visual feedback: flat lines mean the dish is parked, slopes mean it's slewing, and the noise
|
||
floor after arrival tells you how much stepper backlash you're dealing with.
|
||
|
||
Below the sparklines, velocity controls let you tune motor speed on the fly — set AZ and EL
|
||
max velocity in °/s and hit Apply. The firmware stores these in STEP> as microsteps/sec,
|
||
but the TUI handles the conversion. Home AZ, Home EL, and E/R (engage/release motors)
|
||
round out the bottom toolbar.
|
||
|
||
#### Craft Mode
|
||
|
||

|
||
|
||
Craft mode connects directly to the [space.warehack.ing](https://space.warehack.ing) API for
|
||
satellite and celestial body tracking — no rotctld server, no external controller. Type a name
|
||
in the search bar and the catalog returns matches with NORAD ID, type, and group. Select a
|
||
target from the results table, then hit **Track** to start following it at 1 Hz.
|
||
|
||

|
||
|
||
The status panel shows the tracking lifecycle: **IDLE** (nothing selected), **TRACKING** (target
|
||
above minimum elevation, dish is moving), or **WAITING** (target below the minimum elevation
|
||
floor — the dish holds position until the target rises). Sat AZ and Sat EL update each second
|
||
with the target's computed position. The **Passes** button fetches upcoming pass predictions
|
||
for LEO targets — rise time, peak elevation, and duration.
|
||
|
||
Pass state transitions (WAITING → TRACKING at AOS, TRACKING → WAITING at LOS) feed into the
|
||
Camera overlay's automatic trigger system, so you can capture images at acquisition of signal,
|
||
closest approach, or loss of signal without manual intervention.
|
||
|
||
### F3 — Signal
|
||
|
||

|
||
|
||
Signal strength from two sources: the BCM4515 DVB tuner's RSSI (bounded, averaged)
|
||
and the raw ADC reading (single-shot). Three modes: **Monitor** (live gauges), **Sweep**
|
||
(automated peaking), and **Sky Map** (2D RSSI heatmap from `azscanwxp` scans).
|
||
|
||
In Monitor mode, the gauge uses sub-character Unicode block elements (▏▎▍▌▋▊▉█) for
|
||
smooth visual resolution. The Receiver panel alongside shows tuner frequency, symbol rate,
|
||
LNB voltage/polarity, lock status, and SNR. On a live dish with the LNA enabled
|
||
(`lnbdc odu`), the noise floor sits around RSSI 500 (ADC) or 230–490 (DVB, polarity-dependent).
|
||
|
||
### F4 — System
|
||
|
||

|
||
|
||
The system dashboard with three tabs: **Hardware**, **Motors**, and **NVS Config**.
|
||
|
||
The Hardware tab shows the firmware identity banner — version 02.02.48, K60 MCU at 96 MHz,
|
||
antenna ID "12-IN G2" — and A3981 stepper driver diagnostics: fault pin status for both
|
||
drivers (AZ/EL DIAG: OK or FAULT), step size mode (AUTO means the driver handles
|
||
microstepping transitions automatically), and current control mode.
|
||
|
||
The NVS Config tab provides a scrollable browser of all 134 non-volatile storage values.
|
||
Current, saved, and default columns let you see what's been modified. Refresh and Export
|
||
buttons at the bottom do what you'd expect.
|
||
|
||
### F5 — Console
|
||
|
||

|
||
|
||
Raw firmware console access with guardrails. Type commands directly to the dish's serial
|
||
interface, see responses with color-coded prompt tracking (`TRK>`, `MOT>`, `DVB>`, `NVS>`,
|
||
etc.). Command history with up/down arrows.
|
||
|
||
The safety gates matter here: the console warns before sending `q` at root level (which
|
||
kills the firmware shell — requires power cycle to recover), before `reboot`, and before
|
||
NVS writes. The firmware doesn't have an "are you sure?" prompt. Birdcage does.
|
||
|
||
### F6 — Camera
|
||
|
||

|
||
|
||
A modal overlay for image capture during satellite passes. Toggle it with **F6** or dismiss
|
||
with **Escape** — it floats over whichever screen you're on.
|
||
|
||
Three trigger modes:
|
||
|
||
- **Manual** — press **c** to capture a single frame on demand
|
||
- **Interval** — set a timer and capture automatically at a fixed cadence
|
||
- **Pass events** — fire on AOS (acquisition of signal), TCA (time of closest approach),
|
||
or LOS (loss of signal) transitions from the Craft or Track tracking loops
|
||
|
||
Each capture produces a JPEG frame paired with a JSON sidecar containing dish position,
|
||
target name, timestamp, trigger type, and tracking state at the moment of capture. The
|
||
status bar at the top of the overlay shows the active camera, capture count, last capture
|
||
time, output formats, and which triggers are armed.
|
||
|
||
<Aside type="tip">
|
||
Pass-event triggers work in demo mode — the simulated LEO satellites cycle through
|
||
AOS → TCA → LOS transitions, so you can test the full capture pipeline without hardware
|
||
or a live pass.
|
||
</Aside>
|
||
|
||
## Demo Mode
|
||
|
||
Pass `--demo` and Birdcage substitutes a `DemoDevice` for the serial bridge. The simulator models:
|
||
|
||
- **Motor physics** — position changes at ~10°/s with configurable settling noise
|
||
(±0.05° random perturbation to simulate stepper backlash)
|
||
- **RSSI signal** — Gaussian model centered on a target position, so signal strength
|
||
increases as you point closer to the simulated source
|
||
- **All 12 submenus** — `TRK>`, `MOT>`, `DVB>`, `NVS>`, `A3981>`, `ADC>`, `OS>`, `STEP>`,
|
||
`PEAK>`, `EE>`, `GPIO>`, `LATLON>`, `DIPSWITCH>`. Menu navigation with `mot`, `dvb`, `nvs`,
|
||
etc. and `q` to go back all work correctly
|
||
- **Full NVS dump** — the complete 134-entry table from firmware 02.02.48, captured from a
|
||
live unit on 2026-02-12
|
||
- **Satellite catalog** — 8 canned objects (ISS, NOAA 19, SO-50, TEVEL-2, AO-91, Moon, Sun,
|
||
Jupiter) with time-varying LEO arcs that cycle through AOS → TCA → LOS transitions on
|
||
~10-minute periods. Camera pass-event triggers fire on these transitions.
|
||
|
||
Every screen, every widget, every button works in demo mode. It's the same code path —
|
||
the only difference is what's on the other end of the bridge.
|
||
|
||
## The Story
|
||
|
||
The hardware was never the hard part. RV satellite dishes show up at salvage yards for
|
||
$50–200 because the RV got totaled or the owner switched to Starlink. The mechanicals are
|
||
built to survive highway speeds and hailstorms. The motors are Allegro A3981 stepper drivers
|
||
with 1/16 microstepping — more precise than most amateur radio rotators at ten times the price.
|
||
|
||
The gap was always software.
|
||
|
||
Gabe Emerson (KL1FI) bridged it first. Five repositories for five Winegard variants — the
|
||
Trav'ler (HAL 0.0.00 and HAL 2.05), the Trav'ler Pro, the original Carryout, and the
|
||
Carryout G2. Python scripts that send `a 0 180` to point azimuth south and `a 1 45` to tilt
|
||
elevation to 45°. A rotctld bridge so Gpredict could drive the dish. Proof that the idea
|
||
worked.
|
||
|
||
Chris Davidson took the G2 further — mapped the RS-422 wiring (four wires, not two, and
|
||
polarity matters or you get garbled data at the correct baud rate), discovered the `azscanwxp`
|
||
command buried in the motor submenu, and turned a TV satellite dish into an RF imager.
|
||
|
||
Birdcage picks up where they left off. We reverse-engineered over 100 commands across
|
||
12 firmware submenus using automated probing and interactive exploration. We documented
|
||
the full NVS table (134 entries), the GPIO pin map, the SPI bus layout (4 MHz to the motor
|
||
drivers, 6.857 MHz to the DVB tuner), the DiSEqC 2.x interface, and the boot sequence from
|
||
bootloader through motor calibration to prompt.
|
||
|
||
The TUI is what ties it together. Not because a terminal interface is fashionable, but because
|
||
when you're on a roof with a laptop and a USB-to-RS422 adapter, you want something that runs
|
||
over SSH and shows you everything at once.
|
||
|
||
A 2013 microcontroller. A 2026 terminal. A dish that costs less than the cable to connect it.
|
||
|
||
That's the project.
|