telemetry-anomdet 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. telemetry_anomdet-0.2.0/PKG-INFO +222 -0
  2. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/README.md +59 -8
  3. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/pyproject.toml +33 -20
  4. telemetry_anomdet-0.2.0/src/telemetry_anomdet/evaluation.py +388 -0
  5. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/ingest/csv_loader.py +13 -5
  6. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/ingest/smap.py +29 -3
  7. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/models/__init__.py +3 -2
  8. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/models/base.py +7 -7
  9. telemetry_anomdet-0.2.0/src/telemetry_anomdet/models/deep/__init__.py +18 -0
  10. telemetry_anomdet-0.2.0/src/telemetry_anomdet/models/deep/_kan.py +217 -0
  11. telemetry_anomdet-0.2.0/src/telemetry_anomdet/models/deep/_net.py +233 -0
  12. telemetry_anomdet-0.2.0/src/telemetry_anomdet/models/deep/codegen.py +905 -0
  13. telemetry_anomdet-0.2.0/src/telemetry_anomdet/models/deep/distill.py +440 -0
  14. telemetry_anomdet-0.2.0/src/telemetry_anomdet/models/deep/gdn.py +421 -0
  15. telemetry_anomdet-0.2.0/src/telemetry_anomdet/models/deep/kan_gdn.py +99 -0
  16. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/models/ensemble.py +1 -1
  17. telemetry_anomdet-0.2.0/src/telemetry_anomdet/thresholding.py +393 -0
  18. telemetry_anomdet-0.2.0/src/telemetry_anomdet.egg-info/PKG-INFO +222 -0
  19. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet.egg-info/SOURCES.txt +20 -1
  20. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet.egg-info/requires.txt +3 -1
  21. telemetry_anomdet-0.2.0/tests/test_csv_loader.py +263 -0
  22. telemetry_anomdet-0.2.0/tests/test_deep_codegen.py +696 -0
  23. telemetry_anomdet-0.2.0/tests/test_deep_distill.py +218 -0
  24. telemetry_anomdet-0.2.0/tests/test_deep_gdn.py +268 -0
  25. telemetry_anomdet-0.2.0/tests/test_deep_kan.py +85 -0
  26. telemetry_anomdet-0.2.0/tests/test_deep_kangdn.py +110 -0
  27. telemetry_anomdet-0.2.0/tests/test_deep_smoothing.py +103 -0
  28. telemetry_anomdet-0.2.0/tests/test_evaluation_prauc.py +87 -0
  29. telemetry_anomdet-0.2.0/tests/test_evaluation_sequences.py +122 -0
  30. telemetry_anomdet-0.2.0/tests/test_score_channels.py +167 -0
  31. telemetry_anomdet-0.2.0/tests/test_threshold_budget.py +112 -0
  32. telemetry_anomdet-0.2.0/tests/test_thresholding.py +271 -0
  33. telemetry_anomdet-0.1.0/PKG-INFO +0 -364
  34. telemetry_anomdet-0.1.0/src/telemetry_anomdet/evaluation.py +0 -167
  35. telemetry_anomdet-0.1.0/src/telemetry_anomdet/visualization.py +0 -13
  36. telemetry_anomdet-0.1.0/src/telemetry_anomdet.egg-info/PKG-INFO +0 -364
  37. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/LICENSE +0 -0
  38. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/setup.cfg +0 -0
  39. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/__init__.py +0 -0
  40. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/feature_extraction/features.py +0 -0
  41. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/ingest/__init__.py +0 -0
  42. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/ingest/ccsds_loader.py +0 -0
  43. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/ingest/dataset.py +0 -0
  44. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/models/supervised/__init__.py +0 -0
  45. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/models/unsupervised/__init__.py +0 -0
  46. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/models/unsupervised/gaussian_nb.py +0 -0
  47. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/models/unsupervised/isolation_forest.py +0 -0
  48. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/models/unsupervised/kmeans.py +0 -0
  49. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/models/unsupervised/pca.py +0 -0
  50. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/preprocessing/__init__.py +0 -0
  51. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/preprocessing/preprocessing.py +0 -0
  52. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/py.typed +0 -0
  53. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet/version.py +0 -0
  54. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet.egg-info/dependency_links.txt +0 -0
  55. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/src/telemetry_anomdet.egg-info/top_level.txt +0 -0
  56. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/tests/test_base_detector.py +0 -0
  57. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/tests/test_ensemble.py +0 -0
  58. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/tests/test_evaluation.py +0 -0
  59. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/tests/test_import.py +0 -0
  60. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/tests/test_preprocessing.py +0 -0
  61. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/tests/test_smap.py +0 -0
  62. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/tests/test_unsup_kmeans.py +0 -0
  63. {telemetry_anomdet-0.1.0 → telemetry_anomdet-0.2.0}/tests/test_unsup_pca.py +0 -0
@@ -0,0 +1,222 @@
1
+ Metadata-Version: 2.4
2
+ Name: telemetry-anomdet
3
+ Version: 0.2.0
4
+ Summary: Spacecraft telemetry anomaly detection: classical and graph deep detectors, label-free thresholding, and distillation to C for flight hardware.
5
+ Author-email: Julio Anzaldo <anzaldojuliojesus@gmail.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/JulioAnzaldo/telemetry-anomdet
8
+ Project-URL: Repository, https://github.com/JulioAnzaldo/telemetry-anomdet
9
+ Keywords: telemetry,anomaly-detection,machine-learning,python
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Natural Language :: English
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: C
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
24
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
25
+ Classifier: Topic :: Software Development :: Libraries
26
+ Classifier: Topic :: Software Development :: Embedded Systems
27
+ Classifier: Topic :: Software Development :: Code Generators
28
+ Classifier: Topic :: System :: Monitoring
29
+ Classifier: Typing :: Typed
30
+ Requires-Python: >=3.10
31
+ Description-Content-Type: text/markdown
32
+ License-File: LICENSE
33
+ Requires-Dist: pandas<3.0.0,>=2.2.2
34
+ Requires-Dist: numpy<3.0,>=2.1
35
+ Requires-Dist: scikit-learn<2.0,>=1.3
36
+ Requires-Dist: typing-extensions>=4.16.0
37
+ Provides-Extra: ccsds
38
+ Requires-Dist: ccsdspy<3.0,>=2.0.0; extra == "ccsds"
39
+ Provides-Extra: deep
40
+ Requires-Dist: torch>=2.0; extra == "deep"
41
+ Dynamic: license-file
42
+
43
+ <h2 align="center">telemetry-anomdet</h2>
44
+
45
+ <p align="center">
46
+ <a href="https://julioanzaldo.github.io/telemetry-anomdet/"><img alt="Documentation" src="https://img.shields.io/badge/Docs-Online-blue"></a>
47
+ <a href="https://test.pypi.org/project/telemetry-anomdet/"><img alt="Test PyPI" src="https://img.shields.io/badge/Test-PyPI-yellow"></a>
48
+ <a href="https://pypi.org/project/telemetry-anomdet/"><img alt="PyPI" src="https://img.shields.io/pypi/v/telemetry-anomdet?label=PyPi"></a>
49
+ <a href="https://github.com/JulioAnzaldo/telemetry-anomdet/actions/workflows/ci.yml"><img alt="Testing" src="https://img.shields.io/github/actions/workflow/status/JulioAnzaldo/telemetry-anomdet/ci.yml?branch=main&label=Testing&logo=github&logoColor=white"></a>
50
+ <a href="https://github.com/JulioAnzaldo/telemetry-anomdet/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-Apache%202.0-blue"></a>
51
+ </p>
52
+
53
+ **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.
54
+
55
+ Benchmarked on SMAP (NASA). MSL (NASA) and ESA-ADB (ESA) results land in v0.3.0.
56
+
57
+ ---
58
+
59
+ <table>
60
+ <tr>
61
+ <td align="center"><a href="https://julioanzaldo.github.io/telemetry-anomdet/getting_started.html"><b>Getting Started</b></a><br>Installation and setup</td>
62
+ <td align="center"><a href="https://julioanzaldo.github.io/telemetry-anomdet/api/models/base.html"><b>API Reference</b></a><br>BaseDetector, models, ensemble</td>
63
+ <td align="center"><a href="https://julioanzaldo.github.io/telemetry-anomdet/tutorials/real_time_example.html"><b>Tutorial</b></a><br>End-to-end detection example</td>
64
+ <td align="center"><a href="https://julioanzaldo.github.io/telemetry-anomdet/user_guide/pipeline_overview.html"><b>Pipeline Overview</b></a><br>Ingest, detect, explain</td>
65
+ <td align="center"><a href="https://github.com/JulioAnzaldo/telemetry-anomdet/discussions"><b>Discussions</b></a><br>Questions and feedback</td>
66
+ </tr>
67
+ </table>
68
+
69
+ ---
70
+
71
+ ## Quick Install
72
+
73
+ ```bash
74
+ # recommended
75
+ uv add telemetry-anomdet
76
+
77
+ # or with pip
78
+ pip install telemetry-anomdet
79
+ ```
80
+
81
+ The base install is torch-free. The deep detectors (`GDN`, `KANGDN`) need the
82
+ `deep` extra:
83
+
84
+ ```bash
85
+ uv add "telemetry-anomdet[deep]"
86
+ ```
87
+
88
+ ## Example Usage
89
+
90
+ ### Fit the Ensemble on Nominal Telemetry
91
+
92
+ All detectors are trained on **nominal data only**, no anomaly labels used during training. Labels are used exclusively for evaluation.
93
+
94
+ ```python
95
+ import numpy as np
96
+ from telemetry_anomdet.models.unsupervised.pca import PCAAnomaly
97
+ from telemetry_anomdet.models.unsupervised.kmeans import KMeansAnomaly
98
+ from telemetry_anomdet.models.ensemble import AnomalyEnsemble
99
+
100
+ # X_train: (n_windows, window_size, n_features), produced by windowify()
101
+ X_train = ...
102
+
103
+ ensemble = AnomalyEnsemble(
104
+ models = {
105
+ "pca": PCAAnomaly(n_components = 10),
106
+ "kmeans": KMeansAnomaly(n_clusters = 5, scale = True),
107
+ },
108
+ combine = "mean",
109
+ normalize = "robust",
110
+ percentile = 95.0,
111
+ )
112
+
113
+ ensemble.fit(X_train)
114
+ ```
115
+
116
+ ### Inference and Anomaly Flags
117
+
118
+ ```python
119
+ scores = ensemble.decision_function(X_test) # higher = more anomalous
120
+ flags = ensemble.is_anomaly(X_test) # boolean mask at training threshold
121
+
122
+ # Runtime sensitivity override, no retraining needed
123
+ flags_strict = ensemble.is_anomaly(X_test, percentile = 98.0)
124
+ flags_custom = ensemble.is_anomaly(X_test, threshold = 0.75)
125
+ ```
126
+
127
+ ### Per-Model Score Decomposition
128
+
129
+ `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.
130
+
131
+ ```python
132
+ components = ensemble.score_components(X_test)
133
+ # {"pca": array(100,), "kmeans": array(100,)}
134
+ ```
135
+
136
+ ### Getting the SMAP dataset
137
+
138
+ The benchmarks and examples need a local copy of the NASA SMAP/MSL data, which
139
+ is not redistributed with the toolkit. From the root of the repo:
140
+
141
+ ```bash
142
+ pip install kaggle
143
+
144
+ # Requires a Kaggle API key in ~/.kaggle/kaggle.json
145
+ kaggle datasets download -d patrickfleith/nasa-anomaly-detection-dataset-smap-msl \
146
+ && mv nasa-anomaly-detection-dataset-smap-msl.zip data.zip \
147
+ && unzip -o data.zip \
148
+ && rm data.zip \
149
+ && mv data/data tmp && rm -r data && mv tmp data
150
+ ```
151
+
152
+ That leaves `data/train/`, `data/test/` and `labeled_anomalies.csv`. Point the
153
+ examples at it:
154
+
155
+ ```bash
156
+ export TAD_SMAP_DIR=data # Windows: $env:TAD_SMAP_DIR = "data"
157
+ python examples/smap_demo.py
158
+ python examples/smap_benchmark.py
159
+ ```
160
+
161
+ Datasets are never committed; `data/` is gitignored.
162
+
163
+ ### Coming in Phase 3+
164
+
165
+ ```python
166
+ # Per-channel SHAP attribution (Phase 3)
167
+ # shap_values = explainer.explain(ensemble, X_anomalous)
168
+ # {"sensor_01": 0.42, "sensor_02": 0.18, ...}
169
+
170
+ # LLM diagnostic report (Phase 4)
171
+ # report = llm_engine.explain(shap_values, context, channel_names)
172
+ # DiagnosticReport(
173
+ # anomaly_type = "power subsystem fault",
174
+ # severity = "high",
175
+ # primary_channels = ["sensor_01", "sensor_07"],
176
+ # explanation = "...",
177
+ # recommended_action = "...",
178
+ # confidence = 0.87,
179
+ # )
180
+ ```
181
+
182
+ ## Features
183
+
184
+ **Available now**
185
+
186
+ - **One interface for every detector.** Classical and deep detectors share the same `fit` / `decision_function` / `predict` / `is_anomaly` API, so stacking or swapping models needs no per-model glue.
187
+ - **Graph deviation detectors.** `GDN` and `KANGDN` forecast 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.
188
+ - **Alarms without labels.** `threshold_for_budget` and `dynamic_threshold` pick 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.
189
+ - **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.
190
+ - **Runs on flight hardware.** A fitted `KANGDN` distills to a torch-free NumPy evaluator, then to Power of Ten conformant C with golden vectors. Host and ESP32-S3 targets are in [`targets/`](targets/).
191
+ - **Stacking ensemble** with robust (median + IQR) score normalization, built for anomaly scores that are extreme by definition.
192
+ - **Per-model score decomposition** via `score_components()`, the hook that enables per-channel attribution without retraining.
193
+ - **Spacecraft-native ingestion.** SMAP and CSV load straight into a long-form `TelemetryDataset`, no schema wrangling.
194
+ - **Runtime sensitivity control.** `is_anomaly()` accepts a percentile or threshold override, so operators re-tune without retraining.
195
+
196
+ **On the roadmap**
197
+
198
+ - `TranAD`: transformer-based sequence reconstruction
199
+ - `SHAPExplainer`: per-channel attribution over `score_components()` (Phase 3)
200
+ - LLM diagnostic reports, SHAP chart supplied as an image (Phase 4)
201
+ - Human-in-the-loop threshold feedback (Phase 5)
202
+ - MSL results, using the existing SMAP loader (`spacecraft = "MSL"`) (v0.3.0)
203
+ - ESA-ADB evaluation: the multivariate benchmark that exercises the graph (v0.3.0)
204
+
205
+ ## AI Transparency
206
+
207
+ AI assistants are used while writing and maintaining this project's documentation: drafting API reference pages, tightening prose, and catching claims that go stale.
208
+
209
+ What that does not change:
210
+
211
+ - **Numbers come from runs.** Benchmark figures are produced by [`examples/smap_benchmark.py`](examples/smap_benchmark.py), and the hardware results in the deployment docs are measured on an ESP32-S3 by the conformance harness in [`targets/`](targets/). Nothing quoted in these docs is estimated or inferred.
212
+ - **Behavior claims are backed by tests.** The suite gates CI, and the documented API is covered by it.
213
+
214
+ Everything published here is reviewed before it lands.
215
+
216
+ ## Getting Help
217
+
218
+ ### Discussions
219
+ To suggest improvements, or ask for help, please see [GitHub Discussions](https://github.com/JulioAnzaldo/telemetry-anomdet/discussions)
220
+
221
+ ### Bug reports
222
+ To submit a report on any bugs or issues, [open an issue here](https://github.com/JulioAnzaldo/telemetry-anomdet/issues)
@@ -1,14 +1,16 @@
1
1
  <h2 align="center">telemetry-anomdet</h2>
2
2
 
3
3
  <p align="center">
4
- <a href="https://julioanzaldo.github.io/telemetry-anomdet/"><img alt="Documentation" src="https://img.shields.io/badge/docs-online-blue"></a>
4
+ <a href="https://julioanzaldo.github.io/telemetry-anomdet/"><img alt="Documentation" src="https://img.shields.io/badge/Docs-Online-blue"></a>
5
5
  <a href="https://test.pypi.org/project/telemetry-anomdet/"><img alt="Test PyPI" src="https://img.shields.io/badge/Test-PyPI-yellow"></a>
6
- <a href="https://pypi.org/project/telemetry-anomdet/"><img alt="PyPI" src="https://img.shields.io/pypi/v/telemetry-anomdet"></a>
7
- <a href="https://github.com/JulioAnzaldo/telemetry-anomdet/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/JulioAnzaldo/telemetry-anomdet/actions/workflows/ci.yml/badge.svg?branch=main"></a>
8
- <a href="https://github.com/JulioAnzaldo/telemetry-anomdet/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/badge/license-Apache%202.0-blue"></a>
6
+ <a href="https://pypi.org/project/telemetry-anomdet/"><img alt="PyPI" src="https://img.shields.io/pypi/v/telemetry-anomdet?label=PyPi"></a>
7
+ <a href="https://github.com/JulioAnzaldo/telemetry-anomdet/actions/workflows/ci.yml"><img alt="Testing" src="https://img.shields.io/github/actions/workflow/status/JulioAnzaldo/telemetry-anomdet/ci.yml?branch=main&label=Testing&logo=github&logoColor=white"></a>
8
+ <a href="https://github.com/JulioAnzaldo/telemetry-anomdet/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-Apache%202.0-blue"></a>
9
9
  </p>
10
10
 
11
- **telemetry-anomdet** is an open-source anomaly detection toolkit for spacecraft telemetry. It runs a stacking ensemble of classical and deep learning detectors with per-channel SHAP attribution, SymTorch symbolic fault expressions, and LLM-generated diagnostic reports.
11
+ **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.
12
+
13
+ Benchmarked on SMAP (NASA). MSL (NASA) and ESA-ADB (ESA) results land in v0.3.0.
12
14
 
13
15
  ---
14
16
 
@@ -34,6 +36,13 @@ uv add telemetry-anomdet
34
36
  pip install telemetry-anomdet
35
37
  ```
36
38
 
39
+ The base install is torch-free. The deep detectors (`GDN`, `KANGDN`) need the
40
+ `deep` extra:
41
+
42
+ ```bash
43
+ uv add "telemetry-anomdet[deep]"
44
+ ```
45
+
37
46
  ## Example Usage
38
47
 
39
48
  ### Fit the Ensemble on Nominal Telemetry
@@ -82,6 +91,33 @@ components = ensemble.score_components(X_test)
82
91
  # {"pca": array(100,), "kmeans": array(100,)}
83
92
  ```
84
93
 
94
+ ### Getting the SMAP dataset
95
+
96
+ The benchmarks and examples need a local copy of the NASA SMAP/MSL data, which
97
+ is not redistributed with the toolkit. From the root of the repo:
98
+
99
+ ```bash
100
+ pip install kaggle
101
+
102
+ # Requires a Kaggle API key in ~/.kaggle/kaggle.json
103
+ kaggle datasets download -d patrickfleith/nasa-anomaly-detection-dataset-smap-msl \
104
+ && mv nasa-anomaly-detection-dataset-smap-msl.zip data.zip \
105
+ && unzip -o data.zip \
106
+ && rm data.zip \
107
+ && mv data/data tmp && rm -r data && mv tmp data
108
+ ```
109
+
110
+ That leaves `data/train/`, `data/test/` and `labeled_anomalies.csv`. Point the
111
+ examples at it:
112
+
113
+ ```bash
114
+ export TAD_SMAP_DIR=data # Windows: $env:TAD_SMAP_DIR = "data"
115
+ python examples/smap_demo.py
116
+ python examples/smap_benchmark.py
117
+ ```
118
+
119
+ Datasets are never committed; `data/` is gitignored.
120
+
85
121
  ### Coming in Phase 3+
86
122
 
87
123
  ```python
@@ -106,6 +142,10 @@ components = ensemble.score_components(X_test)
106
142
  **Available now**
107
143
 
108
144
  - **One interface for every detector.** Classical and deep detectors share the same `fit` / `decision_function` / `predict` / `is_anomaly` API, so stacking or swapping models needs no per-model glue.
145
+ - **Graph deviation detectors.** `GDN` and `KANGDN` forecast 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.
146
+ - **Alarms without labels.** `threshold_for_budget` and `dynamic_threshold` pick 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.
147
+ - **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.
148
+ - **Runs on flight hardware.** A fitted `KANGDN` distills to a torch-free NumPy evaluator, then to Power of Ten conformant C with golden vectors. Host and ESP32-S3 targets are in [`targets/`](targets/).
109
149
  - **Stacking ensemble** with robust (median + IQR) score normalization, built for anomaly scores that are extreme by definition.
110
150
  - **Per-model score decomposition** via `score_components()`, the hook that enables per-channel attribution without retraining.
111
151
  - **Spacecraft-native ingestion.** SMAP and CSV load straight into a long-form `TelemetryDataset`, no schema wrangling.
@@ -113,12 +153,23 @@ components = ensemble.score_components(X_test)
113
153
 
114
154
  **On the roadmap**
115
155
 
116
- - `GDN` and `TranAD` deep detectors: inter-sensor relational and transformer-reconstruction faults (Phase 2)
156
+ - `TranAD`: transformer-based sequence reconstruction
117
157
  - `SHAPExplainer`: per-channel attribution over `score_components()` (Phase 3)
118
158
  - LLM diagnostic reports, SHAP chart supplied as an image (Phase 4)
119
159
  - Human-in-the-loop threshold feedback (Phase 5)
120
- - OPS-SAT cross-dataset generalization (Phase 6)
121
- - SymTorch symbolic distillation to closed-form fault expressions for edge / microcontroller deployment (stretch)
160
+ - MSL results, using the existing SMAP loader (`spacecraft = "MSL"`) (v0.3.0)
161
+ - ESA-ADB evaluation: the multivariate benchmark that exercises the graph (v0.3.0)
162
+
163
+ ## AI Transparency
164
+
165
+ AI assistants are used while writing and maintaining this project's documentation: drafting API reference pages, tightening prose, and catching claims that go stale.
166
+
167
+ What that does not change:
168
+
169
+ - **Numbers come from runs.** Benchmark figures are produced by [`examples/smap_benchmark.py`](examples/smap_benchmark.py), and the hardware results in the deployment docs are measured on an ESP32-S3 by the conformance harness in [`targets/`](targets/). Nothing quoted in these docs is estimated or inferred.
170
+ - **Behavior claims are backed by tests.** The suite gates CI, and the documented API is covered by it.
171
+
172
+ Everything published here is reviewed before it lands.
122
173
 
123
174
  ## Getting Help
124
175
 
@@ -1,9 +1,10 @@
1
1
  [project]
2
2
  name = "telemetry-anomdet"
3
- version = "0.1.0"
4
- description = "Telemetry anomaly detection toolkit with preprocessing, feature extraction, and unsupervised models."
3
+ version = "0.2.0"
4
+ description = "Spacecraft telemetry anomaly detection: classical and graph deep detectors, label-free thresholding, and distillation to C for flight hardware."
5
5
  readme = "README.md"
6
- license = { file = "LICENSE" }
6
+ license = "Apache-2.0"
7
+ license-files = ["LICENSE"]
7
8
  requires-python = ">=3.10"
8
9
  authors = [
9
10
  { name = "Julio Anzaldo", email = "anzaldojuliojesus@gmail.com" }
@@ -12,50 +13,62 @@ dependencies = [
12
13
  "pandas>=2.2.2,<3.0.0",
13
14
  "numpy>=2.1,<3.0",
14
15
  "scikit-learn>=1.3,<2.0",
15
- "click>=8.4.2,<9.0",
16
16
  "typing-extensions>=4.16.0",
17
17
  ]
18
18
  keywords = ["telemetry", "anomaly-detection", "machine-learning", "python"]
19
+ # License is declared as an SPDX expression above, not a classifier: license
20
+ # classifiers are deprecated and setuptools warns on them.
19
21
  classifiers = [
22
+ "Development Status :: 3 - Alpha",
23
+ "Intended Audience :: Science/Research",
24
+ "Intended Audience :: Developers",
25
+ "Natural Language :: English",
26
+ "Operating System :: OS Independent",
20
27
  "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3 :: Only",
21
29
  "Programming Language :: Python :: 3.10",
22
30
  "Programming Language :: Python :: 3.11",
23
31
  "Programming Language :: Python :: 3.12",
24
32
  "Programming Language :: Python :: 3.13",
25
- "Operating System :: OS Independent",
26
- "Topic :: Software Development :: Libraries",
33
+ # The toolkit is pure Python; the artifact it generates for flight targets is C.
34
+ "Programming Language :: C",
27
35
  "Topic :: Scientific/Engineering :: Artificial Intelligence",
28
- "Intended Audience :: Science/Research",
29
- "Intended Audience :: Developers",
30
- "Development Status :: 3 - Alpha",
31
- "License :: OSI Approved :: Apache Software License",
32
- "Typing :: Typed"
36
+ "Topic :: Scientific/Engineering :: Information Analysis",
37
+ "Topic :: Scientific/Engineering :: Astronomy",
38
+ "Topic :: Software Development :: Libraries",
39
+ "Topic :: Software Development :: Embedded Systems",
40
+ "Topic :: Software Development :: Code Generators",
41
+ "Topic :: System :: Monitoring",
42
+ "Typing :: Typed",
33
43
  ]
34
44
 
35
45
  [project.optional-dependencies]
36
46
  ccsds = [
37
47
  "ccsdspy>=2.0.0,<3.0",
38
48
  ]
49
+ # Phase 2 deep detector (GDN). Optional so the base install stays
50
+ # torch-free; torch is imported lazily at fit time.
51
+ deep = [
52
+ "torch>=2.0",
53
+ ]
39
54
 
40
55
  [dependency-groups]
41
56
  dev = [
42
57
  "pytest>=9.1.1,<10.0",
43
58
  "pytest-cov>=7.1.0,<8.0",
44
- "pre-commit>=4.6.1,<5.0",
59
+ "pre-commit>=4.6.2,<5.0",
45
60
  "black>=26.5.1,<27.0",
46
- "ruff>=0.16.0,<1.0",
61
+ "ruff>=0.16.2,<1.0",
47
62
  ]
48
63
  docs = [
49
64
  "sphinx>=7.0",
50
- "sphinx-rtd-theme>=2.0",
65
+ "pydata-sphinx-theme>=0.15",
51
66
  "sphinx-autodoc-typehints>=2.0",
52
67
  "numpydoc>=1.7",
68
+ "sphinx-sitemap>=2.6",
69
+ "sphinxext-opengraph>=0.9",
70
+ "sphinx-notfound-page>=1.0",
53
71
  ]
54
- # Phase 2 -- uncomment when GDN/TranAD training starts
55
- # deep = [
56
- # "torch>=2.0",
57
- # "torch-geometric",
58
- # ]
59
72
  # Phase 3
60
73
  # xai = [
61
74
  # "shap",
@@ -72,7 +85,7 @@ Homepage = "https://github.com/JulioAnzaldo/telemetry-anomdet"
72
85
  Repository = "https://github.com/JulioAnzaldo/telemetry-anomdet"
73
86
 
74
87
  [build-system]
75
- requires = ["setuptools>=83.0.0", "wheel"]
88
+ requires = ["setuptools>=84.0.0", "wheel"]
76
89
  build-backend = "setuptools.build_meta"
77
90
 
78
91
  [tool.ruff]