1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
//! # dsfb-robotics — DSFB Structural Semiotics Engine for Robotics Health Monitoring
//!
//! **What this crate is, in one paragraph.** A deterministic, `no_std`,
//! `no_alloc`, zero-`unsafe` *observer* that reads residual streams — joint
//! torque identification residuals, inverse-dynamics residuals, whole-body
//! MPC force residuals, centroidal-momentum observer residuals, bearing
//! envelope-spectrum residuals, health-index trajectories — which existing
//! robot control and prognostics pipelines already compute, and structures
//! them into a typed grammar of human-readable episodes. DSFB does **not**
//! replace inverse-dynamics identification, Kalman / Luenberger observers,
//! whole-body controllers, MPC, rainflow RUL estimators, or vibration-based
//! FDD classifiers — it **augments** them by giving operators a structural
//! view of what those systems discard. Removing DSFB leaves the upstream
//! control and prognostics stack unchanged.
//!
//! ---
//!
//! **Invariant Forge LLC** — Prior art under 35 U.S.C. § 102.
//! Commercial deployment requires a separate written license.
//! Reference implementation: Apache-2.0.
//! <licensing@invariantforge.net>
//!
//! ## Positioning — Augmentation, not competition
//!
//! DSFB **does not compete** with existing robotics sensing, kinematic
//! identification, whole-body balance control, or PHM methods. Existing
//! methods will continue to outperform DSFB at their own tasks — earlier
//! fault detection, lower false-alarm rates, better RUL accuracy, tighter
//! tracking control. DSFB's role is orthogonal: it reads the **residuals
//! those methods already produce and usually discard**, and structures
//! them into a human-readable grammar (Admissible / Boundary / Violation)
//! with typed episodes and provenance-tagged audit trails.
//!
//! This makes existing methods **more important**, not less — DSFB is
//! literally dependent on a functioning upstream observer chain to have
//! anything to interpret.
//!
//! ## Architectural Contract
//!
//! - **Observer-only.** Public API accepts `&[f64]` (immutable reference
//! only). There is no mutable write path into any upstream data
//! structure. Enforced by type signature.
//! - **`#![no_std]`.** Core modules link against neither the Rust standard
//! library nor any OS runtime. Deployable on bare-metal MCUs (Cortex-M4F,
//! RISC-V 32-bit) alongside a safety-gate companion to an industrial
//! robot controller.
//! - **`no_alloc` in core.** All internal structures use fixed-capacity
//! array-backed types. The canonical [`observe`] signature takes a
//! caller-supplied `&mut [Episode]` output buffer. No heap allocation in
//! any hot path of the default build.
//! - **Zero `unsafe`.** No `unsafe` blocks, no `UnsafeCell`, no `RefCell`
//! in any observer code path. Enforced at compile time by
//! `#![forbid(unsafe_code)]` below.
//!
//! ## Non-Claims (from companion paper §11)
//!
//! This crate does **not** provide:
//! - Fault classification (bearing fault type, root-cause identification)
//! - Calibrated Pd/Pfa or F1/ROC-AUC guarantees
//! - Earlier detection than incumbent threshold alarms, RMS monitors, or
//! CUSUM/EWMA change-point detectors
//! - Hard real-time latency bounds under specific controller platforms
//! - RUL (remaining useful life) prediction
//! - ISO 10218-1/-2:2025 or IEC 61508 certification
//! - A replacement for any upstream observer, estimator, or controller
//!
//! ## Feature Flags
//!
//! | Feature | Description |
//! |---------|-------------|
//! | *(none)* | Core engine: `no_std` + `no_alloc` + zero unsafe |
//! | `alloc` | Opt-in heap via `alloc` crate for host-side convenience wrappers |
//! | `std` | Opt-in std library for pipeline and output modules |
//! | `serde` | JSON artefact serialization (requires `std`) |
//! | `paper_lock` | Headline-metric enforcement for deterministic reproducibility |
//! | `real_figures` | Real-dataset figure bank for the companion paper (requires `std`) |
//! | `experimental` | Exploratory extensions not validated in the companion paper |
//!
//! ## Minimal usage (bare-metal, `no_std` + `no_alloc`)
//!
//! ```
//! use dsfb_robotics::{Episode, observe};
//! let residuals: &[f64] = &[0.01, 0.02, 0.05, 0.12, 0.21];
//! let mut out = [Episode::empty(); 16];
//! let n = observe(residuals, &mut out);
//! for e in &out[..n] {
//! // advisory only — no write-back, no upstream coupling
//! let _ = (e.index, e.grammar, e.decision);
//! }
//! ```
//!
//! ## Streaming engine usage (per-observation API)
//!
//! ```
//! use dsfb_robotics::engine::DsfbRoboticsEngine;
//! use dsfb_robotics::platform::RobotContext;
//!
//! // W=8 drift window, K=4 persistence threshold
//! let mut eng = DsfbRoboticsEngine::<8, 4>::new(0.1);
//!
//! let residual_norm: f64 = 0.045; // ‖r(k)‖ from your upstream observer
//! let ep = eng.observe_one(residual_norm, false, RobotContext::ArmOperating, 0);
//! let _ = (ep.grammar, ep.decision);
//! // upstream robot controller: UNCHANGED
//! ```
// ---------------------------------------------------------------
// Conditional std/alloc imports — core does not require either.
// ---------------------------------------------------------------
extern crate alloc;
extern crate std;
// ---------------------------------------------------------------
// Core modules — unconditionally no_std + no_alloc + zero unsafe
// ---------------------------------------------------------------
/// `libm`-free f64 helpers for `no_std` + `no_alloc` core.
/// Robot operating context: commissioning, operating, stance, swing, maintenance.
/// Residual sign tuple σ(k) = (‖r‖, ṙ, r̈).
/// Admissibility envelope `E(k) = {r : ‖r‖ ≤ ρ(k)}`.
/// Grammar FSM: `Admissible | Boundary[ReasonCode] | Violation`.
/// Canonical [`Episode`] struct emitted by the observer.
/// Advisory policy layer: grammar → decision.
/// Heuristics bank: typed robotics motifs.
/// Syntax layer: classify sign tuples into named motifs (see
/// [`heuristics::RoboticsMotif`] for the typed motif catalogue).
/// Shared residual helper for kinematic-identification datasets.
/// Shared residual helper for balancing datasets.
/// Healthy-window envelope calibration.
/// Wide-sense-stationarity check for calibration windows.
/// Uncertainty budget per GUM JCGM 100:2008.
/// Streaming DSFB engine orchestrator. See
/// [`engine::DsfbRoboticsEngine`] and [`grammar::GrammarEvaluator`]
/// for the canonical per-sample pipeline.
/// Per-dataset residual adapters across PHM (CWRU, IMS, FEMTO-ST),
/// kinematics (KUKA LWR-IV+, Franka Panda Gaz, DLR-class
/// Giacomuzzo, UR10 Polydoros), and balancing (MIT Mini-Cheetah,
/// iCub push-recovery, ANYmal, Unitree G1, ergoCub Sorrentino,
/// plus the LeRobot ALOHA / Mobile-ALOHA / SO-100 / DROID / OpenX
/// teleoperation slates). See [`datasets::DatasetId`] for the
/// canonical slug enumeration.
/// Paper-lock driver: per-dataset DSFB evaluation, deterministic
/// JSON emission, bit-exact reproducibility gate. Feature-gated on
/// `paper_lock` (which pulls in `std` + `serde` + `serde_json`).
// Kani formal-verification harnesses — compiled only when the crate is
// built with `#[cfg(kani)]` (which Kani itself sets). Invisible in
// stock `cargo build` output. See `src/kani_proofs.rs` for the
// harness inventory.
// ---------------------------------------------------------------
// Public flat re-exports — the most-used types at crate root so that
// `use dsfb_robotics::{Episode, observe, GrammarState, DsfbRoboticsEngine};`
// is idiomatic.
// ---------------------------------------------------------------
pub use crateDsfbRoboticsEngine;
pub use crateAdmissibilityEnvelope;
pub use crateEpisode;
pub use crate;
pub use crateRobotContext;
pub use cratePolicyDecision;
pub use crate;
// ---------------------------------------------------------------
// Top-level convenience observe()
// ---------------------------------------------------------------
/// Read-only one-shot DSFB observation of a residual slice.
///
/// Constructs a default-parameter engine (`W = 8`, `K = 4`, envelope
/// radius ρ calibrated from the **first 20 %** of the input under the
/// paper's Stage III protocol) and streams `residuals` into `out`.
///
/// Returns the number of episodes written. Never writes past
/// `out.len()`. Callers that need a custom drift window, persistence
/// threshold, or a pre-computed envelope should use
/// [`DsfbRoboticsEngine`] directly.
///
/// This is the advertised `no_alloc` entry point:
/// `observe(&[f64], &mut [Episode]) -> usize`.
///
/// # Determinism
///
/// Pure function; identical ordered inputs produce identical outputs.
/// No global state, no allocation, no side effects, no panic paths.
///
/// # Non-finite input samples
///
/// Treated as below-floor (missingness-aware): they always produce
/// `grammar = "Admissible"`, `decision = "Silent"` and are not
/// counted toward drift or envelope statistics.
///
/// # Edge cases
///
/// - Empty input or empty output buffer → `0`.
/// - Calibration window (first 20 %) contains no finite samples →
/// all episodes `Admissible` / `Silent` (the engine runs with a
/// zero-radius envelope, which is then suppressed by the
/// non-finite-input fall-through).
// ---------------------------------------------------------------
// Top-level smoke tests — crate-level invariants
// ---------------------------------------------------------------