HydraWarden is a passive radio-frequency drone detection system. It listens to the 2.4 GHz ISM band with a HackRF One software-defined radio, converts the raw IQ samples into time-frequency spectrograms, and runs a two-stage convolutional pipeline:
- Stage 1 — Detection. A binary CNN decides whether the spectrogram contains a drone control link or not.
- Stage 2 — Classification. A multi-class CNN identifies the manufacturer / model when a drone is present (DJI Mavic, Phantom, Parrot Anafi, Autel EVO II, Skydio 2, …).
A proximity tracker then estimates the drone's range from RSSI using a free-space
path-loss model, computes approach/retreat trends from a rolling buffer of measurements,
and raises tactical alerts (GREEN / YELLOW / RED).
The whole stack ships with a React + TypeScript tactical HUD that displays detections, range, velocity, threat level and history in real time over a WebSocket. It runs in two modes:
- Real mode — uses the HackRF capture, trained models, and the proximity tracker.
- Simulator mode — a built-in drone simulator drives the same UI with realistic approach / retreat / patrol / hover patterns. No SDR or trained models required — perfect for demos and frontend work.
Tactical HUD — GREEN OBSERVED state. DJI Phantom 4 detected at low confidence, stable range trend.
YELLOW CAUTION — DJI Mavic Pro identified with high confidence, RSSI history and signal-quality bands updating live.
RED IMMINENT — DJI Phantom 4 at 99% confidence, close range and rising RSSI trigger the highest threat tier.
| Layer | Technology |
|---|---|
| SDR capture | HackRF One @ 20 MHz sample rate, 2.4 GHz center |
| Signal processing | NumPy, SciPy STFT (256-point window, 50% overlap) |
| Detection model | ResNet-18 (binary head) — PyTorch / ONNX |
| Classification model | ResNet-18 (N-class head) — PyTorch / ONNX |
| Proximity tracking | Free-space path-loss + linear-regression trend on RSSI |
| Backend | FastAPI + Uvicorn, WebSocket streaming |
| Frontend | Vite + React 19 + TypeScript + Tailwind v3 + Recharts |
| Python tooling | uv (lockfile, virtualenv, scripts) |
| JS tooling | pnpm |
| Target hardware | Mac mini / x86 Linux (PyTorch) · Raspberry Pi 5 (ONNX) |
┌──────────────────────────────────────────────────────────────────────┐
│ HARDWARE LAYER │
│ HackRF One ──► IQ samples @ 2.4 GHz, 20 MHz BW, 100 ms blocks │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ SIGNAL PROCESSING │
│ src/processing/spectrogram.py │
│ • STFT (nperseg=256, noverlap=128, hann window) │
│ • log-power → min/max normalize → 224×224 uint8 │
│ • compute_rssi() (avg power → dBm) │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ TWO-STAGE INFERENCE PIPELINE │
│ src/models/inference.py │
│ │
│ Stage 1 ── DroneDetector (ResNet-18, 2 classes) │
│ ├─ no drone → emit "No activity" │
│ └─ drone ↓ │
│ Stage 2 ── DroneClassifier (ResNet-18, N classes) │
│ ├─ conf ≥ threshold → emit brand/model │
│ └─ conf < threshold → emit "Unknown brand" │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ PROXIMITY TRACKER │
│ src/tracking/proximity.py │
│ • Free-space path loss: d = 10^((RSSI_ref - RSSI)/(10n)) │
│ • Trend (APPROACHING / MOVING_AWAY / STABLE / DRONE_LOST) │
│ • Alert level (GREEN / YELLOW / RED) from estimated distance │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ FASTAPI BACKEND (src/api/server.py) │
│ • /ws WebSocket — pushes DetectionPayload every cycle │
│ • /api/status Health check │
│ • /api/demo/* Simulator control (spawn / approach / patrol …) │
│ • Serves the built React app from web/dist/ │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ REACT TACTICAL HUD (web/) │
│ • Alert banner: AREA CLEAR · APPROACHING · HOSTILE INBOUND … │
│ • Range / velocity / threat-level / RSSI panels │
│ • 60-second rolling charts for RSSI and range │
│ • Contact log table │
│ • Demo controls (only visible when WARDEN_DEMO=1) │
│ • Day / night theming (desert ops / tactical HUD) │
└──────────────────────────────────────────────────────────────────────┘
hydra-warden/
├── config.py All thresholds, frequencies and paths
├── Makefile make / make demo / make dev / make build …
├── pyproject.toml Python deps managed by uv
├── src/
│ ├── api/server.py FastAPI app, WebSocket, demo endpoints
│ ├── capture/
│ │ ├── hackrf.py Live HackRF IQ capture (real mode)
│ │ ├── replay.py Offline replay from a dataset directory
│ │ └── demo.py Interactive drone simulator
│ ├── processing/spectrogram.py STFT + normalization
│ ├── models/
│ │ ├── detector.py Stage-1 binary detector (PyTorch/ONNX)
│ │ ├── classifier.py Stage-2 multi-class classifier
│ │ └── inference.py Two-stage pipeline wrapper
│ ├── tracking/proximity.py RSSI → distance, trend, alert level
│ └── utils/signal.py Spectrogram → tensor/numpy helpers
├── notebooks/
│ ├── 01_preprocess_dronerf.ipynb
│ ├── 02_preprocess_dronedetect.ipynb
│ ├── 03_preprocess_drone_rc.ipynb
│ ├── 04_train_detector.ipynb
│ ├── 05_train_classifier.ipynb
│ └── 06_export_models.ipynb
├── tests/ pytest suite (spectrogram, inference, proximity)
├── web/ Vite + React + TS frontend
│ ├── src/
│ │ ├── App.tsx
│ │ ├── api/client.ts
│ │ ├── hooks/ useDetectionFeed, useTheme
│ │ └── components/ HudPanel, AlertBanner, DemoControls, …
│ └── public/HydraWarden.png
├── models/ Trained weights (gitignored)
└── data/ Datasets (gitignored)
| Stage | Architecture | Input | Output | File |
|---|---|---|---|---|
| 1 — Detection | ResNet-18 (ImageNet init, 2-class head) | 3×224×224 spectrogram | [drone, no_drone] softmax |
models/detector_full.pth |
| 2 — Classification | ResNet-18 (ImageNet init, N-class head) | 3×224×224 spectrogram | softmax over N brands | models/classifier_full.pth |
For Raspberry Pi 5 deployments both models are exported to ONNX
(detector_lite.onnx, classifier_lite.onnx) and served via onnxruntime.
Class names for the classifier live in models/class_names.json.
Datasets used during training (none of them shipped in the repo):
- DroneRF (Allahham et al.)
- DroneDetect
- Drone-RC
Preprocessing notebooks turn each raw dataset into 224×224 spectrogram PNGs that the training notebooks consume.
- Python 3.11+
uv(Python package & venv manager)- Node 20+ and
pnpm - (Optional, for real mode) HackRF One + libhackrf
git clone https://github.com/iRuperth/hydra-warden.git
cd hydra-warden
make install # uv sync + pnpm install (web/)make demoThis starts the FastAPI backend on :8000 with WARDEN_DEMO=1, the Vite dev server
on :5173, and opens the dashboard in your browser. Inside the dashboard, the
Tactical Simulator panel lets you:
Spawna drone with a chosen brand and starting rangeApproach,Retreat,Hover,Patrol(orbit at constant distance)Signal Lost,Reset- Toggle
AUTOmode — the drone cycles through coherent patterns on its own (approach → patrol → retreat → hover) with directional inertia.
make dev # fails loudly if models/ is emptyThis sets WARDEN_DEMO=0 so the backend refuses to silently fall back to the
simulator — if models/detector_full.pth or the HackRF aren't available, you'll
get a clear error.
To replay a dataset directory instead of using the HackRF:
WARDEN_REPLAY=/path/to/dataset make devmake build # bundles the React app into web/dist/
make run # FastAPI serves the bundle and the WebSocket on :8000make same as `make dev` (real mode)
make dev Real mode: API + Vite, fails if no models
make demo Simulator mode: API + Vite, no models needed
make dev-api Only the FastAPI backend (auto-detect mode)
make dev-web Only the Vite dev server
make build Build the frontend into web/dist/
make run Serve the built frontend from FastAPI on a single port
make install uv sync + pnpm install
make help Print this list
All tunable parameters live in config.py:
| Section | Key parameters |
|---|---|
| HackRF | HACKRF_CENTER_FREQ, HACKRF_SAMPLE_RATE, HACKRF_LNA_GAIN, HACKRF_VGA_GAIN |
| STFT | STFT_NPERSEG, STFT_NOVERLAP, STFT_WINDOW, SPECTROGRAM_SIZE |
| Cycle | CAPTURE_DURATION_S, CYCLE_INTERVAL_S |
| CNN thresholds | DETECTION_THRESHOLD, CLASSIFICATION_THRESHOLD |
| Proximity | RSSI_REF, PATH_LOSS_EXPONENT, RSSI_BUFFER_SIZE, TREND_WINDOW |
| Alerts | ALERT_RED_MAX, ALERT_YELLOW_MAX |
| Deployment | DEPLOYMENT_MODE (fixed = PyTorch, mobile = ONNX) |
Environment overrides:
| Variable | Effect |
|---|---|
WARDEN_DEMO=1 |
Force simulator mode |
WARDEN_DEMO=0 |
Force real mode (fail if models/SDR missing) |
WARDEN_REPLAY=<dir> |
Use a dataset directory instead of the HackRF |
DEPLOYMENT_MODE=mobile |
Use ONNX models instead of PyTorch |
uv run pytestCovers spectrogram generation, the two-stage inference path (with mock models), and the proximity tracker (distance estimation, trend detection, alert levels).
- Download DroneRF / DroneDetect / Drone-RC into
data/. - Run notebooks
01-03to generate spectrograms indata/spectrograms/. - Run
04_train_detector.ipynband05_train_classifier.ipynb. Weights land inmodels/. - Run
06_export_models.ipynbto produce ONNX exports for the Pi 5.
📩 The trained model isn't shipped here. If you'd like to try HydraWarden in real mode with the actual weights, reach out to me privately and I'll share them with you.
HydraWarden — Tactical RF drone detection
