Tuning algorithms Phase 2 (SOTA parity) + Phase 3 (differentiators) - #435
Merged
Conversation
PID Tune's headline overshoot/rise/settling now come from a Wiener- deconvolved step response stacked across the whole flight (the Plasmatree/PIDtoolbox method) instead of arithmetic means of individually measured steps — single noisy steps no longer distort axis metrics (observed on a real log: per-step overshoot read 56% where the stacked estimate measures 2%). - estimateSplitTransferFunction: magnitude-split estimation (<500 vs >=500 deg/s per Welch window — Betaflight's FF/D-setpoint transition differs between regimes); windows without commanded input (<50 deg/s) are excluded rather than diluting coherence. - Input-energy-weighted coherence: the stick-band mean is weighted by S_xx per bin, so bins the pilot never excited don't drag it down. Applies to the main estimator too (BodeResult.coherenceWeights). - StepResponseStacker: per-axis low/high/primary metrics, trusted only with >=2 windows and weighted coherence >= 0.5; per-step means remain the fallback (metricsSource: 'per_step') and the cross-check — >50% relative disagreement emits step_deconv_disagreement. - Time-domain threshold recalibration: deconvolved estimates read ~half the overshoot/settling of per-step measurements for the same physical response (calibrated on the demo generator's known second-order plant) — DECONV_THRESHOLD_SCALE=0.5 scales overshoot/settling thresholds when an axis uses deconvolved metrics; rise thresholds unchanged; PID_STYLE_THRESHOLDS stay per-step-calibrated. - Synthetic step duration extended to 0.5s for the stacker (normalizing by a mid-ring final value underestimated overshoot). Golden fixtures regenerated (the designated time-domain recalibration event): demo-pid and real-vx35-pid metrics now deconvolved; spurious per-step-driven P/D cuts on the real log no longer fire. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
Refine noise-peak source classification with measured throttle-tracking
evidence from the throttle spectrogram, replacing reliance on the
equal-spacing heuristic over the whole-flight average spectrum:
- NoiseAnalyzer.reclassifyPeaksWithThrottle(): re-locates each detected
peak per throttle band (±30% relative search window, ≥6 dB prominence
over the band's local floor) and regresses band-peak frequency against
throttle. Tracks-throttle (Pearson r ≥ 0.6, relative range ≥ 15%) →
motor_harmonic; stationary (relative range ≤ 8%) → frame_resonance
(inside size-aware band) or electrical (>500 Hz); ambiguous keeps the
heuristic classification.
- Peaks carry classifiedBy ('throttle_track' | 'heuristic') and the
measured throttleTrack (throttle midpoints + tracked frequencies) for
UI/telemetry and future RPM-filter rules (P2.6).
- FilterAnalyzer wires reclassification into both analysis paths when
≥3 throttle bands have usable spectra.
- Demo generator realism: motor harmonic noise now tracks throttle via a
phase-continuous oscillator (0.4-1.4× base frequency) instead of a
fixed 160 Hz tone — validates the classifier end-to-end (fixed 600 Hz
ESC noise now correctly classified electrical, tracking harmonics
correctly classified motor_harmonic).
- Golden fixtures regenerated (demo spectra changed with the generator).
- 7 new NoiseAnalyzer tests; TESTING.md inventory updated (incl. the
previously missing StepResponseStacker entry); KB + audit doc updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
New RpmFilterRecommender module (active RPM filter only), consuming the measured harmonic tracks from P2.2 throttle-track classification and the dynamic-idle floor from BBL headers: - F-RPM-MIN-IDLE: rpm_filter_min_hz aligned with the dynamic-idle fundamental (target 0.9 x idleHz, clamped 40-150 Hz, 15 Hz deadzone). Floor above the idle fundamental -> uncovered low-throttle gap (lower, medium confidence); floor far below -> wasted deep notching (raise, low confidence, latency-aware). - F-RPM-MIN-TRACK: without dyn-idle info, a measured fundamental track dipping below the current floor proves a coverage gap -> lower-only recommendation (never raises from track data; the flight may not have visited low throttle). - F-RPM-HARM-UP: a measured track at ~kx the fundamental (integer ratio within +/-0.25) above the current rpm_filter_harmonics count proves an unfiltered harmonic order -> raise count (max 3). Suppresses F-MOTOR-DIAG (residual explained by missing notch order). - F-RPM-FADE (informational): fade disabled -> suggest BF default 50. - F-RPM-WEIGHTS (informational, BF 4.5+): full-depth weights -> suggest community per-size weights; gated on the BBL header reporting rpm_filter_weights (proof of firmware support). Supporting changes: - CurrentFilterSettings += rpm_filter_fade_range_hz, rpm_filter_weights, dyn_idle_min_rpm; enriched from BBL headers in headerValidation. - verifyAppliedConfig: rpm_filter_harmonics/min_hz verified via MSP_FILTER_CONFIG read-back; fade_range marked CLI-only (skip). - BF_SETTING_RANGES: firmware bounds for min_hz (30-200), harmonics (0-3), fade_range (0-1000). - Golden fixture: real BF 4.5.2 log now emits the F-RPM-WEIGHTS advisory (additive; header carries rpm_filter_weights 100,100,100). - 18 new tests (RpmFilterRecommender.test.ts); KB, audit doc, TESTING.md and CLAUDE.md updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
Magnitude-response models for the Betaflight filter chain, rendered over measured noise data (parity with Blackbox Explorer 2025.12): - New shared module src/shared/utils/filterResponse.ts: PT1/PT2/PT3 cascades with BF cutoff corrections (1.554/1.961 so the cascade is -3 dB at the configured cutoff), Butterworth biquad, notch magnitude, and the firmware's dynamic-LPF throttle curve (curve = t*(1-t)*expo/10 + t). computeFilterChainCurve() combines a chain's active stages; gyroLpf1CutoffAtThrottle() for spectrograms. - SpectrumChart: optional filterSettings prop overlays the configured gyro + D-term chain attenuation on a right axis (dashed curves, legend) and shades the dynamic notch tracking range. - ThrottleSpectrogramChart: optional filterSettings prop draws the gyro LPF1 cutoff across throttle bands — dynamic LPF traces its actual throttle curve (incl. expo), static configs draw a straight line. - FilterAnalysisResult.filterSettings carries the BBL-enriched settings the analysis ran against (both analyzer paths); wired into AnalysisOverview, FilterAnalysisStep and QuickAnalysisStep. - CurrentFilterSettings += gyro_lpf1_dyn_expo (BBL header enrichment). - 23 new tests (18 model + 3 SpectrumChart + 2 spectrogram overlays); TESTING.md, CLAUDE.md docs updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
Replace the fixed LPF2 heuristics and the fixed 2 ms group-delay warning
with an attenuation-vs-latency decision against a per-size budget:
- FILTER_LATENCY_BUDGET_BY_SIZE (gyro/D-term ms at the 80 Hz reference):
5" 1.5/3.0, 3-4" and 6" 2.0/3.5, 1"/2.5" and 7" 2.5/4.0; default
2.0/3.5 when size is unknown (matches the legacy warning threshold).
- GroupDelayEstimator.estimateGroupDelay takes an optional droneSize and
stamps gyroBudgetMs/dtermBudgetMs/gyroOverBudget/dtermOverBudget on
FilterGroupDelay; the delay warning is now size-aware.
- FilterRecommender LPF2 rules consume the measured delay:
- Disable (RPM + clean): upgraded to high confidence when the chain is
over budget; reason surfaces "Filter latency: X ms (budget Y ms)".
- Enable (no RPM + noisy): gated on prospective delay (current chain +
PT1 LPF2 at 250/150 Hz). When it would exceed the budget, an
informational F-LPF2-BUDGET-GYRO/DTERM advisory recommends fixing
the noise at its source (props/bearings, RPM filter, soft-mount)
instead of stacking filter delay.
- FilterAnalyzer computes group delay before recommendations in both
paths and threads it into recommend().
- 9 new tests; KB, audit doc, TESTING.md, CLAUDE.md updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
Extend the per-throttle-band transfer function analysis beyond roll and
turn its trends into measured TPA recommendations:
- ThrottleTFAnalyzer now analyzes pitch alongside roll (roll stays
top-level for compatibility; pitch attaches as result.pitch). The TPA
variance warning reflects the worst axis.
- New recommendTPAFromThrottleTF(): the low-to-high-band overshoot trend
on the worst axis drives tpa_rate and tpa_breakpoint:
- TPA-TF-RATE-UP: overshoot grows >=10 pp with throttle -> raise
tpa_rate by 10 (cap 80), medium confidence.
- TPA-TF-BREAKPOINT: breakpoint lowered to the measured oscillation
onset (mapped to us, clamped 1250-1750, 100 us deadzone).
- TPA-TF-RATE-DOWN: high bands overdamped (<5% overshoot) while low
bands overshoot -> lower tpa_rate by 10 (floor 30), low confidence.
- Precedence in PIDAnalyzer: measured TF rules override the static
size-based P-TPA advisory per setting; propwash safety rules
(PW-TPA-*) always win. Requires tpa_rate from BBL headers and >=3
bands with TF data.
- Golden fixture: demo flash log now emits TPA-TF-RATE-UP (additive).
- 7 new tests; KB, audit doc, TESTING.md, CLAUDE.md updated.
(tpa_low_* emission deferred to the P2.5 version-capabilities layer.)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
Structured evidence on recommendations and measurement-quality display — the direct answer to black-box NN tuning tools: - New RecommendationEvidence type (shared): measurements (label/value), trigger (the fired condition), anchorFrequencyHz (chart anchor). Optional evidence field on FilterRecommendation + PIDRecommendation. - Evidence populated by: noise-floor rules (measured per-axis floors, computed target, deadzone), resonance rules (peak freq/amplitude/type, anchored to the peak), F-DN-MIN, F-YAW-RES, RPM filter rules (idle floor, tracked fundamental, harmonic ratio), and the TF-driven TPA rules (per-band overshoot trend). - RecommendationCard renders a collapsible "Why? Measured evidence" block (all six card call sites pass evidence through). - SpectrumChart tags peak markers with the rule they triggered when a recommendation's evidence anchors to that frequency (+-8 Hz) — "Frame 160Hz -> F-RES-GYRO". - BodePlot gains a coherence section: per-bin gamma^2 per axis with the 0.5 recommendation-gate reference line and an explanatory note (coherence already flowed through the TF result; now typed + drawn). - 8 new tests; TESTING.md, CLAUDE.md docs, audit doc updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
…P2.4)
New PreviousSessionComparison component shown inside FilterAnalysisStep
and PIDAnalysisStep when the profile has archived tuning history:
- Filter mode: overlays the last completed session's compact 128-bin
spectrum (NoiseComparisonChart) against the current analysis with the
existing delta pill. PID mode: per-axis step-metric before/after grid
(StepResponseComparison).
- Prefers the previous session's verification-flight metrics (its final
tuned state) over its pre-tuning analysis metrics.
- Cross-scale guard: refuses comparisons when spectrumScaleVersion
differs (legacy v1 spectra sit ~10 dB below v2) with a note instead
of a misleading chart.
- Cross-method guard: PIDMetricsSummary gains a metricsSource stamp
('per_step' | 'deconvolved', written by extractPIDMetrics) —
deconvolved and per-step overshoot/settling live on different scales,
so mismatched records show a note instead of deltas.
- 6 new tests; TESTING.md, CLAUDE.md, audit doc updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
New src/shared/utils/bfVersionCapabilities.ts maps the connected firmware version to feature availability and CLI-name differences: - parseBFVersion handles classic semver (4.5.2) and the calendar scheme (2025.12 = BF 4.6); calendar versions rank above every classic 4.x. - getBFCapabilities: hasTpaLow / hasRpmWeights / hasAntiGravityCutoff (4.5+), usesDMax / hasChirp (4.6+). Unknown or unparseable versions get the conservative BF 4.3 baseline. - translateSettingForVersion: d_min_gain/advance/roll/pitch/yaw -> d_max_* on 4.6+ (name-level rename; gain semantics unchanged). Wired into both CLI apply stages using the cached FCInfo version; AppliedChange records keep the canonical name so MSP read-back verification (layout-based) is unaffected. - New rule P-TPA-LOW: severe propwash + tpa_low_always disabled -> recommend enabling low-throttle TPA. Gated on firmware support via header presence (lowAlways undefined on pre-4.5 logs). - F-RPM-WEIGHTS remains gated on rpm_filter_weights header presence (same capability, log-level proof). Slider-move deltas already ship via sliderDelta; anti_gravity_cutoff/p_gain rules deferred (no measured evidence source yet). - 14 new tests; TESTING.md, CLAUDE.md, audit doc updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
Predict the step response of proposed PID gains BEFORE applying them — the differentiator no shipping FPV tuning tool has: - New SystemIdentifier.ts: divides the known Betaflight PID controller (firmware physical-unit scales P*0.032029, I*0.244381, D*0.000529) out of the measured closed-loop transfer function (T -> L = T/(1-T) -> plant = L/C), then fits a 2nd-order + transport-delay plant model with a coherence-weighted grid search + refinement (analytic gain per candidate). - Gates: mean coherence >= 0.5 over the 2-60 Hz fit band, fit quality (1 - sqrt(relative residual)) >= 0.6, >= 8 usable bins. No gate pass, no prediction. - predictResponse() re-closes the identified plant with any gains analytically (4096-bin grid -> IFFT impulse -> synthetic step + metrics); computeWhatIf() produces current-gains (sanity anchor) and proposed-gains predictions. - PIDAnalyzer (Flash Tune) attaches PIDAnalysisResult.whatIf for roll/pitch with proposed gains from buildRecommendedPIDs. - QuickAnalysisStep renders "Predicted Response with Proposed Gains" (before/after comparison chart, explicitly labeled as a simulation, fit quality shown); BodePlot in the step now receives coherence so the P3.1 coherence section renders in Flash Tune. - Verified end-to-end in tests against an analytic known plant: model recovery within tolerances, D-raise reduces predicted overshoot, P-raise speeds rise, unity settling; 11 new tests total. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
Discrete search for the latency-optimal filter configuration that still covers every measured noise peak: - New FilterPlacementOptimizer.ts: enumerates (gyro LPF1 cutoff, LPF2 on/off, dynamic notch count/Q) candidates, minimizing gyro-chain group delay at the 80 Hz reference subject to every significant roll/pitch peak (>=12 dB) being attenuated to <=6 dB above the floor. Attenuation uses the PT1 magnitude models from filterResponse.ts plus a -20 dB effective depth per SDFT dynamic notch (notches cover the strongest peaks inside the configured range). - Safety rails: LPF1=0 candidates only with an active RPM filter; a configuration with no lowpass at all is never considered. - FilterAnalyzer attaches FilterAnalysisResult.filterPlacement and emits an informational F-OPT-PLACEMENT advisory (with structured evidence) when the optimum saves >=0.3 ms vs the current config. Advisory only. - 9 new tests: notch-vs-lowpass tradeoff, infeasible low-frequency peak, RPM gate for disabling LPF1, delay delta, advisory thresholds. - TESTING.md, CLAUDE.md, audit doc updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
Per-motor order/spectral analysis in MechanicalHealthChecker — compares the four motor-command spectra against each other (relative, so the absolute motor scale cancels): - motor_prop_signature: a narrowband peak on ONE motor >=8 dB above the other motors at the same frequency in the rotation-order band (60-350 Hz). The PID loop counteracting a 1x/rev vibration shows up in that corner's motor command — consistent with a bent or unbalanced prop. - motor_bearing_signature: one motor's broadband median >=6 dB above the others in the 200-500 Hz bearing band — consistent with bearing wear. - Both flags are info severity + experimental: true — they never degrade the overall mechanical-health status; thresholds will be promoted once calibrated via telemetry (per the audit plan). - HealthSeverity gains 'info'; FilterAnalysisStep renders info-severity issues with info styling (previously only non-ok statuses displayed). - 5 new tests (synthetic single-motor tone, broadband elevation, symmetric silence, short-data skip, status invariance). - TESTING.md, CLAUDE.md, audit doc updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
- README: analysis module count 27 -> 31, project tree gains RpmFilterRecommender/FilterPlacementOptimizer/StepResponseStacker/ SystemIdentifier, feature bullet extended. - ARCHITECTURE: diagram counts (31 modules / 1207 analysis tests), Analysis Engine table gains the 4 new modules, Shared Utilities table gains filterResponse.ts + bfVersionCapabilities.ts, testing-strategy area table resummed to 154 files / 3370 tests. - docs/README + TUNING_ALGORITHMS_AUDIT: implementation status updated to Phase 2 + P3.1-P3.4 implemented, P3.5 deliberately deferred. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
There was a problem hiding this comment.
Pull request overview
This PR advances FPVPIDlab’s analysis and tuning engine to Phase 2 (SOTA parity) and Phase 3 (differentiators) of the tuning algorithms audit roadmap, adding new analysis primitives (deconvolved step response, throttle-aware peak classification, filter-response modeling, BF version capability gates) and renderer/UI explanations/overlays to make recommendations more trustworthy and interpretable.
Changes:
- Adds shared Betaflight filter-response models + renderer overlays (spectrum + throttle spectrogram) and explainable recommendation evidence rendering.
- Introduces deconvolved/stacked step-response metrics (with coherence gating and per-step cross-check warnings), plus “what-if” system identification prediction for Flash Tune.
- Adds BF version capability parsing + CLI setting translation hooks, RPM filter verification support, latency budget plumbing, and previous-session comparison UI with cross-scale/cross-method guards.
Reviewed changes
Copilot reviewed 71 out of 71 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/shared/utils/metricsExtract.ts | Persists PID metricsSource into history summaries. |
| src/shared/utils/filterResponse.ts | New shared magnitude-response models for BF filter chains + dyn LPF curve. |
| src/shared/utils/filterResponse.test.ts | Unit coverage for filterResponse models and chain computation. |
| src/shared/utils/bfVersionCapabilities.ts | New BF version parsing + capabilities + CLI rename translation. |
| src/shared/utils/bfVersionCapabilities.test.ts | Unit tests for version parsing/capabilities/translation. |
| src/shared/types/tuning-history.types.ts | Adds PIDMetricsSummary.metricsSource for history comparisons. |
| src/renderer/components/TuningWizard/TuningSummaryStep.tsx | Threads recommendation evidence into cards. |
| src/renderer/components/TuningWizard/RecommendationCard.tsx | Renders collapsible “Why?” evidence block. |
| src/renderer/components/TuningWizard/RecommendationCard.test.tsx | Tests evidence rendering behavior. |
| src/renderer/components/TuningWizard/RecommendationCard.css | Styles for evidence details block. |
| src/renderer/components/TuningWizard/QuickAnalysisStep.tsx | Adds filter overlays, evidence, TF coherence plumbing, and what-if chart section. |
| src/renderer/components/TuningWizard/QuickAnalysisStep.test.tsx | Tests what-if prediction section rendering. |
| src/renderer/components/TuningWizard/PreviousSessionComparison.tsx | New previous-session overlay component with guards. |
| src/renderer/components/TuningWizard/PreviousSessionComparison.test.tsx | Tests cross-scale and cross-method refusal + selection behavior. |
| src/renderer/components/TuningWizard/PreviousSessionComparison.css | Styling for comparison panel. |
| src/renderer/components/TuningWizard/PIDAnalysisStep.tsx | Adds PreviousSessionComparison + evidence to PID step. |
| src/renderer/components/TuningWizard/FilterAnalysisStep.tsx | Adds PreviousSessionComparison, filter overlays, severity mapping for mech health, evidence threading. |
| src/renderer/components/TuningWizard/charts/ThrottleSpectrogramChart.tsx | Overlays gyro LPF1 cutoff line across throttle bands. |
| src/renderer/components/TuningWizard/charts/ThrottleSpectrogramChart.test.tsx | Tests LPF1 overlay presence/omission. |
| src/renderer/components/TuningWizard/charts/SpectrumChart.tsx | Adds filter response curves + dyn-notch shading + peak rule tagging. |
| src/renderer/components/TuningWizard/charts/SpectrumChart.test.tsx | Tests overlays and rule tagging. |
| src/renderer/components/TuningWizard/charts/SpectrumChart.css | Adds filter overlay legend styles. |
| src/renderer/components/TuningWizard/charts/BodePlot.tsx | Adds coherence plot section + gate line. |
| src/renderer/components/TuningWizard/charts/BodePlot.test.tsx | Tests coherence section conditional rendering. |
| src/renderer/components/TuningWizard/charts/BodePlot.css | Styles coherence note. |
| src/renderer/components/AnalysisOverview/AnalysisOverview.tsx | Enables spectrum/spectrogram overlays in overview view. |
| src/renderer/CLAUDE.md | Documents new UI components and overlays. |
| src/main/utils/verifyAppliedConfig.ts | Verifies RPM filter min_hz/harmonics and marks fade-range CLI-only. |
| src/main/ipc/handlers/tuningHandlers.ts | Adds BF setting bounds + translates renamed CLI setting names on apply. |
| src/main/demo/DemoDataGenerator.ts | Makes demo motor harmonics track throttle via integrated phase. |
| src/main/CLAUDE.md | Documents BF version capability layer and apply translation behavior. |
| src/main/analysis/TransferFunctionEstimator.ts | Adds input-energy-weighted coherence mean + magnitude-split TF estimation + configurable step duration. |
| src/main/analysis/ThrottleTFAnalyzer.test.ts | Adds tests for TF-driven TPA recommendations. |
| src/main/analysis/SystemIdentifier.test.ts | Adds tests for system identification and what-if prediction logic. |
| src/main/analysis/StepResponseStacker.ts | New deconvolved/stacked step response extraction with trust gating. |
| src/main/analysis/StepResponseStacker.test.ts | Tests stacked response accuracy, split behavior, and trust gating. |
| src/main/analysis/PIDRecommender.ts | Scales thresholds for deconvolved metrics + adds P-TPA-LOW rule. |
| src/main/analysis/PIDRecommender.test.ts | Tests new P-TPA-LOW behavior. |
| src/main/analysis/PIDAnalyzer.ts | Integrates stacked metrics + disagreement warning + TF-driven TPA precedence + what-if attachment. |
| src/main/analysis/NoiseAnalyzer.ts | Adds throttle-track peak reclassification with regression logic. |
| src/main/analysis/NoiseAnalyzer.test.ts | Tests throttle-track classification outcomes. |
| src/main/analysis/MechanicalHealthChecker.ts | Adds experimental per-motor spectral fault signatures. |
| src/main/analysis/MechanicalHealthChecker.test.ts | Tests new experimental motor signature flags. |
| src/main/analysis/headerValidation.ts | Enriches additional RPM/dyn-expo headers. |
| src/main/analysis/GroupDelayEstimator.ts | Adds per-size latency budgets and over-budget flags. |
| src/main/analysis/GroupDelayEstimator.test.ts | Tests latency budget attachment/behavior. |
| src/main/analysis/FilterRecommender.test.ts | Adds tests for latency-budget-gated LPF2 rules and evidence. |
| src/main/analysis/FilterPlacementOptimizer.test.ts | Adds tests for filter placement optimizer advisory. |
| src/main/analysis/FilterAnalyzer.ts | Wires throttle-track reclassification, latency budgets, placement optimizer, and exposes filterSettings. |
| src/main/analysis/constants.ts | Adds new thresholds/constants for P2/P3 features (deconv, rpm rules, latency budgets, etc.). |
| src/main/analysis/CLAUDE.md | Documents new analysis features and how they wire into the pipeline. |
| src/main/analysis/fixtures/golden/real-vx35-pid.json | Updates golden PID fixture outputs for new metrics source/warnings. |
| src/main/analysis/fixtures/golden/real-vx35-filter.json | Updates golden filter fixture outputs for new RPM weights advisory. |
| src/main/analysis/fixtures/golden/demo-pid-cycle0.json | Updates demo golden PID outputs for stacked metrics changes. |
| src/main/analysis/fixtures/golden/demo-flash-cycle0.json | Updates demo golden Flash outputs for new TPA-TF outputs. |
| src/main/analysis/fixtures/golden/demo-filter-cycle2.json | Updates demo golden filter peaks for throttle-track realism. |
| src/main/analysis/fixtures/golden/demo-filter-cycle0.json | Updates demo golden filter peaks for throttle-track realism. |
| SPEC.md | Updates test counts. |
| README.md | Updates feature list + analysis module count + test counts. |
| docs/TUNING_ALGORITHMS_AUDIT.md | Marks Phase 2/3 items implemented and documents what shipped. |
| docs/README.md | Updates audit doc status summary. |
| docs/PID_TUNING_KNOWLEDGE.md | Adds/updates KB sections for new analysis/recommendation logic. |
| ARCHITECTURE.md | Updates counts and module lists (analysis modules, tests, etc.). |
Comments suppressed due to low confidence (1)
src/renderer/components/TuningWizard/charts/SpectrumChart.tsx:337
- The filter overlay legend is shown whenever hasFilterOverlay is true, but it always includes both “Gyro filters” and “D-term filters” entries even if one curve is absent. This can mislead users when, for example, D-term filters are disabled.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+67
to
+73
| return { | ||
| hasTpaLow: atLeast(v, 4, 5), | ||
| hasRpmWeights: atLeast(v, 4, 5), | ||
| hasAntiGravityCutoff: atLeast(v, 4, 5), | ||
| usesDMax: v.calendar, | ||
| hasChirp: v.calendar, | ||
| }; |
Comment on lines
+269
to
+293
| {/* Configured filter chain response (right axis, attenuation dB) */} | ||
| {hasFilterOverlay && ( | ||
| <Line | ||
| yAxisId="filter" | ||
| dataKey="gyroFilter" | ||
| stroke={FILTER_CURVE_COLORS.gyro} | ||
| strokeWidth={1.5} | ||
| strokeDasharray="6 3" | ||
| dot={false} | ||
| isAnimationActive={false} | ||
| name="Gyro filters" | ||
| /> | ||
| )} | ||
| {hasFilterOverlay && ( | ||
| <Line | ||
| yAxisId="filter" | ||
| dataKey="dtermFilter" | ||
| stroke={FILTER_CURVE_COLORS.dterm} | ||
| strokeWidth={1.5} | ||
| strokeDasharray="2 3" | ||
| dot={false} | ||
| isAnimationActive={false} | ||
| name="D-term filters" | ||
| /> | ||
| )} |
- bfVersionCapabilities: real BF 2025.12 firmware still reports "4.6.0" via MSP_FC_VERSION, so usesDMax/hasChirp now key off classic >= 4.6 (calendar naming alone would never match real firmware). Test added for the "4.6.0" form. - SpectrumChart: gyro and D-term overlay curves and their legend entries now render independently — a disabled chain no longer produces an empty series and a misleading legend entry. Test added for the gyro-only case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements Phases 2 and 3 of
docs/TUNING_ALGORITHMS_AUDIT.md(follow-up to #434, which shipped Phase 0+1). Only P3.5 (crowd benchmarking) remains, deliberately deferred until production fleet data exists.Phase 2 — SOTA parity
StepResponseStacker.ts): Wiener/stacked response as the headline metrics source, split by input magnitude at 500 deg/s, input-energy-weighted coherence trust gate, per-step path kept as cross-check with astep_deconv_disagreementwarning; time-domain thresholds recalibrated (DECONV_THRESHOLD_SCALE).reclassifyPeaksWithThrottle): each peak re-located per throttle band and Pearson-regressed against throttle — tracks-throttle → motor harmonic, stationary → frame/electrical; measuredthrottleTrackstamped on peaks. Demo generator's motor noise now genuinely tracks throttle, validating the classifier end-to-end.shared/utils/filterResponse.ts): PT1/PT2/PT3 (BF cutoff corrections), Butterworth biquad and notch magnitude models, BF dynamic-LPF throttle curve; configured filter response overlaid on the noise spectrum (right axis + dyn-notch shading) and the gyro LPF1 cutoff line drawn across the throttle spectrogram.PreviousSessionComparison): last completed session's compact spectrum / step metrics overlaid against the current analysis, with cross-scale (spectrumScaleVersion) and cross-method (metricsSource) guards.shared/utils/bfVersionCapabilities.ts): semver + calendar (2025.12 = 4.6) parsing, capability gates (tpa_low / RPM weights / anti-gravity cutoff 4.5+, d_max rename + chirp 4.6+), d_min→d_max CLI translation in the apply flow, new P-TPA-LOW rule.RpmFilterRecommender.ts):rpm_filter_min_hzfrom the dynamic-idle floor or measured fundamental track,rpm_filter_harmonicsraised when a measured integer-ratio track proves an unfiltered order, fade-range and BF 4.5+ weights advisories.FILTER_LATENCY_BUDGET_BY_SIZE): LPF2 enable/disable decisions weigh measured group delay against per-size budgets; enable is gated on prospective delay with an informational fix-the-source fallback; "Filter latency: X ms (budget Y ms)" surfaced.tpa_rate/tpa_breakpoint(TPA-TF-*), overriding the static size advisory while propwash safety rules keep precedence.Phase 3 — differentiators
evidence(measurements + trigger + chart anchor) on recommendations, rendered as a "Why?" block; spectrum peaks tagged with the rule they fired; per-bin coherence plotted in BodePlot with the 0.5 gate line.SystemIdentifier.ts): 2nd-order + delay plant fit by dividing the BF PID (firmware physical scales) out of the measured closed loop; proposed gains re-closed analytically → predicted step response shown before apply, gated on coherence and fit quality and labeled as a simulation. Verified against an analytic known plant in tests.FilterPlacementOptimizer.ts): discrete search minimizing group delay subject to attenuation constraints on measured peaks; informational F-OPT-PLACEMENT advisory when ≥0.3 ms can be saved.Safety & verification
BF_SETTING_RANGES(rpm_filter_min_hz 30-200, harmonics 0-3, fade 0-1000, tpa_low_always 0-1) and convergent (deadzones / one-shot conditions).verifyAppliedConfigverifies appliedrpm_filter_harmonics/min_hzvia MSP read-back; fade-range marked CLI-only.Tests & docs
tsc --noEmitclean.docs/PID_TUNING_KNOWLEDGE.mdextended (throttle-track classification, RPM rules, latency budget, TF-driven TPA, deconvolved metrics source); doc-sync audit ran and its fixes are included.🤖 Generated with Claude Code
https://claude.ai/code/session_01EJhhk2frYDDnpNbSHRxGdv
Generated by Claude Code