SADAR
Smart Anomaly Detection for Aviation Routes
Technical Report
Project: SADAR · Flight Conformance Monitor Domain: Air traffic surveillance · ADS-B data Airport of study: Madrid-Barajas (LEMD) Approach: One-class deep learning for trajectory anomaly detection Date: 2026
Executive summary
SADAR is a deep-learning system that learns the normal pattern of approach and departure operations at Madrid-Barajas (LEMD) from ADS-B surveillance data and surfaces, in real time, any trajectory that deviates from that pattern. The system is built as a flight conformance monitor: it does not classify specific events, it raises an interpretable anomaly score for every flight in the airspace so that an operator can review the cases that fall outside the learned distribution.
Three sequence autoencoders are trained under identical conditions on roughly 20.000 real flights from the OpenSky Network (an LSTM autoencoder, a Transformer autoencoder and a VAE-LSTM), together with a non-sequential Isolation Forest baseline. The four detectors are evaluated on the same held-out test set and on a synthetic anomaly bench with five injection types and several intensities. The VAE-LSTM is selected as the production model: it achieves the highest PR-AUC on real held-out data (0.299) and the highest synthetic mean ROC-AUC (0.792). Two ensemble strategies (consensus and most-sensitive) are also evaluated and discarded because neither beats the best individual model.
The selected model is wrapped in a FastAPI inference service and served through a React + Vite dashboard that combines a tower-style live radar, a ranked anomaly list, a metrics page and a fully interactive simulator. The whole system is reproducible from a single Docker image and ships ready for deployment on Hugging Face Spaces.
1. Motivation and scope
Trajectory monitoring is a core function of air traffic management. Today it relies on deterministic rules and human supervision: separation, descent profiles, holding patterns and emergency squawk codes are all monitored against fixed thresholds. These rules work well for the situations they were designed for, but they cannot anticipate behavior that is unusual without being a textbook violation.
SADAR explores a complementary, data-driven approach. Instead of encoding rules, the system learns what a normal operation at LEMD looks like from a large sample of real flights, and treats every new trajectory as more or less compatible with that learned distribution. The output is a continuous conformance score that an operator can rank, filter and threshold.
The scope of the project is deliberately narrow and well defined:
- What it does. Detects deviations from the normal traffic pattern at one airport (LEMD) based on a fixed set of dynamic features extracted from ADS-B reports.
- What it does not do. Predict specific operational events, replace controllers, produce certified safety recommendations or generalize without retraining to airports with different procedures.
This framing as a conformance monitor is what makes the problem tractable with the data available: it requires only examples of normal flight, which are abundant, and it keeps the interpretation of the output clear ("this trajectory is unlike the ones the model has seen") without overclaiming.
2. Data
2.1 Source
The dataset is built from ADS-B reports collected by the OpenSky Network around the Madrid-Barajas terminal area. It covers approximately 18 days of operations sampled between June 2017 and March 2020. Each aircraft is reported once every 10 seconds, which is the natural cadence of ADS-B.
| Property | Value |
|---|---|
| Total trajectories | ≈ 20.000 (≈ 950 to 1.200 per day) |
| Rows per day | 140.000 to 240.000 |
| Columns | 21 |
| Points per flight (min · median · max) | 40 · 188 · 1.246 |
2.2 Variables
The raw schema includes positional information (latitude, longitude, time), dynamics
(barometric and geometric altitude, ground velocity, heading, vertical rate), derived
fields (distance to runway, flight phase), identity (ICAO24, callsign, flight id) and
transponder state (squawk, on-ground flag, alert flag, special-purpose indicator, last
contact). One field, operation, is entirely missing and is dropped.
2.3 Quality and cleaning
A profiling pass over the full dataset surfaces several issues that are handled in the preprocessing pipeline:
- Missing values.
geoaltitudeis missing in 34.9 % of rows,baroaltitudein 19.6 %, kinematic fields (velocity,heading,vertrate) around 19 %. The rest are below 4 %. - Duplicate file. One day is shipped twice with different filenames and is deduplicated by content hash.
- Empty or errored days. A handful of days arrive with zero rows or with manifest errors and are dropped.
- Coverage gaps. About 8 % of airborne flights show a gap greater than 120 s between consecutive reports. These gaps are almost always receiver-coverage artifacts, not operational events, and are used as a feature, never as a label.
After cleaning, the dataset contains a stable set of normal LEMD operations spanning multiple seasons and including the start of 2020, which doubles as a natural distribution shift to stress-test the temporal split.
2.4 Notable observations
The exploratory analysis confirms a small set of rare cases that are kept aside for validation only:
- Four flights carry an emergency squawk code at some point of their trajectory.
- Roughly one hundred flights show a clear go-around pattern (an approach followed by a sustained climb).
None of these flights enter the training set. They serve as a sanity check for the deployed model.
3. Problem formulation
The dataset contains many normal flights and almost no labeled anomalies. This rules out supervised classification and points to one-class learning: train a model that captures the distribution of normal trajectories and score new flights by how compatible they are with that distribution.
Concretely, all three deep models are autoencoders trained on normal flights only. The training objective is to reconstruct the input window from a compressed latent representation; at inference time the reconstruction error is used as the conformance score. A trajectory that resembles the normal patterns is reconstructed accurately and gets a low score; a trajectory that departs from those patterns is harder to reconstruct and gets a high score.
This formulation has three properties that match the operational context. It needs only normal data, which is the only data available at scale. It produces a continuous, interpretable score instead of a hard label, which is the right output for a tool that supports a human operator. And it gracefully handles unknown failure modes: any trajectory the model has not learned to reconstruct will surface as anomalous regardless of what makes it unusual.
4. Preprocessing pipeline
The preprocessing pipeline turns raw parquet files into fixed-length, standardized
tensors ready for training. Every step is implemented as a small, testable function under
src/sadar/data/.
- Load and merge. All parquet files under
$SADAR_DATA_DIRare read with pyarrow, concatenated and deduplicated. Empty or errored days are dropped. - Coordinate transform. Latitude and longitude are projected to a runway-relative
metric system using
pyproj. This makes the model robust to the curvature of coordinates and gives the network meters as input units. - Heading encoding. Heading is encoded as
(sin(heading), cos(heading))to avoid the discontinuity at 0 / 360 degrees. - Missing-value handling. Short gaps inside an otherwise complete trajectory are filled with linear interpolation. Trajectories with too many missing samples are discarded.
- Resampling. Every flight is resampled to a uniform 10 s grid, which matches the nominal cadence of ADS-B.
- Windowing. Each resampled trajectory is sliced into fixed-length windows. The
window length is a hyperparameter, set in
configs/preprocessing.yaml. - Standardization. Each feature is normalized to zero mean and unit variance. The
scaler is fit only on the training split to avoid leakage and saved as
data/processed/scaler.npz, so the exact same transform is applied at inference time. - Leak-free split. The split is done jointly by
flight_idand by date: no flight ever appears in more than one split, and the test split is strictly later in time than the training split. Years 2017 to 2019 are used for training and validation; 2020 is reserved for testing. This realistic split is what allows latency numbers to be taken at face value. - Training set cleaning. The few flights with emergency squawks and the suspected go-arounds are removed from the training split. Only "normal" trajectories enter the training set.
The input to all deep models is a window of seven features:
[x_rel, y_rel, baroaltitude, velocity, sin_hdg, cos_hdg, vertrate].
5. Models
5.1 Isolation Forest baseline
A non-sequential reference detector. Each window is summarized by per-feature statistics (mean, standard deviation, min, max) and fed to an Isolation Forest. The baseline is useful to quantify how much sequential modeling actually adds.
5.2 LSTM autoencoder
A two-layer LSTM encoder compresses the input window into a latent vector, and a mirror LSTM decoder reconstructs it. Trained end-to-end with MSE loss and the shared training loop described below. This is the workhorse architecture: stable to train, fast to score and well aligned with the literature on ADS-B trajectory modeling.
5.3 Transformer autoencoder
The same encoder-decoder skeleton, but with multi-head self-attention instead of recurrence. The Transformer captures long-range dependencies inside the window without the sequential bottleneck of LSTMs, at the cost of more parameters and a larger hyperparameter surface.
5.4 VAE-LSTM (selected production model)
A variational autoencoder built on LSTM encoder and decoder. Instead of producing a single latent vector, the encoder produces a posterior distribution; the decoder reconstructs from a sample drawn from it. The training loss is the standard ELBO: reconstruction MSE plus a KL term that regularizes the posterior toward a unit Gaussian, scaled by a beta factor. This adds a principled, probabilistic interpretation to the score and behaves better at the threshold-selection step.
5.5 Ensembling (evaluated and discarded)
Two ensemble strategies are evaluated as part of the comparison. After per-model z-score calibration on validation normals, the three deep models are combined either by their mean score (consensus) or by their max score (most-sensitive). Neither combination beats the best individual model on the real test set, so the ensemble is documented and discarded. The final detector is the VAE-LSTM alone.
5.6 Shared training infrastructure
All three deep models share the same training loop, implemented once in
src/sadar/models/training.py. The loop uses:
- Adam with weight decay,
- a ReduceLROnPlateau scheduler,
- early stopping on the validation loss,
- MLflow for tracking (losses, hyperparameters, best checkpoint),
- Optuna for hyperparameter search, used here mainly for the VAE-LSTM.
Each architecture has its own YAML configuration file under configs/. Reproducibility
is enforced by fixed seeds and by storing the exact preprocessing config alongside the
checkpoint.
6. Synthetic anomaly bench
Because real anomalies are rare and unlabeled, a controlled benchmark is built on top of normal test flights. Each scenario starts from a real normal window and injects a known, labeled perturbation that ramps in halfway through the window. Five types of injection are implemented, each at multiple intensities:
| Type | Description |
|---|---|
| Route deviation | Lateral departure from the corridor, parameterized in meters. |
| Altitude anomaly | Altitude offset inconsistent with the flight phase. |
| Speed anomaly | Multiplicative acceleration or deceleration. |
| Holding | A repeated 360 ° turn at a configurable period. |
| Transponder freeze | The signal is held constant from a chosen point. |
The bench produces ground-truth labels (each injection knows exactly when it starts) and therefore enables two metrics that real data cannot give us: PR-AUC per anomaly type and detection latency (median seconds between the injection onset and the first moment the per-step score crosses the alert threshold).
The synthetic bench is used only for evaluation. No synthetic window ever enters the training set.
7. Evaluation
7.1 Real test set
The four detectors are scored on the same held-out test split. The headline metric is PR-AUC, which is more informative than ROC-AUC under the strong class imbalance present in real data.
| Model | ROC-AUC | PR-AUC | Synthetic mean ROC-AUC | Median latency |
|---|---|---|---|---|
| Isolation Forest baseline | 0.515 | 0.133 | 0.593 | n/a |
| LSTM autoencoder | 0.648 | 0.260 | 0.779 | 115 s |
| Transformer autoencoder | 0.614 | 0.227 | 0.743 | 115 s |
| VAE-LSTM (selected) | 0.659 | 0.299 | 0.792 | 120 s |
| Ensemble (mean) | 0.652 | 0.273 | 0.778 | n/a |
| Ensemble (max) | 0.658 | 0.278 | 0.774 | n/a |
Three conclusions stand out:
- The deep models add substantial value over the baseline (PR-AUC roughly doubles).
- The VAE-LSTM is the best overall: highest PR-AUC on real data, highest synthetic mean ROC-AUC, competitive latency.
- The two ensemble strategies do not beat the best individual model and are not retained.
7.2 Synthetic bench (per-anomaly ROC-AUC)
The VAE-LSTM performs best across all anomaly types. The pattern is consistent: large, sustained perturbations are easier than subtle ones, and structured maneuvers (holding, high-magnitude speed change) are easier than small drifts.
| Anomaly | Intensity | Baseline | LSTM | Transformer | VAE-LSTM |
|---|---|---|---|---|---|
| Route deviation | 20.000 m | 0.513 | 0.556 | 0.543 | 0.558 |
| Route deviation | 40.000 m | 0.537 | 0.684 | 0.657 | 0.700 |
| Route deviation | 80.000 m | 0.618 | 0.883 | 0.865 | 0.899 |
| Altitude offset | 300 m | 0.502 | 0.512 | 0.505 | 0.514 |
| Altitude offset | 800 m | 0.518 | 0.581 | 0.531 | 0.593 |
| Altitude offset | 1.500 m | 0.561 | 0.726 | 0.606 | 0.752 |
| Speed factor | x1.6 | 0.620 | 0.901 | 0.835 | 0.927 |
| Speed factor | x2.2 | 0.743 | 0.985 | 0.965 | 0.989 |
| Speed factor | x0.4 | 0.673 | 0.901 | 0.884 | 0.925 |
| Holding | 240 s/turn | 0.614 | 0.972 | 0.979 | 0.978 |
| Holding | 120 s/turn | 0.634 | 0.975 | 0.985 | 0.984 |
| Transponder freeze | stuck | 0.582 | 0.668 | 0.558 | 0.686 |
7.3 Curves and confusion matrices
Precision-recall and ROC curves are produced for every detector, together with a
confusion matrix taken at the validation-99-percentile threshold. The figures are
saved as PNGs under reports/figures/ and the underlying arrays are stored inside
reports/model_comparison.json, so the dashboard can render them without recomputing.
8. Serving
The selected VAE-LSTM is served by a small FastAPI application
(src/sadar/serve/app.py). At startup, the service loads:
- the checkpoint of the production model,
- the scaler fit on the training data,
- the precomputed metrics report,
- and a curated sample of trajectories so the dashboard can run without raw data.
The HTTP surface is intentionally small. The dashboard talks to the backend through five endpoints:
| Endpoint | Purpose |
|---|---|
GET /api/health |
Liveness probe (also serves a build identifier). |
GET /api/scene |
Current radar scene: aircraft, scores, threshold. |
GET /api/flights |
Ranked list of monitored flights with metadata. |
GET /api/metrics |
Pre-computed comparative metrics and curves. |
POST /api/simulate |
Inject an anomaly on a chosen flight and return the response. |
In production, the same FastAPI process also serves the built React app (the SPA is
mounted on /, the API stays under /api). The whole system runs in a single
container.
9. Frontend
The dashboard is a React 18 + Vite + TypeScript application designed to look and feel like a tower control console: dark background, monospace data blocks, criticality encoded by color (green/cyan for normal, amber/red for alert). It exposes four screens:
- Tower console. A live radar scope centered on LEMD with a side panel listing every tracked flight and its current state. Tracked vs alerted aircraft are color-coded.
- Simulator. The interactive heart of the demo. The operator picks a flight, an anomaly type, an intensity and an onset, and injects the perturbation in real time. The score on the right reacts to the injection and the detection latency is reported in seconds.
- Metrics. The full comparative report: ROC and PR curves, per-anomaly PR-AUC breakdown, model selection rationale and final-detector summary.
- Presentation. A guided walkthrough used for live presentations of the project.
The app is internationalized in English and Spanish through a small translation context.
10. Reproducibility and deployment
The project is engineered so that the entire pipeline (preprocessing, training, evaluation, serving) can be reproduced from scratch.
- Python environment. Locked with
uvandpyproject.toml; one command (uv sync) rebuilds the environment. - Frontend environment. Managed with
pnpmandpnpm-lock.yaml. - Configuration. All knobs (paths, window length, model dimensions, training
schedule, evaluation thresholds) live in YAML files under
configs/. The code reads no literal paths. - Seeds. A single seed is set at the top of every entry point; it propagates to NumPy, scikit-learn and PyTorch.
- Tracking. MLflow logs all training runs locally; Optuna stores its search history alongside the resulting hyperparameters.
- Containerization. A single Dockerfile builds a self-contained image that bundles
the trained model, the scaler, the precomputed reports and the SPA. The same image is
used locally (
docker compose up) and in production (Hugging Face Spaces).
A Makefile exposes the workflow as named targets: make install, make preprocess,
make train-lstm, make train-transformer, make train-vae, make tune-vae,
make compare, make serve, make dev, make docker-up.
11. Limitations and future work
11.1 Limitations
- Scope. The dataset covers one airport (LEMD) and a limited time span. The model is not expected to transfer to other terminals without retraining.
- No flight plans. "Intended route" is approximated as "learned normal pattern". A flight that deviates from its filed route but stays inside the normal traffic pattern may not be flagged.
- Coverage gaps. Transponder gaps in ADS-B are dominated by receiver coverage. They are used as an input feature, not as a label.
- Class imbalance. Real anomalous events are extremely rare. The PR-AUC numbers on the real test set are computed on a small positive set; the synthetic bench is the primary quantitative benchmark.
11.2 Future work
- Extend the dataset to additional airports and validate cross-airport generalization.
- Incorporate filed flight plans, when available, as an auxiliary signal.
- Combine the conformance score with a downstream classifier trained on labeled operational events (go-arounds, missed approaches, holding) for more interpretable alerts.
- Explore probabilistic ensembles that weight each model by its calibrated likelihood rather than by a fixed reducer.
12. Conclusions
SADAR shows that a one-class deep-learning approach is a viable foundation for trajectory conformance monitoring at a major airport. Three sequence autoencoders trained on the same data and evaluated on the same bench produce a clear, defensible model choice: the VAE-LSTM combines the best PR-AUC on real data, the best mean performance on the synthetic bench and a competitive median detection latency, while improving substantially over a non-sequential baseline. The system is fully reproducible, packaged as a single container and served through a dashboard built around a real operational use case.
The result is not a finished product, but it is a solid prototype of how data-driven conformance monitoring could complement the rule-based tools that air traffic operators use today.
Repository. Smart Anomaly Detection for Aviation Routes (SADAR). Madrid-Barajas (LEMD) · 2026.