Talk to me · Julian Reder · Resume · GitHub

PyAerial

AERPAW · 2024 - Present

An airspace awareness and aircraft tracking system that ingests Mode S and ADS-B signals, evaluates geofence boundaries, and displays live flights on a radar map.

2024 - Present View Project ↗
PythonpyModeSShapelyRedisSQLiteFastAPIReactDockerRTL-SDRADS-B / Mode-S
RTL-SDR dump1090 AVR/Beast Engine merge, pyModeS Kinematics Kalman, ETA Geofence Shapely, KML Redis live tracker owns keys SQLite WAL flight history Portal, FastAPI + React WebSocket radar, Beast, webhooks
Pipeline overview showing how radio signals pass from dump1090 through pyModeS decoding, geofence checks, and Redis caching before reaching the web map.

Built

I built PyAerial as a complete rewrite of my earlier aircraft tracking project, airstrik.py. The application decodes raw ADS-B radio signals using pyModeS, evaluates custom Shapely geofence boundaries, and separates the real-time tracking daemon from the web dashboard so that user interface traffic never interrupts signal decoding.

Hard parts

  • Decoding Compact Position Reporting coordinates requires a reliable reference position separate from boundary areas to prevent receiver errors from producing false aircraft locations.
  • Merging signals from multiple ground receivers requires comparing signal strength and Hamming distance while correctly wrapping compass heading boundaries across zero degrees.
  • Estimated arrival calculations had to account for both straight paths and turning maneuvers to ensure aircraft approaching boundaries are detected consistently.
  • Geofence notifications needed timing hysteresis to avoid sending multiple alerts when an aircraft briefly skirts the edge of an airspace boundary.
  • The background tracker maintains a persistent heartbeat in Redis so that read-only web workers cannot accidentally overwrite live tracking state.

Learned

  • Handling radio broadcast formats requires validating packet integrity and message order before attempting to plot coordinates on a map.
  • Separating real-time state in Redis from long-term flight logs in SQLite provides much better performance and reliability than trying to use a single database for both.
  • Geofence rules work best when they include explicit flight parameters like altitude ceilings, ground speed, and arrival time rather than just simple point checks.

Also

  • Custom boundary definitions imported from inline coordinates, KML files, or GeoJSON layers.
  • Kinematic trajectory calculations including both straight-line and turning arrival time estimations.
  • FastAPI backend paired with a React and Leaflet radar interface that streams live aircraft updates.
  • Docker Compose configurations that support both live software-defined radio feeds and recorded data.
  • Automated testing pipeline validating Python decoding logic and frontend build scripts.

Airspace awareness for the testbed

While AERPAW vehicles operate on controlled frequencies and communicate through flight filters, our team also needed visibility into commercial and general aviation traffic flying near the test site. Most aircraft broadcast their identification, altitude, and position continuously over Mode S and ADS-B radio frequencies. I created PyAerial to ingest these radio broadcasts from local ground receivers, determine whether any planes are approaching testbed boundaries, and display current airspace activity on a live map.

This project replaced an earlier prototype called airstrik.py, which used simple circular distance checks, MongoDB, and Kafka. PyAerial redesigns the entire workflow around Redis for live state caching, SQLite for permanent flight logs, and configurable webhooks for alert notifications, creating a much simpler and more reliable deployment for field operations.

Decoupling tracking from the web interface

The tracking daemon runs as a dedicated background process that connects to dump1090 receiver streams, decodes raw message packets with pyModeS, and combines overlapping signals from multiple receivers based on signal strength. It writes active aircraft coordinates into Redis with short expiration timeouts while recording completed flight sessions into an append-only SQLite database using write-ahead logging.

The web dashboard runs as a separate process built with FastAPI and React, providing map visualizations and status reports by reading from the shared cache. Because the web server never touches the raw radio stream, heavy web traffic or browser reconnections can never slow down or interrupt time-sensitive position decoding in the tracker.

RTL-SDR dump1090 AVR/Beast Engine merge, pyModeS Kinematics Kalman, ETA Geofence Shapely, KML Redis live tracker owns keys SQLite WAL flight history Portal, FastAPI + React WebSocket radar, Beast, webhooks
Architecture diagram highlighting the boundary between the radio decoding daemon and the read-only web portal.

Position decoding and geofence evaluation

One of the trickiest parts of processing ADS-B signals is Compact Position Reporting, an encoding format that compresses latitude and longitude into alternating even and odd message frames. Because the math requires an initial reference point to resolve positions unambiguously, using an arbitrary boundary as home can cause distant receivers to compute ghost positions that look plausible at first glance. PyAerial anchors decoding to a fixed ground station location, rejects sudden unrealistic position jumps, and properly handles angular wrap-around when checking heading ranges across north.

Airspace boundaries are evaluated using geometric polygons defined in GeoJSON or KML files. Rather than only checking if an aircraft is currently inside the boundary, the engine calculates estimated arrival times based on speed and heading, taking the safer estimate between straight-line flight and curved trajectories. A configurable dwell time is also applied so that an aircraft momentarily clipping the outer perimeter does not trigger repetitive alert notifications.

Mode S / ADS-B dump1090, pyModeS CPR decode home != fence, 0.5 deg reject Kinematics Kalman, heading wrap Shapely geofence alt, speed, ETA rules ETA min(curved, straight) Dwell hysteresis, no spam
Boundary evaluation workflow showing signal decoding, speed and heading kinematics, and rule validation.

Storage design and deployment

Splitting storage between Redis and SQLite solved the performance bottlenecks I experienced in earlier prototypes. Redis handles fast in-memory key lookups for currently active aircraft, while SQLite safely stores historical flight trajectories without competing for write locks on every incoming radio packet.

The entire application is packaged with Docker Compose, allowing it to run either on a server connected directly to an RTL-SDR software-defined radio receiver or on a workstation replaying recorded network feeds. Continuous integration tests run against supported Python versions to verify that message parsing, kinematic calculations, and API routes remain stable as new features are added.