telemetry-anomdet
telemetry-anomdet is an open-source anomaly detection toolkit for spacecraft telemetry. It runs classical and graph-based deep detectors behind one interface, selects alarm thresholds without labels, and distills a trained detector down to Power of Ten conformant C that runs on flight hardware. Per-channel SHAP attribution and LLM-generated diagnostic reports are on the roadmap.
Benchmarked on SMAP (NASA). MSL (NASA) and ESA-ADB (ESA) results land in v0.3.0.
| Getting Started Installation and setup |
API Reference BaseDetector, models, ensemble |
Tutorial End-to-end detection example |
Pipeline Overview Ingest, detect, explain |
Discussions Questions and feedback |
Quick Install
# recommended
uv add telemetry-anomdet
# or with pip
pip install telemetry-anomdet
The base install is torch-free. The deep detectors (GDN, KANGDN) need the
deep extra:
uv add "telemetry-anomdet[deep]"
Example Usage
Fit the Ensemble on Nominal Telemetry
All detectors are trained on nominal data only, no anomaly labels used during training. Labels are used exclusively for evaluation.
import numpy as np
from telemetry_anomdet.models.unsupervised.pca import PCAAnomaly
from telemetry_anomdet.models.unsupervised.kmeans import KMeansAnomaly
from telemetry_anomdet.models.ensemble import AnomalyEnsemble
# X_train: (n_windows, window_size, n_features), produced by windowify()
X_train = ...
ensemble = AnomalyEnsemble(
models = {
"pca": PCAAnomaly(n_components = 10),
"kmeans": KMeansAnomaly(n_clusters = 5, scale = True),
},
combine = "mean",
normalize = "robust",
percentile = 95.0,
)
ensemble.fit(X_train)
Inference and Anomaly Flags
scores = ensemble.decision_function(X_test) # higher = more anomalous
flags = ensemble.is_anomaly(X_test) # boolean mask at training threshold
# Runtime sensitivity override, no retraining needed
flags_strict = ensemble.is_anomaly(X_test, percentile = 98.0)
flags_custom = ensemble.is_anomaly(X_test, threshold = 0.75)
Per-Model Score Decomposition
score_components() returns raw per-model scores before normalization or combination. This is the SHAP hook: perturb input channels, measure how each sensor drives each model's score independently.
components = ensemble.score_components(X_test)
# {"pca": array(100,), "kmeans": array(100,)}
Getting the SMAP dataset
The benchmarks and examples need a local copy of the NASA SMAP/MSL data, which is not redistributed with the toolkit. From the root of the repo:
pip install kaggle
# Requires a Kaggle API key in ~/.kaggle/kaggle.json
kaggle datasets download -d patrickfleith/nasa-anomaly-detection-dataset-smap-msl \
&& mv nasa-anomaly-detection-dataset-smap-msl.zip data.zip \
&& unzip -o data.zip \
&& rm data.zip \
&& mv data/data tmp && rm -r data && mv tmp data
That leaves data/train/, data/test/ and labeled_anomalies.csv. Point the
examples at it:
export TAD_SMAP_DIR=data # Windows: $env:TAD_SMAP_DIR = "data"
python examples/smap_demo.py
python examples/smap_benchmark.py
Datasets are never committed; data/ is gitignored.
Coming in Phase 3+
# Per-channel SHAP attribution (Phase 3)
# shap_values = explainer.explain(ensemble, X_anomalous)
# {"sensor_01": 0.42, "sensor_02": 0.18, ...}
# LLM diagnostic report (Phase 4)
# report = llm_engine.explain(shap_values, context, channel_names)
# DiagnosticReport(
# anomaly_type = "power subsystem fault",
# severity = "high",
# primary_channels = ["sensor_01", "sensor_07"],
# explanation = "...",
# recommended_action = "...",
# confidence = 0.87,
# )
Features
Available now
- One interface for every detector. Classical and deep detectors share the same
fit/decision_function/predict/is_anomalyAPI, so stacking or swapping models needs no per-model glue. - Graph deviation detectors.
GDNandKANGDNforecast each channel from its learned top-k neighbours and score the deviation, catching readings that are individually plausible but wrong in relation to each other. - Alarms without labels.
threshold_for_budgetanddynamic_thresholdpick an operating point from the score distribution alone. Operations can state an alarm rate; they cannot state a recall they have no way to observe. - Honest evaluation. Event-level scoring alongside the conventional point-adjusted F1, plus a random baseline row in the benchmark, because point-adjusted F1 rates uniform random noise above every trained configuration on SMAP.
- Runs on flight hardware. A fitted
KANGDNdistills to a torch-free NumPy evaluator, then to Power of Ten conformant C with golden vectors. Host and ESP32-S3 targets are intargets/. - Stacking ensemble with robust (median + IQR) score normalization, built for anomaly scores that are extreme by definition.
- Per-model score decomposition via
score_components(), the hook that enables per-channel attribution without retraining. - Spacecraft-native ingestion. SMAP and CSV load straight into a long-form
TelemetryDataset, no schema wrangling. - Runtime sensitivity control.
is_anomaly()accepts a percentile or threshold override, so operators re-tune without retraining.
On the roadmap
TranAD: transformer-based sequence reconstructionSHAPExplainer: per-channel attribution overscore_components()(Phase 3)- LLM diagnostic reports, SHAP chart supplied as an image (Phase 4)
- Human-in-the-loop threshold feedback (Phase 5)
- MSL results, using the existing SMAP loader (
spacecraft = "MSL") (v0.3.0) - ESA-ADB evaluation: the multivariate benchmark that exercises the graph (v0.3.0)
AI Transparency
AI assistants are used while writing and maintaining this project's documentation: drafting API reference pages, tightening prose, and catching claims that go stale.
What that does not change:
- Numbers come from runs. Benchmark figures are produced by
examples/smap_benchmark.py, and the hardware results in the deployment docs are measured on an ESP32-S3 by the conformance harness intargets/. Nothing quoted in these docs is estimated or inferred. - Behavior claims are backed by tests. The suite gates CI, and the documented API is covered by it.
Everything published here is reviewed before it lands.
Getting Help
Discussions
To suggest improvements, or ask for help, please see GitHub Discussions
Bug reports
To submit a report on any bugs or issues, open an issue here
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file telemetry_anomdet-0.2.0.tar.gz.
File metadata
- Download URL: telemetry_anomdet-0.2.0.tar.gz
- Upload date:
- Size: 96.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e0a3b11a849979a9a2eeb98ef7d6de435c20e7f3a24fb278b6463451a788462
|
|
| MD5 |
a00ac890b11c8c6d8342ad456bae2690
|
|
| BLAKE2b-256 |
99dc4122f9eb9007a5d9a00108209ebe3e9e0bfa765584d42a481d1cfdeb5b91
|
Provenance
The following attestation bundles were made for telemetry_anomdet-0.2.0.tar.gz:
Publisher:
publish.yml on JulioAnzaldo/telemetry-anomdet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
telemetry_anomdet-0.2.0.tar.gz -
Subject digest:
7e0a3b11a849979a9a2eeb98ef7d6de435c20e7f3a24fb278b6463451a788462 - Sigstore transparency entry: 2464846091
- Sigstore integration time:
-
Permalink:
JulioAnzaldo/telemetry-anomdet@9c5f3a9c7720110de518492ec0458de85552dd69 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/JulioAnzaldo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9c5f3a9c7720110de518492ec0458de85552dd69 -
Trigger Event:
release
-
Statement type:
File details
Details for the file telemetry_anomdet-0.2.0-py3-none-any.whl.
File metadata
- Download URL: telemetry_anomdet-0.2.0-py3-none-any.whl
- Upload date:
- Size: 77.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e3e2fd2a6abbf106731f5981eaa9c0dab1e64d7c0048774039cc9fd7b6cd55e
|
|
| MD5 |
9d72f877324dd6a4dc07307457073d4c
|
|
| BLAKE2b-256 |
03bdbbab76d992e3111baffd95bfa36bf7c61e85e9f05e98ab7eda92b4d074ab
|
Provenance
The following attestation bundles were made for telemetry_anomdet-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on JulioAnzaldo/telemetry-anomdet
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
telemetry_anomdet-0.2.0-py3-none-any.whl -
Subject digest:
1e3e2fd2a6abbf106731f5981eaa9c0dab1e64d7c0048774039cc9fd7b6cd55e - Sigstore transparency entry: 2464847099
- Sigstore integration time:
-
Permalink:
JulioAnzaldo/telemetry-anomdet@9c5f3a9c7720110de518492ec0458de85552dd69 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/JulioAnzaldo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9c5f3a9c7720110de518492ec0458de85552dd69 -
Trigger Event:
release
-
Statement type: