diff --git a/CHANGELOG.md b/CHANGELOG.md index cbd2a304..a2db4593 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Fixed +- **`adaptive_classifier.rs:94` no longer panics on NaN feature values** (closes #611). + `sorted.sort_by(|a, b| a.partial_cmp(b).unwrap())` returned `None` and panicked + whenever a single `NaN` reached the classifier from real ESP32 hardware (silent + DSP div-by-zero, empty buffer). One bad frame killed the entire sensing-server + process. Swapped for `unwrap_or(Ordering::Equal)`, matching the pattern the + same file already used at lines 149-150 and 155. Per-frame hot path; this was + a real production crash vector. - **`ui/utils/pose-renderer.js` no longer divides by zero** when two render frames land in the same `performance.now()` tick (issue #519 Bug 2). `deltaTime` is now `Math.max(currentTime - lastFrameTime, 1)` before the `1000 / deltaTime` division, capping displayed FPS at 1000 — far above any real render rate, but finite so the EMA `averageFps = averageFps * 0.9 + fps * 0.1` no longer poisons itself to `Infinity` on a single zero-dt tick. ### Removed diff --git a/v2/crates/wifi-densepose-sensing-server/src/adaptive_classifier.rs b/v2/crates/wifi-densepose-sensing-server/src/adaptive_classifier.rs index b89cb58c..cc652f43 100644 --- a/v2/crates/wifi-densepose-sensing-server/src/adaptive_classifier.rs +++ b/v2/crates/wifi-densepose-sensing-server/src/adaptive_classifier.rs @@ -91,7 +91,11 @@ fn subcarrier_stats(amps: &[f64]) -> (f64, f64, f64, f64, f64, f64, f64, f64) { // IQR (inter-quartile range). let mut sorted = amps.to_vec(); - sorted.sort_by(|a, b| a.partial_cmp(b).unwrap()); + // partial_cmp returns None on NaN — fall back to Equal so a single NaN + // frame from real ESP32 hardware (silent DSP div-by-zero, empty buffer) + // can't panic the whole sensing server (#611). The same file already + // uses unwrap_or(Equal) at lines 149-150 and 155; this was an oversight. + sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); let q1 = sorted[sorted.len() / 4]; let q3 = sorted[3 * sorted.len() / 4]; let iqr = q3 - q1;