Skip to content

DanielreshGithub/H.A.R.P

 
 

Repository files navigation

Find My Force

RF Classification · Geolocation · Tactical Awareness

"What's nearby? Is it friendly? When was it last seen?"

Every design decision in this system exists to answer those three questions faster and with more confidence than the operator could alone.


The Problem

In contested electromagnetic environments, a signals intelligence operator is drowning in data. A network of receivers is reporting hundreds of RF detections per minute — each one a raw 256-element IQ snapshot with a signal strength reading and a timestamp. Somewhere in that stream are friendly UAVs, ground vehicles, and sensor networks. Also in that stream are adversary radars and communications systems that the operator has never seen before. And civilian devices. All interleaved. All unlabeled.

The operator doesn't have time to run an FFT. They need a map, color-coded, with confidence indicators, updating in real time. They need Find My Force.

The Insight That Drives Everything

This is not a supervised classification problem.

We have labeled training data for six friendly signal types. We have zero labeled examples of hostile emitters. The guide is explicit: "Intelligence suggests adversary forces are operating radar and communications systems in the area, but you don't know exactly what they look like. Your system must figure that out."

A team that treats this as a six-class classification task will correctly label every friendly signal and miss every hostile emitter entirely. That's not a signals intelligence system — it's a friendly-force tracker with a blind spot the size of the threat.

Our system is built around a semi-supervised pipeline. We learn what friendly looks like, and we detect what doesn't fit. Then we go further: we cluster the anomalies to distinguish between hostile subtypes, giving the operator not just "unknown" but "Unknown-Alpha" and "Unknown-Bravo" — distinct threat categories discovered from the data itself.

Architecture

┌─────────────────────────────────────────────────────────────┐
│                     SIMULATION FEED                          │
│         (flat stream of independent receiver observations)   │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│              SIGNAL CLASSIFIER (ML PIPELINE)                 │
│                                                              │
│  ┌──────────────┐  ┌──────────────┐  ┌────────────────────┐ │
│  │  Feature      │  │  Supervised  │  │  Anomaly Detection │ │
│  │  Extraction   │──│  Classifier  │──│  (Autoencoder +    │ │
│  │  (25+ feats)  │  │  (Ensemble)  │  │   OOD Scoring)     │ │
│  └──────────────┘  └──────────────┘  └────────────────────┘ │
│                                              │               │
│                                     ┌────────▼─────────┐    │
│                                     │  Hostile Subtype  │    │
│                                     │  Clustering       │    │
│                                     │  (DBSCAN)         │    │
│                                     └──────────────────┘    │
│                                                              │
│  Output: label, confidence, is_friendly, is_ood, embedding   │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│              OBSERVATION ASSOCIATION ENGINE                   │
│                                                              │
│  Three-criteria grouping:                                    │
│    1. Temporal proximity (configurable window)               │
│    2. Signal similarity (classification + embedding cosine)  │
│    3. RSSI geometric consistency (single-source hypothesis)  │
│                                                              │
│  Output: grouped observations ready for geolocation          │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                  GEOLOCATION ENGINE                          │
│                                                              │
│  RSSI → distance (path-loss model)                          │
│  Weighted nonlinear least squares (scipy.optimize)           │
│  Confidence gates: bad fit → inflated uncertainty            │
│  Coordinate conversion: lat/lon ↔ local ENU                 │
│                                                              │
│  Output: lat/lon, uncertainty radius, contributing receivers │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                    TRACK MANAGER                             │
│                                                              │
│  Create / Update / Age tracks                                │
│  Position: Kalman filter (constant-velocity model, numpy)    │
│  Classification: rolling majority vote weighted by conf.     │
│  Staleness: fresh (<10s) → stale (10-60s) → lost (>60s)    │
│  Platform correlation: co-located emitters → single entity   │
│                                                              │
│  Output: persistent tracks with full history                 │
└──────────────────────────┬──────────────────────────────────┘
                           │
                       WebSocket
                           │
                           ▼
┌─────────────────────────────────────────────────────────────┐
│              COMMON OPERATING PICTURE (COP)                  │
│                                                              │
│  Mapbox GL JS + OpenStreetMap                                │
│  Color-coded markers (friendly / hostile / unknown / civ.)   │
│  Uncertainty rings scaled to CEP                             │
│  Staleness visualization (solid → faded → ghost)             │
│  Click-to-detail panel with full track metadata              │
│  Hostile alert banner                                        │
│  CoT/ATAK XML export                                        │
└─────────────────────────────────────────────────────────────┘

How We Score — Mapped to the Rubric

Signal Classification — 25%

Requirement Our Implementation
Classify known friendly types Ensemble classifier: XGBoost on 25+ engineered features combined with a 1D CNN on raw IQ. The ensemble hedges risk — tree-based models are interpretable and strong on tabular features; the CNN captures temporal patterns that hand-crafted features miss.
Detect out-of-distribution signals Three-layer anomaly detection: (1) softmax confidence threshold as a safety net, (2) autoencoder trained exclusively on friendly signals — high reconstruction error flags OOD, (3) embedding-space distance via one-class SVM for corroboration.
Distinguish hostile subtypes DBSCAN clustering on anomalous embeddings. We don't just flag "unknown" — we discover and label coherent hostile categories (Unknown-Alpha, Unknown-Bravo) from the data itself. Distinct colors on the COP per subtype.
Handle low SNR Below 0 dB, we shift weight toward frequency-domain features (spectral centroid, spectral flatness, Welch PSD, band energy ratios) which are empirically more noise-robust than time-domain features.
Calibrated confidence Temperature scaling on a held-out validation set. A reported 80% confidence means the model is correct approximately 80% of the time — operationally meaningful, not just raw softmax.

Feature engineering spans three domains. Time-domain: amplitude envelope statistics (mean, std, kurtosis, crest factor), zero-crossing rate, duty cycle, I/Q energy ratio. Phase/frequency: instantaneous frequency statistics, circular variance, phase wrap count. Frequency-domain: FFT spectral centroid, bandwidth, flatness, entropy, band energy ratios, significant peak count. These were selected to discriminate between the six modulation types in the training data (FMCW, BPSK, GFSK, DSSS-OQPSK, DSSS-CCK, ASK) with explicit attention to the confusion pairs that matter most: Zigbee vs. Wi-Fi (both DSSS) and any signal vs. noise at extreme low SNR.

Geolocation Accuracy — 20%

Requirement Our Implementation
Position estimation Nonlinear least squares trilateration via scipy.optimize.least_squares. RSSI converted to distance using the provided path-loss model. Coordinate math done in a local ENU frame to avoid lat/lon distortion.
Weighted estimation Per-receiver weights based on inverse RSSI noise variance and classification confidence. Closer, higher-confidence receivers contribute more.
Uncertainty estimation Residual-based CEP: the fit quality directly maps to the uncertainty ring radius on the COP. Confidence gates: if the fit residual exceeds 2× the RSSI noise standard deviation, uncertainty is inflated and the fix is flagged as degraded.
Fewer than 3 receivers Graceful degradation. A 2-receiver group produces a low-confidence fix; a 1-receiver group preserves the track at last-known position with a "degraded" indicator. The system never crashes or drops a track because of sparse data.
Moving targets Kalman filter with a constant-velocity motion model (numpy). The filter estimates position and velocity jointly, predicts between updates for accurate track matching, and fuses each new fix weighted by measurement uncertainty. EMA fallback if Kalman state is unavailable.

COP Usability — 25%

This is where half the competition will lose. A perfect classifier behind a mediocre map is operationally useless. Our COP is designed so that an operator can answer all three core questions — What's nearby? Is it friendly? When was it last seen? — within five seconds of looking at the screen.

Element Design Rationale
Color coding Green (friendly), red (hostile), orange (unknown/unverified), blue (civilian). Immediate visual parsing with no legend lookup required after first glance. Hostile subtypes get distinct red/orange shades.
Uncertainty rings Semi-transparent circles proportional to the estimated CEP. A tight ring means high confidence. A wide ring means the operator should treat the position as approximate. This is more than cosmetic — it communicates the quality of the intelligence.
Staleness Fresh tracks are solid and opaque. Stale tracks (10–60s since last observation) fade. Lost tracks (>60s) render as grey ghosts with dashed outlines. The operator can instantly distinguish live intelligence from aged data.
Detail panel Click any track: signal type, modulation, confidence %, lat/lon, uncertainty radius, observation count, contributing receiver IDs, last-seen timestamp, full position history. Everything the operator needs to assess the track without switching tools.
Alert banner When a new hostile track is created, a banner flashes at the top of the COP. The most operationally critical event — a new threat — is impossible to miss.
Visual hierarchy Hostile/unknown tracks render above friendly tracks in the z-order. The threat is always the top visual layer. Receiver positions are shown as distinct icons (antenna markers, blue) for spatial context without visual competition with emitter tracks.
Performance UI updates are batched to 5 Hz. The pipeline never blocks on rendering. WebSocket push from FastAPI ensures sub-second latency from observation to screen.

Technical Depth — 15%

We chose to go deep on two hard problems rather than shallow across ten:

1. Semi-supervised hostile discovery. The autoencoder-plus-clustering pipeline is architecturally distinct from the supervised classifier. It learns a manifold of "normal" (friendly) signals and measures how far each new observation deviates. The DBSCAN layer then organizes the anomalies into coherent groups — discovering hostile signal types from unlabeled data. This is a real-world SIGINT workflow: you know your own forces' signatures; you discover the adversary's by observing what doesn't match.

2. Observation association under ambiguity. The simulation feed is a flat stream of independent detections. Before any geolocation can happen, we must solve the combinatorial problem of grouping observations into emitter events. Our three-criteria association engine (temporal proximity, classification similarity, RSSI geometric consistency) handles simultaneous emitters, missing receivers, and classification disagreements between receivers. This is the hidden hard problem that determines whether the map shows meaningful positions or noise.

Additional technical depth includes: calibrated confidence scores (temperature scaling), confidence gates on geolocation (degraded fixes are flagged, not hidden), CoT/ATAK XML export for interoperability with the actual military COP ecosystem, and a deadman switch for feed resilience (automatic fallback to replay mode if the live feed fails).

Presentation & Demo — 15%

Our demo is designed around a narrative arc, not a feature list:

  1. The problem — three questions an operator must answer under pressure.
  2. The key insight — this is semi-supervised; we discover hostile signals, not just classify friendly ones.
  3. Live COP walkthrough — markers appearing in real time, color-coded, with uncertainty rings and staleness.
  4. The "aha" moment — hostile subtypes emerging on the map that were never in the training data.
  5. Operational credibility — CoT export button, demonstrating awareness of the ATAK ecosystem the CAF actually uses.

A pre-recorded video backup ensures the demo runs regardless of network conditions.

Tech Stack

Component Technology Rationale
ML / Pipeline Python 3.10+, XGBoost, PyTorch, scikit-learn, scipy, numpy Industry-standard ML stack. CPU-trainable on consumer hardware.
Anomaly Detection Custom autoencoder (PyTorch), DBSCAN (scikit-learn), one-class SVM Semi-supervised pipeline — learns friendly manifold, discovers hostile types.
Geolocation scipy.optimize.least_squares, numpy (Kalman) Nonlinear optimization for trilateration. Custom Kalman filter for moving targets.
Backend API FastAPI + WebSocket Native async, WebSocket support for real-time COP push, auto-docs.
COP Frontend Mapbox GL JS v3.6.0 + OpenStreetMap GPU-accelerated WebGL rendering, smooth animations, sufficient for <100 entities.
Export Cursor on Target (CoT) XML NATO-standard format, ingestible by ATAK/TAK — the COP the CAF uses.

Project Structure

find-my-force/
├── shared/
│   ├── models.py          # Typed dataclasses — the interface contract
│   └── config.py          # Receiver positions, path-loss params, constants
├── ml/
│   ├── explore.ipynb      # Signal exploration and visualization
│   ├── features.py        # 25+ feature extraction from IQ vectors
│   ├── classifier.py      # Supervised ensemble (XGBoost + CNN)
│   ├── anomaly.py         # Autoencoder + one-class SVM OOD detection
│   ├── cluster.py         # DBSCAN hostile subtype discovery
│   └── predict.py         # Unified inference entry point
├── pipeline/
│   ├── associate.py       # Three-criteria observation association
│   ├── trilaterate.py     # Weighted RSSI trilateration + confidence gates
│   ├── tracker.py         # Track state machine (create/update/age)
│   └── orchestrator.py    # Main loop: feed → classify → associate → geo → tracks
├── feed/
│   ├── consumer.py        # Live simulation feed client
│   ├── mock_feed.py       # Synthetic feed for offline testing
│   └── replay.py          # JSON-lines replay with deadman switch
├── api/
│   └── server.py          # FastAPI + WebSocket bridge to COP
├── cop/
│   ├── index.html         # Common Operating Picture dashboard
│   ├── app.js             # Map logic, WebSocket client, UI state
│   └── styles.css         # COP styling and visual hierarchy
├── export/
│   └── cot.py             # Cursor on Target XML export
├── data/                  # Training data, receiver config, path-loss params
├── tests/
│   └── test_pipeline.py   # End-to-end integration tests
└── run_demo.sh            # One-command boot: pipeline + API + COP

Local Data Placement

  • Put downloaded RadarComm-style datasets in data/RadarCommDataset-main/ or data/raw/.
  • Put generated training artifacts and transformed data in data/training/ or data/processed/.
  • Keep only lightweight config files that should be versioned in data/ (for example receivers.json, path_loss.json).
  • Large datasets, archives, and model artifacts in these paths are intentionally ignored by .gitignore to keep the repo lean.

Running the System

1. Set Up the Python Virtual Environment

Create a virtual environment

python3 -m venv .venv

2. Activate it

macOS / Linux:

source .venv/bin/activate

Windows (PowerShell)

.venv\Scripts\activate

3. Install dependencies

pip install -r requirements.txt

3.5 Store Competition API Key Locally (Untracked)

For competition mode, keep your key in a local file that is ignored by git:

New-Item -ItemType Directory -Force .secrets | Out-Null
Set-Content -Path .secrets/competition_api_key.txt -Value "<your-api-key>"

The backend automatically reads this file via COMPETITION_API_KEY_FILE (default path: .secrets/competition_api_key.txt). You can still override with env vars if needed.

4. Start the System

Start everything (mock feed mode by default)

./run_demo.sh

— or connect to the live simulation feed —

FEED_MODE=live FEED_URL=<simulation-endpoint> ./run_demo.sh

5. Open in Your Browser

Service URL
COP (map) http://localhost:8080
API docs http://localhost:8000/docs
API root http://localhost:8000

Both ports are configurable via environment variables COP_PORT and BACKEND_PORT.

6. Tear Down

Stop the system --> Press Ctrl+C in the terminal running run_demo.sh

7. Deactivate the virtual environment

deactivate

8. (Optional) Remove the virtual environment entirely

rm -rf .venv

Running the Mobile Companion App

The H.A.R.P. mobile client is a React Native (Expo) app that mirrors the COP on iOS and Android — live track counts, hostile alerts, and an interactive satellite map with real-time blips.

Prerequisites

  • Node.js 18+
  • Expo Go installed on your phone (App Store / Google Play)
  • Phone and laptop on the same Wi-Fi network

1. Install dependencies

cd mobile
npm install

2. Set your LAN IP

Find your laptop's local IP:

# macOS
ipconfig getifaddr en0

# Linux
hostname -I | awk '{print $1}'

# Windows (PowerShell)
(Get-NetIPAddress -AddressFamily IPv4 -InterfaceAlias Wi-Fi).IPAddress

Edit mobile/src/config/env.ts and replace the LAN_IP value:

const LAN_IP = "<your-lan-ip>";

3. Start the backend

In a separate terminal (from the project root):

./run_demo.sh

4. Start the Expo dev server

cd mobile
npx expo start --clear

5. Open on your phone

Scan the QR code shown in the terminal with your phone's camera (iOS) or the Expo Go app (Android).

6. Verify connectivity

Indicator Meaning
LIVE (green badge) Connected — tracks streaming
RECONNECTING (orange) Can't reach backend — check that the LAN IP is correct and both devices are on the same network
OFFLINE (red) WebSocket closed — restart the backend

Tip: If the badge stays on RECONNECTING, open http://<your-lan-ip>:8000/docs in Safari on your phone. If it doesn't load, your network has client isolation enabled — try a mobile hotspot or use ngrok http 8000 to tunnel.

Team

Role Responsibility
ML Lead (P1) Classifier architecture, feature engineering, CNN, autoencoder
ML / Eval (P2) Data exploration, preprocessing, confidence calibration, scoring iteration
Association + Geo (P3) Observation grouping, trilateration, track management, Kalman filter
COP Lead (P4) Dashboard UI, map rendering, visual hierarchy, uncertainty display
Integration (P5) Feed consumer, API layer, pipeline orchestration, CoT export, demo prep

Why This Should Win

We didn't build a classifier and bolt on a map. We built a signals intelligence pipeline that mirrors the real-world SIGINT workflow: detect, classify, associate, locate, track, and present. Every stage is typed, tested, and decoupled.

The semi-supervised anomaly detection pipeline is the architectural differentiator. Most teams will train a six-class classifier and score zero on hostile detection. We learn what friendly looks like and discover what hostile looks like — then we cluster the threats into distinguishable subtypes that appear as distinct entities on the operator's map.

The COP is built for the operator, not for the engineer. Color, shape, opacity, and ring size each encode a distinct dimension of information. An operator can glance at the map and know, in under five seconds, what's out there, whether it's friendly, and how much to trust the data.

The CoT export isn't a checkbox — it's a statement that we understand where this technology lives in the real CAF ecosystem. Find My Force doesn't replace ATAK. It feeds ATAK.


Built for the Find My Force Hackathon — March 7, 2026 — University of British Columbia

About

Red Team Hackathon Project

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Python 67.6%
  • JavaScript 19.0%
  • HTML 10.0%
  • TypeScript 2.3%
  • Shell 1.1%