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.
This commit is contained in:
parent
c010cee282
commit
a2e807f973
161
CLAUDE.md
161
CLAUDE.md
@ -4,18 +4,19 @@ Control a Winegard Trav'ler motorized satellite dish via RS-485 for amateur radi
|
|||||||
|
|
||||||
## Project
|
## Project
|
||||||
|
|
||||||
- **Package:** `travler-rotor` (installed via `uv sync`)
|
- **Packages:** `birdcage` + `console-probe` (installed via `uv sync`)
|
||||||
- **CLI entry point:** `travler-rotor` (init / serve / pos / move)
|
- **CLI entry points:** `birdcage` (init / serve / pos / move), `console-probe` (probe / discover)
|
||||||
- **Source layout:** `src/travler_rotor/` (src-layout)
|
- **Source layout:** `src/birdcage/` and `src/console_probe/` (src-layout)
|
||||||
- **Original upstream:** `Trav-ler-Rotor-For-HAL-2.05/` — Gabe Emerson's scripts, kept as reference (do not modify)
|
- **Original upstream:** `Trav-ler-Rotor-For-HAL-2.05/` — Gabe Emerson's scripts, kept as reference (do not modify)
|
||||||
|
|
||||||
## Build & Lint
|
## Build & Lint
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv sync # Install deps + package
|
uv sync # Install deps + both packages
|
||||||
uv run ruff check src/ # Lint
|
uv run ruff check src/ # Lint
|
||||||
uv run ruff format --check src/ # Format check
|
uv run ruff format --check src/ # Format check
|
||||||
uv run travler-rotor --help # CLI smoke test
|
uv run birdcage --help # CLI smoke test
|
||||||
|
uv run console-probe --help # Probe tool smoke test
|
||||||
```
|
```
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
@ -25,13 +26,29 @@ protocol.py — FirmwareProtocol ABC + HAL205Protocol / HAL000Protocol
|
|||||||
Serial I/O owned here. Each firmware version is a subclass.
|
Serial I/O owned here. Each firmware version is a subclass.
|
||||||
leapfrog.py — Pure function: apply_leapfrog(target, current) -> adjusted
|
leapfrog.py — Pure function: apply_leapfrog(target, current) -> adjusted
|
||||||
Predictive overshoot to compensate for mechanical motor lag.
|
Predictive overshoot to compensate for mechanical motor lag.
|
||||||
antenna.py — TravlerAntenna: high-level control wrapping protocol + leapfrog
|
antenna.py — BirdcageAntenna: high-level control wrapping protocol + leapfrog
|
||||||
This is what consumers (CLI, rotctld, future MCP server) call.
|
This is what consumers (CLI, rotctld, future MCP server) call.
|
||||||
rotctld.py — RotctldServer: Hamlib rotctld TCP protocol (p/P/S/_/q)
|
rotctld.py — RotctldServer: Hamlib rotctld TCP protocol (p/P/S/_/q)
|
||||||
Bridges Gpredict to the antenna.
|
Bridges Gpredict to the antenna.
|
||||||
cli.py — Click CLI with init/serve/pos/move subcommands
|
cli.py — Click CLI with init/serve/pos/move subcommands
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### console-probe package
|
||||||
|
|
||||||
|
```
|
||||||
|
profile.py — DeviceProfile + HelpEntry dataclasses
|
||||||
|
serial_io.py — Prompt-aware serial I/O (fixes > termination bug)
|
||||||
|
discovery.py — Auto-discovery, help parsing, submenu probing, candidates
|
||||||
|
report.py — JSON report with format_version 2 (menus/help/undiscovered)
|
||||||
|
cli.py — argparse CLI: --discover-only, --deep, --submenu, --json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
console-probe --port /dev/ttyUSB2 --baud 115200 --discover-only --json /tmp/d.json
|
||||||
|
console-probe --port /dev/ttyUSB2 --baud 115200 --deep --wordlist scripts/wordlists/winegard.txt
|
||||||
|
```
|
||||||
|
|
||||||
## Firmware Variants
|
## Firmware Variants
|
||||||
|
|
||||||
Five known Winegard dish variants documented by Gabe Emerson (KL1FI) / saveitforparts and cdavidson0522:
|
Five known Winegard dish variants documented by Gabe Emerson (KL1FI) / saveitforparts and cdavidson0522:
|
||||||
@ -455,6 +472,138 @@ v — go to velocity (continuous spin): `v [motor] [ustep/sec]`
|
|||||||
? / q — help / return to TRK>
|
? / q — help / return to TRK>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### K60 GPIO Functional Pin Map (Carryout G2)
|
||||||
|
|
||||||
|
Cross-referenced from live `gpio dir`/`gpio regs` queries (2026-02-13), K60 datasheet
|
||||||
|
pin mux table (MK60DN512VLQ10, 144-LQFP), boot log peripheral init, and A3981 datasheet.
|
||||||
|
|
||||||
|
**SPI1 — A3981 Stepper Motor Drivers (4 MHz, mode 0x03)**
|
||||||
|
|
||||||
|
| K60 Pin | GPIO | Alt | Function | Dir | State | Notes |
|
||||||
|
|---------|------|-----|----------|-----|-------|-------|
|
||||||
|
| PTE0 | E0 | ALT2 | SPI1_PCS1 | OUT | 1 | A3981 #2 chip select (EL motor) |
|
||||||
|
| PTE1 | E1 | ALT2 | SPI1_SOUT | (periph) | 1 | MOSI — MCU to A3981 |
|
||||||
|
| PTE2 | E2 | ALT2 | SPI1_SCK | (periph) | 1 | SPI clock |
|
||||||
|
| PTE3 | E3 | ALT2 | SPI1_SIN | (periph) | 0 | MISO — A3981 to MCU |
|
||||||
|
| PTE4 | E4 | ALT2 | SPI1_PCS0 | IN* | 1 | A3981 #1 chip select (AZ motor) |
|
||||||
|
| PTE5 | E5 | ALT2 | SPI1_PCS2 | OUT | 1 | Possibly A3981 RESET or enable |
|
||||||
|
|
||||||
|
*PTE4 shows INPUT in GPIO dir register, but this is irrelevant when muxed to SPI peripheral.
|
||||||
|
The SPI controller manages chip select assertion/deassertion directly.
|
||||||
|
|
||||||
|
**SPI2 — BCM4515 DVB-S2 Tuner (6.857 MHz, mode 0x03)**
|
||||||
|
|
||||||
|
| K60 Pin | GPIO | Alt | Function | Dir | State | Notes |
|
||||||
|
|---------|------|-----|----------|-----|-------|-------|
|
||||||
|
| PTD11 | D11 | ALT2 | SPI2_PCS0 | OUT | 1 | BCM4515 chip select |
|
||||||
|
| PTD12 | D12 | ALT2 | SPI2_SCK | IN* | 1 | SPI clock |
|
||||||
|
| PTD13 | D13 | ALT2 | SPI2_SOUT | IN* | 1 | MOSI — MCU to BCM4515 |
|
||||||
|
| PTD14 | D14 | ALT2 | SPI2_SIN | — | 0 | MISO — BCM4515 to MCU |
|
||||||
|
| PTD15 | D15 | ALT2 | SPI2_PCS1 | — | 0 | Secondary chip select (unused?) |
|
||||||
|
|
||||||
|
*GPIO dir register not meaningful for peripheral-muxed pins.
|
||||||
|
|
||||||
|
**UART4 — RS-422 Console (115200 baud)**
|
||||||
|
|
||||||
|
| K60 Pin | GPIO | Alt | Function | Dir | State | Notes |
|
||||||
|
|---------|------|-----|----------|-----|-------|-------|
|
||||||
|
| PTE24 | E24 | ALT3 | UART4_TX | OUT | 1 | Console TX (to computer RX pair) |
|
||||||
|
| PTE25 | E25 | ALT3 | UART4_RX | IN | 1 | Console RX (from computer TX pair) |
|
||||||
|
| PTE26 | E26 | ALT3 | UART4_CTS | IN | 1 | Hardware flow control (idle high) |
|
||||||
|
| PTE27 | E27 | — | GPIO | IN | 1 | Unknown (RTS? or pullup) |
|
||||||
|
| PTE28 | E28 | — | GPIO | IN | 1 | Unknown |
|
||||||
|
|
||||||
|
**DIP Switch GPIOs**
|
||||||
|
|
||||||
|
`dipswitch` reads raw value `val:ffffff01` (all OFF/up) → `app_dipswitch:101` (DISH 110+119+129W).
|
||||||
|
Exact GPIO pins TBD — likely Port A or Port C inputs with internal pullups. The 0xffffff01
|
||||||
|
raw value suggests a 32-bit register read where bits 1-24 are all high (pullup, switches open)
|
||||||
|
and bit 0 is high (LSB).
|
||||||
|
|
||||||
|
**A3981 Diagnostic Pins**
|
||||||
|
|
||||||
|
The `a3981 diag` command reads fault status from two GPIO pins (one per motor driver).
|
||||||
|
Confirmed both read "OK" when motors are healthy. The A3981 DIAG output is active-low
|
||||||
|
open-drain, pulled high when no fault. Exact GPIO pins TBD.
|
||||||
|
|
||||||
|
**Unidentified High-State Outputs**
|
||||||
|
|
||||||
|
| GPIO | Dir | State | Likely Function |
|
||||||
|
|------|-----|-------|-----------------|
|
||||||
|
| D10 | OUT | 1 | BCM4515 reset or power enable |
|
||||||
|
| B0-B3 | — | 1 | SPI0 or I2C bus (B0-B3 cluster) |
|
||||||
|
| B11 | — | 1 | Status LED or peripheral enable |
|
||||||
|
| C10-C13 | — | 1 | Contiguous block — possibly bus interface |
|
||||||
|
| C18 | — | 1 | LNB voltage control or relay |
|
||||||
|
|
||||||
|
### azscanwxp — Radio Telescope Mode (Carryout G2)
|
||||||
|
|
||||||
|
The `azscanwxp` command in MOT> performs an azimuth sweep while cycling through
|
||||||
|
DVB transponders at each position. This is the core of Davidson's winegard-sky-scan
|
||||||
|
project for RF imaging of the sky.
|
||||||
|
|
||||||
|
**Usage:** `azscanwxp [motor] [span] [resolution] [num_xponders]`
|
||||||
|
|
||||||
|
| Parameter | Type | Units | Description |
|
||||||
|
|-----------|------|-------|-------------|
|
||||||
|
| motor | int | — | Motor ID (0=AZ, 1=EL) |
|
||||||
|
| span | float | degrees | Total azimuth sweep range |
|
||||||
|
| resolution | int | centidegrees (0.01 deg) | Step size per position |
|
||||||
|
| num_xponders | int | — | Number of transponders to cycle at each position |
|
||||||
|
|
||||||
|
**Example:** `azscanwxp 0 10 100 3` — sweep 10 degrees on AZ at 1.00 degree steps,
|
||||||
|
checking 3 transponders per position.
|
||||||
|
|
||||||
|
**Output format** (from ADC `scan` documentation):
|
||||||
|
```
|
||||||
|
Motor:<id> Angle:<cdeg> RSSI:<adc> Lock:<0/1> SNR:<dB> Scan Delta:<step>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Safety:** Requires homed motors. Do NOT run on uncalibrated axes — the firmware
|
||||||
|
may target INT_MAX (2147483647 steps) and deadlock the shell.
|
||||||
|
|
||||||
|
**For ham radio sky mapping:** Set the DVB tuner to a frequency near your target
|
||||||
|
(e.g., 10 GHz Ku-band downconverted through the LNB to ~1178 MHz IF), enable LNA
|
||||||
|
with `dvb` → `lnbdc odu`, then run azscanwxp. The RSSI values map RF power at
|
||||||
|
each AZ/EL grid point. Post-process the output into a 2D heatmap for sky imaging.
|
||||||
|
|
||||||
|
### DiSEqC 2.x Interface (Carryout G2)
|
||||||
|
|
||||||
|
The BCM4515 provides a DiSEqC 2.x controller accessible from the DVB> submenu.
|
||||||
|
DiSEqC (Digital Satellite Equipment Control) uses 22 kHz tone bursts on the coax
|
||||||
|
LNB bias line to control switches, LNB polarity, and band selection.
|
||||||
|
|
||||||
|
**Timing Parameters (confirmed live 2026-02-13):**
|
||||||
|
|
||||||
|
| Command | Value | Description |
|
||||||
|
|---------|-------|-------------|
|
||||||
|
| `ovraddr` | 0x11 | Target LNB address (standard first LNB) |
|
||||||
|
| `rrto` | 210 ms | Receive reply timeout |
|
||||||
|
| `pretx` | 15 ms | Pre-command TX delay |
|
||||||
|
| `tdthresh` | 110 | Tone detect threshold (0.16 counts/mV) |
|
||||||
|
|
||||||
|
**DiSEqC Commands:**
|
||||||
|
|
||||||
|
| Command | Function | Status |
|
||||||
|
|---------|----------|--------|
|
||||||
|
| `di2conf` | Read LNB config register | RxReplyTimeout (no switch connected) |
|
||||||
|
| `di2id` | Read LNB hardware ID | RxReplyTimeout |
|
||||||
|
| `di2stat` | Read LNB status flags | RxReplyTimeout |
|
||||||
|
| `di2rcs` | Read committed switch status | RxReplyTimeout |
|
||||||
|
| `di2cs` | Configure committed switch | Needs parameters |
|
||||||
|
| `di2sc` | Short circuit test | Untested |
|
||||||
|
| `send <hex>` | Raw DiSEqC packet (max 6 bytes) | Functional |
|
||||||
|
|
||||||
|
**Raw DiSEqC packets:** The `send` command accepts space-delimited hex bytes.
|
||||||
|
Standard DiSEqC 1.x commands use the format: `send E0 10 38 Fx` where the
|
||||||
|
last byte selects the switch port (F0-F3 for ports 1-4).
|
||||||
|
|
||||||
|
**For ham radio:** DiSEqC can control LNB polarity (13V=V-pol, 18V=H-pol) and
|
||||||
|
22 kHz tone (band select) without rewiring. The `lnbdc odu` command sets 13V;
|
||||||
|
boot default is 18V. Polarity affects which transponders are visible and RSSI
|
||||||
|
readings from `rssits` in the PEAK> submenu, which alternates between even
|
||||||
|
(H-pol/18V) and odd (V-pol/13V) transponders.
|
||||||
|
|
||||||
### Known NVS Indices
|
### Known NVS Indices
|
||||||
|
|
||||||
Full dump in `docs/g2-nvs-dump.md` (firmware 02.02.48, captured 2026-02-12).
|
Full dump in `docs/g2-nvs-dump.md` (firmware 02.02.48, captured 2026-02-12).
|
||||||
|
|||||||
@ -3,9 +3,9 @@ requires = ["hatchling"]
|
|||||||
build-backend = "hatchling.build"
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "travler-rotor"
|
name = "birdcage"
|
||||||
version = "2025.06.11"
|
version = "2026.02.12.1"
|
||||||
description = "Python library for controlling Winegard Trav'ler satellite dishes via RS-485"
|
description = "Winegard satellite dish control for amateur radio sky tracking"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
authors = [{name = "Ryan Malloy", email = "ryan@supported.systems"}]
|
authors = [{name = "Ryan Malloy", email = "ryan@supported.systems"}]
|
||||||
@ -15,7 +15,8 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
travler-rotor = "travler_rotor.cli:main"
|
birdcage = "birdcage.cli:main"
|
||||||
|
console-probe = "console_probe.cli:main"
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
target-version = "py311"
|
target-version = "py311"
|
||||||
@ -25,4 +26,4 @@ src = ["src"]
|
|||||||
select = ["E", "F", "I", "UP", "B", "SIM"]
|
select = ["E", "F", "I", "UP", "B", "SIM"]
|
||||||
|
|
||||||
[tool.hatch.build.targets.wheel]
|
[tool.hatch.build.targets.wheel]
|
||||||
packages = ["src/travler_rotor"]
|
packages = ["src/birdcage", "src/console_probe"]
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
"""travler-rotor: Control Winegard Trav'ler satellite dishes via RS-485."""
|
"""birdcage: Winegard satellite dish control for amateur radio sky tracking."""
|
||||||
|
|
||||||
from travler_rotor.antenna import AntennaConfig, TravlerAntenna
|
from birdcage.antenna import AntennaConfig, BirdcageAntenna
|
||||||
from travler_rotor.leapfrog import apply_leapfrog
|
from birdcage.leapfrog import apply_leapfrog
|
||||||
from travler_rotor.protocol import (
|
from birdcage.protocol import (
|
||||||
CarryoutG2Protocol,
|
CarryoutG2Protocol,
|
||||||
FirmwareProtocol,
|
FirmwareProtocol,
|
||||||
HAL000Protocol,
|
HAL000Protocol,
|
||||||
@ -10,10 +10,11 @@ from travler_rotor.protocol import (
|
|||||||
Position,
|
Position,
|
||||||
RssiReading,
|
RssiReading,
|
||||||
)
|
)
|
||||||
from travler_rotor.rotctld import RotctldServer
|
from birdcage.rotctld import RotctldServer
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"AntennaConfig",
|
"AntennaConfig",
|
||||||
|
"BirdcageAntenna",
|
||||||
"CarryoutG2Protocol",
|
"CarryoutG2Protocol",
|
||||||
"FirmwareProtocol",
|
"FirmwareProtocol",
|
||||||
"HAL000Protocol",
|
"HAL000Protocol",
|
||||||
@ -21,6 +22,5 @@ __all__ = [
|
|||||||
"Position",
|
"Position",
|
||||||
"RssiReading",
|
"RssiReading",
|
||||||
"RotctldServer",
|
"RotctldServer",
|
||||||
"TravlerAntenna",
|
|
||||||
"apply_leapfrog",
|
"apply_leapfrog",
|
||||||
]
|
]
|
||||||
@ -10,8 +10,8 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from travler_rotor.leapfrog import apply_leapfrog
|
from birdcage.leapfrog import apply_leapfrog
|
||||||
from travler_rotor.protocol import (
|
from birdcage.protocol import (
|
||||||
MOTOR_AZIMUTH,
|
MOTOR_AZIMUTH,
|
||||||
MOTOR_ELEVATION,
|
MOTOR_ELEVATION,
|
||||||
FirmwareProtocol,
|
FirmwareProtocol,
|
||||||
@ -31,7 +31,7 @@ class AntennaConfig:
|
|||||||
leapfrog_enabled: bool = True
|
leapfrog_enabled: bool = True
|
||||||
|
|
||||||
|
|
||||||
class TravlerAntenna:
|
class BirdcageAntenna:
|
||||||
"""High-level interface to a Winegard Trav'ler dish.
|
"""High-level interface to a Winegard Trav'ler dish.
|
||||||
|
|
||||||
Manages the full lifecycle: connect, initialize (boot + search kill),
|
Manages the full lifecycle: connect, initialize (boot + search kill),
|
||||||
@ -1,4 +1,4 @@
|
|||||||
"""CLI entry point for travler-rotor.
|
"""CLI entry point for birdcage.
|
||||||
|
|
||||||
Provides subcommands for initialization, position queries, manual moves,
|
Provides subcommands for initialization, position queries, manual moves,
|
||||||
and running a full rotctld-compatible server for Gpredict integration.
|
and running a full rotctld-compatible server for Gpredict integration.
|
||||||
@ -11,9 +11,9 @@ import sys
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from travler_rotor.antenna import AntennaConfig, TravlerAntenna
|
from birdcage.antenna import AntennaConfig, BirdcageAntenna
|
||||||
from travler_rotor.protocol import get_protocol
|
from birdcage.protocol import get_protocol
|
||||||
from travler_rotor.rotctld import RotctldServer
|
from birdcage.rotctld import RotctldServer
|
||||||
|
|
||||||
|
|
||||||
def _setup_logging(verbose: bool) -> None:
|
def _setup_logging(verbose: bool) -> None:
|
||||||
@ -25,19 +25,19 @@ def _setup_logging(verbose: bool) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _build_antenna(port: str, firmware: str, **config_kwargs) -> TravlerAntenna:
|
def _build_antenna(port: str, firmware: str, **config_kwargs) -> BirdcageAntenna:
|
||||||
"""Create a TravlerAntenna from CLI options."""
|
"""Create a BirdcageAntenna from CLI options."""
|
||||||
protocol = get_protocol(firmware)
|
protocol = get_protocol(firmware)
|
||||||
# G2 defaults: 115200 baud, 18 deg min elevation
|
# G2 defaults: 115200 baud, 18 deg min elevation
|
||||||
if firmware.lower() == "g2":
|
if firmware.lower() == "g2":
|
||||||
config_kwargs.setdefault("baudrate", 115200)
|
config_kwargs.setdefault("baudrate", 115200)
|
||||||
config_kwargs.setdefault("min_elevation", 18.0)
|
config_kwargs.setdefault("min_elevation", 18.0)
|
||||||
config = AntennaConfig(port=port, **config_kwargs)
|
config = AntennaConfig(port=port, **config_kwargs)
|
||||||
return TravlerAntenna(protocol, config)
|
return BirdcageAntenna(protocol, config)
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
@click.version_option(package_name="travler-rotor")
|
@click.version_option(package_name="birdcage")
|
||||||
@click.option("-v", "--verbose", is_flag=True, help="Enable debug logging.")
|
@click.option("-v", "--verbose", is_flag=True, help="Enable debug logging.")
|
||||||
def main(verbose: bool) -> None:
|
def main(verbose: bool) -> None:
|
||||||
"""Control a Winegard Trav'ler satellite dish via RS-485."""
|
"""Control a Winegard Trav'ler satellite dish via RS-485."""
|
||||||
@ -47,14 +47,14 @@ def main(verbose: bool) -> None:
|
|||||||
@main.command()
|
@main.command()
|
||||||
@click.option(
|
@click.option(
|
||||||
"--port",
|
"--port",
|
||||||
envvar="TRAVLER_PORT",
|
envvar="BIRDCAGE_PORT",
|
||||||
default="/dev/ttyUSB0",
|
default="/dev/ttyUSB0",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
help="Serial port for the RS-485 adapter.",
|
help="Serial port for the RS-485 adapter.",
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--firmware",
|
"--firmware",
|
||||||
envvar="TRAVLER_FIRMWARE",
|
envvar="BIRDCAGE_FIRMWARE",
|
||||||
default="hal205",
|
default="hal205",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
type=click.Choice(["hal205", "hal000", "g2"], case_sensitive=False),
|
type=click.Choice(["hal205", "hal000", "g2"], case_sensitive=False),
|
||||||
@ -76,14 +76,14 @@ def init(port: str, firmware: str) -> None:
|
|||||||
@main.command()
|
@main.command()
|
||||||
@click.option(
|
@click.option(
|
||||||
"--port",
|
"--port",
|
||||||
envvar="TRAVLER_PORT",
|
envvar="BIRDCAGE_PORT",
|
||||||
default="/dev/ttyUSB0",
|
default="/dev/ttyUSB0",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
help="Serial port for the RS-485 adapter.",
|
help="Serial port for the RS-485 adapter.",
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--firmware",
|
"--firmware",
|
||||||
envvar="TRAVLER_FIRMWARE",
|
envvar="BIRDCAGE_FIRMWARE",
|
||||||
default="hal205",
|
default="hal205",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
type=click.Choice(["hal205", "hal000", "g2"], case_sensitive=False),
|
type=click.Choice(["hal205", "hal000", "g2"], case_sensitive=False),
|
||||||
@ -91,14 +91,14 @@ def init(port: str, firmware: str) -> None:
|
|||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--host",
|
"--host",
|
||||||
envvar="TRAVLER_LISTEN_HOST",
|
envvar="BIRDCAGE_LISTEN_HOST",
|
||||||
default="127.0.0.1",
|
default="127.0.0.1",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
help="Address to listen on for rotctld connections.",
|
help="Address to listen on for rotctld connections.",
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--listen-port",
|
"--listen-port",
|
||||||
envvar="TRAVLER_LISTEN_PORT",
|
envvar="BIRDCAGE_LISTEN_PORT",
|
||||||
default=4533,
|
default=4533,
|
||||||
show_default=True,
|
show_default=True,
|
||||||
type=int,
|
type=int,
|
||||||
@ -136,14 +136,14 @@ def serve(
|
|||||||
@main.command()
|
@main.command()
|
||||||
@click.option(
|
@click.option(
|
||||||
"--port",
|
"--port",
|
||||||
envvar="TRAVLER_PORT",
|
envvar="BIRDCAGE_PORT",
|
||||||
default="/dev/ttyUSB0",
|
default="/dev/ttyUSB0",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
help="Serial port for the RS-485 adapter.",
|
help="Serial port for the RS-485 adapter.",
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--firmware",
|
"--firmware",
|
||||||
envvar="TRAVLER_FIRMWARE",
|
envvar="BIRDCAGE_FIRMWARE",
|
||||||
default="hal205",
|
default="hal205",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
type=click.Choice(["hal205", "hal000", "g2"], case_sensitive=False),
|
type=click.Choice(["hal205", "hal000", "g2"], case_sensitive=False),
|
||||||
@ -170,14 +170,14 @@ def pos(port: str, firmware: str) -> None:
|
|||||||
@main.command()
|
@main.command()
|
||||||
@click.option(
|
@click.option(
|
||||||
"--port",
|
"--port",
|
||||||
envvar="TRAVLER_PORT",
|
envvar="BIRDCAGE_PORT",
|
||||||
default="/dev/ttyUSB0",
|
default="/dev/ttyUSB0",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
help="Serial port for the RS-485 adapter.",
|
help="Serial port for the RS-485 adapter.",
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--firmware",
|
"--firmware",
|
||||||
envvar="TRAVLER_FIRMWARE",
|
envvar="BIRDCAGE_FIRMWARE",
|
||||||
default="hal205",
|
default="hal205",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
type=click.Choice(["hal205", "hal000", "g2"], case_sensitive=False),
|
type=click.Choice(["hal205", "hal000", "g2"], case_sensitive=False),
|
||||||
@ -21,12 +21,12 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
from travler_rotor.antenna import TravlerAntenna
|
from birdcage.antenna import BirdcageAntenna
|
||||||
from travler_rotor.protocol import CarryoutG2Protocol
|
from birdcage.protocol import CarryoutG2Protocol
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
MODEL_NAME = "Winegard Trav'ler RS-485 Rotor"
|
MODEL_NAME = "Birdcage — Winegard RS-485 Rotor"
|
||||||
|
|
||||||
|
|
||||||
class RotctldServer:
|
class RotctldServer:
|
||||||
@ -34,7 +34,7 @@ class RotctldServer:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
antenna: TravlerAntenna,
|
antenna: BirdcageAntenna,
|
||||||
host: str = "127.0.0.1",
|
host: str = "127.0.0.1",
|
||||||
port: int = 4533,
|
port: int = 4533,
|
||||||
) -> None:
|
) -> None:
|
||||||
30
uv.lock
generated
30
uv.lock
generated
@ -2,6 +2,21 @@ version = 1
|
|||||||
revision = 3
|
revision = 3
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "birdcage"
|
||||||
|
version = "2026.2.12.1"
|
||||||
|
source = { editable = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "click" },
|
||||||
|
{ name = "pyserial" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [
|
||||||
|
{ name = "click", specifier = ">=8.0" },
|
||||||
|
{ name = "pyserial", specifier = ">=3.5" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "click"
|
name = "click"
|
||||||
version = "8.3.1"
|
version = "8.3.1"
|
||||||
@ -31,18 +46,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6
|
|||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" },
|
{ url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "travler-rotor"
|
|
||||||
version = "2025.6.11"
|
|
||||||
source = { editable = "." }
|
|
||||||
dependencies = [
|
|
||||||
{ name = "click" },
|
|
||||||
{ name = "pyserial" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.metadata]
|
|
||||||
requires-dist = [
|
|
||||||
{ name = "click", specifier = ">=8.0" },
|
|
||||||
{ name = "pyserial", specifier = ">=3.5" },
|
|
||||||
]
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user