A production-grade Multilateration (MLAT) system that localizes aircraft in real-time using distributed Mode-S receivers on the Neuron/Hedera network. Built entirely in Go as a single statically-linked binary with zero external dependencies.
The system is running on AWS EC2, connected to 9 sensors across Cornwall and the Scilly Isles (UK). Here are real metrics from a live session:
Aircraft tracked: 101
MLAT positions: 1,776 successful fixes
ADS-B decoded: 66,314 GPS reference positions
Active EKF tracks: 99
Sensors connected: 9 (all available on the network)
============ MLAT POSITION ============
ICAO: aa2184
Latitude: 50.194807
Longitude: -5.410565
Altitude: 12086 m (39651 ft)
GDOP: 3.89
Residual: 5.3 m
Sensors: 7
ADS-B err: 16357 m (GPS: 50.047724, -5.414330, 11689m)
=======================================
============ MLAT POSITION ============
ICAO: 4d20d6
Latitude: 50.083626
Longitude: -5.347520
Altitude: 13263 m (43513 ft)
GDOP: 4.63
Residual: 0.0 m
Sensors: 4
ADS-B err: 6995 m (GPS: 50.020717, -5.347521, 13106m)
[EKF] Smoothed: 50.083626, -5.347520, 13263m | 0 kts hdg 0 | VS 0 fpm
=======================================
Neuron Network (9 Sensors)
|
| libp2p / QUIC (neuron/ADSB/0.0.2)
v
+------+--------+ Hedera Testnet
| Data Ingestion | <-- Smart Contract Discovery
| Packet Parser | Peer-to-peer streams
+------+--------+
|
Raw Mode-S frames with nanosecond timestamps
|
+----v-----+ +----------------+
| Clock | <-- | ADS-B Decoder |
| Sync KF | | (DF17 CPR) |
+----+-----+ | Sync beacons |
| +-------+--------+
Corrected timestamps |
| GPS positions for validation
+----v-----------+
| Correlation |
| Engine |
| (200ms window) |
+----+-----------+
|
Time-correlated readings (4+ distinct sensors)
|
+----v-----------+ +------------------+
| MLAT Solver | | Outlier Rejection|
| Frisch TOA | <---> | (iterative, |
| + TDOA fallback| | per-sensor) |
| + C(n,4) subset| +------------------+
+----+-----------+
|
+----v-----------+ +------------------+
| Position Cache | <---> | Mach 3 Gating |
| + Velocity | | Physical checks |
+----+-----------+ +------------------+
|
+----v-----------+
| EKF Track |
| Smoother |
| Adaptive noise |
+----+-----------+
|
Smoothed tracks with speed, heading, vertical rate
- Frisch TOA (primary): Analytically eliminates transmission time, reducing 4D to 3D. Superior for clustered sensor geometries.
- TDOA Gauss-Newton (fallback): Classical linearized least-squares with altitude regularization.
Per-receiver-pair 2-state Kalman filter tracking clock offset and drift. Uses decoded ADS-B positions as sync beacons with MST-based multi-hop timestamp normalization.
Exhaustively searches all C(n,4) sensor combinations for optimal geometry. With 9 sensors, that is 126 subsets evaluated per aircraft per epoch.
After each solve, computes per-sensor TOA residuals. Removes the worst sensor if residual exceeds 3km, then re-solves. Repeats until clean or floor of 3 sensors.
6-state constant-velocity model (position + velocity in ECEF). Adaptive measurement noise scaled by each fix's GDOP and residual. Innovation gating at 95th percentile chi-squared (3-DOF). Outputs smoothed positions with derived ground speed, heading, and vertical rate.
When only 3 sensors see an aircraft, uses known barometric altitude from Mode-S as a constraint (pseudo-measurement with weight 10.0) to pin the vertical dimension.
Decodes DF17 CPR positions for dual purpose: (1) clock calibration beacons, (2) accuracy benchmarking. Provides real-time error metrics in meters vs GPS ground truth.
- Registers as a data buyer on Hedera testnet (account
0.0.7301787) - Discovers seller peers via Hedera smart contract queries
- Negotiates encrypted P2P streams over libp2p/QUIC
- Every Mode-S frame is purchased through the decentralized Neuron marketplace
- No central server touches the raw aviation data
12 unit tests covering all subsystems:
$ go test ./mlat/ -v
=== RUN TestECEFRoundTrip PASS
=== RUN TestMLATWithSyntheticData PASS (lat err: 0.000016 deg)
=== RUN TestExtractICAO PASS
=== RUN TestSolve3x3 PASS
=== RUN TestFrischTOASyntheticData PASS
=== RUN TestBestPositionSubsetSelection PASS (bad sensor excluded)
=== RUN TestOutlierRejection PASS (error: 0.102 -> 0.013 deg)
=== RUN TestTest3SensorAltitude PASS
=== RUN TestPositionCache PASS (Mach 3 gating works)
=== RUN TestClockPair PASS (converges, rejects outlier)
=== RUN TestEKFSmoothing PASS (500 kts, 45 deg heading)
=== RUN TestADSBDecode PASS (lat=52.0000, lon=4.5000)
PASS
ok quickstart/mlat 0.794s
main_mlat.go Main entry point, stream handler, MLAT processor
mlat/
mlat.go Frisch TOA + TDOA solvers, ECEF transforms, GDOP, ADS-B decoder
ekf.go Extended Kalman Filter track smoother
clock_sync.go Per-pair clock calibration Kalman filter
position_cache.go Position cache with velocity prediction + outlier rejection
mlat_test.go 12 comprehensive tests
aggregator/
aggregator.go Time-correlated reading aggregation
- Go 1.24+
- A publicly reachable UDP port (cloud VM recommended, home networks often have CGNAT)
- Buyer credentials from the 4DSky Discord
git clone https://github.com/kamalbuilds/4dsky-mlat-challenge
cd 4dsky-mlat-challenge
cp .buyer-env.example .buyer-env
# Fill in your credentials from Discord
go mod download
go test ./mlat/ -v # Run test suite
go build -o mlat_buyer main_mlat.go./mlat_buyer --port=61336 --mode=peer \
--buyer-or-seller=buyer \
--list-of-sellers-source=env \
--envFile=.buyer-envGOOS=linux GOARCH=arm64 go build -o mlat_buyer_linux main_mlat.go
# Binary is statically linked, zero dependencies
scp mlat_buyer_linux user@your-server:~| Variable | Description |
|---|---|
eth_rpc_url |
Hedera testnet RPC endpoint |
hedera_evm_id |
Your Hedera EVM account ID |
hedera_id |
Your Hedera account ID |
private_key |
Private key for authentication |
smart_contract_address |
Neuron smart contract address |
list_of_sellers |
Comma-separated seller public keys |
mirror_api_url |
Hedera mirror node API |
location |
Your geographic location (JSON) |
The system uses libp2p/QUIC for peer-to-peer data streams. Sellers initiate connections to buyers, so the buyer needs a publicly reachable UDP port. If you are behind CGNAT (common with ISPs like Airtel, Jio), deploy on a cloud VM (AWS, GCP, DigitalOcean) instead. The SDK's hole-punching is not yet implemented.
See the repository license file for details.