Autonomous FT8 amateur radio contact bot. Interfaces with WSJTX via its UDP API to make contacts without operator intervention — search and pounce, call CQ, or let the bot decide.
Status: Active development. Core S&P and CQ calling work. Band switching and AI integration coming next.
ft8bot connects to a running WSJTX instance over UDP. It monitors decoded FT8 signals, selects stations to work, and lets WSJTX handle the full QSO exchange via its auto-sequencer. The bot reads your callsign and grid locator directly from WSJTX — no duplicate configuration needed.
FT8 Bot <--UDP--> WSJTX <--CAT/Audio--> Radio
- WSJTX 3.0+ installed and configured with your radio
- Python 3.9+
- A valid amateur radio license
- WSJTX must have "Accept UDP requests" enabled (Settings > Reporting)
git clone https://github.com/richcannings/ft8bot.git
cd ft8bot
python3 -m venv venv
source venv/bin/activate
pip install -e .Make sure WSJTX is running and decoding. Then:
# Monitor mode — watch decodes, no transmitting
python -m ft8bot
# Bot mode — fully autonomous, strategy chosen automatically
python -m ft8bot --botThe bot reads your callsign and grid from WSJTX automatically.
python -m ft8bot [CALL] [options]
| Option | Default | Description |
|---|---|---|
CALL |
(from WSJTX) | Your callsign. Optional — read from WSJTX if omitted. |
--grid GRID |
(from WSJTX) | Your Maidenhead grid locator. Optional — read from WSJTX if omitted. |
--bot |
off | Enable bot mode (autonomous operation). Without this flag, the bot only monitors. |
--strategy {both,sp,cq} |
both |
Operating strategy (see below). |
--min-snr N |
-20 |
Minimum SNR (dB) to attempt a contact. Stations weaker than this are ignored. |
--max-retries N |
5 |
Max TX attempts per QSO step before giving up. |
--cq-threshold N |
3 |
In both mode: switch to CQ when fewer than N answerable CQs per cycle. |
--cq-max-cycles N |
10 |
In both mode: switch back to S&P after N CQ cycles with no response. |
--import-adif PATH |
Import an existing ADIF log for duplicate checking. | |
--dupe-check MODE |
callsign+band |
Duplicate check: callsign, callsign+band, or callsign+band+mode. |
--host ADDR |
127.0.0.1 |
WSJTX UDP address. |
--port PORT |
2237 |
WSJTX UDP port. |
--debug |
off | Enable debug logging. |
| Strategy | Description |
|---|---|
both |
Use both S&P and CQ. Start in search & pounce, switch to CQ when the band is quiet, switch back when CQ gets no responses. Best for unattended operation. |
sp |
Search & pounce only. Answer other stations' CQs, never call CQ yourself. |
cq |
CQ only. Call CQ and work anyone who responds, never answer other stations' CQs. The bot picks the best TX frequency slot automatically. |
python -m ft8botMonitor mode. Prints every FT8 decode with SNR, frequency offset, and message. No transmitting. Good for checking band conditions.
python -m ft8bot --botBoth strategy (default). Answers CQs when available, calls CQ when the band is quiet, switches between them based on activity. Reads callsign and grid from WSJTX.
python -m ft8bot --bot --strategy spOnly answers other stations' CQs. Never calls CQ. Good for busy bands where there are plenty of stations to work.
python -m ft8bot --bot --strategy cqCalls CQ and works anyone who responds. The bot automatically picks the best TX frequency offset (least-occupied 50 Hz slot in 300-2700 Hz range).
python -m ft8bot --bot --min-snr -10Ignores anything weaker than -10 dB. Higher completion rate, fewer wasted TX cycles.
python -m ft8bot --bot --import-adif ~/logs/my_logbook.adiLoads your existing ADIF log at startup. The bot won't call anyone you've already worked on the same band.
python -m ft8bot --bot --max-retries 3Give up after 3 TX attempts instead of the default 5. Moves on faster when stations don't respond.
python -m ft8bot --bot --debugShows all state machine transitions, heartbeats, strategy switches, and protocol messages. Useful for troubleshooting.
python -m ft8bot --bot --strategy both --min-snr -15 --max-retries 5 --import-adif ~/logs/wsjtx_log.adi --dupe-check callsign+band --debug- Watches each 15-second FT8 decode cycle for CQ calls
- Filters out duplicates (already worked on this band) and weak signals (below min SNR)
- Picks the strongest remaining station
- Sends a Reply to WSJTX, which starts the auto-sequencer
- WSJTX handles the full exchange (grid, report, R+report, RR73) autonomously
- Logs the completed QSO to SQLite and ADIF on QSOLogged
- Analyzes recent decode audio offsets to find the least-occupied frequency slot
- Configures WSJTX TX offset and clears the DX Call field
- User enables TX in WSJTX (one-time) — WSJTX handles CQ and all exchanges
- After each QSO, WSJTX automatically returns to calling CQ
- Lost detection: If the station you're calling responds to someone else, the bot immediately moves on
- Stuck exchange detection: If a station keeps repeating their report (not hearing your R+report), the TX counter still climbs and the bot will time out
- Callsign sync: Reads your callsign and grid from WSJTX at runtime — always in sync, no configuration drift
- HaltTx on failure: When a QSO times out, the bot immediately halts TX so WSJTX stops transmitting
- Settings > Reporting: Enable UDP server (default port 2237)
- Settings > Reporting: Check "Accept UDP requests" (required for the bot to control WSJTX)
- File > Configurations: Create named profiles for each band (e.g., "FT8-20m" at 14.074 MHz) — needed for future band switching
- Recommended: enable auto-logging so completed QSOs are logged without clicking OK
src/ft8bot/
├── main.py # Entry point, bot orchestrator
├── protocol/ # WSJTX UDP protocol layer
│ ├── messages.py # Message dataclasses (16 types)
│ ├── decoder.py # Binary -> Python
│ ├── encoder.py # Python -> binary
│ └── wsjtx.py # Async UDP client
├── ft8/ # FT8 logic
│ ├── decode.py # Message text parser
│ ├── qso.py # QSO state machine
│ ├── frequency.py # TX offset analysis (best-slot)
│ └── strategy.py # S&P / CQ / both mode switching
├── log/ # Contact logging
│ └── contact_log.py # SQLite + ADIF
├── selector/ # Contact selection (future)
├── bands/ # Band management (future)
└── ui/ # Terminal dashboard (future)
source venv/bin/activate
python -m pytest tests/ -v111 tests covering the protocol layer, FT8 parser, QSO tracker, TX offset analysis, and strategy manager.
- Product Requirements — full feature roadmap
- Design Document — architecture, milestones, design decisions
- WSJTX UDP protocol (all 16 message types)
- FT8 message parser
- QSO state machine (S&P + CQ caller exchanges)
- Contact log with ADIF import/export and duplicate detection
- Search & pounce (automatic CQ answering)
- CQ calling with best-slot TX offset selection
- Strategy manager (auto-switch between S&P and CQ)
- Band switching via WSJTX configurations
- Online propagation data (NOAA, PSKReporter)
- Goal system (DX, WAS, VUCC, grids)
- AI-assisted contact selection (Ollama / Anthropic)
- Desktop GUI (PySide6, cross-platform)
Apache 2.0
The operator is legally responsible for all transmissions made by their station, including those initiated by this software. Ensure you hold a valid amateur radio license and comply with all applicable regulations.