35 Commits

Author SHA1 Message Date
bbdd406bca Configure git-lfs for binary assets
Track PDFs, images (PNG/JPG), SVGs, and 3D models (STEP/WRL) via
git-lfs to keep repo clone size manageable as datasheets and
extracted page images accumulate.
2026-02-14 18:36:23 -07:00
1c27a8d15d Add console-probe package for firmware console discovery
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).
2026-02-14 18:05:15 -07:00
13a9d804c6 Add G2 bootloader and EEPROM exploration scripts
boot_baud_probe.py: Probe all standard baud rates during G2 power-on
to detect bootloader UART configuration differences.

boot_capture.py: Capture and analyze G2 boot sequence output, with
optional interrupt sequence injection to explore bootloader entry.

ee_dump.py: Dump EEPROM indices from EE> submenu, identifying
initialized vs. uninitialized (0x10101 sentinel) entries.
2026-02-14 18:05:03 -07:00
145763fcfb Rebuild TUI as 4-tab layout with 10 new widgets
Replace sidebar + 5-screen layout with horizontal tab bar (F1-F4)
and persistent StatusStrip. Consolidate Position + Scan screens into
Signal (F3) with Monitor/Sweep/Sky Map sub-modes via ModeBar.

Screens:
  - F1 Dashboard: system health, tracking panel, quick actions, presets
  - F2 Control: motor tuning, compass rose, preset management
  - F3 Signal: RSSI monitor, 1D sweep (SweepPlot), 2D sky map (heatmap)
  - F4 System: NVS editor with regex filter, EEPROM, firmware info
  - F5 Console: push/pop overlay (no longer a tab)

New widgets: StatusStrip, ModeBar, SweepPlot, QuickActions, PresetList,
ReceiverInfo, MotorTuning, NvsFilter, SystemHealth, TrackingPanel.

Removed: PositionScreen, ScanScreen, DeviceStatusBar (functionality
absorbed into new screens and StatusStrip).

App-level position poll feeds StatusStrip and active screen at ~2 Hz.
Fix shared threading.Event across instances (class-level mutable default).
2026-02-14 18:04:50 -07:00
e7e71c47d7 Wire Stop button to cancel blocked firmware sweeps
Stop handler now calls cancel_operation() on the device bridge,
which sets a threading.Event that interrupts the 2s-timeout serial
read loop in send_with_timeout(). InterruptedError is caught
separately to prevent falling back to software sweep on cancel.

disconnect() uses acquire(timeout=5) with force-close fallback
instead of blocking lock acquisition — prevents deadlock when a
stuck worker holds the serial lock during shutdown.

Add 3 Textual async tests (pytest-asyncio) to verify Stop behavior:
firmware sweep stop, software sweep stop, and sweep restart.
2026-02-14 17:12:11 -07:00
972c26b22f Guard finally blocks against app teardown during Ctrl+C
The sweep/scan finally blocks call self.app.call_from_thread() to
reset button state, but self.app raises NoActiveAppError if the
Textual context is already torn down during Ctrl+C shutdown.
Wrap with contextlib.suppress so the flag reset still happens.
2026-02-14 16:58:21 -07:00
c6ac958ee8 Make firmware sweep cancellable to prevent shutdown deadlock
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.
2026-02-14 16:56:14 -07:00
2ee2f47275 Fix sweep/scan Stop button and state cleanup
_sweeping/_scanning flags were never reset when workers finished,
leaving the UI stuck in "Stopping..." forever. Both _do_sweep and
_do_scan now use try/finally to always clear state and reset button
styles. Firmware sweep checks the flag after the blocking serial
call returns and discards results if Stop was pressed mid-execution.
2026-02-14 16:50:08 -07:00
3cd6424168 Wire firmware-accelerated AZ sweep via azscanwxp
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.
2026-02-14 16:40:53 -07:00
ba8859cc31 Fix 300s executor shutdown with threading.Event
Replace time.sleep() with threading.Event.wait() in all poll
loops so worker threads exit immediately on shutdown instead of
blocking for up to 500ms per iteration. Fixes the on_unmount
crash (NoMatches from querying removed DOM nodes) by signaling
the event directly rather than iterating child widgets.

Three shutdown paths covered: q key (on_unmount), Ctrl+C
(try/finally in main), and Textual internal shutdown.
2026-02-14 10:21:42 -07:00
48746937a7 Fix executor shutdown warning on TUI exit
Poll threads (position at 2 Hz, signal when monitoring) run
while-loops with time.sleep() that never exit on app quit.
Add on_unmount() to PositionScreen, SignalScreen, and BirdcageApp
to clear polling flags and disconnect the device, so worker threads
exit within the sleep interval instead of hitting the 300s timeout.
2026-02-14 09:41:47 -07:00
83c1f79caf Document birdcage.warehack.ing deployment in CLAUDE.md
Repo, domain, server path, make targets, and deploy workflow for
the Astro/Starlight docs site hosted via caddy-docker-proxy.
2026-02-13 15:17:00 -07:00
7271b53c63 Add Birdcage TUI: 5-screen Textual interface for Carryout G2
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.
2026-02-13 08:53:03 -07:00
a70b9b0a29 Ignore site/ directory — now its own repo (warehack.ing/birdcage-docs) 2026-02-13 05:20:34 -07:00
7ba0a99279 Merge feature/skyscan-rssi: G2 sky-scan RSSI, console-probe, birdcage rename 2026-02-13 05:16:10 -07:00
a2e807f973 Rename project from travler-rotor to birdcage
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.
2026-02-13 05:16:00 -07:00
c010cee282 Document full G2 command inventory from interactive submenu exploration
All 12 submenus explored via live hardware ? help. Key findings:
- A3981: 6 cmds (sm/ss/st for microstep and torque control)
- ADC: 5 cmds (bdid=STATIONARY, bdrevid=A, scan deadlock hazard)
- MOT: 25 cmds (azscanwxp radio telescope, pid tuning, vms velocity)
- DVB: 38 cmds (full DiSEqC 2.x suite, blind scan, NID streaming)
- PEAK: 6 cmds (rssits polarity-switching RSSI, H=489 V=235)
- GPIO: 4 cmds (regs dumps all 92 K60 pins across ports A-E)
- EEPROM: 3 cmds (inv=INVALIDATE not inventory, mostly unused)
- STEP: 7 cmds (raw ustep API, Kp=250 Kv=50, velocity/position)
- LATLON: satellite triangulation calculator (4-param, centidegrees)
- DIPSWITCH: raw GPIO + interpreted config (101=DISH 110+119+129)

Boot sequence enriched with SPI bus speeds and antenna ID string.
Added Known Console Hazards section (scan deadlock, q shell kill).
2026-02-12 23:24:42 -07:00
7ff91b08ea Refactor probe tool to generic embedded console scanner, document full G2 command inventory
Rewrote hidden_menu_probe.py from Winegard-hardcoded to auto-discovering:
detects prompt, error string, and submenu structure from any firmware console.
Extracted Winegard-specific candidate words to scripts/wordlists/winegard.txt.

Deep probe of all 12 G2 submenus discovered commands across A3981 (driver
diagnostics), ADC (RSSI monitoring + position sweep), DVB (extended help via
man, transponder selection), EEPROM (read/write), GPIO (pin R/W), LATLON
(calculator), MOT (azscan, sw), PEAK (EchoStar switch), and STEP (raw
stepper control). NVS submenu generates false positives — treats any input
as sequential index reads.

Safety: added q/Q to default blocklist, bare-CR check before navigate_to_root
to prevent accidental shell termination between submenus.
2026-02-12 21:05:33 -07:00
6b94f079aa Fix G2 position/RSSI parsers, document motor and DVB test results
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.
2026-02-12 09:34:42 -07:00
71ffafdd3f Document G2 firmware 02.02.48 findings from live hardware session
Confirmed via DSD TECH SH-U11 RS-422 adapter at 115200 baud:
- Firmware version 02.02.48, bootloader 1.01, Kinetis MCU, BCM4515 DVB
- Position format is Angle[0]/Angle[1], not AZ=/EL= like Trav'ler
- Prompts are TRK>/MOT>/NVS> (not bare >)
- NVS 20 disable tracker confirmed working after power cycle
- Full NVS dump captured (indices 0-143)
- RJ-12 wire colors documented (differ from Davidson's guide)
- RS-422 polarity swap symptoms documented (garbled RX vs silent TX)
- Cable wrap range confirmed: -423.33° to +23.33° (446.66° total)
2026-02-12 09:21:06 -07:00
7db4204d26 Parse GGA/GSA fix quality from NMEA, add PAIR command init sequence
Replace altitude-validity heuristic with authoritative GGA quality
field (SPS/DGPS/RTK) and GSA nav mode (2D/3D) via TinyGPSCustom
extractors. Send $PAIR062 commands at boot to filter NMEA output
to only GGA/GSA/RMC/GSV and configure PPS for fix-only pulses.
GpsPayload struct gains fix_quality field (16 -> 14 bytes packed).
2026-02-11 18:28:59 -07:00
80158e10d7 Fix GPS command protocol references: PAIR, not UBX/PMTK
The RYS352A uses Airoha AG3352 engine with $PAIR proprietary
commands. UBX is u-blox, PMTK is MediaTek — neither applies.
Also document TinyGPS++ v1.1 requirement for GN talker ID
(multi-constellation) support and reference the PAIR command guide.
2026-02-11 16:11:44 -07:00
f2c1eb84d2 Fix GPS baud rate: RYS352A defaults to 115200, not 9600
The AG3352 GNSS engine in the RYS352A ships at 115200 8N1 per
the datasheet spec table. 9600 was a generic assumption that
would cause the UART to read garbage and never acquire a fix.
2026-02-11 16:06:31 -07:00
f218cd468b Add GPS, IMU, and barometer sensor suite to BLE bridge firmware
RYS352A GPS on UART2 (GPIO5/6) with PPS interrupt (GPIO7),
MPU-9250 IMU and BMP388 barometer on shared I2C bus (GPIO8/9).
Sensor data exposed via dedicated BLE service with binary
notify characteristics alongside the existing NUS serial bridge.
Sensors degrade gracefully if not wired.
2026-02-11 15:47:20 -07:00
e05edb92a0 Document MPU-9250 and BMP388 sensor wiring for dish orientation
MPU-9250 provides magnetometer (auto north alignment), accelerometer
(elevation verification), and gyroscope (slew quality). BMP388 provides
pressure and temperature for atmospheric refraction correction at low
elevation angles. Both share I2C bus on GPIO8/9.
2026-02-11 14:51:48 -07:00
420a8e2039 Fix board ID and NimBLE 2.x API for ESP32-S3-N16R8
- Use esp32-s3-devkitc1-n16r8 board (16MB flash, 8MB PSRAM OPI)
  instead of generic esp32-s3-devkitc-1 (8MB, no PSRAM)
- Remove setScanResponse() — NimBLE 2.x dropped this method
- Board definition handles PSRAM build flags automatically

Verified: clean boot, no PSRAM errors, BLE advertising as Travler-G2.
2026-02-11 14:37:09 -07:00
068f38d7eb Add ESP32-S3 BLE-to-RS422 bridge firmware for Carryout G2
NimBLE-based Nordic UART Service (NUS) bridge on ESP32-S3-DevKitC-1.
Transparent passthrough: BLE client writes → UART1 TX → RS-422 → G2,
and G2 → RS-422 → UART1 RX → BLE notifications. USB serial serves as
debug monitor and fallback input.

Uses two MAX485 modules (one locked TX, one locked RX) with a SparkFun
BSS138 level converter for 3.3V/5V translation. Wiring schematic and
RJ-12 pinout documented in docs/ble-bridge-wiring.md.
2026-02-11 14:33:10 -07:00
1192b31166 Add R/L/D protocol extensions for RSSI sky scanning
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.
2026-02-11 11:55:05 -07:00
da066cfb3b Document RS-485 vs RS-422 signaling, connector pinouts, and MAX490 failsafe
Add comprehensive section covering half-duplex RS-485 vs full-duplex
RS-422 differences, dual-mode Trav'ler pinout (T/R + RXD pairs),
G2 wire color mapping, adapter chain table, and DIYables MAX490
module notes including bus tri-state failsafe concern with
workaround options.
2026-02-11 10:31:11 -07:00
e0488eb85a Add Carryout G2 as fifth firmware variant with protocol support
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.
2026-02-11 10:06:58 -07:00
b68bb1f6f4 Update Carryout specs from Radio-Telescope repo findings
Correct Carryout elevation limits (22 deg firmware-enforced min,
73 deg firmware default max), fix dvb command not being Pro-only,
add DIP switch and motor stall calibration notes, add
Carryout-Radio-Telescope to upstream references.
2026-02-11 06:32:44 -07:00
579bad9921 Expand docs with full Winegard hardware reference
Add Carryout and Trav'ler Pro to firmware variant table, SK-1000
physical specs, error messages, calibration procedures, emergency
stow notes, motor behavior details, and Gpredict setup config.
Note leap-frog bug is also present in Pro repo.
2026-02-11 06:18:48 -07:00
780de4fe2b Implement HAL 0.0.00 search kill, expand CLAUDE.md with firmware reference
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.
2026-02-11 06:05:06 -07:00
2c28b0edc2 Add CLAUDE.md with project context and protocol notes 2026-02-11 05:47:03 -07:00
c93bbef26d Initial travler-rotor library scaffolding
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.
2026-02-11 04:10:17 -07:00