Flight Alerter & Air Traffic Visualization Migration to Uncrew
Goal: Retire the Clojure-based UTM Themis flight-alerter and airspace visualization by migrating all features into the Uncrew platform (Go backend + Apollo React frontend).
1. Executive Summary
DroneUp currently runs a Clojure-based UTM Themis stack for Detect-and-Avoid (DAA) functionality:
- utm-themis-daa — Ingests raw ADS-B data (ASTERIX CAT021) from ground receivers, parses, geo-filters, stores plots in PostgreSQL, and publishes to Redis.
- utm-themis-api — REST + WebSocket API that streams enriched plots (with intrusion markers) to subscribers, manages flight plans, and computes OCDV/OCAV intrusion detection.
- utm-themis-grpc — gRPC streaming service that reads from Redis journals, fetches full plots from PostgreSQL, runs DAA intrusion detection, and streams to consumers.
- utm-themis-spaces — Clojure/ClojureScript full-stack web app providing the Airspace Manager UI with real-time 3D map, DAA alert visualization, audio alerts, flight plan management, and operator compliance reports.
- utm-clojure-kit (“duck”) — Shared library containing CAT021 parser, DAA algorithms (OCDV/OCAV), geometry functions, and infrastructure abstractions.
This PRD covers migrating everything downstream of ADS-B ingestion (i.e., the flight-alerter / conflict detection logic, real-time streaming, and frontend visualization). The CAT021 ingestion pipeline (utm-themis-daa) is explicitly out of scope since Uncrew already handles air traffic data ingestion (see Airdex and future onboard ADS-B ingestion).
2. Scope
In Scope
| Area | Description |
|---|---|
| Conflict Detection Engine | OCDV/OCAV intrusion detection algorithm, ported to Go |
| Air Traffic Data Streaming (extension) | Already exists for Airdex traffic; may need to be adopted |
| Air Traffic Map Visualization (extension) | Already exists for Airdex traffic; may need to be adopted |
| View | All traffic displays in Mission Manager. Mission Console only displays traffic relevant (close proximity) to an RPIC’s assigned, active missions. |
| DAA Alert Visualization | OCDV/OCAV alert circles/volumes rendered on the map |
| Audio Alerts | Audible warnings when aircraft enter/exit detection/alert volumes |
| Alert Notification Panel | UI panel showing active intrusion alerts with status |
| Operator Reports | Post-flight compliance reports showing intrusion events |
| Hub/Location Management | Dynamic hub locations (replacing hardcoded Clojure list) |
Out of Scope
| Area | Reason |
|---|---|
| ADS-B/CAT021 ingestion from UDP receivers | Already handled by Uncrew’s existing ingestion pipeline |
| ASTERIX binary parser | Not needed — Uncrew already parses this data |
| Redis journal for plot IDs | Implementation detail of the old system |
| Frontegg authentication | Apollo already uses Frontegg/Auth0 |
| Air Traffic Source Availability | Flight Alerter implementation was never complete (PoC integration with ResilienX for outage detection and impact assessment). Do not port this functionality over. Availability will need to be treated as a new feature. |
| Operational Intent Visualization | Inherited feature from the original Spaces application use case. Does not serve a purpose for DAA and should not be migrated. |
| New features beyond current functionality | This is a 1:1 migration, not an enhancement (outside of supporting 1:m which Flight Alerter did not do) |
3. Current System Architecture (What We’re Replacing)
ADS-B Receiver (uAvionix)
| UDP (ASTERIX CAT021)
v
utm-themis-daa (Clojure) -- ALREADY REPLACED BY UNCREW INGESTION
| Redis journal + PostgreSQL
v
utm-themis-grpc (Clojure)
| gRPC stream (enriched plots with OCDV/OCAV markers)
v
utm-themis-api (Clojure)
| WebSocket push
v
utm-themis-spaces (ClojureScript)
| Browser: Mapbox GL + Threebox 3D
v
Operator sees: aircraft, drones, alerts, airspaceTarget Architecture (What We’re Building)
Uncrew ADS-B Ingestion (existing)
| Internal data store (plots)
v
Uncrew Conflict Detection Service (NEW Go service)
| Reads plots + drone telemetry
| Computes OCDV/OCAV intrusions
| Streams alerts + enriched plots via gRPC
v
Uncrew Apollo Frontend (ENHANCED React app)
| gRPC-Web / Connect
| Mapbox GL + deck.gl
v
Operator sees: aircraft, drones, alerts, airspace4. Backend: Conflict Detection Service (Go)
4.1 Service Purpose
A new Go microservice within the Uncrew ecosystem that continuously evaluates proximity between active drone flights and nearby manned aircraft (ADS-B plots), producing real-time intrusion alerts.
4.2 Core Algorithm: Two-Volume DAA Model
The service must implement the same OCDV/OCAV intrusion detection algorithm currently in utm-clojure-kit/daac.cljc, with updated timing constants per VT MAAP Chapter 1 Report (March 2025) section 4.5.5 recommendations:
OCDV (Ownship Centric Detection Volume)
- Shape: Cylinder centered on the drone (ownship)
- Horizontal radius: 2,778 meters (~1.5 statute miles)
- Vertical half-height: 76.2 meters (250 feet)
- Purpose: Outer detection zone — “be aware, monitor”
OCAV (Ownship Centric Alert Volume)
- Shape: Cylinder centered on the drone (ownship)
- Horizontal radius: Dynamic, depends on intruder speed and relative geometry
- Vertical half-height: 76.2 meters (250 feet)
- Purpose: Inner alert zone — “take evasive action NOW”
OCAV Radius Calculation
OCAV_radius = well_clear_radius + (intruder_airspeed * time_to_avoid)
time_to_avoid = intruder_position_uncertainty
+ pilot_delay
+ gcs_latency
+ time_to_initiate_maneuver
+ time_to_well_clear(ownship, intruder)
+ deceleration_buffer
+ safety_marginAlgorithm Constants
Updated per VT MAAP Chapter 1 Report (March 2025) section 4.5.5 recommendations. Previous values shown for traceability.
| Constant | Value | Unit | Description | Previous Value |
|---|---|---|---|---|
ocdv_radius | 2,778 | meters | OCDV horizontal radius (~1.5 statute miles) | unchanged |
ocdv_height | 76.2 | meters | OCDV vertical half-height (250 ft) | unchanged |
ocav_height | 76.2 | meters | OCAV vertical half-height (250 ft) | unchanged |
well_clear_radius | 610 | meters | Minimum well-clear distance (2,000 ft) | unchanged |
max_intruder_airspeed | 77.17 | m/s | Default if speed unknown (150 knots) | unchanged |
descent_rate | 1.219 | m/s | Drone descent rate (4 fps / 240 ft/min). Chapter 1 test data measured 4.1-4.9 fps; 4 fps is the conservative value per recommendation #4. | was 1.524 m/s (5 fps) |
pilot_delay | 3 | seconds | Time for pilot to react. Chapter 1 test data confirmed this value (Tpil measured at ~1.5 sec average, but 3 sec is the design margin). | was 5 seconds |
gcs_latency | 1 | second | Ground control station communication delay. Confirmed by Chapter 1 test data (Tsl measured at ~0.13 sec). | unchanged |
time_to_initiate_maneuver | 17 | seconds | Time to begin and establish evasive maneuver (Ttransition). Chapter 1 test data measured 16.42 sec; rounded up per recommendation #2. | was 10 seconds |
intruder_position_uncertainty | 1 | second | ADS-B position uncertainty margin. Confirmed by Chapter 1 test data (Tpos measured at ~1.1 sec). | unchanged |
deceleration_buffer | 5 | seconds | NEW. Time for drone to decelerate at bottom of descent maneuver. Chapter 1 data showed 5.6 sec difference between descent-to-shielded and descent-to-vertical-WC due to deceleration. Per recommendation #3a. | not previously included |
safety_margin | 10 | seconds | NEW. Accounts for intruder descent after warning, C2 delays, and statistical variances in telemetry readings. Per recommendation #3b. Required to keep DAA performance on the safe side of the acceptable Risk Ratio threshold (LoWC RR < 0.4). | not previously included |
Intrusion Detection Logic
intrusion?(ownship, radius, height, plot):
distance = haversine_distance(ownship, plot)
delta_altitude = abs(plot.altitude - ownship.altitude)
// If plot altitude is unknown, assume same altitude (worst case)
if plot.altitude == nil:
delta_altitude = 0
return delta_altitude <= height AND distance <= radiusAlert States
| State | Condition | Severity |
|---|---|---|
| All Clear | No plots within OCDV | INFO |
| OCDV Intrusion | Plot within OCDV but outside OCAV | CAUTION |
| OCAV Intrusion | Plot within OCAV | WARNING |
4.3 Required Data Inputs
| Data | Source | Update Frequency |
|---|---|---|
| ADS-B Plots | Uncrew’s existing ingestion pipeline | ~1-2 Hz per aircraft |
| Drone Telemetry | Uncrew missions service (drone position, altitude, speed) | ~1-2 Hz per drone |
| Site Locations | Database or configuration service | On change |
4.4 Required Data Outputs
| Output | Format | Consumers |
|---|---|---|
| Enriched Plots | gRPC stream | Apollo frontend |
| Intrusion Alerts | gRPC stream | Apollo frontend, notification systems |
| Alert State Changes | Events (enter/exit OCDV/OCAV) | Apollo frontend (for audio alerts) |
| Historical Intrusion Records | Database writes | Operator reports |
4.5 gRPC Service Definition (Proposed)
service ConflictDetectionService {
// Stream enriched plot data with intrusion status
rpc StreamPlots(StreamPlotsRequest) returns (stream PlotUpdate);
// Stream intrusion alerts for active flights
rpc StreamAlerts(StreamAlertsRequest) returns (stream IntrusionAlert);
// Get historical intrusion events for a flight
rpc GetFlightIntrusions(GetFlightIntrusionsRequest) returns (FlightIntrusionsResponse);
// Get current alert state for all active flights
rpc GetActiveAlerts(GetActiveAlertsRequest) returns (ActiveAlertsResponse);
}
message PlotUpdate {
string aircraft_id = 1;
double latitude = 2;
double longitude = 3;
double altitude_meters = 4; // WGS-84 meters
double airspeed_mps = 5;
double heading = 6;
google.protobuf.Timestamp as_of = 7;
AircraftType aircraft_type = 8; // FIXED_WING, HELICOPTER, UAV, UNKNOWN
IntrusionStatus intrusion_status = 9; // CLEAR, OCDV, OCAV
string nearest_mission_id = 10; // Which drone is this closest to
}
message IntrusionAlert {
string alert_id = 1;
string mission_id = 2; // The drone flight
string aircraft_id = 3; // The intruding aircraft
AlertType alert_type = 4; // OCDV_ENTER, OCDV_EXIT, OCAV_ENTER, OCAV_EXIT
double distance_meters = 5;
double ocav_radius_meters = 6;
google.protobuf.Timestamp timestamp = 7;
}
enum IntrusionStatus {
CLEAR = 0;
OCDV = 1;
OCAV = 2;
}
enum AlertType {
OCDV_ENTER = 0;
OCDV_EXIT = 1;
OCAV_ENTER = 2;
OCAV_EXIT = 3;
}
enum AircraftType {
UNKNOWN = 0;
FIXED_WING = 1;
HELICOPTER = 2;
UAV = 3;
}4.6 Geospatial Filtering
The service must filter plots by geographic relevance before running DAA detection:
- Site proximity filter: Only process plots within 10 miles (16,093.4 meters) of any active site
- Haversine distance: Use great-circle distance for all proximity calculations
- Site locations should be database-driven (not hardcoded like the current Clojure implementation)
4.7 Staleness & Cleanup
- Plot staleness: Plots older than 5 seconds should be considered stale and removed from active tracking
- Telemetry staleness: Drone telemetry older than 5 seconds should be considered stale
4.8 Performance Requirements
- Latency: Plot-to-alert latency must be < 500ms
- Throughput: Must handle 1000+ aircraft plots/second across all hubs simultaneously
- Concurrent flights: Must support 50+ simultaneous active drone flights
5. Frontend: Apollo Air Traffic & Alert Features
5.1 Technology Context
The uncrew-apollo-frontend is a React/TypeScript application using:
- Map: Mapbox GL JS v2.14+ with react-map-gl, deck.gl v9
- State: Zustand
- Data: gRPC-Web via @connectrpc/connect, TanStack Query
- UI: MUI Material v7, @droneup/uncrew-react-design-system
- Telemetry: Existing drone telemetry subscription via gRPC streaming
- Geometry: @turf/turf for geospatial operations
- Auth: Frontegg (@frontegg/react)
5.2 Feature: Air Traffic (ADS-B Plot) Visualization
Priority: P0 — Must Have
Display nearby manned aircraft positions on the existing map in real-time.
Requirements
| ID | Requirement |
|---|---|
| AT-1 | Display manned aircraft as icons on the Mapbox map, updated in real-time (~1-2 Hz) |
| AT-2 | Aircraft icons must be rotated to match heading direction |
| AT-3 | Aircraft icons must be visually distinct from drone icons already on the map |
| AT-5 | Each aircraft must show a label with altitude (in feet) and optionally the aircraft ID (tail number) |
| AT-6 | Aircraft ID label visibility must be toggleable via keyboard shortcut or UI control |
| AT-7 | Stale plots (>5 seconds old) must be automatically removed from the map |
| AT-8 | Aircraft icon color must reflect intrusion status: blue/grey (clear), yellow (OCDV), red (OCAV) |
| AT-9 | Clicking an aircraft icon must show a detail panel with: aircraft ID, lat/lon, altitude, airspeed, heading, data timestamp |
| AT-10 | Aircraft data must be deduplicated by aircraft ID (only show latest position per aircraft) |
Data Source
- gRPC stream from traffic source
- Filtered by geographic bounds visible on the map
Visual Specification (Matching Current Spaces Behavior)
| Aircraft State | Icon Color | Meaning |
|---|---|---|
| Clear | Blue/Grey (#617EC6) | Not intruding on any drone |
| OCDV Intrusion | Yellow (#E3C039) | Within detection volume of a drone |
| OCAV Intrusion | Red (#B83032) | Within alert volume — evasive action needed |
5.3 Feature: DAA Alert Visualization
Priority: P0 — Must Have
Visualize OCDV and OCAV volumes around active drones, showing when manned aircraft are intruding.
Requirements
| ID | Requirement |
|---|---|
| DA-1 | Draw OCAV alert circles around drones that have nearby manned aircraft within OCDV |
| DA-2 | OCAV circles must use the dynamically computed radius based on intruder speed (not hardcoded) |
| DA-3 | OCAV circles must be rendered as red semi-transparent circular polygons |
| DA-4 | Optionally show OCDV detection volume as a larger yellow semi-transparent circle |
| DA-5 | Alert circles must move with the drone’s real-time position |
| DA-6 | Alert circles must appear/disappear as aircraft enter/exit detection volumes |
5.4 Feature: Audio Alerts
Priority: P0 — Must Have
Provide audible warnings when aircraft enter or exit detection/alert volumes.
Requirements
| ID | Requirement |
|---|---|
| AU-1 | Play an “attention” sound when an aircraft enters the OCDV (detection volume) |
| AU-2 | Play a “warning” sound (more urgent) when an aircraft enters the OCAV (alert volume) |
| AU-3 | Play an “all clear” sound when an aircraft exits all volumes |
Audio Files
Reuse existing audio assets from utm-themis-spaces:
enter.mp3— attention alertwarning.mp3— critical alert (2-second duration)exit.mp3— all-clear
5.5 Feature: Alert Notification Panel
Priority: P0 — Must Have
A sidebar or panel showing active intrusion alerts and their status.
Requirements
| ID | Requirement |
|---|---|
| NP-1 | Display a list of active intrusion alerts showing: drone mission ID, intruding aircraft ID, alert type (OCDV/OCAV), distance, timestamp |
| NP-2 | Alerts must be sorted by severity (OCAV first, then OCDV) and recency |
| NP-3 | Clicking an alert must center the map on the relevant drone-aircraft pair |
| NP-4 | Alert count badge must be visible in the navigation/header when alerts are active |
| NP-5 | Visual distinction between OCDV (yellow) and OCAV (red) alerts |
| NP-6 | Alerts must auto-resolve (disappear) when the aircraft exits the volume |
5.8 Feature: Operator Compliance Reports
Priority: P1 — Should Have
Post-flight reports showing intrusion events for regulatory compliance.
Requirements
| ID | Requirement |
|---|---|
| OR-1 | Generate reports for a given time range and operational authority |
| OR-2 | Report must include: all intrusion events, timestamps, distances, aircraft IDs, drone mission IDs |
| OR-3 | Report must be viewable in-app and exportable (PDF or CSV) |
| OR-4 | Historical intrusion data sourced from the Conflict Detection Service |
6. New Zustand Stores Required
| Store | Purpose |
|---|---|
airTrafficState | Manages the collection of current ADS-B plots on the map. Keyed by aircraft_id. Handles deduplication, staleness cleanup (>5s). |
intrusionAlerts | Manages active intrusion alerts. Tracks OCDV/OCAV state per drone-aircraft pair. Drives the notification panel and audio alerts. |
airspaceState | Manages airspace boundary and operational intent data for map rendering. |
daaSettings | User preferences: audio enable/disable, volume, aircraft label visibility, layer toggles. |
7. New gRPC Clients Required
| Client | Service | Purpose |
|---|---|---|
conflictDetectionClient | ConflictDetectionService | Stream plots, stream alerts, fetch historical intrusions |
8. Migration Considerations
8.1 Data Continuity
- No data migration needed — this is a runtime service, not a data store.
- Historical intrusion data from the old system is retained in the Themis PostgreSQL database for regulatory compliance; it does not need to be migrated but should remain accessible.
8.2 Parallel Operation
- The new system should run in parallel with the old Themis Spaces UI during transition.
- Operators should be able to use either system until confidence is established.
8.3 Known Issues to Fix During Migration
These issues exist in the current Clojure implementation and should be addressed:
| Issue | Current Behavior | Target Behavior |
|---|---|---|
| Hardcoded OCAV radius | OCAV radius hardcoded to 2,172m in frontend | Use dynamically computed radius from backend |
| Hardcoded helicopter identification | Only 3 tail numbers recognized as helicopters | Use ADS-B emitter category data for aircraft type |
| Static hub list | 18 hubs hardcoded in Clojure source | Database-driven hub locations |
| Single-region map | Hardcoded to Norfolk area only | Support all operational regions dynamically |
| WebSocket stream never closes | stream/close! is a no-op (TODO in code) | Properly close gRPC streams on page exit |
| No client-side error boundary | Entire SPA crashes on render error | React error boundary (Apollo already has react-error-boundary) |
| Hardcoded KNGU airspace geometry | Client-side override of server data | Use server-provided geometry exclusively |
8.4 What Can Be Retired After Migration
Once all features are live in Apollo and verified:
| Repository | Can Retire? | Notes |
|---|---|---|
utm-themis-spaces | Yes, fully | All features migrated to Apollo |
utm-themis-grpc | Yes, fully | Replaced by Conflict Detection Service |
utm-themis-daa | Yes, fully | ADS-B ingestion already in Uncrew |
utm-themis-api | Partially | Only the plot/intrusion streaming parts; flight plan API may still be needed |
utm-clojure-kit (“duck”) | Partially | DAA algorithm ported to Go; other utilities may still be used by remaining Themis services |
9. Acceptance Criteria Summary
P0 (Must Have — Required for launch)
- Conflict Detection Service running in Go, computing OCDV/OCAV in real-time
- ADS-B aircraft visible on Apollo map with real-time position updates
- Aircraft color-coded by intrusion status (clear/OCDV/OCAV)
- OCAV alert circles rendered around drones with nearby aircraft
- Audio alerts on intrusion state changes
- Alert notification panel showing active intrusions
- Plot staleness cleanup (>5s auto-removal)
- DAA algorithm constants match updated values from VT MAAP Chapter 1 section 4.5.5 (safety-critical)
P1 (Should Have — Required for Themis retirement)
- Dynamic hub location management
P2 (Nice to Have — Improvements over current system)
- Aircraft type detection from ADS-B emitter category (instead of hardcoded tail numbers)
10. Dependencies & Risks
| Risk | Mitigation |
|---|---|
| DAA algorithm port introduces calculation differences | Extensive test suite porting from daac_spec.cljc with identical test vectors. Additionally, validate against Chapter 1 encounter data (27 encounters). |
| Real-time performance regression | Load testing with simulated traffic at 2x expected volume |
| Regulatory compliance gap during transition | Parallel operation period; old system remains available |
| Uncrew ADS-B ingestion data format differs from Themis plots | Data mapping layer in the Conflict Detection Service |
| Mapbox GL rendering performance with many aircraft | Use deck.gl (already in Apollo) for efficient WebGL rendering of large point sets |
| Updated timing constants produce larger OCAV than previously tested | Validate updated constants through M&S simulation to confirm LoWC Risk Ratio < 0.4 per ASTM F3442-23 before go-live |
11. References
- Deep Analysis of UTM Themis DAA
- Deep Analysis of UTM Themis Spaces
- utm-themis-daa repository
- utm-themis-spaces repository
- utm-clojure-kit (duck) — DAA algorithm source (
src/cljc/droneup/duck/daac.cljc) - uncrew-apollo-frontend repository
- uncrew-missions-service repository
- VT MAAP Chapter 1 Report — DroneUp DAA System Testing (March 2025). Section 4.5.5 Recommendations.
- ASTM F3442M-23 — Standard Specification for Detect and Avoid Performance Requirements