Skip to content
Flight Alerter & Air Traffic Visualization Migration to Uncrew

Flight Alerter & Air Traffic Visualization Migration to Uncrew

Andi Lamprecht Andi Lamprecht ·· 13 min read· Draft

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

AreaDescription
Conflict Detection EngineOCDV/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
ViewAll traffic displays in Mission Manager. Mission Console only displays traffic relevant (close proximity) to an RPIC’s assigned, active missions.
DAA Alert VisualizationOCDV/OCAV alert circles/volumes rendered on the map
Audio AlertsAudible warnings when aircraft enter/exit detection/alert volumes
Alert Notification PanelUI panel showing active intrusion alerts with status
Operator ReportsPost-flight compliance reports showing intrusion events
Hub/Location ManagementDynamic hub locations (replacing hardcoded Clojure list)

Out of Scope

AreaReason
ADS-B/CAT021 ingestion from UDP receiversAlready handled by Uncrew’s existing ingestion pipeline
ASTERIX binary parserNot needed — Uncrew already parses this data
Redis journal for plot IDsImplementation detail of the old system
Frontegg authenticationApollo already uses Frontegg/Auth0
Air Traffic Source AvailabilityFlight 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 VisualizationInherited feature from the original Spaces application use case. Does not serve a purpose for DAA and should not be migrated.
New features beyond current functionalityThis 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, airspace

Target 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, airspace

4. 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_margin

Algorithm Constants

Updated per VT MAAP Chapter 1 Report (March 2025) section 4.5.5 recommendations. Previous values shown for traceability.

ConstantValueUnitDescriptionPrevious Value
ocdv_radius2,778metersOCDV horizontal radius (~1.5 statute miles)unchanged
ocdv_height76.2metersOCDV vertical half-height (250 ft)unchanged
ocav_height76.2metersOCAV vertical half-height (250 ft)unchanged
well_clear_radius610metersMinimum well-clear distance (2,000 ft)unchanged
max_intruder_airspeed77.17m/sDefault if speed unknown (150 knots)unchanged
descent_rate1.219m/sDrone 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_delay3secondsTime 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_latency1secondGround control station communication delay. Confirmed by Chapter 1 test data (Tsl measured at ~0.13 sec).unchanged
time_to_initiate_maneuver17secondsTime 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_uncertainty1secondADS-B position uncertainty margin. Confirmed by Chapter 1 test data (Tpos measured at ~1.1 sec).unchanged
deceleration_buffer5secondsNEW. 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_margin10secondsNEW. 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 <= radius

Alert States

StateConditionSeverity
All ClearNo plots within OCDVINFO
OCDV IntrusionPlot within OCDV but outside OCAVCAUTION
OCAV IntrusionPlot within OCAVWARNING

4.3 Required Data Inputs

DataSourceUpdate Frequency
ADS-B PlotsUncrew’s existing ingestion pipeline~1-2 Hz per aircraft
Drone TelemetryUncrew missions service (drone position, altitude, speed)~1-2 Hz per drone
Site LocationsDatabase or configuration serviceOn change

4.4 Required Data Outputs

OutputFormatConsumers
Enriched PlotsgRPC streamApollo frontend
Intrusion AlertsgRPC streamApollo frontend, notification systems
Alert State ChangesEvents (enter/exit OCDV/OCAV)Apollo frontend (for audio alerts)
Historical Intrusion RecordsDatabase writesOperator 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:

  1. Site proximity filter: Only process plots within 10 miles (16,093.4 meters) of any active site
  2. Haversine distance: Use great-circle distance for all proximity calculations
  3. 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

IDRequirement
AT-1Display manned aircraft as icons on the Mapbox map, updated in real-time (~1-2 Hz)
AT-2Aircraft icons must be rotated to match heading direction
AT-3Aircraft icons must be visually distinct from drone icons already on the map
AT-5Each aircraft must show a label with altitude (in feet) and optionally the aircraft ID (tail number)
AT-6Aircraft ID label visibility must be toggleable via keyboard shortcut or UI control
AT-7Stale plots (>5 seconds old) must be automatically removed from the map
AT-8Aircraft icon color must reflect intrusion status: blue/grey (clear), yellow (OCDV), red (OCAV)
AT-9Clicking an aircraft icon must show a detail panel with: aircraft ID, lat/lon, altitude, airspeed, heading, data timestamp
AT-10Aircraft 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 StateIcon ColorMeaning
ClearBlue/Grey (#617EC6)Not intruding on any drone
OCDV IntrusionYellow (#E3C039)Within detection volume of a drone
OCAV IntrusionRed (#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

IDRequirement
DA-1Draw OCAV alert circles around drones that have nearby manned aircraft within OCDV
DA-2OCAV circles must use the dynamically computed radius based on intruder speed (not hardcoded)
DA-3OCAV circles must be rendered as red semi-transparent circular polygons
DA-4Optionally show OCDV detection volume as a larger yellow semi-transparent circle
DA-5Alert circles must move with the drone’s real-time position
DA-6Alert 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

IDRequirement
AU-1Play an “attention” sound when an aircraft enters the OCDV (detection volume)
AU-2Play a “warning” sound (more urgent) when an aircraft enters the OCAV (alert volume)
AU-3Play an “all clear” sound when an aircraft exits all volumes

Audio Files

Reuse existing audio assets from utm-themis-spaces:

  • enter.mp3 — attention alert
  • warning.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

IDRequirement
NP-1Display a list of active intrusion alerts showing: drone mission ID, intruding aircraft ID, alert type (OCDV/OCAV), distance, timestamp
NP-2Alerts must be sorted by severity (OCAV first, then OCDV) and recency
NP-3Clicking an alert must center the map on the relevant drone-aircraft pair
NP-4Alert count badge must be visible in the navigation/header when alerts are active
NP-5Visual distinction between OCDV (yellow) and OCAV (red) alerts
NP-6Alerts 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

IDRequirement
OR-1Generate reports for a given time range and operational authority
OR-2Report must include: all intrusion events, timestamps, distances, aircraft IDs, drone mission IDs
OR-3Report must be viewable in-app and exportable (PDF or CSV)
OR-4Historical intrusion data sourced from the Conflict Detection Service

6. New Zustand Stores Required

StorePurpose
airTrafficStateManages the collection of current ADS-B plots on the map. Keyed by aircraft_id. Handles deduplication, staleness cleanup (>5s).
intrusionAlertsManages active intrusion alerts. Tracks OCDV/OCAV state per drone-aircraft pair. Drives the notification panel and audio alerts.
airspaceStateManages airspace boundary and operational intent data for map rendering.
daaSettingsUser preferences: audio enable/disable, volume, aircraft label visibility, layer toggles.

7. New gRPC Clients Required

ClientServicePurpose
conflictDetectionClientConflictDetectionServiceStream 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:

IssueCurrent BehaviorTarget Behavior
Hardcoded OCAV radiusOCAV radius hardcoded to 2,172m in frontendUse dynamically computed radius from backend
Hardcoded helicopter identificationOnly 3 tail numbers recognized as helicoptersUse ADS-B emitter category data for aircraft type
Static hub list18 hubs hardcoded in Clojure sourceDatabase-driven hub locations
Single-region mapHardcoded to Norfolk area onlySupport all operational regions dynamically
WebSocket stream never closesstream/close! is a no-op (TODO in code)Properly close gRPC streams on page exit
No client-side error boundaryEntire SPA crashes on render errorReact error boundary (Apollo already has react-error-boundary)
Hardcoded KNGU airspace geometryClient-side override of server dataUse server-provided geometry exclusively

8.4 What Can Be Retired After Migration

Once all features are live in Apollo and verified:

RepositoryCan Retire?Notes
utm-themis-spacesYes, fullyAll features migrated to Apollo
utm-themis-grpcYes, fullyReplaced by Conflict Detection Service
utm-themis-daaYes, fullyADS-B ingestion already in Uncrew
utm-themis-apiPartiallyOnly the plot/intrusion streaming parts; flight plan API may still be needed
utm-clojure-kit (“duck”)PartiallyDAA 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

RiskMitigation
DAA algorithm port introduces calculation differencesExtensive test suite porting from daac_spec.cljc with identical test vectors. Additionally, validate against Chapter 1 encounter data (27 encounters).
Real-time performance regressionLoad testing with simulated traffic at 2x expected volume
Regulatory compliance gap during transitionParallel operation period; old system remains available
Uncrew ADS-B ingestion data format differs from Themis plotsData mapping layer in the Conflict Detection Service
Mapbox GL rendering performance with many aircraftUse deck.gl (already in Apollo) for efficient WebGL rendering of large point sets
Updated timing constants produce larger OCAV than previously testedValidate updated constants through M&S simulation to confirm LoWC Risk Ratio < 0.4 per ASTM F3442-23 before go-live

11. References

Last updated on