Inspiration
At Lockheed Martin, I wrote the flight software that moves messages around a hybrid airship. Every check I wrote asked one question: did this packet arrive intact? None of them asked whether it was true. I knew why that matters. My high school sneaker bot got past Nike’s anti-bot defenses because every request it sent looked perfectly valid. Physics is harder to fake than a checksum. So we built the check my flight software was missing, then trained an AI to beat it.
What it does
ARGUS is a ground station that watches a live telemetry stream and flags frames that pass every link check but describe impossible motion. It replays real Falcon 9 ascents (31 launches of webcast telemetry) and NASA SMAP/MSL spacecraft telemetry through a CCSDS-style link with CRC-16 and sequence numbers. A man-in-the-middle rewrites the values inside a frame and re-encodes it with a valid CRC, so sync, checksum, sequence, and mission clock all pass. Three layers then judge the frame: L1, eleven windowed physics rules; L2, a GRU that has learned how Falcon 9 flies; and L3, a discriminator trained on attacker output. A PPO red-team agent learns, against the live detector, how to grow the operator's altitude error without tripping an alert. The detector is hardened against that agent, and the console can hot-swap attackers and detectors mid-flight. The console shows the true trajectory next to the reported one, each layer's score, and the gap between them. Alerts are spoken in a mission-control voice. The headline on a forged frame is the whole product: link 100% valid, picture off by kilometers. On SES-9, a launch the detector never trained on, the base detector lets the PPO attacker walk altitude off by 23.8 km over 315 seconds before alerting. The hardened detector catches the same attack at 34 seconds, with a 0.6 km lie.
How we built it
The live path is Python 3.12. A link codec and an L0 monitor check sync, CRC-16/CCITT-FALSE, sequence, and time. Sources cover Falcon 9 replay, a quadcopter simulator, and SMAP/MSL replay. A MAVLink bridge for ArduPilot SITL is in the repo; the verified demo does not fly it. Attacks are a scripted ladder (bias, drift, replay, freeze, coordinated) plus a Gymnasium environment and a Stable-Baselines3 PPO agent.
L1 checks that vertical velocity agrees with the change in altitude over 10 seconds and over 60 seconds, that horizontal velocity agrees with downrange, that speed agrees with the velocity components, that dynamic pressure agrees with a fitted atmosphere, and that the trajectory stays in the corridor every launch has flown, among other rules. Thresholds are calibrated on clean training launches only. L2 is a GRU next-step predictor over the last 2 seconds, exported to ONNX and run on CPU. L3 is a 1D-CNN over the last 4 seconds of L1 and L2 features. The corridor feature is left out of L3 so "near the edge of the training envelope" is not treated as forgery. L1 and L2 combine with a noisy-OR, and an alert needs 3 of 5 frames over τ = 0.66. L3 keeps its own alert and must stay over its threshold for 40 of 50 frames. The frame score is the max of the two.
The pipeline is FastAPI plus a WebSocket, with live attack and detector hot-swap. Frames and verdicts go to Tiger Data (TimescaleDB 2.31.0) hypertables, with a one-second continuous aggregate. A verified demo run wrote 8,961 rows in 64 batches with 0 errors. The console is Next.js, rendering at 10 Hz, with Cartesia sonic-3.6 speaking alerts.
The learned layers also have a fleet benchmark: TensorRT 11.4 FP16 and INT8 engines on an RTX 5060 Laptop GPU. The live demo stays on ONNX Runtime on CPU. A C++ edge monitor reimplements the base detector and is bit-exact with Python on the golden frames. Its live UDP path is not measured yet.
Every held-out number is written by a script into results/*.json. Every 5th launch is held out of training, and the demo runs on one of those launches.
Challenges we ran into
Webcast telemetry is messy. The Falcon 9 numbers are read off the broadcast overlay, so on clean flights the gap between vertical velocity and the change in altitude reaches 81.9 m/s over 10 seconds. Pointwise physics rules false-alert constantly. Every rule is windowed and integrated. Over 60 seconds those glitches cancel, and the clean maximum falls to 13.6 m/s. Two half-suspicious layers made a false alert. On three clean launches the altitude readout lags and then jumps. L1 and L2 each stayed under their own limit, but their noisy-OR crossed the alert line. We recalibrated τ on the combined score, using training and validation launches only, and checked it on test launches the calibration never saw. The worst clean peak was 0.6546, on ABS-2A, so τ sits at 0.66. The RL attacker first learned to do nothing. The first reward penalized the detector's score on every step, so "never lie" and "get caught immediately" both beat any real lie. Charging only for score above a safety margin, and making an alert much more costly than a slow lie, fixed it. A second policy got caught in seconds because it also nudged downrange, which the tightest rule checks. The agent we ship lies about altitude and the velocities that go with it. The discriminator learned shortcuts. L3 first learned "late flight means forged," because every attack started mid-flight. Then it learned "near the edge of the training corridor means forged": zeroing that feature dropped its logit on a clean first-stage coast from 23 to 1. We spread attack onsets across the flight, balanced training windows by phase, removed the corridor feature, and gave L3 its own persistence, 40 of 50 frames, so a 1–3 second boost back transient cannot set the threshold.
Accomplishments that we're proud of
On six held-out launches, PPO round 1 is caught at 190 seconds mean with a 12.3 km lie by the base detector, and at 41 seconds mean with a 0.72 km lie by the hardened detector. Bias, replay, and freeze are caught in 0.6 seconds. A 30 m/s drift takes the base detector 137 seconds and the hardened detector 18.5 seconds. The coordinated, kinematically consistent fake is caught in 3.6 to 5.3 seconds. We ran two rounds of the arms race. PPO retrained against the first hardened detector survived it 2.5× longer (167 seconds versus 66) with a lie of about 2 km, and the base detector missed that attack on one launch, JCSAT-16. L3 retrained on that attacker's output, the detector we ship, catches it in 36 seconds with a 0.38 km lie, and still catches the round-1 attacker in 41 seconds. Zero false alerts on 18 clean held-out flights, 2.58 flight-hours, for both detectors. The test half, which no threshold was fit on, is 0 of 9. Cost is 1.1ms per frame for the base detector and 1.3ms for the hardened one, against a 100ms frame at 10 Hz. On NASA SMAP/MSL, event-level F1 is 72.8% combined against Telemanom's 83.6%, a gap of 10.8 points. We reimplemented the published protocol and we are below it. On the laptop GPU, FP16 TensorRT scores 16,384 streams of L2 and L3 together in 15.9ms, 35× and 8.7× faster than the CPU, with zero detection decisions changed.
What we learned
A passing checksum only proves the bits arrived intact. Once an attacker can recompute the CRC, the defender has to check whether the motion is possible. Physics is a strong, cheap prior, and a learned attacker has to fight it on purpose. Calibration mattered more than model size. Most of the work went into thresholds that stay quiet on real webcast flights: window lengths, the noisy-OR, and a persistence rule so a one-second glitch cannot page the operator. An RL attacker finds the weakest check quickly. The reward had to be designed as carefully as the detector, or the best policy was to tell the truth.
What's next for ARGUS
Round 3: retrain the attacker against the round-2 detector and keep alternating. The numbers we quote are on held-out launches, against attackers the discriminator has seen. Run the MAVLink bridge against real ArduPilot SITL, and against real drone logs. Move to higher-rate vehicle telemetry, where the physics rules can be much tighter than webcast overlays allow.
Log in or sign up for Devpost to join the conversation.