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