# Winegard Trav'ler Control a Winegard Trav'ler motorized satellite dish via RS-485 for amateur radio satellite tracking. ## Project - **Package:** `travler-rotor` (installed via `uv sync`) - **CLI entry point:** `travler-rotor` (init / serve / pos / move) - **Source layout:** `src/travler_rotor/` (src-layout) - **Original upstream:** `Trav-ler-Rotor-For-HAL-2.05/` — Gabe Emerson's scripts, kept as reference (do not modify) ## Build & Lint ```bash uv sync # Install deps + package uv run ruff check src/ # Lint uv run ruff format --check src/ # Format check uv run travler-rotor --help # CLI smoke test ``` ## Architecture ``` protocol.py — FirmwareProtocol ABC + HAL205Protocol / HAL000Protocol Serial I/O owned here. Each firmware version is a subclass. leapfrog.py — Pure function: apply_leapfrog(target, current) -> adjusted Predictive overshoot to compensate for mechanical motor lag. antenna.py — TravlerAntenna: high-level control wrapping protocol + leapfrog This is what consumers (CLI, rotctld, future MCP server) call. rotctld.py — RotctldServer: Hamlib rotctld TCP protocol (p/P/S/_/q) Bridges Gpredict to the antenna. cli.py — Click CLI with init/serve/pos/move subcommands ``` ## Hardware Protocol Notes - RS-485 serial, 57600 baud, 8N1 - Motor commands: `a ` (0=azimuth, 1=elevation) - Position query: `a` returns `AZ = EL = SK = ` - HAL 2.05 motor submenu: `motor` command; search kill via `ngsearch -> s -> q` - HAL 0.0.00 motor submenu: `mot` command; search kill sequence undocumented - Boot signals (HAL 2.05): "NoGPS" or "No LNB Voltage" indicate homing complete - Elevation floor: HAL 2.05 unreliable below 15 degrees with direct motor commands ## Known Bugs (from upstream) - Leap-frog elevation bug: original `travler_rotor.py` lines 98-105 modify `target_az` instead of `target_el`. Fixed in `leapfrog.py`. See `docs/bugs.md`. ## Testing No hardware-in-the-loop tests yet. Protocol implementations can be mocked for unit testing — `FirmwareProtocol` is an ABC with clear method contracts.