Commit f506c57
fix: guard debug[0-2] gyroUnfilt fallback to GYRO_SCALED mode only (#157)
* Implement ESO gain optimization and markdown report generation
- Add src/eso.rs: 2nd-order LESO bandwidth optimization via golden-section search
- Add src/report.rs: Per-axis signal statistics and ESO results in markdown format
- Add CLI flags: --eso, --eso-axis, --eso-b0, --report
- Add ESO constants to src/constants.rs (omega_0 bounds, GSS tolerance)
- Update lib.rs and main.rs to wire modules and implement ESO/report execution
- Update OVERVIEW.md with ESO and report sections
- Add INFORMATION/ESO_HOWTO.md user guide
- Tested on Betaflight 2025.12 and EmuFlight 0.4.3 full flights
* eso: replace hand-rolled GSS with argmin, fix cost function, add time-domain plot
- Add argmin 0.11 dependency; implement CostFunction trait for ESO bandwidth search
- Replace monotone 1-step MSE cost with N-step-ahead open-loop prediction cost
(unimodal: low omega0 = stale f_hat; high omega0 = noise amplification)
Constants: ESO_N_AHEAD_STEPS=5, ESO_WARMUP_FRACTION=0.20
- EsoResult now carries omega_meas_trace, omega_hat_trace, f_hat_trace, timestamps
- Add plot_eso.rs: stacked time-domain ESO output plot per axis
(omega_meas blue thin over omega_hat orange thick, f_hat green scaled)
- Wire ESO output plot into main.rs after optimization; generated as *_ESO_output_stacked.png
- Update report.rs: MSE label clarified to N-step-ahead prediction
- Add ESO plot colors to constants.rs (COLOR_ESO_MEAS/HAT/FHAT)
- ESO_GSS_MAX_ITER type corrected to u64 for argmin compatibility
* eso: auto-estimate b0 via OLS on rate derivatives (QuickFlash method)
- estimate_b0(): OLS closed-form b0 = sum(u*d_omega) / (Ts * sum(u^2))
on samples where |PID sum| >= ESO_B0_MIN_CONTROL_THRESHOLD (10.0)
- b0 auto-estimated from data when user has not overridden --eso-b0;
falls back to ESO_DEFAULT_B0 (1.0) when too few valid samples
- EsoResult.b0_auto: true when estimated, false when user-supplied
- main.rs: print shows b0=X.XXXX (estimated|user)
- report.rs: b0 row labels (auto-estimated|user-supplied); updated guidance
- Add ESO_B0_MIN_CONTROL_THRESHOLD = 10.0 to constants.rs
- Fix pre-existing clippy collapsible_match in pid_metadata.rs
* Fix CodeRabbitAI review issues from PR#136
- OVERVIEW.md: move *_report.md bullet out of 'Generated PNG Plots'
into a new 'Generated Reports' subsection (was self-contradictory)
- src/eso.rs: add upfront validation in run_eso_optimization
* axis >= AXIS_COUNT → descriptive Err instead of panic
* non-finite / non-positive sample_rate → Err
* invalid EsoConfig fields (omega0_min/max, b0) → Err
* add control-input excitation guard: reject axes where pid_sum
is all-zero / sub-epsilon so flat-input axes are not 'optimised'
- src/report.rs: fix PID term collection in extract_axis_signals
* replace unwrap_or(0.0) with per-field Option guards so absent
P/I/D/F terms are not silently reported as real zero samples
* replace hardcoded 'axis < 4' magic literal with
row.setpoint.get(axis).copied().flatten() (bounds-safe)
- src/plot_functions/plot_eso.rs + src/constants.rs:
* add ESO_FHAT_Y_FRACTION = 0.5 constant (no more magic 0.5)
* store fhat_max_abs in AxisEsoData instead of pre-computed scale
* compute fhat_scale inside draw_stacked_plot after half_range is
known: (half_range * ESO_FHAT_Y_FRACTION) / fhat_max_abs
fixes f_hat being squashed when half_range > UNIFIED_Y_AXIS_MIN_SCALE
- src/main.rs: --eso-axis and --eso-b0 now imply --eso
(previously silently ignored when --eso was omitted)
* Fix CodeRabbitAI review issues from PR#136 (review 4172427843)
- src/eso.rs: fix estimate_b0 doc/behavior mismatch and magic numbers
* replace 'count < 10' with ESO_B0_MIN_OLS_SAMPLES constant
* replace 'den.abs() < 1e-12' with VALUE_EPSILON (already imported)
* replace 'b0.abs() > 1e-9' with 'b0 > ESO_B0_ESTIMATE_MIN_POSITIVE'
to enforce strictly positive estimates (negative b0 = inverted sign
convention, was accepted silently despite docstring saying otherwise)
* update docstring to reflect strict positivity requirement
- src/eso.rs + src/main.rs: replace fragile float epsilon b0 detection
* add b0_user_override: bool to EsoConfig
* add eso_b0_user_override: bool to PlotConfig
* --eso-b0 CLI flag now sets eso_b0_user_override = true
* run_eso_optimization branches on config.b0_user_override instead of
(config.b0 - ESO_DEFAULT_B0).abs() < 1e-12 so an explicit
'--eso-b0 1.0' is respected rather than silently overridden by OLS
- src/constants.rs: add two new constants for estimate_b0 thresholds
* ESO_B0_MIN_OLS_SAMPLES: usize = 10
* ESO_B0_ESTIMATE_MIN_POSITIVE: f64 = 1e-9
* add explanatory comment on ESO_OMEGA0_MAX conservative ceiling
(links to min(sample_rate/3, config.omega0_max) clamping behavior)
- src/report.rs: replace AXIS_NAMES.len().min(3) with AXIS_COUNT
* imports AXIS_COUNT from axis_names alongside AXIS_NAMES
* eliminates redundant .min(3) magic number
- src/main.rs: fix --eso-axis help text and error message
* help now lists 'roll,pitch,yaw,all (or 0,1,2)'
* error message lists 'roll, pitch, yaw, all, or 0, 1, 2'
* Fix CodeRabbitAI review nitpicks from PR#136 (review 4172945541)
- src/constants.rs: add ESO_OMEGA0_STABILITY_RATIO = 3.0
* named constant for the LESO discrete-time stability divisor
(omega_0 < sample_rate / ESO_OMEGA0_STABILITY_RATIO)
- src/eso.rs: replace hardcoded 3.0 stability divisor with constant
* omega0_max_stable = (sample_rate / ESO_OMEGA0_STABILITY_RATIO).min(...)
* error threshold = config.omega0_min * ESO_OMEGA0_STABILITY_RATIO
* import ESO_OMEGA0_STABILITY_RATIO from constants
- src/main.rs: replace all [bool; 3] / [true; 3] / [false; 3] with AXIS_COUNT
* PlotConfig::eso_axes: [bool; AXIS_COUNT]
* Default / none() initializers: [true; AXIS_COUNT]
* --eso-axis parser: [false; AXIS_COUNT] and [true; AXIS_COUNT] for 'all'
* eso_results declaration: [Option<eso::EsoResult>; AXIS_COUNT]
* import crate::axis_names::AXIS_COUNT at top level
- src/main.rs: replace fragile save/restore with PlotConfig::disable_plots()
* add disable_plots(&mut self) method that zeroes all plot-type flags
but leaves run_eso, run_report, eso_b0, eso_b0_user_override, eso_axes
untouched — prevents future ESO field additions from silently regressing
* remove now-unused PlotConfig::none() (was only called in the
save/restore block)
* replace the 9-line save/restore block with plot_config.disable_plots()
- src/report.rs: change eso_results parameter type from [_; 3] to [_; AXIS_COUNT]
- src/report.rs: document population-variance convention on compute_signal_stats
* add doc comment stating N (not N-1) divisor is intentional for a
complete time-series and showing how to switch to sample variance
* fix: resolve remaining PR#136 nitpicks
- --eso-axis error message now matches help text (roll,pitch,yaw,all or 0,1,2)
- add duplicate-arg detection for --eso-b0 and --eso-axis (mirrors --dps pattern)
- remove unreachable any_valid guard; assignment is now unconditional
- rename p95_idx -> pctl_idx in plot_eso.rs (derives from UNIFIED_Y_AXIS_PERCENTILE)
- expand ESO_OMEGA0_MAX comment explaining conservative ~80 Hz ceiling
* feat: flag ESO ceiling in legend and result struct
Add at_ceiling: bool to EsoResult, set when omega0_opt reaches the
stability-constrained search bound (omega0_max_stable - GSS_TOLERANCE).
Propagate through AxisEsoData into the plot legend as ' [at ceiling]'
so users can see at a glance when the optimizer hit its upper bound
rather than finding a true interior optimum.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: guard debug[0-2] gyroUnfilt fallback to GYRO_SCALED mode only (#154)
Previously, debug[0-2] was unconditionally used as a fallback for
gyroUnfilt when dedicated gyroUnfilt columns were absent. This produced
incorrect unfiltered gyro analysis for any debug_mode other than
GYRO_SCALED (6), e.g. RC_SMOOTHING (7) or ACCELEROMETER (4), where
debug[0-2] does not contain raw gyro data.
Now the fallback is guarded: only proceeds when debug_mode=6, warns and
skips otherwise. When debug_mode is absent from headers, the fallback is
allowed with an assumption warning.
Closes #154
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: named constant for GYRO_SCALED and remove obsolete --eso-axis/--report docs
- Add DEBUG_MODE_GYRO_SCALED = 6 to src/constants.rs; use it in
log_parser.rs instead of hardcoded magic number 6 (CodeRabbit #157)
- Remove --eso-axis and --report flag references from OVERVIEW.md;
these flags were previously removed from the CLI but documentation
was not updated (CodeRabbit #157)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>1 parent 3e54f4f commit f506c57
11 files changed
Lines changed: 1128 additions & 26 deletions
File tree
- src
- data_input
- plot_functions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| 31 | + | |
30 | 32 | | |
31 | 33 | | |
32 | 34 | | |
| |||
163 | 165 | | |
164 | 166 | | |
165 | 167 | | |
166 | | - | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
167 | 186 | | |
168 | 187 | | |
169 | 188 | | |
| |||
182 | 201 | | |
183 | 202 | | |
184 | 203 | | |
| 204 | + | |
| 205 | + | |
185 | 206 | | |
186 | 207 | | |
187 | 208 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
273 | 273 | | |
274 | 274 | | |
275 | 275 | | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
276 | 299 | | |
277 | 300 | | |
278 | 301 | | |
| |||
370 | 393 | | |
371 | 394 | | |
372 | 395 | | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
0 commit comments