Real-Time Tracking: ADS-B, MLAT, and Unfiltered APIs
Real-time flight tracking enables immediate awareness of deportation operations. This guide covers the technologies and data sources required for effective monitoring.
ADS-B Technology
What is ADS-B?
Automatic Dependent Surveillance-Broadcast (ADS-B) is a surveillance technology where aircraft broadcast their position and other data via radio.
| Component | Description |
|---|---|
| Automatic | No pilot input required |
| Dependent | Relies on onboard GPS |
| Surveillance | Enables tracking |
| Broadcast | Omnidirectional radio transmission |
ADS-B Data Fields
| Field | Description | Tracking Value |
|---|---|---|
| ICAO hex code | Unique aircraft identifier | Primary watchlist match |
| Latitude/Longitude | GPS position | Location tracking |
| Altitude | Barometric/GPS altitude | Descent detection |
| Ground speed | Current velocity | Flight phase identification |
| Heading | Direction of travel | Route analysis |
| Vertical rate | Climb/descent rate | Landing detection |
| Call sign | ATC identifier | "RPN" detection |
| Squawk | Transponder code | Emergency detection |
ADS-B Broadcast Characteristics
| Attribute | Value |
|---|---|
| Frequency | 1090 MHz (Mode S Extended Squitter) |
| Range | Up to 250 nautical miles (line of sight) |
| Update rate | 1-2 transmissions per second |
| Encryption | None (plaintext broadcast) |
| Legal reception | Protected activity (public radio waves) |
Receiving ADS-B Data
Hardware Requirements
| Component | Specification | Estimated Cost |
|---|---|---|
| SDR receiver | RTL-SDR, 1090 MHz tuned | $25-40 |
| Filtered receiver | FlightAware Pro Stick Plus | $35-50 |
| Antenna | 1090 MHz collinear, high-gain | $30-80 |
| Compute | Raspberry Pi 4 (1GB+) | $35-55 |
| Storage | MicroSD 32GB+ | $10-15 |
Software Stack
| Software | Function |
|---|---|
| dump1090-fa | RF demodulation, ADS-B decoding |
| readsb | Alternative decoder, enhanced features |
| tar1090 | Web interface, JSON API, memory management |
| graphs1090 | Performance visualization |
Basic Setup Flow
1. Install Raspberry Pi OS Lite (headless)
2. Compile dump1090-fa or readsb
3. Deploy tar1090 web interface
4. Configure Nginx for JSON serving
5. Verify reception at http://localhost/tar1090
Multilateration (MLAT)
When ADS-B Fails
Some aircraft disable ADS-B positional broadcast, reverting to basic Mode S (altitude and identity only). MLAT provides a countermeasure.
How MLAT Works
| Step | Process |
|---|---|
| 1 | Aircraft transmits Mode S signal |
| 2 | Multiple ground receivers capture signal |
| 3 | System measures Time Difference of Arrival (TDOA) |
| 4 | Central server triangulates position |
| 5 | Position data merged with ADS-B feed |
MLAT Requirements
| Requirement | Minimum |
|---|---|
| Ground receivers | 3+ with line of sight |
| Geographic spread | Diverse locations |
| Time synchronization | GPS-disciplined clocks |
| Network participation | Feed to aggregator network |
Enabling MLAT
To contribute to MLAT networks:
# In tar1090 configuration
MLAT_CONFIG=--results beast,connect,localhost:30104
MLAT_CONFIG=--results basestation,listen,31003
Dark Flights
What Are Dark Flights?
Aircraft that completely disable transponders cannot be tracked via ADS-B or MLAT.
| Scenario | Visibility |
|---|---|
| ADS-B active | Full tracking |
| Mode S only | MLAT possible |
| Transponder off | Dark flight |
Detection Methods
| Method | Approach |
|---|---|
| Secondary radar | FAA/military radar returns (not public) |
| Airspace gaps | Anomalous holes in normal traffic |
| Departure/arrival | Visible takeoff, invisible flight, visible landing |
| ATC recordings | Voice communications may reference dark flights |
Filtered vs. Unfiltered Networks
Commercial Services (Filtered)
| Service | LADD Compliance | PIA Compliance | Cost |
|---|---|---|---|
| FlightAware | Yes | Yes | $49-499/mo |
| FlightRadar24 | Yes | Yes | Free-$49/mo |
| Aviationstack | Yes | Yes | Usage-based |
Problem: Contractor aircraft enrolled in LADD/PIA simply disappear from these services.
Unfiltered Services
| Service | LADD/PIA Filtering | Coverage |
|---|---|---|
| ADS-B Exchange | None | Global (15,000+ receivers) |
| OpenSky Network | None | Academic/research access |
ADS-B Exchange is the primary resource for monitoring government and contractor aircraft.
ADS-B Exchange API
Overview
| Attribute | Value |
|---|---|
| Base URL | https://adsbexchange.com/api/aircraft/v2 |
| Authentication | API key required |
| Format | JSON |
| Rate limits | Varies by subscription tier |
Key Endpoints
| Endpoint | Method | Function |
|---|---|---|
/v2/hex/[hex] |
GET | Query specific ICAO hex code |
/v2/callsign/[callsign] |
GET | Query specific call sign |
/v2/ladd/ |
GET | All LADD-tagged aircraft |
/v2/pia/ |
GET | All PIA-tagged aircraft |
/v2/point/[lat]/[lon]/[rad] |
GET | Aircraft within radius |
/v2/mil/ |
GET | Military aircraft |
Example: Query Watchlist Aircraft
import requests
API_KEY = "your_api_key"
WATCHLIST_HEX = ["A12345", "A67890", "ABCDEF"]
for hex_code in WATCHLIST_HEX:
url = f"https://adsbexchange.com/api/aircraft/v2/hex/{hex_code}"
headers = {"api-auth": API_KEY}
response = requests.get(url, headers=headers)
if response.status_code == 200:
data = response.json()
if data.get("ac"):
aircraft = data["ac"][0]
print(f"Found: {hex_code}")
print(f" Position: {aircraft.get('lat')}, {aircraft.get('lon')}")
print(f" Altitude: {aircraft.get('alt_baro')} ft")
print(f" Call sign: {aircraft.get('flight')}")
Response Data Fields
| Field | Description | Type |
|---|---|---|
hex |
ICAO 24-bit address | string |
flight |
Call sign (8 chars max) | string |
lat |
Latitude | float |
lon |
Longitude | float |
alt_baro |
Barometric altitude (ft) | int |
alt_geom |
Geometric altitude (ft) | int |
gs |
Ground speed (knots) | float |
track |
Track angle (degrees) | float |
baro_rate |
Vertical rate (ft/min) | int |
squawk |
Transponder code | string |
dbFlags |
Database flags (military, PIA, etc.) | int |
Database Flags (dbFlags)
| Bit | Meaning |
|---|---|
| 1 | Military |
| 2 | Interesting |
| 4 | PIA (Privacy ICAO Address) |
| 8 | LADD enrolled |
# Check if aircraft is using PIA
if aircraft.get("dbFlags", 0) & 4:
print("Aircraft using Privacy ICAO Address")
Local JSON Polling
Using tar1090 JSON
For local receiver setups, poll the aircraft.json file:
import requests
import time
LOCAL_URL = "http://localhost/tar1090/data/aircraft.json"
WATCHLIST = ["A12345", "A67890"]
while True:
response = requests.get(LOCAL_URL)
data = response.json()
for aircraft in data.get("aircraft", []):
hex_code = aircraft.get("hex", "").upper()
if hex_code in WATCHLIST:
print(f"ALERT: {hex_code} detected!")
print(f" Position: {aircraft.get('lat')}, {aircraft.get('lon')}")
print(f" Altitude: {aircraft.get('alt_baro')}")
time.sleep(5) # Poll every 5 seconds
JSON Structure
{
"now": 1711382400.0,
"messages": 12345678,
"aircraft": [
{
"hex": "a12345",
"flight": "RPN1234 ",
"lat": 32.1234,
"lon": -110.5678,
"alt_baro": 35000,
"gs": 450,
"track": 180,
"baro_rate": -64,
"seen": 0.1
}
]
}
Tracking Strategies
Watchlist Monitoring
| Approach | Method |
|---|---|
| Hex code list | Query known contractor aircraft |
| Call sign filter | Monitor for "RPN*" patterns |
| Operator tracking | All aircraft from known contractors |
| LADD/PIA filter | Flag aircraft actively hiding |
Geofencing
| Strategy | Implementation |
|---|---|
| Staging hub monitoring | Alert when aircraft approaches Mesa, Alexandria, etc. |
| Detention facility proximity | Track aircraft near ICE facilities |
| Airport geofences | Define radius around target airports |
Haversine Distance Calculation
from math import radians, sin, cos, sqrt, atan2
def haversine(lat1, lon1, lat2, lon2):
"""Calculate great-circle distance in miles"""
R = 3959 # Earth radius in miles
lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2])
dlat = lat2 - lat1
dlon = lon2 - lon1
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
c = 2 * atan2(sqrt(a), sqrt(1-a))
return R * c
# Example: Check if aircraft is within 30 miles of staging hub
ALEXANDRIA_LAT, ALEXANDRIA_LON = 31.3274, -92.5499
aircraft_lat, aircraft_lon = 31.5, -92.3
distance = haversine(ALEXANDRIA_LAT, ALEXANDRIA_LON, aircraft_lat, aircraft_lon)
if distance < 30:
print(f"Aircraft within {distance:.1f} miles of Alexandria hub")
Rate Limiting and Efficiency
Polling Strategies
| Aircraft State | Poll Interval |
|---|---|
| Cruising (>35,000 ft) | 60 seconds |
| Descending | 15 seconds |
| Low altitude (<10,000 ft) | 5 seconds |
| Near geofence | 5 seconds |
| Not detected | 60 seconds (watchlist sweep) |
Adaptive Polling
def get_poll_interval(aircraft):
altitude = aircraft.get("alt_baro", 40000)
baro_rate = aircraft.get("baro_rate", 0)
if altitude < 10000:
return 5 # Landing approach
elif baro_rate < -500:
return 15 # Descending
else:
return 60 # Cruising
Related Resources
Last updated: March 25, 2026