ADS-B Flight Tracking Platform
Table of Contents
Overview
Personal ADS-B (Automatic Dependent Surveillance-Broadcast) flight tracking platform built on FreeBSD, located 0.8 km from Boston Logan International Airport (KBOS).
- FlightAware: jwalsh4271
- Platform Version: 0.2.0
- Host: hydra (FreeBSD 14.3-RELEASE)
Location
| Property | Value |
|---|---|
| Coordinates | 42.37°N, 71.04°W |
| Altitude | 29m |
| Distance to KBOS | ~0.8 km |
| Neighborhood | East Boston |
Why This Location Matters
Per FlightAware's engineering blog, receivers within 5 miles of airports provide enhanced surface-level tracking. Our location provides:
- Direct line-of-sight to all KBOS runways
- Surface movement detection (taxiing aircraft)
- Low-altitude approach/departure coverage
- Harbor traffic visibility
Architecture
hydra (FreeBSD 14.3) pi (Debian) ├── RTL-SDR Blog V4 + dipole ├── NESDR SMArt v5 + 1090MHz antenna ├── dump1090 :30003 ├── dump1090-mutability :30003 ├── adsb-processor (v1.4.0) └── piaware → FlightAware (Site 264028) ├── adsb-server :8081 ├── adsb-websocket :8083 └── sbs-logger → /mnt/usb/adsb/raw/
Data Flow
- RTL-SDR receives 1090 MHz signals
- dump1090 decodes Mode S / ADS-B messages
- adsb-processor aggregates from multiple receivers
- SQLite database stores aircraft + metadata
- adsb-server provides HTTP API
- Web UI displays real-time map
Components
Hardware
| Component | Receiver | Location |
|---|---|---|
| RTL-SDR Blog V4 + 1090MHz dipole | hydra | Indoor, window (raised) |
| NESDR SMArt v5 + magnetic mount | pi | Indoor, top of bookshelf |
Software Stack
| Component | Version | Description |
|---|---|---|
| adsb-processor | 1.4.0 | Multi-receiver message aggregator |
| sbs-logger | 1.1.0 | Raw CSV message logger |
| adsb-server | 1.0.0 | HTTP API server |
| adsb-websocket | 1.0.0 | Real-time WebSocket updates |
| Database schema | v4 | SQLite with 30-column metadata |
External Integrations
OpenSky Network
- REST API for aircraft metadata enrichment
- binCraft binary format for area queries
- Trino access for historical data (pending application)
# Test OpenSky metadata curl -s "http://localhost:8081/aircraft-metadata/A0AF06" | jq
FlightAware
- PiAware feeding from pi.lan (Site 264028)
- AeroAPI available for commercial queries
ADS-B Exchange
- Crowd-sourced unfiltered data
- Globe visualization: https://globe.adsbexchange.com/
Database Design
Decision: SQLite vs PostgreSQL
Status: Stay with SQLite (ADR-001)
| Metric | Current | PostgreSQL Trigger |
|---|---|---|
| Database size | 312 KB | >10 GB |
| Message rate | 38/sec | >1000/sec |
| Concurrent writers | 1 | >1 |
SQLite advantages for this use case:
- Zero administration
- Single file deployment
- Python built-in support
- WAL mode for concurrent reads
Schema (v4)
CREATE TABLE aircraft ( icao TEXT PRIMARY KEY, callsign TEXT, first_seen TEXT, last_seen TEXT, total_messages INTEGER, total_positions INTEGER, receivers TEXT ); CREATE TABLE aircraft_metadata ( icao TEXT PRIMARY KEY, registration TEXT, type_code TEXT, type_desc TEXT, manufacturer_name TEXT, operator TEXT, owner TEXT, -- ... 20+ additional columns fetched_at TEXT, source TEXT ); CREATE TABLE callsign_history ( icao TEXT, callsign TEXT, first_seen TEXT, last_seen TEXT, PRIMARY KEY (icao, callsign) );
Features
Aircraft Swap Detection
Track when flight numbers switch aircraft (e.g., AAL2763 from A321 to B738).
SELECT * FROM flight_aircraft_history WHERE callsign = 'AAL2763';
OpenSky Enrichment
- Metadata caching (permanent, no TTL)
USE_OPENSKYflag for offline operation- Rate limiting (2 sec between calls)
- Retry failed lookups after 24 hours
Real-time Web UI
- Leaflet map with aircraft icons
- Selection circles and tracking
- Altitude color coding
- External links (ADSBx, FlightAware)
- Copy aircraft info to clipboard
Route Analysis
Sample routes tracked (KSEA ↔ KBOS):
| Direction | Primary Route | Distance |
|---|---|---|
| KSEA→KBOS | MONTN2…PONCT JFUND2 | 2550 sm |
| KBOS→KSEA | HYLND7…MLP GLASR3 | 2523-2628 sm |
Data files:
data/routes/KSEA-KBOS-routes.mddata/routes/KBOS-KSEA-routes.mddata/routes/KSEA-KBOS-routes.mmd(Mermaid diagram)
Visualizations
Generated from 3 days of data (Dec 26-28, 2025). Full gallery at aygp-dr/adsb-reports.
Geographic Coverage
Figure 1: Position density heatmap showing receiver coverage
Flight Tracks
Figure 2: Top aircraft flight paths over 24 hours
KBOS Approach Analysis
Figure 3: Approach traffic patterns for Boston Logan
Fleet Composition
Figure 4: Aircraft types and classes observed
Multi-Receiver Comparison
Figure 5: Coverage overlap between hydra and pi receivers
Daily Traffic Comparison
Figure 6: Message volume across Dec 26-28
Statistics
Daily performance (typical):
| Metric | Value |
|---|---|
| Aircraft tracked | 700-900 |
| Messages processed | 1.5-2M |
| Message rate | 35-40/sec |
| Metadata cache | 800+ aircraft |
| Database size | ~300 KB |
References
FlightAware Engineering
Technical Documentation
- OpenSky REST API
- The 1090 Megahertz Riddle (Mode S decoding)
- Radar Tutorial: SSR/Mode S
Project Files
hydra.toml- Platform configuration (IaC)VERSION- Platform versiondocs/adr/ADR-001-sqlite-vs-postgresql.md- Database decisiondocs/CHANGELOG-0.2.0.md- Release notes
Future Work
Planned
- Playwright UI testing (hydra-setup-2nd)
- FlightAware AeroAPI integration (hydra-setup-bn1)
- Apache Baremaps custom aviation maps (hydra-setup-hdc)
- Route frequency analysis
Potential ML Projects
- Callsign anomaly detection
- Flight path prediction
- Aircraft type classification from signatures
- Delay pattern analysis
Local Commands
# Check system status pgrep -f dump1090 && pgrep -f adsb-processor && echo "Running" # Database stats sqlite3 /mnt/usb/adsb/adsb.db "SELECT COUNT(*) FROM aircraft" # Recent aircraft sqlite3 /mnt/usb/adsb/adsb.db \ "SELECT icao, callsign, altitude FROM aircraft ORDER BY last_seen DESC LIMIT 10" # OpenSky enrichment test curl -s "http://localhost:8081/opensky-bincraft?box=41.5,43.0,-72.0,-69.5" | \ python3 -c "import sys,json; print(len(json.load(sys.stdin).get('aircraft',[])))"
Testing Procedures
Antenna Baseline Testing
Quick baseline for message rate and aircraft count:
# Message rate (10-second sample) timeout 10 nc localhost 30003 | wc -l # Aircraft count from dump1090 curl -s http://localhost:8080/data.json | jq '. | length' # Pi receiver check ssh pi.lan 'curl -s http://localhost:8504/data/aircraft.json | \ jq "{aircraft: (.aircraft | length), with_position: ([.aircraft[] | select(.lat)] | length)}"'
Multi-Receiver Comparison
Capture from both receivers simultaneously and compare coverage:
# Capture 15 seconds from both timeout 15 nc localhost 30003 > /tmp/hydra_msgs.txt & timeout 15 nc pi.lan 30003 > /tmp/pi_msgs.txt & wait # Compare message counts echo "Hydra: $(wc -l < /tmp/hydra_msgs.txt) messages" echo "Pi: $(wc -l < /tmp/pi_msgs.txt) messages" # Extract unique ICAOs and diff cut -d, -f5 /tmp/hydra_msgs.txt | sort -u > /tmp/hydra_icao.txt cut -d, -f5 /tmp/pi_msgs.txt | sort -u > /tmp/pi_icao.txt # What each receiver sees exclusively comm -23 /tmp/hydra_icao.txt /tmp/pi_icao.txt # Hydra only comm -13 /tmp/hydra_icao.txt /tmp/pi_icao.txt # Pi only comm -12 /tmp/hydra_icao.txt /tmp/pi_icao.txt # Both
Antenna Swap Procedure
When swapping antennas:
- Run quick baseline (pre-swap)
- Swap antenna (USB may disconnect)
- Check service status:
systemctl status dump1090-mutability - Restart if needed:
systemctl restart dump1090-mutability - Wait 30 seconds for stabilization
- Run new baseline (post-swap)
Critical: USB disconnect causes dump1090 to lose device handle even though service stays running. Always restart after physical antenna changes.
Hourly Comparison Test
For extended A/B testing during antenna swaps:
# Start hourly test (samples every 60 seconds for 1 hour) cat > /tmp/receiver-comparison.sh << 'EOF' #!/usr/bin/env bash LOG=logs/receiver-comparison-hourly.log echo "Time H-msgs H-ac P-msgs P-ac" > $LOG for i in $(seq 1 60); do timeout 10 nc localhost 30003 > /tmp/h.txt & timeout 10 nc pi.lan 30003 > /tmp/p.txt & wait printf "%-12s %8s %8s %8s %8s\n" "$(date +%H:%M:%S)" \ "$(wc -l < /tmp/h.txt)" "$(cut -d, -f5 /tmp/h.txt | sort -u | wc -l)" \ "$(wc -l < /tmp/p.txt)" "$(cut -d, -f5 /tmp/p.txt | sort -u | wc -l)" >> $LOG sleep 50 done EOF nohup bash /tmp/receiver-comparison.sh &
Performance Benchmarks
Observed results from 2025-12-28 testing:
| Antenna Config | Location | Msgs/10sec | Aircraft |
|---|---|---|---|
| 978MHz | Indoor | ~12 | 3-5 |
| Rabbit ears | Indoor | ~34 | 3-4 |
| 1090MHz dipole | Window | ~30 | 4 |
| 1090MHz dipole | Window (raised) | ~150 | 4-6 |
| Magnetic mount | Bookshelf | ~440 | 7-9 |
Key findings:
- Elevation matters more than antenna tuning
- Proper 1090MHz tuning provides more consistent reception
- Bookshelf placement (pi) outperforms window placement (hydra) by ~3x
Changelog
| Date | Version | Changes |
|---|---|---|
| 2025-12-28 | 0.2.0 | OpenSky integration, metadata expansion, callsign history |
| 2025-12-27 | 0.1.x | Multi-receiver support, basic tracking |
| 2025-12-26 | 0.1.0 | Initial setup |