Skip to content

Creedmoor4529/ATC-Bot

Repository files navigation

DCS ATC Bot

An AI-powered air traffic controller for DCS World Server. The bot listens on SRS radio frequencies, transcribes pilot transmissions using faster-whisper STT, generates realistic ATC responses using an LLM, and broadcasts them back over SRS using Piper TTS.

Note: This project is in early development. Expect frequent updates as bugs are fixed and the AI is trained to produce more accurate ATC responses.


Requirements

  • Windows 10/11, Linux (tested on Ubuntu 24.04 LTS), or macOS (untested — may require manual setup)
  • DCS World Server — a dedicated server instance is required (the bot reads telemetry and weather exports from the running server). Runs on Windows natively or on Linux via Wine. The bot itself can run on a separate machine, connecting to the DCS server over the network.
  • SRS (SimpleRadio Standalone) — server must be running
  • Tacview — with real-time telemetry enabled
  • Python 3.11+
  • An AI provider account (OpenAI, Groq, or local Ollama)

Installation

1. Run the install script

Windows:

install.bat

Linux:

chmod +x install.sh && ./install.sh

The install script will:

  • Create a Python virtual environment (.venv/)
  • Install Python dependencies
  • Download and extract Piper TTS
  • Download the voice model set in config.lua (default: en_US-amy-medium)
  • Create .env from .env.example

NVIDIA GPU support (optional but recommended)

If you have an NVIDIA GPU, install CUDA dependencies for GPU-accelerated speech recognition. Without these, faster-whisper falls back to CPU which is significantly slower.

Option A — Python packages (quickest, works on any distro):

source .venv/bin/activate
pip install nvidia-cublas-cu12 nvidia-cudnn-cu12

Option B — System CUDA toolkit:

Ubuntu / Debian:

sudo apt install nvidia-cuda-toolkit

Fedora / RHEL:

sudo dnf install cuda-toolkit

Arch Linux:

sudo pacman -S cuda

Running the bot on Linux

source .venv/bin/activate
python3 main.py

Linux note: DCS World is Windows-only. The bot can run on Linux and connect to an SRS server running on a Windows machine over the network. Set BOT_HOST in dcs_atc_export.lua to the Linux machine's IP address.

2. Install the DCS weather export hook

  1. Copy dcs_atc_export.lua to:
    %USERPROFILE%\Saved Games\DCS_Server Instance\Scripts\Hooks\
    
  2. Open the file and set BOT_HOST:
    • Same machine as DCS: "127.0.0.1"
    • Bot on a different machine: set to that machine's local IP (e.g. "192.168.1.50")

4. Configure the bot

Copy .env.example to .env and fill in your API key:

OPENAI_API_KEY=sk-...       # if using OpenAI
GROQ_API_KEY=gsk_...        # if using Groq (free)

Then create your local config and DCS hook:

cp dcs_atc_export.lua.example dcs_atc_export.lua

Create a file called config.local.lua in the project root with your airfield settings. The fastest way is to copy the bundled template:

cp config.local.lua.example config.local.lua

Then edit config.local.lua to match your airfield, map, and SRS frequencies. The template contains the minimum required fields:

AIRPORT_ICAO  = "LCRA"
DCS_MAP       = "Syria"      -- DCS theatre name (see config.lua for valid values)
ATC_CALLSIGN  = "Akrotiri"
ACTIVE_RUNWAY = "28"
FREQ_APPROACH   = 119000000
FREQ_APPROACH_2 = 257000000
FREQ_TOWER      = 119500000
FREQ_TOWER_2    = 257500000
FREQ_GROUND     = 118000000
FREQ_GROUND_2   = 258000000

DCS_MAP is required. It must match the theatre your DCS server is running, and the chosen AIRPORT_ICAO must exist on that map. The bot will refuse to start with a clear error message if either is missing or mismatched. This disambiguates airfields that exist on multiple maps (e.g. Beirut OLBA appears on both Syria and Sinai). The DCS_MAP value next to each map header in config.lua is the exact string to use.

Important: Do NOT edit config.lua directly — it is tracked by git and will cause merge conflicts on git pull. Put all your settings in config.local.lua instead. This file is gitignored and will never be overwritten by updates. Any value in config.local.lua overrides the same value in config.lua.

Copy dcs_atc_export.lua to your DCS hooks folder (%USERPROFILE%\Saved Games\DCS\Scripts\Hooks\) and set BOT_HOST to your bot machine's IP.

Upgrading from an older version? If you previously edited config.lua directly, rename it to config.local.lua and run git checkout config.lua to restore the default. Future git pull will be conflict-free.

5. Enable Tacview real-time telemetry

On a dedicated server, Tacview is configured via options.lua rather than the GUI. Open:

%USERPROFILE%\Saved Games\DCS_DEDICATED_SERVER\Config\Options.lua

Find the ["Tacview"] block inside ["plugins"] and set the following values:

["tacviewRealTimeTelemetryEnabled"] = true,
["tacviewRealTimeTelemetryPort"] = "42674",
["tacviewRemoteControlEnabled"] = true,
["tacviewRemoteControlPort"] = "42675",
["tacviewPlaybackDelay"] = 0,

Important: tacviewPlaybackDelay must be 0. Any non-zero value introduces a delay in the telemetry stream and the bot will not receive live position data.

If the ["Tacview"] block does not exist, add it inside the ["plugins"] table. Leave ["tacviewRealTimeTelemetryPassword"] and ["tacviewRemoteControlPassword"] as empty strings unless you want password protection.


Configuration

All operational settings are in config.lua (defaults) and config.local.lua (your overrides). The default config.lua is self-documented with a full airfield reference at the top. Only the values you want to change need to go in config.local.lua.

Key settings

Setting Description
AIRPORT_ICAO ICAO code of your airfield (see reference list in config.lua)
DCS_MAP Required. DCS theatre name e.g. "Syria", "Caucasus", "PersianGulf" (see config.lua for full list)
ATC_CALLSIGN Base callsign e.g. "ANAPA" — bot appends APPROACH / TOWER / GROUND automatically
ACTIVE_RUNWAY Active runway designator e.g. "22"
MAGNETIC_VAR Magnetic variation in degrees east for your map
TACAN_CHANNEL TACAN channel e.g. "99X" — set to "" to disable
TACAN_INBOUND_COURSE TACAN inbound course in magnetic degrees
TACAN_MDA_FT Minimum descent altitude in feet
AI_PROVIDER "openai", "groq", or "ollama"
AI_MODEL Override the default model (optional)
FREQ_APPROACH Approach frequency in Hz e.g. 131000000
FREQ_TOWER Tower frequency in Hz
FREQ_GROUND Ground frequency in Hz
INSTRUCTIONS Optional custom ATC instructions appended to the system prompt
PIPER_VOICE Voice model name e.g. "en_GB-alan-medium" — see available voices
DCS_CHAT_ENABLED true to post ATC responses in DCS in-game chat (default: true)
DCS_CHAT_PORT UDP port the Lua hook listens on for chat (default: 15100)

DCS In-Game Chat

When enabled, every ATC response is also posted to the DCS in-game text chat so pilots can read what was said. This is useful when TTS audio is hard to understand or when reviewing instructions.

  • Enabled by default. Set DCS_CHAT_ENABLED = false in config.local.lua to disable.
  • Requires the updated dcs_atc_export.lua hook (re-copy to your DCS hooks folder after updating).
  • The bot sends UDP to the DCS machine on port 15100 (configurable via DCS_CHAT_PORT).
  • If the bot runs on a different machine than DCS, set DCS_CHAT_HOST in .env to the DCS server's IP.

Logging

Set LOG_LEVEL in your .env to control output verbosity:

Level Description
DEBUG All messages including SRS pings, Tacview updates, audio details (default)
INFO Transmissions, ATC responses, model loading — recommended for normal use
WARNING Only warnings and errors
ERROR Only errors

Example .env setting to disable debug noise:

LOG_LEVEL=INFO

AI Providers

Provider Cost Setup
Groq Free tier Sign up at console.groq.com, add GROQ_API_KEY to .env
Ollama Free, local Install from ollama.com, run ollama pull llama3.1
OpenAI Paid Add OPENAI_API_KEY to .env

Set AI_PROVIDER in config.local.lua to switch between them.

Speech-to-Text (Whisper) Models

The bot uses faster-whisper for speech recognition. Set WHISPER_MODEL in your .env to choose a model (default: distil-large-v3).

Model Size Speed Accuracy VRAM
tiny 75 MB Fastest Low ~1 GB
base 142 MB Very fast Fair ~1 GB
small 466 MB Fast Good ~2 GB
medium 1.5 GB Moderate Very good ~5 GB
large-v3 3.1 GB Slow Best ~10 GB
distil-large-v3 1.5 GB Fast Near-best ~4 GB

Example .env setting:

WHISPER_MODEL=small

Models are downloaded automatically from HuggingFace on first run. Smaller models are faster but less accurate — distil-large-v3 offers the best speed/accuracy tradeoff for ATC use.

Custom Instructions

Add site-specific ATC rules to the INSTRUCTIONS field in config.local.lua:

INSTRUCTIONS = "Expect fast jet traffic. All aircraft report 10 nautical miles. Preferred approach is straight-in runway 22."

Running the Bot

Option A — Executable (recommended)

Run build_launcher.bat once to compile the launcher, then double-click ATC Bot.exe. A console window will open showing the bot status. Press Ctrl+C or close the window to stop the bot. All output is also written to bot.log.

Option B — Command line

Windows:

python main.py

Linux:

source .venv/bin/activate
python3 main.py

Output is logged to both the terminal and bot.log on all platforms.


How It Works

  1. Bot connects to SRS and listens on all configured frequencies simultaneously
  2. Pilot transmits on any ATC frequency
  3. Audio is decoded and transcribed by faster-whisper STT (distil-large-v3)
  4. Pilot callsign is extracted from the transmission
  5. LLM generates an ATC response using live traffic data (Tacview) and weather (DCS export)
  6. Piper TTS synthesises the response to audio
  7. Bot transmits the audio back on the same frequency the pilot used
  8. Bot callsign automatically matches the service: APPROACH, TOWER, or GROUND

Callsign Tips

The bot uses speech-to-text to identify your callsign from each transmission. Clear, NATO-style callsigns work best.

Recommended format: Callsign Number separated by a pipe | from your pilot name in the SRS client.

Whiskey 1-1 | Bob
Viper 11 | Maverick
Chevy 2-1 | Dave

The bot reads the portion before the pipe as your radio callsign. Keep it simple:

  • Short phonetic words: Whiskey, Viper, Chevy, Dodge, Hawk, Fury, Rage
  • One or two digit group: 1-1, 11, 2-1, 31

Avoid intricate or unusual callsigns — the speech-to-text model may struggle to transcribe them accurately, leading to the bot not recognising you or misreading your callsign.


Frequencies

Set frequencies in config.local.lua to match your SRS server. Each service has a primary and secondary frequency:

FREQ_APPROACH   = 131000000   -- 131.000 MHz
FREQ_APPROACH_2 = 260000000   -- 260.000 MHz (UHF)
FREQ_TOWER      = 131000000
FREQ_TOWER_2    = 260000000
FREQ_GROUND     = 131000000
FREQ_GROUND_2   = 260000000

Remote / Off-Site Hosting

The bot can run on a machine that is not on the same LAN as the DCS server. Each connection requires a different approach:

SRS

No changes needed — SRS already connects to a hostname/IP and works over the internet.

Tacview (TCP — bot connects outbound to DCS server)

  1. On the DCS server's router, forward TCP port 42674 to the DCS machine's LAN IP
  2. In the bot's .env, set TACVIEW_HOST to the DCS server's public IP or DDNS hostname:
    TACVIEW_HOST=your-server.ddns.net
    

DCS weather export (UDP — DCS server sends outbound to bot)

  1. On the bot machine's router, forward UDP port 15099 to the bot machine's LAN IP
  2. In dcs_atc_export.lua, set BOT_HOST to the bot machine's public IP or DDNS hostname:
    local BOT_HOST = "your-bot-machine.ddns.net"
  3. The default port 15099 can be changed by editing both BOT_PORT in dcs_atc_export.lua and DCS_EXPORT_PORT in .env

Summary

Connection Direction Port to forward Where to forward
Tacview Bot → DCS server TCP 42674 on DCS router DCS machine LAN IP
DCS weather export DCS server → Bot UDP 15099 on bot router Bot machine LAN IP
SRS Bot → SRS server None (outbound only)

System Requirements

The bot runs alongside a DCS World Server instance. The heaviest local component is faster-whisper (speech-to-text) — everything else is lightweight.

Minimum (CPU-only, no dedicated GPU)

Component Requirement
CPU 4-core, Intel 8th gen+ / Ryzen 2000+
RAM 8 GB
GPU None required (Whisper runs on CPU, but slower)
Storage ~2 GB for models + Python environment

On CPU-only systems, transcription with distil-large-v3 will be slow (~3-5x real-time). Set WHISPER_MODEL=small or base in .env for faster results at the cost of accuracy.

Recommended (with NVIDIA GPU)

Component Requirement
CPU 4+ cores
RAM 16 GB
GPU NVIDIA with 4+ GB VRAM and CUDA support (e.g. GTX 1650 / RTX 3050)
Storage ~2 GB for models + Python environment

The bot auto-detects GPU availability (device="auto" in faster-whisper). With a CUDA-capable GPU, transcription is near real-time.

Bottom line: If your machine can run DCS, it can run the bot — especially with a smaller Whisper model like small or base.


Troubleshooting

Bot shows wrong callsign / old airfield name Your .env file may be overriding config.local.lua. Remove any ATC_CALLSIGN, AIRPORT_ICAO, or frequency entries from .env — these should only be in config.local.lua.

No weather data Check %USERPROFILE%\Saved Games\DCS_Server Instance\Logs\atc_export.log. If it shows socket=false, the DCS Lua socket library is not available. Ensure DCS is not in a restricted export mode.

STT not transcribing faster-whisper downloads the distil-large-v3 model from HuggingFace on first run — this may take a few minutes. Check bot.log for errors.

Piper not found Verify piper/piper.exe exists in the project root and the voice model is in piper/voices/.

Bot not responding on radio

  • Confirm SRS server is running and the bot has connected (check bot.log)
  • Confirm Tacview real-time telemetry is enabled
  • Confirm frequencies in config.local.lua match the SRS frequencies you are transmitting on

File Overview

File Purpose
config.lua Default configuration and airfield reference — do not edit, use config.local.lua for overrides
config.local.lua Your personal settings (gitignored) — overrides values in config.lua
dcs_atc_export.lua.example Example DCS hook — copy to dcs_atc_export.lua and install in DCS hooks folder
.env Secret keys (API keys, server addresses) — never committed to git
dcs_atc_export.lua DCS Lua hook — install to Saved Games/DCS_Server Instance/Scripts/Hooks/
main.py Bot entry point and audio pipeline
components/atc_brain.py LLM interface — generates ATC responses
components/atc_state.py Tracks aircraft strips, squawks, runway state
components/tacview_client.py Reads live traffic from Tacview
components/srs_client.py SRS radio audio send/receive
components/stt_engine.py faster-whisper speech-to-text
components/tts_engine.py Piper text-to-speech
components/dcs_export.py Receives weather data from DCS
components/airfield_db.py ICAO coordinate lookup table
bot.log Runtime log — always written on all platforms

Acknowledgements

This project is built on the following open-source projects and services:

Project Role License / Link
DCS World Combat flight simulator Eagle Dynamics
SRS (SimpleRadio Standalone) In-game radio communications Ciribob
Tacview Real-time flight telemetry Vyrtuoz
faster-whisper Speech-to-text (CTranslate2 Whisper) MIT
OpenAI Whisper Original Whisper model architecture MIT
Piper TTS Text-to-speech engine MIT
OpenAI API LLM provider (GPT-4o-mini) OpenAI
Groq LLM provider (free tier) Groq
Ollama Local LLM inference MIT
NumPy Audio array processing BSD
SciPy Audio signal processing BSD
opuslib Opus audio codec bindings BSD
python-dotenv Environment variable management BSD
CTranslate2 Optimised transformer inference MIT
HuggingFace Model hosting and distribution HuggingFace

Development

This project was developed with Claude Code (Anthropic).

About

AI-powered air traffic controller for DCS World — listens on SRS radio, transcribes pilot transmissions, generates realistic ATC responses, and talks back over SRS using text-to-speech.

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors