A production-grade Python framework for SDR-based drone communication analysis
📡 SDR Capture • 🧠 ML Classification • 🛰️ MAVLink / DJI Parsing • ⚖️ FCC-Compliant Injection
DroneCMD is a production-grade Python framework for SDR-based drone communication analysis. It provides a full signal pipeline — capture → demodulate → classify → parse — with FCC-compliant signal injection, a from-scratch ML classifier training system, and a plugin architecture for extensibility.
⚠️ This software is for lawful security research and authorized testing only.
| Capability | Description |
|---|---|
| Live IQ Capture | HackRF / RTL-SDR with async streaming and USB overrun recovery |
| OFDM & Single-Carrier RX | Full OFDM demod chain and single-carrier receivers with CFO/phase recovery |
| Blind Link Resolution | Recognizes the PHY with no prior — single-carrier sps, OFDM FFT size / cyclic prefix — via a sync gate + trial-demod EVM tiebreak |
| Channel Coding (FEC) | Convolutional + soft Viterbi, Reed-Solomon (errors + erasures), and BCH — with coded BER/FER measured end-to-end |
| Protocol Classification | Sklearn ensemble trained on your own labeled IQ captures |
| Packet Parsing | MAVLink v1/v2, DJI OcuSync/Lightbridge, generic protocol analysis |
| FHSS Engine | Frequency hopping with FCC CFR 47 §15.247 compliance enforcement |
| Signal Injection | Power-limited, dwell-time-enforced injection with typed compliance exceptions |
| Plugin System | Extensible per-manufacturer protocol plugins (DJI, Parrot, custom) |
| Validation & T&E | Measured detect→classify performance — Pd/Pfa, ROC, accuracy-vs-SNR with bootstrap CIs — over synthetic + real ground truth (dronecmd validate) |
| CLI | Full-featured argparse CLI with JSON output support |
- Python 3.8+
- HackRF tools (
hackrf_info) or RTL-SDR drivers for hardware capture - No hardware required for signal analysis, parsing, classification, or testing
git clone https://github.com/rbenzing/DroneCMD.git
cd DroneCMD
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -e ".[dev]"Optional feature sets:
pip install -e ".[sdr]" # HackRF support (pyhackrf)
pip install -e ".[crypto]" # Reed-Solomon FEC + cryptography
pip install -e ".[viz]" # Matplotlib / Plotly visualization
pip install -e ".[all]" # EverythingDroneCMD selects a HackRF receive backend automatically:
- SoapySDR (
SoapyHackRFHardware) — in-process, used when the SoapySDR Python bindings are importable in the running interpreter. hackrf_transferCLI (HackRFTransferHardware) — a portable subprocess fallback used when SoapySDR's Python bindings are missing or ABI-mismatched. Works on any Python version.
Windows: install PothosSDR
(bundles libhackrf, hackrf_info, hackrf_transfer, SoapySDR + SoapyHackRF),
then run Zadig → Options ▸ List All Devices → select HackRF One →
install the WinUSB driver. Verify with hackrf_info (it should print the
board ID/serial). DroneCMD finds the PothosSDR tools automatically even if they
are not on PATH.
Note: PothosSDR ships the SoapySDR Python bindings for Python 3.9 only. On a newer interpreter the SoapySDR backend is not importable, so DroneCMD falls back to the
hackrf_transferCLI backend automatically — no action needed. (For the in-process SoapySDR path on Windows, run under Python 3.9 or use Linux distropython3-soapysdrpackages that match your interpreter.)
Linux: install hackrf and soapysdr-module-hackrf (plus
python3-soapysdr for the in-process backend) from your distribution.
All HackRF backends are receive-only — they never transmit.
# Capture IQ samples from SDR hardware (2.44 GHz needs HackRF; default is rtl_sdr)
dronecmd capture --platform hackrf --frequency 2.44e9 --duration 30 --output capture.iq
# Hardware self-test: exercise every capture parameter, API and pipeline stage
# against a connected HackRF (receive-only; --json for machine-readable output)
dronecmd selftest
# Analyze a capture file
dronecmd analyze --input capture.iq --protocols mavlink,dji
# Train the protocol classifier on labeled captures
dronecmd train --data-dir captures/ --output-dir models/
# Replay a recorded signal (FCC compliance enforced)
dronecmd replay --input capture.iq --strategy intelligent --count 5
# Generate and transmit an FHSS signal
dronecmd generate fhss --frequency 2.44e9 --data "test payload"
# Show/set configuration
dronecmd config show
dronecmd config set capture.default_sample_rate 2048000
# Measure detect→classify performance (empirical validation / T&E)
dronecmd validate synth --protocols mavlink,dji --snr=-20:20:2 --n 50 --out ds/
dronecmd validate run --dataset ds/ --models models/ --report report.json --plotsThe classifier is not pre-trained — it learns from your own labeled IQ captures. This is intentional: effective classification requires data collected from the specific hardware and environment you are analyzing.
Collect captures → label them → train → classify
See docs/iq_capture_guide.md for the full data collection procedure, directory layout, labeling format, and verification steps.
# After collecting and labeling captures:
dronecmd train --data-dir captures/ --output-dir models/ --cv-folds 5
# Models are then auto-loaded from models/ on next run
dronecmd analyze --input new_capture.iqDroneCMD exposes two API layers that are both maintained:
Simple layer — factory functions and synchronous classes for quick scripting:
from capture.manager import CaptureManager
manager = CaptureManager(platform="hackrf", sample_rate=2_048_000)
iq_data = manager.load_file("capture.iq")
packets = manager.extract_packets(iq_data, threshold=0.1)Enhanced layer — async streaming, structured configs, typed results:
from core.capture import EnhancedLiveCapture, SDRConfig, SDRPlatform
# Field names are frequency_hz / sample_rate_hz; SDRConfig validates against
# the platform's limits at construction (2.44 GHz requires HackRF).
config = SDRConfig(
frequency_hz=2.44e9,
sample_rate_hz=2_048_000,
platform=SDRPlatform.HACKRF,
)
async with EnhancedLiveCapture(config) as capture:
async for samples in capture.stream_samples():
process(samples) # each block is a numpy complex64 IQ arrayThe EnhancedProtocolClassifier.classify() method operates on extracted
packet bytes, not raw IQ — run demodulation/packet extraction first, then
classify the resulting bytes.
Module map:
core/— Signal pipeline:capture,demodulation,classification,fhss,signal_processing,replay,parsing; OFDM & single-carrier receivers (ofdm— including an adaptive bit-loaded OFDM path,modulate_ofdm_loaded/demodulate_ofdm_loaded, alongside the fixed-QPSK chain;single_carrier); adaptive bit-loading (bitloading— square-QAM {QPSK/16-QAM/64-QAM} + per-subcarrier SNR + Chow's rate-adaptive loading); link-profile catalog + blind resolution (profiles,blind); channel coding (coding— uncoded/repetition/convolutional/RS/BCH/LDPC/turbo/polar/fountain codecs — the full seven-family FEC sheet is now complete,galois— the reusable GF(2^m) algebra for Reed-Solomon/BCH,polar— Arıkan polar transform + GA frozen set + CA-SCL decoder,fountain— Raptor-style precode + Robust-Soliton LT + GF(2) Gaussian-elimination erasure decoder)capture/— Simple layer:manager,detector,snifferinjector/— Packet injection engine with FCC compliance enforcementplugins/— Protocol plugin system:base,registry,protocols/(DJI, Parrot, generic)training/— Classifier training pipeline:dataset,trainvalidation/— Validation & T&E spine: synthetic + real ground truth → detect→classify metrics (Pd/Pfa, ROC, accuracy-vs-SNR) plus the coded-link (coded BER/FER) metric and blind link resolution, with bootstrap CIs and a reproducibility manifest (dronecmd validate)utils/— IQ file I/O, YAML config, logging, crypto, compatcli.py— argparse-based CLI entry point
Design & decisions: architecture rationale lives in docs/adr/ (Architecture Decision Records) and per-phase design specs in docs/design/.
Use plugins/protocols/_template.py as the starting point. Subclass
BaseProtocolPlugin (from plugins/base.py) and implement get_name(),
get_version(), get_supported_protocols(), detect() (returns
ProtocolDetectionResult), and parse_packet() (returns ProtocolParseResult).
Register the exact class name in pyproject.toml:
[project.entry-points."dronecmd.plugins"]
myplugin = "plugins.protocols.myplugin:MyProtocolPlugin"pytest # All tests
pytest -m "not slow and not hardware" # Skip hardware/slow tests
pytest tests/test_fhss.py -v # Single file
pytest -n auto # Parallel execution230+ tests, no hardware required. Hardware-dependent tests are marked @pytest.mark.hardware and excluded by default; CI runs the full suite on Python 3.9–3.12 for every push and PR.
Versions are derived from git tags by setuptools_scm — no version string is
hardcoded anywhere. To publish a release:
git tag v1.2.3 # semver; the tag is the version
git push origin v1.2.3Pushing a v* tag triggers the release workflow, which builds the sdist + wheel
(pinned to the tag) and publishes a GitHub Release with auto-generated notes.
Signal injection enforces hard limits — violations raise typed exceptions, not silent log warnings:
| Violation | Exception |
|---|---|
| Power exceeds limit | PowerLimitError |
| Dwell time exceeded | DwellTimeError |
| Frequency out of band | FrequencyViolationError |
These are non-bypassable. InjectionConfig.validate() returns a list of errors before transmission begins.
This software is for educational and authorized security research only. Use against hardware, drones, or networks you do not own or do not have explicit permission to test is prohibited.
© 2025 Russell Benzing | MIT License
Built by Russell Benzing. DroneCMD is a defensive/research SDR toolkit for lawful drone-communication analysis and authorized testing.
- Issues: GitHub Issues
- Data collection guide: docs/iq_capture_guide.md
If DroneCMD is useful to you, you can support the work: