Skip to content

Tuning algorithms Phase 2 (SOTA parity) + Phase 3 (differentiators) - #435

Merged
eddycek merged 14 commits into
mainfrom
claude/fpv-tuning-algorithms-audit-7vmx2p
Jul 24, 2026
Merged

Tuning algorithms Phase 2 (SOTA parity) + Phase 3 (differentiators)#435
eddycek merged 14 commits into
mainfrom
claude/fpv-tuning-algorithms-audit-7vmx2p

Conversation

@eddycek

@eddycek eddycek commented Jul 24, 2026

Copy link
Copy Markdown
Owner

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

  • P2.1 Deconvolved step response for PID Tune (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 a step_deconv_disagreement warning; time-domain thresholds recalibrated (DECONV_THRESHOLD_SCALE).
  • P2.2 Throttle-track peak classification (reclassifyPeaksWithThrottle): each peak re-located per throttle band and Pearson-regressed against throttle — tracks-throttle → motor harmonic, stationary → frame/electrical; measured throttleTrack stamped on peaks. Demo generator's motor noise now genuinely tracks throttle, validating the classifier end-to-end.
  • P2.3 Filter response models + overlays (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.
  • P2.4 Before/after comparison (PreviousSessionComparison): last completed session's compact spectrum / step metrics overlaid against the current analysis, with cross-scale (spectrumScaleVersion) and cross-method (metricsSource) guards.
  • P2.5 BF version-capabilities layer (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.
  • P2.6 RPM filter tuning rules (RpmFilterRecommender.ts): rpm_filter_min_hz from the dynamic-idle floor or measured fundamental track, rpm_filter_harmonics raised when a measured integer-ratio track proves an unfiltered order, fade-range and BF 4.5+ weights advisories.
  • P2.7 Latency budget (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.
  • P2.8 ThrottleTF pitch + measured TPA: per-band TF now covers pitch; low→high-band overshoot trends drive tpa_rate/tpa_breakpoint (TPA-TF-*), overriding the static size advisory while propwash safety rules keep precedence.

Phase 3 — differentiators

  • P3.1 Explainable recommendations: structured 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.
  • P3.2 System identification + what-if (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.
  • P3.3 Filter placement optimizer (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.
  • P3.4 Mechanical fault signatures: per-motor spectral comparison — bent-prop order-peak and bearing broadband signatures, info severity + experimental flag, never degrade overall status.

Safety & verification

  • All new actionable rules are bounds-checked in 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).
  • verifyAppliedConfig verifies applied rpm_filter_harmonics/min_hz via MSP read-back; fade-range marked CLI-only.
  • P3.x outputs are advisory/prediction-only — nothing from them reaches the FC.
  • Golden-output regression fixtures updated only where justified (throttle-tracking demo realism in P2.2; additive F-RPM-WEIGHTS and TPA-TF-RATE-UP outputs).

Tests & docs

  • 3347 passing unit tests (+127 on this branch), tsc --noEmit clean.
  • TESTING.md inventory, ARCHITECTURE.md, README.md, SPEC.md counts synced; docs/PID_TUNING_KNOWLEDGE.md extended (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

claude added 13 commits July 24, 2026 14:53
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
Copilot AI review requested due to automatic review settings July 24, 2026 20:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
@eddycek
eddycek merged commit 76354a0 into main Jul 24, 2026
1 check passed
@eddycek
eddycek deleted the claude/fpv-tuning-algorithms-audit-7vmx2p branch July 24, 2026 20:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants