Move bridge.py, demo.py, craft_client.py from tui/src/birdcage_tui/ to
src/birdcage/ so both TUI and MCP server can share the device layer
without a circular dependency on textual.
Automated deep probe of Winegard firmware console interfaces.
Discovers available commands via help parsing, submenu probing,
and wordlist brute-force. Handles prompt-terminated serial I/O
with the > character termination strategy.
Modules: serial_io (prompt-aware I/O), discovery (auto-discovery),
profile (DeviceProfile dataclass), report (JSON output), cli (argparse).
send_with_timeout now uses a 2s per-byte timeout with a deadline
loop instead of one long blocking read, checking a cancel event
between reads. SerialBridge.disconnect() sets the cancel event
before acquiring the lock, so a blocked firmware sweep aborts
within ~2s and releases the lock for clean port shutdown.
Adds send_with_timeout() to CarryoutG2Protocol for long-running
commands, and az_sweep_firmware() to both SerialBridge and DemoDevice.
Sweep and Sky Map modes now try the firmware path first (single
azscanwxp command, streaming results) and fall back to software
step-dwell-measure on error or when "Software mode" checkbox is
checked. Software sweep fixed to set EL once and move AZ only.
F1 Position (compass rose, motor control, sparklines),
F2 Signal (RSSI gauge with sub-char precision, DVB/ADC sparklines, LNA toggle),
F3 Scan (AZ/EL grid sweep with heatmap and CSV export),
F4 System (NVS table, A3981 diagnostics, motor dynamics),
F5 Console (raw serial terminal with prompt detection and safety gates).
Includes SerialBridge (thread-safe protocol wrapper), DemoDevice
(synthetic simulation for --demo mode), dark RF theme with rounded
borders and teal accents, and send_raw() on CarryoutG2Protocol.
The radome looks like a birdcage, ham operators call satellites
"birds", and it's a nod to saveitforparts saving dishes "for parts."
Package, CLI entry point, class names (BirdcageAntenna), env vars
(BIRDCAGE_PORT, etc.), and CLAUDE.md updated. Hardware references
(Winegard Trav'ler, Trav'ler Pro, Carryout G2) unchanged.
Position parser now matches the actual Angle[0]/Angle[1] format
instead of falling back to fragile raw-float extraction. RSSI parser
uses a proper named-group regex matching the real firmware output
format (Reads:<n> RSSI[avg: <v> cur: <v>]) — the old index-based
approach would fail on the actual 5-field response.
Motor test results: both axes move correctly, direction-dependent
overshoot of 0.01-0.06 degrees confirmed. DVB subsystem explored:
BCM4515 Rev B0, firmware v113.37, full command set documented
including DiSEqC 2.x, transponder scanning, and streaming AGC/SNR.
RSSI noise floor is ~500.
Extend the rotctld wire protocol with three new commands that enable
signal strength measurement through the Carryout G2's DVB subsystem:
- R [n]: Read RSSI (handles motor→dvb→rssi→motor menu dance internally)
- L: Enable LNA for signal reception (one-time pre-scan setup)
- D: Discover capabilities (returns CAPS:rssi,lna for G2, empty for others)
Non-G2 protocols return RPRT -6 (not available) for R and L commands.
The menu state invariant is maintained after every operation so P commands
continue to work between RSSI reads.
Implement CarryoutG2Protocol based on cdavidson0522/winegard-sky-scan:
prompt-terminated reads via '>' char, 115200 baud RS-422, h <id>
motor homing, DVB/RSSI signal strength measurement. Update CLAUDE.md
with G2 variant column, NVS index 20, dvb sub-commands, and wiring
differences. CLI now accepts --firmware g2.
HAL 0.0.00 kills the search task via the OS task manager (os -> kill Search)
rather than the ngsearch submenu used by HAL 2.05. Boot signal is NoGPS only.
Source: saveitforparts/Travler_Rotor upstream repo.
CLAUDE.md now includes firmware variant comparison table, full command
reference, NVS indices, RS-485 pinout, and cable wiring notes.
Extract Gabe Emerson's Trav'ler rotor scripts into a proper Python
library with firmware protocol abstraction (HAL 2.05 + HAL 0.0.00),
Hamlib rotctld TCP server, Click CLI, and isolated leap-frog algorithm
with the elevation copy-paste bug fixed.