FT-991A Control Package
AI-controllable Yaesu FT-991A transceiver interface with LCARS-themed web GUI, command-line tools, and MCP server for AI integration.
🤖 AI Assistants: This package exposes FT-991A ham radio control via MCP (Model Context Protocol). Use
ft991a-mcpto launch the MCP server, then control frequency, mode, power, PTT, and monitor S-meter readings through standardized MCP tools.
Features
🌐 LCARS Web GUI (Star Trek Theme)
- 10+ tabbed interface — VFO Control, Audio/Waterfall, Band Scanner, Memory Channels, System, Config, Status, Diagnostics
- Audio Waterfall — Real-time spectrogram via PCM2903B USB CODEC + browser-side FFT
- Spectrum Analyzer — Overlay with gradient fill, grid lines, center frequency marker
- 5 Color Palettes — RADIO (default), AMBER, BLUE, GREEN, GRAY with pre-computed color LUTs
- Inline VFO Tuner — Click-to-type frequency entry, UP/DN buttons, step selector (10 Hz–1 MHz), arrow key tuning
- S-Meter History Chart — Last 120 readings with peak/avg labels and color gradient
- Band Scanner — Sweep frequency ranges with configurable step size and squelch, click-to-tune results, CSV export
- Memory Channel Manager — List, recall, store, and clear channels 001-099
- Antenna Auto-Tune — ATU control button (AC001/AC002 CAT commands)
- SDR Panadapter — Wideband waterfall via SDRplay RSP2pro (SoapySDR)
- Audio Streaming — Listen to radio audio directly in the browser
- WebSocket Real-time Updates — Live frequency, mode, S-meter, TX status
- Mobile Responsive — Horizontal scrollable tab bar for phone/tablet
🖥️ CLI Tools
ft991a-cli status— Get radio statusft991a-cli freq set <hz>— Set frequencyft991a-cli mode set <mode>— Change modeft991a-cli cw encode/decode/send— Morse code operationsft991a-cli scan band --heatmap [--max-bins N] [--hotspots --hotspot-threshold S --hotspot-top N] [--hotspot-windows --window-gap-hz HZ] [--window-plan --plan-cycle-ms MS] [--window-timeline] [--window-clock] [--window-now] [--window-upcoming --upcoming-count N] [--window-brief --upcoming-count N] [--window-cue] [--window-action --action-ready-ms MS] [--window-decision --action-critical-ms MS] [--window-ops --upcoming-count N] [--window-directive --upcoming-count N] [--window-handoff --upcoming-count N] [--window-snapshot --upcoming-count N] [--window-fingerprint] [--window-stability] [--window-route] [--window-control]— Adaptive RX band activity heatmap + ranked hotspots + merged hotspot windows + ranked RX review plan + cycle timeline projection + wall-clock sync schedule + upcoming handoff projection + compact live handoff brief + one-line live handoff cue + HOLD/READY/SWITCH handoff action signal + urgency/recheck decision signal + compact ops card with queue snapshot + actionable directive checklist + compact shift-handoff packet + machine-friendly one-line snapshot + deterministic fingerprint + stability classification + route directive + consolidated machine control packet (Features 1/20–20/20)POST /api/cw/hunt— RX-only CW hunt snapshot with band-aware candidates, review queue, and control guidancePOST /api/cw/sweep— persistence-aware multi-band CW sweep with ranked cross-band shortlist and durable log outputGET /api/rx/state— parsed read-onlyIF;receiver state plus current receive-control snapshot for automation clientsGET /api/rx/capabilities/GET /api/rx/controls/POST /api/rx/cw_profile— inspect and apply documented CW-relevant receive controls (clarifier, IF shift, width, RF gain, NB/NR, preamp, and related RX helpers)
Adaptive Heatmap — Operator Note (Interface-Controlled)
- Scope is restricted to the existing
scan bandcommand path. --heatmapenables adaptive RX activity summarization;--max-binslimits output density.--hotspotsadds ranked candidate frequencies from the same adaptive heatmap model (--hotspot-threshold,--hotspot-top).--hotspot-windowsmerges nearby hotspot bins into tune-ready windows (--window-gap-hz) to reduce frequency hopping during manual review.--window-planbuilds a ranked, dwell-timed RX review sequence from hotspot windows (--plan-cycle-ms) for faster manual monitoring loops.--window-timelineprojects that ranked plan into one-cycle offsets (start/end/revisit) so operators can coordinate listen windows without changing radio state.--window-clockanchors timeline offsets to current local wall-clock time so teams can synchronize manual RX monitoring in real time.--window-nowresolves the current active window plus next scheduled step from that wall-clock plan for live RX handoff guidance.--window-upcomingprojects the next queued handoff steps from the current wall-clock position (--upcoming-count) so operators can pre-brief upcoming manual tune sequence.--window-briefprovides one compact RX handoff brief (active frequency, switch countdown, and upcoming queue) to reduce operator context switching during manual monitoring.--window-cueprovides a single-line active→next handoff cue for low-overhead terminal glance checks.--window-actionadds a one-line HOLD/READY/SWITCH directive derived from live handoff timing; tune READY sensitivity via--action-ready-ms.--window-decisionadds one-line action+urgency escalation (LOW/MEDIUM/HIGH/CRITICAL) plus a recommended recheck cadence; tune HIGH/CRITICAL boundary via--action-critical-ms.--window-opsadds a compact multi-line operator ops card that combines decision state with a near-term handoff queue (--upcoming-count) for lower-overhead manual RX execution.--window-directiveadds a concise actionable checklist distilled from ops-card state to reduce handoff misses during manual RX loops.--window-handoffadds a compact shift-handoff packet (headline + immediate steps + queued steps) for fast operator transitions.--window-snapshotadds a one-line machine-friendly handoff snapshot (key=value) for logs/automation glue while staying RX-only.--window-fingerprintadds deterministic snapshot signatures for dedupe/change-tracking automation.--window-stabilityadds a stability score (STABLE/WATCH/VOLATILE) from urgency + countdown pressure.--window-routeadds compact route guidance (MONITOR/PREP_HANDOFF/IMMEDIATE_HANDOFF).--window-controladds a consolidated machine control packet combining snapshot, fingerprint, stability, and route.- These features are receive-side analysis only; they do not add or modify TX behavior.
- No new top-level CLI groups or web/MCP API surfaces are introduced by these features.
🤖 MCP Server (AI Integration)
- Full radio control via Model Context Protocol
- Compatible with Claude, GPT, and other MCP-capable assistants
📡 CAT Protocol
- Complete Yaesu CAT command implementation
- Frequency, mode, power, PTT, S-meter, band switching
- Memory channel read/write (MR/MC/MW commands)
- Antenna tuner control (AC commands)
- VFO swap, A→B copy
- TX lockout safety
Quick Install
# Install from PyPI
pip install ft991a-control
# Launch LCARS web GUI
ft991a-web --host 0.0.0.0 --port 8000 --radio-port /dev/ttyUSB0
# Launch MCP server for AI
ft991a-mcp
# Direct CLI control
ft991a-cli status
Hardware Setup
Required
- Yaesu FT-991A with USB-A to USB-B cable
- Linux host (tested on Ubuntu 22.04)
- Radio Menu → 031 CAT RATE → 38400 baud
- Add user to
dialoutgroup:sudo usermod -a -G dialout $USER
Recommended
- USB Audio CODEC (e.g., PCM2903B) for audio waterfall
- SDRplay RSP2pro (or compatible SoapySDR device) for wideband panadapter
Udev Rules (Stable Device Paths)
# /etc/udev/rules.d/99-ft991a.rules
SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea70", \
ATTRS{bInterfaceNumber}=="01", SYMLINK+="ft991a-cat"
Then use --radio-port /dev/ft991a-cat for stable access regardless of USB enumeration order.
Architecture
┌─────────────────┐ WebSocket ┌─────────────────┐
│ Web Browser │◄────────────────►│ FastAPI │
│ LCARS GUI │ Real-time │ Web Server │
│ │ Audio/FFT │ (ft991a-web) │
└─────────────────┘ └────────┬─────────┘
│
┌─────────────────┐ │
│ AI Assistant │◄── MCP Protocol ─────────┤
│ (Claude, etc) │ │
└─────────────────┘ │
│
┌─────────────────┐ ┌─────────────────┐ │
│ SDRplay │ │ USB Audio │ │
│ RSP2pro │ │ CODEC │ │
│ (SoapySDR) │ │ (PCM2903B) │ │
└────────┬────────┘ └────────┬────────┘ │
│ IQ Data │ PCM Audio │
└──────────────────────┴────────────┤
│
┌─────────────────────────┴───────────┐
│ FT991A CAT Library │
│ (ft991a.cat) │
└─────────────────┬───────────────────┘
│ USB Serial
│ 38400 baud, 2 stop bits
▼
┌─────────────────┐
│ Yaesu FT-991A │
│ Transceiver │
└─────────────────┘
Web GUI Tabs
| Tab | Description |
|---|---|
| VFO Control | Frequency display, mode buttons, band presets, power slider, VFO swap/copy, ATU |
| Audio | Audio waterfall, spectrum analyzer, inline tuner, S-meter history, listen button |
| SDR | Wideband panadapter waterfall via SDRplay (click-to-tune, bandwidth selector) |
| Scanner | Band scanner with preset bands, custom range, step sizes, squelch threshold, and adaptive RX heatmap (ft991a-cli scan band --heatmap) |
| CW Copy | RX-only CW text copy through /api/cw/copy using the FT-991A USB audio path and multimon-ng |
| Memory | Memory channel manager — list, recall, store, clear channels 001-099 |
| System | Audio device config, WebSocket status, connection info |
| Config | Serial port settings, baud rate, connection test |
| Status | Live radio status, VFO-A/B, mode, power, S-meter, SWR |
| Diagnostics | Error log, raw CAT command testing, system health |
CI/CD
GitHub Actions automatically publishes to PyPI on version tag push:
# Align release version in pyproject.toml, src/ft991a/__init__.py, src/ft991a/web.py,
# and any shipped sidecars such as package.json before tagging, then:
git tag v0.38.1
git push github main --tags
# → GitHub Actions builds and publishes to PyPI
CW Copy API
Run an RX-only CW copy pass against the FT-991A USB audio device:
curl -X POST http://127.0.0.1:8000/api/cw/copy \
-H 'Content-Type: application/json' \
-d '{
"frequency_hz": 7028000,
"duration_sec": 20,
"apply_cw_profile": true,
"bandpass_low_hz": 500,
"bandpass_high_hz": 900
}'
Durable JSONL logs are written under ~/Projects/lbf-ham-radio/logs/cw-copy/.
Configuration
Serial Port Settings
ft991a-web --radio-port /dev/ft991a-cat --radio-baud 38400 --host 0.0.0.0 --port 8000
Radio Menu Settings
- Menu 031 (CAT RATE): 38400 (or match
--radio-baud) - Menu 032 (CAT TOT): 10 min or longer
- Menu 033 (CAT RTS): Enable
Safety
⚠️ Important:
- Always verify proper antenna connection before TX
- Use TX lockout when not actively transmitting
- Ensure amateur radio license privileges for frequency/mode
- Start with low power (5-10W) for testing
- Never transmit without a licensed operator present
🔒 Built-in Safety:
- Frequency range validation (30 kHz – 470 MHz)
- Power limits (5-100W)
- TX lockout toggle
- Mode validation per frequency
- Serial timeout protection
Python API
from ft991a import FT991A, Mode
radio = FT991A(port="/dev/ft991a-cat", baudrate=38400)
radio.connect()
# Read status
status = radio.get_status()
print(f"Frequency: {status.frequency_a} Hz")
print(f"Mode: {status.mode}")
print(f"S-Meter: {status.s_meter}")
# Tune
radio.set_frequency_a(14_250_000) # 20m SSB
radio.set_mode(Mode.USB)
# Antenna tuner
radio.tuner_start() # Auto-tune
# Memory channels
radio.recall_memory(1)
radio.store_memory(5)
REST API
| Endpoint | Method | Description |
|---|---|---|
/api/status |
GET | Radio status (frequency, mode, S-meter, TX) |
/api/rx/state |
GET | Radio status plus parsed read-only IF receiver state |
/api/frequency/a |
POST | Set VFO-A frequency |
/api/frequency/b |
POST | Set VFO-B frequency |
/api/mode |
POST | Set operating mode |
/api/power |
POST | Set TX power |
/api/tuner/start |
POST | Start antenna auto-tune |
/api/tuner/on |
POST | Turn ATU on |
/api/tuner/off |
POST | Turn ATU off |
/api/vfo/swap |
POST | Swap VFO-A ⇄ VFO-B |
/api/vfo/a-to-b |
POST | Copy VFO-A → VFO-B |
/api/scan |
POST | Start band scan |
/api/scan/stop |
POST | Stop scan |
/api/cw/candidates |
GET | Band-aware CW candidate discovery from SDR FFT |
/api/cw/hunt |
POST | RX-only CW hunt snapshot with hotspots and control packet |
/api/cw/decode_samples |
POST | Decode CW from a supplied sample buffer |
/api/sdr/status |
GET | SDR status and current FFT configuration |
/api/sdr/bandwidth |
POST | Set SDR snapshot/stream bandwidth |
/api/sdr/fft_size |
POST | Set SDR FFT size |
/api/memory/list |
GET | List memory channels |
/api/memory/recall |
POST | Recall memory channel |
/api/memory/store |
POST | Store to memory channel |
/api/memory/clear |
POST | Clear memory channel |
/ws |
WebSocket | Real-time status updates |
/ws/audio |
WebSocket | Audio FFT stream |
/ws/sdr |
WebSocket | SDR wideband FFT stream |
Contributing
git clone https://github.com/heliosarchitect/lbf-ham-radio.git
cd lbf-ham-radio
pip install -e .[dev]
pytest tests/
License
MIT License — see LICENSE.
Links
- PyPI: https://pypi.org/project/ft991a-control/
- GitHub: https://github.com/heliosarchitect/lbf-ham-radio
- Gitea: https://gitea.fleet.wood/Helios/lbf-ham-radio
73 de KO4TUV! 📡 AI-controlled ham radio with LCARS style
Metadata
Release files for ft991a-control 0.38.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ft991a_control-0.38.1.tar.gz | 166.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ft991a_control-0.38.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 308.6 kB
Release files / ft991a_control-0.38.1.tar.gz
| Download URL | ft991a_control-0.38.1.tar.gz |
|---|---|
| Size | 166.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9985d0c731bc72153002169d79c895eac903c58f23cccbde5bd0bc2b85f6510d
|
|
BLAKE2b-256 checksum How to use checksums |
2e168c65b59c0f4fdb4207d33eed4e0796670e64a905f5b9a69dffb663bc35ff
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / ft991a_control-0.38.1-py3-none-any.whl
| Download URL | ft991a_control-0.38.1-py3-none-any.whl |
|---|---|
| Size | 142.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a95647f1f63f98e8e06b7893fb88588033a07908b80316fec534ef5b15e972d6
|
|
BLAKE2b-256 checksum How to use checksums |
9fdbb92bc20eb27509230adeca8c0c5e6a37805f002ef8b228f586b2ed59baa4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|