A complete implementation of SELCAL (Selective Calling) codec for aviation HF radio. This codec allows aviation enthusiasts and SDR hobbyists to encode and decode SELCAL signals used to alert aircraft crews on oceanic and remote HF routes.
SELCAL is the selective calling system used on HF aviation radio to notify pilots of incoming radio calls. Because HF radio is noisy and unreliable, pilots don't continuously monitor the audio. Instead, ground stations transmit a unique SELCAL code for each aircraft, which triggers an automatic alert in the cockpit.
Key characteristics:
- 4-letter code unique to each aircraft (e.g., "ABCD", "DGHK")
- Two sequential dual-tone bursts (~1.0 second each, 0.2 second gap)
- Total duration: ~2.2 seconds per transmission
- 16 standard frequencies spaced ~1/6 octave apart (312.6–2122.5 Hz)
- ICAO/ITU standard used on HF oceanic routes since 1950s, still active today
pip install numpypython3 scripts/selcal_encode.py ABCD my_selcal.wavOptions:
--tone-duration N— Tone duration in seconds (default: 1.0)--gap-duration N— Gap between bursts in seconds (default: 0.2)--amplitude N— Signal amplitude 0.0-1.0 (default: 0.8)--repeat N— Transmit the code N times (default: 1)
python3 scripts/selcal_decode.py my_selcal.wavOptions:
--all— Find and report all SELCAL codes in the recording--verbose— Show detailed tone detection analysis--threshold N— Manual detection threshold (auto-calibrated if not set)
| Code | Frequency (Hz) | Code | Frequency (Hz) |
|---|---|---|---|
| A | 312.6 | J | 1029.2 |
| B | 346.7 | K | 1141.4 |
| C | 384.6 | L | 1265.6 |
| D | 426.6 | M | 1403.0 |
| E | 473.2 | P | 1555.5 |
| F | 524.8 | Q | 1725.5 |
| G | 582.1 | R | 1913.5 |
| H | 645.7 | S | 2122.5 |
A valid SELCAL code consists of:
- 4 unique letters from the 16 available (A–S, skipping I, N, O)
- First pair (AB) must be in alphabetical order
- Second pair (CD) must be in alphabetical order
- Examples:
ABCD,DGHK,LMPQ,BERS
Invalid examples:
AABC(duplicate letter)BACD(first pair not ordered)ABDC(second pair not ordered)
from selcal_common import generate_selcal, decode_selcal, validate_selcal_code
# Generate SELCAL audio
audio = generate_selcal('ABCD')
# Decode SELCAL from audio
code = decode_selcal(audio, sample_rate=44100)
# Validate a code
if validate_selcal_code('DGHK'):
print("Valid SELCAL code")- Parse 4-letter code into two tone pairs
- Generate first burst: dual tones (1.0 second)
- Insert gap (0.2 second)
- Generate second burst: dual tones (1.0 second)
- Write to WAV file
- Read audio file, resample to 44.1 kHz if needed
- Scan with sliding windows (100 ms, 50 ms hop)
- For each window, run Goertzel algorithm at all 16 SELCAL frequencies
- Identify windows where exactly 2 tones exceed detection threshold
- Group consecutive matching windows into tone bursts
- Find pairs of bursts separated by appropriate gap
- Validate detected code against SELCAL rules
The decoder uses the Goertzel algorithm for efficient, single-frequency detection. This is key for analyzing real HF recordings with noise and fading.
For SDR hobbyists monitoring HF aviation:
# Decode a recording from an RTL-SDR or other HF receiver
python3 scripts/selcal_decode.py hf_recording.wav --verboseThe codec handles:
- Different sample rates (auto-resampled to 44.1 kHz)
- Mono and stereo audio
- Noisy HF recordings (SNR ~15-20 dB typical)
- Multiple SELCAL codes in one file (
--allflag)
Run the full test suite:
python3 scripts/selcal_test.pyTests cover:
- Tone generation and Goertzel detection for all 16 frequencies
- SELCAL code validation (valid and invalid codes)
- Encode/decode roundtrip for multiple codes
- Variable tone durations
- Multiple SELCAL codes in one recording
- Rejection of non-SELCAL audio (silence, single tone, noise)
- All 16 tone letters correctly detected
- SELCAL with added noise
- Short burst rejection
- CLI encode/decode
- Sample rate: 44.1 kHz (standard for audio)
- Audio format: WAV (PCM, mono or stereo)
- Tone duration: 1.0 ± 0.25 seconds (ITU standard tolerance)
- Gap duration: 0.2 ± 0.1 seconds (ITU standard tolerance)
- Detection algorithm: Goertzel FFT (single-frequency DFT)
- Dependencies: NumPy only
SELCAL (Selective Calling) has been used in aviation since the 1950s. It is specified in:
- ICAO Annex 10 (International Standards and Recommended Practices — Aeronautical Telecommunications)
- ITU-R M.2092 (Frequency management for civil aviation)
The system is still actively used on HF routes:
- Oceanic routes (Atlantic, Pacific, Indian Ocean)
- Remote areas with limited VHF coverage
- High altitude where HF is primary long-range communications
Modern aircraft still have SELCAL capability, though newer generations are moving toward CPDLC (Controller-Pilot Data Link Communications) and VDLC (VHF Data Link).
# Generate codes for different aircraft
python3 scripts/selcal_encode.py ABCD aircraft1.wav
python3 scripts/selcal_encode.py DGHK aircraft2.wav
python3 scripts/selcal_encode.py LMPQ aircraft3.wav# Decode from a recorded HF aviation frequency
# (e.g., from SDR, RTL-SDR, or HF receiver)
python3 scripts/selcal_decode.py hf_recording.wav --all --verbose# Pipe through SOX for audio processing
sox hf_recording.wav -r 44100 resampled.wav
python3 scripts/selcal_decode.py resampled.wav- Single-channel decoding (mono only, though stereo input is converted to mono)
- No error correction (relies on clean tone detection)
- Detection assumes ITU standard timing tolerances
- Real-world HF fading may affect detection of very weak signals
This SELCAL codec implementation is provided as-is for educational and hobbyist use in aviation monitoring and SDR applications.
- ICAO Annex 10 - Aeronautical Telecommunications
- ITU-R M.2092-0 (12/2013) - Frequency management for civil aviation
- Goertzel algorithm for tone detection: https://en.wikipedia.org/wiki/Goertzel_algorithm
- SELCAL technical details: Aviation HF radio communications standards