feat: transition to physics-derived Td targets (Torque-Inertia Profiler)#141
feat: transition to physics-derived Td targets (Torque-Inertia Profiler)#141nerdCopter wants to merge 84 commits into
Conversation
Implements BrianWhite's (PIDtoolbox) theory that optimal response timing is aircraft-specific, determined by power-to-rotational-inertia ratio. Features: - Frame-class-aware Td (time to 50%) targets (3", 5", 7", 10") - Response consistency quality control (CV, std dev tracking) - 10+ recommendation scenarios based on Td deviation analysis - Formatted console output with clear reasoning CLI flags: --estimate-optimal-p: Enable optimal P analysis --frame-class <size>: Specify 3inch, 5inch, 7inch, or 10inch (default: 5inch) Theory/math documented in Discord conversation with BrianWhite, MikeNomatter, demvlad, and eyes.fpv regarding step-response timing targets. Tested on 5", 7", and 10" aircraft logs with accurate frame-class detection. Changes: - New module: src/data_analysis/optimal_p_estimation.rs (567 lines) - Constants: Frame-class Td targets, noise thresholds, P multipliers - Integration: Collects individual Td samples, performs analysis per axis - CLI: Added --estimate-optimal-p and --frame-class flags with help text
… recommendations Improvements: - Show frame class in output with reminder that it can be overridden - Add note in P:D recommendations clarifying they focus on D-term tuning - Cross-reference optimal P estimation when both features are active - Warn if --frame-class is specified without --estimate-optimal-p Behavior clarification: - Optimal P estimation only runs when --estimate-optimal-p is specified - Frame class defaults to 5inch if --estimate-optimal-p is used without --frame-class - P:D ratio recommendations (existing feature) and optimal P estimation (new feature) are complementary: * P:D recommendations: analyze peak overshoot/undershoot → recommend D changes * Optimal P estimation: analyze Td vs frame-class targets → recommend P magnitude changes - Both can run simultaneously to provide complete tuning guidance
Updated all documentation to include the new --estimate-optimal-p and --frame-class features: README.md: - Added flags to usage syntax - Added detailed descriptions for both new flags - Added example command showing optimal P estimation usage - Added console output description for optimal P feature OVERVIEW.md: - Added comprehensive "Optimal P Estimation (Optional)" section - Documented theory foundation (BrianWhite's physics insight) - Listed frame-class targets for all aircraft sizes - Explained analysis components and recommendation types - Clarified relationship between P:D and optimal P features src/main.rs: - Added Examples section to --help output - Included example showing optimal P estimation usage All examples now correctly use 'BlackBox_CSV_Render' binary name.
Extended optimal P estimation to support all common prop sizes from 1" tiny
whoops to 13" heavy-lift platforms. Uses simple numeric values (1-13) instead
of size names ("3inch", "5inch") for cleaner CLI experience.
Physics-based Td targets with ~25% tolerance:
- 1" tiny whoop: 40ms \u00b1 10.0ms
- 2" micro: 35ms \u00b1 8.75ms
- 3" toothpick/cinewhoop: 30ms \u00b1 7.5ms
- 4" racing: 25ms \u00b1 6.25ms
- 5" freestyle/racing: 20ms \u00b1 5.0ms (optimal power/weight)
- 6" long-range: 28ms \u00b1 7.0ms
- 7" long-range: 37.5ms \u00b1 9.5ms
- 8" long-range: 47ms \u00b1 11.75ms
- 9" cinelifter: 56ms \u00b1 14.0ms
- 10" cinelifter: 65ms \u00b1 16.25ms
- 11" heavy-lift: 75ms \u00b1 18.75ms
- 12" heavy-lift: 85ms \u00b1 21.25ms
- 13" heavy-lift: 95ms \u00b1 23.75ms
Note: 5" has fastest response due to optimal power-to-weight ratio.
Response times increase for both smaller (lower power) and larger
(higher rotational inertia) sizes.
Changes:
- Extended FrameClass enum: OneInch through ThirteenInch
- Added Td targets for all 13 frame classes in constants.rs
- Updated CLI to accept 1-13 instead of named sizes
- Updated all documentation (README, OVERVIEW, help text)
Tested on 1", 5", 10", and 13" configurations.
Extended optimal P estimation to appear on the step-response PNG plots when --estimate-optimal-p flag is specified. PNG Display (when enabled): - Separator line to distinguish from P:D recommendations - Section header: "Optimal P (5\")" - Td measurement with target comparison - Deviation percentage and classification - Recommendation summary with suggested P value The optimal P information appears in the legend below the P:D ratio recommendations, providing complete tuning guidance in a single image. Behavior: - Shows ONLY when --estimate-optimal-p is specified - Otherwise PNG shows only traditional P:D recommendations - Console output remains comprehensive in both cases Changes: - Store OptimalPAnalysis results in main.rs for PNG rendering - Pass analysis data to plot_step_response() - Add formatted legend entries with color coding: * Blue header for section * Gray for measurements/deviation * Green for recommendation summary - Handle all PRecommendation variants correctly Tested with and without --estimate-optimal-p flag.
Fixed multiple issues identified in code review:
1. OVERVIEW.md (lines 200-215):
- Removed absolute claim that 5\" has fastest response time
- Updated Theory Foundation with correct physics scaling:
"Td ∝ (rotational inertia)⁻¹ ≈ 1/(mass × radius²)"
- Clarified targets are provisional estimates requiring validation
- Changed 5\" label from "(optimal)" to "(common baseline)"
- Fixed 7\" tolerance: 9.5ms → 9.375ms (correct ±25%)
- Added TODO for bench/flight test validation
2. src/constants.rs (line 329 and around 284-285):
- Removed duplicate comment header "// src/constants.rs"
- Fixed TD_TARGET_7INCH_TOLERANCE: 9.5 → 9.375 (matches 25% of 37.5)
- Updated header comments to reflect provisional nature
- Added physics formula reference and TODO for validation
- Fixed inline comment alignment for P multipliers
- Changed 5\" comment from "(optimal)" to "(common baseline)"
3. src/data_analysis/optimal_p_estimation.rs (lines 560-568):
- Added divide-by-zero checks for all percentage calculations
- Conservative P percentage: safe when current_p == 0 (shows "N/A")
- Moderate P percentage: safe when current_p == 0 (shows "N/A")
- Decrease P percentage: safe when current_p == 0 (shows "N/A")
- Prevents panic when analyzing logs with P gain = 0
All changes verified with cargo check and cargo clippy.
Replaced 26 individual constants (TD_TARGET_*INCH and TD_TARGET_*INCH_TOLERANCE) with a single consolidated TdTargetSpec struct and TD_TARGETS array. Benefits: - Eliminates duplication: tolerance is now computed as target_ms * 0.25 - Prevents drift: all tolerances are guaranteed to be exactly 25% - Reduces verbosity: 13 array entries vs 26 separate constants - Easier maintenance: adding new frame classes only requires one array entry - Type safety: TdTargetSpec struct groups related values together Changes: 1. src/constants.rs: - Added TdTargetSpec struct with target_ms and tolerance_ms fields - Added const fn new() constructor that auto-calculates 25% tolerance - Replaced 26 individual constants with TD_TARGETS: [TdTargetSpec; 13] - Array is indexed 0-12 for 1\"-13\" frame classes 2. src/data_analysis/optimal_p_estimation.rs: - Updated FrameClass::td_target() to use TD_TARGETS array - Added array_index() helper method to map enum variants to indices - Removed all direct references to individual TD_TARGET_* constants All functionality preserved, code is more maintainable and DRY compliant.
Refactored Td (time-to-50%) targets from 26 individual constants to a single maintainable data structure with automatic tolerance calculation. Implemented robust statistical analysis with Bessel's correction for sample variance and epsilon-based float comparison. Key improvements: - Consolidated TD_TARGETS into structured array with TdTargetSpec - Added safe indexing helper (TdTargetSpec::for_frame_inches) - Reduced process_file parameters via AnalysisOptions struct - Added FrameClass::from_inches() constructor for cleaner parsing - Applied Bessel's correction (n-1) for unbiased sample variance - Replaced exact float equality with epsilon-based comparison - Improved documentation clarity on tolerance ranges and validation plan - Removed misleading bench test references (Td requires full system inertia) - Enhanced --help output with frame-class dependency warnings
Added spectral analysis of D-term data to calculate high-frequency energy ratio for noise assessment in optimal P recommendations. Previously always reported 'D-term data unavailable' despite D-term being present in logs. Changes: - Added calculate_hf_energy_ratio() to spectral_analysis.rs - Collects and analyzes D-term data per axis during optimal P estimation - Uses Welch's method PSD to measure energy above DTERM_HF_CUTOFF_HZ (200Hz) - Integrates noise level (Low/Moderate/High) into P gain recommendations - Minimum 100 samples required for reliable spectral analysis
Reformatted optimal P analysis output to match the concise style of P:D ratio recommendations. Reduced from ~40 lines per axis to ~5-7 lines while maintaining all essential information.
Changes:
- Single-line header with key metrics: Td, target, deviation, noise, consistency
- Inline warnings for low consistency instead of separate sections
- Collapsed recommendation format matching P:D style
- Removed verbose 70-char separator lines and multi-section layout
- Marked unused public API fields with #[allow(dead_code)] for future verbose mode
- All information preserved: Td, target comparison, noise level, consistency, recommendations
Example output:
Roll: Td=18.8ms (target 28.0±7.0ms, -33% dev), Noise=Low, Consistency=76%
Current P=53
→ Optimal (no change recommended)
Response is faster than target...
Updated help text, README, and OVERVIEW to explicitly state that --frame-class should match PROPELLER diameter, not frame size. This resolves confusion when running non-standard configurations (e.g., 6" frame with 5" props). Critical clarification: - Rotational inertia scales with prop radius² (I ∝ r²) - Propeller size determines response time, not frame size - Example: 6" frame + 5" props → use --frame-class 5 - Example: 7" frame + 6" props → use --frame-class 6 This explains why INVESTIGATION RECOMMENDED appears when frame class doesn't match actual prop size - the measured Td is significantly different from target because the wrong prop size was specified. Changes: - Updated --help to show 'PROP SIZE' and example usage - Updated README.md with clarification and example - Updated OVERVIEW.md Theory Foundation to emphasize prop size primacy - Added inline example: 6-inch frame with 5-inch props → use --frame-class 5
Breaking change: Renamed --frame-class to --prop-size for clarity. This better communicates that the parameter should match propeller diameter, not frame size. Fixed recommendation logic for faster response + low noise: - Previously: Triggered INVESTIGATION RECOMMENDED (incorrectly implied problem) - Now: Recommends P increase (correctly identifies headroom available) - Rationale: Low noise + faster response = excellent build quality with headroom This fixes the user confusion where clean builds with slightly smaller props (e.g., 6" frame with 5" props) would trigger investigation warnings instead of positive recommendations. Changes: - Renamed all --frame-class references to --prop-size - Updated help text, README, OVERVIEW to use prop-size terminology - Fixed Case 8 logic: SignificantlyFaster + Low noise now recommends P increase - Added note about verifying prop size in recommendation text - Console output now shows 'Prop size:' instead of 'Frame class:' Example fixed scenario: Before: 7" frame + 6" props (--frame-class 6) → INVESTIGATION (confusing) After: 7" frame + 6" props (--prop-size 6) → Increase recommended (helpful)
Added comprehensive validation section to OVERVIEW.md explaining: - How to validate Td targets using physics (I ∝ r²) - Scaling check examples showing targets match theory - Reference to constants file for technical details - Common deviation interpretations - Acceptance criteria for measured vs. target Td Updated OPTIMAL_P_IMPLEMENTATION_STATUS.md: - Marked feature as complete and tested - Added real-world test results section - Documented all implementation phases - Added usage examples - Ready-for-merge checklist Fixed README.md example command: - Changed --frame-class to --prop-size in example All documentation now reflects: - Complete feature implementation - Proper prop-size terminology - Validation methodology - Testing results
Enhanced step response plot legends to include critical information from console output:
Added to PNG legend:
- Noise level (Low/Moderate/High) - helps pilots understand thermal headroom
- Consistency warning (only shown if poor) - alerts to unreliable measurements
- Displayed with color coding (orange for warnings)
This ensures pilots who primarily review PNG plots (not console output) have the essential information for tuning decisions.
Legend format per axis when optimal P enabled:
Optimal P (5")
Td: 18.8ms (target: 20.0ms)
Deviation: -6% (WITHIN TARGET)
Noise: HIGH
⚠ Consistency: 76% (CV=34.7%)
Rec: Current P optimal
Previously only showed Td/deviation/recommendation, missing noise level which is critical for understanding if P can be increased safely.
Improved PNG legend readability for pilots:
Changes:
- Removed warning emoji (⚠) - replaced with [LOW CONSISTENCY] text
* Better cross-platform compatibility (avoids emoji rendering issues)
* Clearer and more explicit
- Spelled out 'Recommendation' in full (was 'Rec')
* Less ambiguous for pilots reading the plots
* PNG width (2560px) provides ample space
- Made recommendation text more explicit:
* 'Increase P to X (+Y)' instead of 'P≈X (+Y)'
* 'Current P is optimal' instead of 'Current P optimal'
* 'See console output for details' instead of abbreviated message
Example new legend format:
Optimal P (5")
Td: 18.8ms (target: 20.0ms)
Deviation: -6% (WITHIN TARGET)
Noise: HIGH
[LOW CONSISTENCY] 76% (CV=34.7%)
Recommendation: Current P is optimal
This makes PNG legends self-contained and comprehensible without referring to console output.
When recommending new P values, now also calculates and displays recommended D values to maintain current P:D ratio. This provides complete tuning guidance in one place.
Changes:
1. Optimal P section now shows D with P recommendations:
- Format: 'P to 54 (+2), D to 45 (+3)'
- Calculates D based on maintaining current P:D ratio
- Shows both conservative and moderate tiers when applicable
- Only displays when D data is available
2. Standardized output format across all sections:
- Console and PNG use 'P to X (+Y)' format consistently
- Removed percentage-based display (simpler absolute values)
- Clear delta indicators (+X for increase, -X for decrease)
3. P:D section labels updated:
- 'Conservative:' → 'Conservative recommendation:'
- 'Moderate:' → 'Moderate recommendation:'
- Clarifies these are separate from optimal-P recommendations
- Applied to both console output and PNG legends
4. PNG legend enhancements:
- Shows D recommendation alongside P in optimal-P section
- Maintains consistent format with console output
- P:D section labels updated to match console
Example output:
Optimal P (5")
...
Recommendation: P to 54 (+2), D to 45 (+3)
This ensures pilots have complete, actionable tuning information whether reading console or PNG plots.
Critical bug fix + format improvement
Changed confusing '[LOW CONSISTENCY] 77%' format to clearer '[WARNING] High variability' format in PNG legends. The old format showed a percentage (77%) that looked good but was labeled 'LOW CONSISTENCY', causing confusion. The new format focuses on what matters: - Emphasizes the problem: High variability (CV=44%) - Clear actionable message: 'results may be unreliable' - Removed confusing percentage that wasn't intuitive Console output already had clear wording and remains unchanged. This makes it immediately obvious to pilots reviewing PNG plots that measurements are unreliable without needing to understand what 'consistency percentage' means.
Simplified output to show only one recommendation tier instead of two, reducing complexity for pilots.
Changes:
- Console now shows only Conservative recommendation (was showing Conservative + Moderate)
- PNG already showed only Conservative (now consistent with console)
- P:D section still shows both Conservative and Moderate (unchanged)
Rationale:
- Reduces decision paralysis for pilots
- Conservative is safer starting point
- P:D section already provides comparison tiers
- Cleaner, less cluttered output
- Pilots can reference P:D recommendations if they want more aggressive values
Example output now:
→ Increase recommended:
P≈54 (+1), D≈45 (+3)
Instead of:
→ Increase recommended:
Conservative: P≈54 (+1), D≈45 (+3)
Moderate: P≈56 (+3), D≈47 (+5)
Removed moderate tier from optimal-P recommendations for simplicity and clarity. Rationale: - PNG legend only showed conservative (space constraints) - Console showed both conservative and moderate (inconsistent) - P:D section already provides conservative and moderate for comparison - Conservative recommendation is safer for pilots to try first - Reduces decision paralysis - one clear recommendation to follow Changes: - Console now shows only conservative P and D recommendations - Matches PNG legend format (consistent output) - Removed unused recommended_pd_moderate field and parameter - Pilots can still see moderate recommendations in P:D section if desired This makes the output cleaner and more actionable while maintaining full information in the separate P:D analysis section.
Removed unused fields and calculations that were being stored but never read: Removed: - current_pd_ratio field (calculated but never used) - hf_energy_percent field (stored but never read) - Simplified noise_level calculation (no longer creates unused hf_percent) Kept #[allow(dead_code)] only where appropriate: - std_dev_ms: Used internally in from_samples() for consistency calculation - P_HEADROOM_AGGRESSIVE_MULTIPLIER: Reserved for potential future use This cleans up the codebase by removing genuinely unused data rather than just suppressing warnings.
Added back 'Conservative:' label to optimal-P recommendations in both console and PNG legend for clarity. Console: 'Conservative: P≈54 (+1), D≈45 (+3)' PNG: 'Recommendation (Conservative): P≈54 (+1), D≈45 (+3)' This makes it clear that the recommendation is the conservative tier, distinguishing it from the separate P:D section which shows both conservative and moderate tiers.
Fixed all references to old --frame-class flag to use correct --prop-size flag: - README.md usage line and features section - main.rs help synopsis and example - Changed 'Frame-class-aware' to 'Prop-size-aware' for consistency Addresses CodeRabbit critical and minor issues about flag name inconsistency.
Added safety checks before D gain calculations to prevent: - Division by zero if rec_pd (recommended P:D ratio) is 0.0 - Invalid calculations if current_d is 0 Guards added in both: - Console output (optimal_p_estimation.rs) - PNG legend (plot_step_response.rs) Addresses CodeRabbit minor issues about potential infinity values.
Moved all magic numbers and hardcoded thresholds to constants.rs per project guidelines: New constants: - TD_MEAN_EPSILON (1e-12): Near-zero mean threshold - TD_SAMPLES_MIN_FOR_STDDEV (2): Minimum samples for std dev - TD_DEVIATION_*_THRESHOLD: Td deviation classification thresholds (30%, 15%, -15%) - OPTIMAL_P_MS_TO_SECONDS_MULTIPLIER (1000.0): Seconds to milliseconds conversion - OPTIMAL_P_MIN_DTERM_SAMPLES (100): Minimum D-term samples for noise analysis All hardcoded values now use named constants for better maintainability and documentation. Addresses CodeRabbit major refactor suggestion.
Fixed three minor CodeRabbit issues: 1. Renamed constant for clarity: - OPTIMAL_P_MS_TO_SECONDS_MULTIPLIER → OPTIMAL_P_SECONDS_TO_MS_MULTIPLIER - The name now correctly reflects seconds→milliseconds conversion 2. Added minimum sample check for consistency: - Single-sample Td sets now trigger consistency warning - Requires num_samples >= 2 for reliable consistency metrics - Prevents false "high consistency" with insufficient data 3. D-term threshold already uses '>=' (was fixed in previous commit) All CodeRabbit nitpicks now addressed.
Replaced hardcoded 0..2 axis loop with named constant for clarity and maintainability. Changes: - Added ROLL_PITCH_AXIS_COUNT constant to axis_names.rs - Updated optimal-P loop to use constant instead of magic number - Removed needless_range_loop allow attribute (no longer needed) The axis loop now explicitly uses ROLL_PITCH_AXIS_COUNT, making it clear that optimal-P analysis is intentionally limited to Roll and Pitch (Yaw excluded due to different dynamics). Fully resolves CodeRabbit's 'move axis counts into constants and axis helpers' issue.
Added support for 14" and 15" propellers for heavy-lift aircraft. Changes: - Extended TD_TARGETS array to include 14" (105ms) and 15" (115ms) - Added FourteenInch and FifteenInch to FrameClass enum - Updated all range checks and help text (1-13 → 1-15) - Updated validation error messages Target values follow same physics-based scaling (I ∝ r²) as existing sizes.
Implements optimal P gain estimation based on measured response timing (Td). Core Features: - Analyzes step response Td (time to 50%) across all valid windows - Compares against empirical frame-class targets (1"-15" props) - Provides P gain recommendations based on deviation and noise levels - Validates response consistency (CV, std dev, sample count) - Integrates D-term high-frequency energy analysis for noise assessment User Interface: - --estimate-optimal-p: Enable optimal P estimation - --prop-size <size>: Specify propeller diameter (1.0-15.0", default: 5.0) - Console output: Td statistics, deviation %, recommendations - PNG overlay: Analysis results on step response plots Frame-Class Targets: - 15 prop sizes from 1" (40ms) to 15" (115ms) - Each with ±25% tolerance bands for user acceptance - Provisional targets requiring flight validation - Defined in src/constants.rs as TD_TARGETS array Code Quality Improvements: - Added axis_names.rs module for centralized axis naming - Refactored plot_step_response with logical parameter structs - Comprehensive validation and bounds checking - Division-by-zero protections throughout - Enhanced error handling with detailed warnings Documentation: - Added optimal P estimation section to OVERVIEW.md - Updated README.md with usage examples - Clarified theory foundation and validation requirements - Added CodeRabbit feedback fixes and refinements Testing: - Validated on APEX 6" flight log: Td=19.1ms (target 20.0±5.0ms) - Removed unused physics model infrastructure (~750 lines) - All clippy warnings resolved, formatting enforced Note: This is a squashed commit containing 43 individual commits from feature development.
…effect Reword README to state that requires to have any effect and that it sets the propeller diameter used during the estimation, removing ambiguity about optional vs required flags.
…d correct inertia expression - Update --help text: indicate --prop-size accepts integer inches (1-15) and requires --estimate-optimal-p to take effect - Update OVERVIEW.md: replace 'I ∝ radius²' with 'I ∝ mass × radius²' to match physics in constants and comments
…o rounding) Update --help text and invalid input error to state that decimals are rejected and no rounding is performed, so users know exact behavior.
Replace unreachable if/else if/else chain with a single map_or expression to compute consistency from coefficient_of_variation. When None (insufficient samples), consistency defaults to 1.0; otherwise calculate fraction within tolerance bounds.
Remove misleading underscore prefix from _td_tolerance_ms variable, which is actually used in struct field assignment. Update both the tuple binding and the struct construction to use the properly-named td_tolerance_ms.
- Make --prop-size mandatory when --estimate-optimal-p flag is set - Remove misleading default (5") that could produce wrong recommendations - Update help text to clarify requirement and no default behavior - Update OVERVIEW.md to state --prop-size is required and no default exists - Users must now explicitly specify actual propeller diameter - Error message guides users if --estimate-optimal-p used without --prop-size
Removed unused typical_weight_g field (marked with #[allow(dead_code)] for 'Phase 2') and the dead new() constructor. Only kept new_simple() which is actually used for empirical targets. No behavioral change.
Changed 'Td ∝ √(I/torque)' presentation from principle to 'approximate heuristic' and defined torque as 'available motor torque at operating point (accounting for battery voltage, propeller load, ESC response)'. Clarified that this is a simplified model, not a strict physical law.
…eRecommendations Allow ergonomic field access on newtype wrappers (e.g., conservative.pd_ratios instead of conservative.0.pd_ratios) by implementing std::ops::Deref for both ConservativeRecommendations and ModerateRecommendations to dereference to PdRecommendations.
Expanded 'Td (time to 50%)' to 'Td (derivative time / time to reach 50%)' and 'CV' to 'CV (coefficient of variation)' in the optimal P estimation console output description to improve user clarity.
…rejected' text Ensure CLI help, README, and OVERVIEW all state '--prop-size <size>: Propeller diameter in inches (1-15, whole-number only). Requires --estimate-optimal-p to have effect.' and remove patronizing 'decimals are rejected' phrasing.
- Add MIN_TD_MS (0.1ms) to constants module for Td validation - Remove local MIN_TD_MS const from optimal_p_estimation.rs - Replace magic 1e-12 with PSD_EPSILON constant in spectral_analysis.rs - Add TOC entry for 'Optimal P Estimation (Optional, Experimental)' section in OVERVIEW.md All domain-specific constants now centralized for better maintainability and consistency.
…dead code comment)
…ERVIEW targets table
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
1a34861 to
cea9980
Compare
|
Closing in favour of a clean squash-rebase replacement. The original branch (
Replacement PR opened as #NEW (see below). |
|
Closing in favour of a clean squash-rebase replacement. The original branch (
Replacement PR: #145 |
* feat: torque-inertia profiler, optimal P estimation, achievability factor (squash) Squash of all feature/torque-inertia-profiler commits for clean rebase onto master. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address CodeRabbit PR #141 feedback - extract_aircraft_key: include craft name from post-date suffix when prefix ends with BLACKBOX_LOG (fixes BTFL files where craft name follows _YYYYMMDD_HHMMSS grouping all into BTFL_BLACKBOX_LOG) - TORQUE_PROFILER_SETTLE_SAMPLES: 3 → 5 samples to better cover ESC/motor lag at 1 kHz (5 ms vs 3 ms) - TORQUE_PROFILER_ACHIEVABILITY_FACTOR: document empirical calibration origin (5" 6S freestyle / HELIO H7) - extract_punch_ratios: comment that Yaw (axis 2) is collected for diagnostics but excluded from optimal-P analysis Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: normalize recommendation label verbiage in plot_step_response All three labels now use consistent "Recommendation (X):" form matching master: conservative, moderate, and aggressive — eliminating the "Conservative recommendation:" / "Moderate recommendation:" variants that leaked in from the feature branch during conflict resolution. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: remove duplicate 'Recommendation: None (Optimal)' legend entry When step response is optimal the conservative block already emits 'Recommendation (none): No obvious tuning adjustments needed'. The moderate else-if fallback then emitted a second redundant 'Recommendation: None (Optimal)' entry. Remove the redundant block; the conservative branch always covers the no-recommendation case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: update OVERVIEW.md for physics-derived Torque-Inertia Profiler Replace the stale prop-size / TD_TARGETS table section with accurate documentation of the new architecture: two-phase profiler (punch-event detection → aircraft grouping → OptimalPAnalysis), key constants, the aircraft grouping fix for generic BTFL filenames, and the relationship between P:D recommendations and optimal-P estimation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: always show skip reason in PNG; always compute D for optimal-P rec; doc cleanup - plot_step_response: show optimal-P section (including skip reason) whenever estimate_optimal_p was requested, not only when a full analysis succeeded. Fixes blank PNG legend when profiler data is insufficient. - optimal_p_estimation + plot_step_response: fall back to current P:D when recommended_pd_conservative is None (optimal step response) so D is always shown alongside the recommended P, not only when overshoot correction exists. - OVERVIEW.md: remove history/change language; replace constant values with constant names; clarify Yaw exclusion rationale and skip-reason output. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: dependability improvements — LOW AUTHORITY warning, n= sample count, Experimental label, skip in PNG - Add LOW_AUTHORITY_SETPOINT_THRESHOLD_DEG_S constant and detect hover/slow-cruise logs where max setpoint < threshold; print warning in both PNG legend and console optimal-P section - Add n= sample count to Td line in PNG and console for statistical-weight context - Label Optimal P section as "Optimal P (Experimental, log-derived)" in PNG - Show skip reason in PNG even when analysis was not performed (not just on success) - D recommendation falls back to current P/D ratio when step response is optimal (no P:D change computed), ensuring D is always shown alongside optimal-P recommendation - OVERVIEW.md: add CV/consistency/low-authority reliability interpretation section Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: uniform skip label, always-show consistency in PNG, windows= clarity - PNG skip branch: use same 'Optimal P (Experimental, log-derived)' label as success branch — skip reason line already conveys the skip; no duplicate marker needed - PNG consistency: always emit Consistency: X% line (orange with CV when poor, grey when good) so PNG matches console parity; previously only warning shown - Rename n= → windows= in PNG and console Td line — more intuitive for pilots (windows = valid step-response windows contributing to the Td mean) - OVERVIEW.md: update windows= description accordingly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: show CV threshold in unreliable warning; align console/PNG verbiage Both console and PNG now report: 'unreliable (>40%)' where 40 is derived from TD_COEFFICIENT_OF_VARIATION_MAX so the displayed value tracks the constant without manual updates. Verbiage aligned between outputs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: update README and OVERVIEW for current functionality README: - Remove --prop-size (no longer exists; torque-inertia ratio is log-derived) - Update --estimate-optimal-p description to match actual CLI - Remove --prop-size from example command - Add optimal P estimation to console output section OVERVIEW: - Windowing section: clarify FRAME_LENGTH_S vs RESPONSE_LENGTH_S design (longer deconvolution window for frequency resolution; only first RESPONSE_LENGTH_S retained for display and analysis) - Optimal P Output bullet: enumerate all current dependability signals (windows=, consistency %, LOW AUTHORITY warning, skip reason) and reference the CV/reliability section for details Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: trim README --estimate-optimal-p description Remove '(no prop-size required)' — the flag description stands on its own. Trim console output bullet to remove redundant LOW AUTHORITY qualifier. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address all CodeRabbit review items on PR #145 Actionable (4): - optimal_p_estimation: filter non-finite (NaN/inf) Td samples before computing statistics in TdStats::from_samples to prevent propagation into mean/deviation/classification - torque_inertia_profiler: replace magic 1000.0 with THROTTLE_COMMAND_SCALE, 1e-9 with TORQUE_PROFILER_MIN_DT_S; both added to constants.rs - torque_inertia_profiler: fix off-by-one in derivative scan loop — resp_start..resp_end.saturating_sub(1) excluded the last valid j/j+1 pair; corrected to resp_start..resp_end - main.rs: persist skip reason into optimal_p_skip_reasons when P gain is unavailable so PNG overlay renders the reason instead of nothing Nitpicks (2): - main.rs + torque_inertia_profiler: replace all hardcoded [T; 3] axis-count literals with AXIS_COUNT (from axis_names.rs); use std::array::from_fn for initialisation - plot_step_response: hoist all inline RGBColor literals in the Optimal P legend section into named constants in constants.rs (COLOR_OPTIMAL_P_DIVIDER/HEADER/TEXT/WARNING/RECOMMENDATION/SKIP); also applies COLOR_OPTIMAL_P_WARNING to the LOW AUTHORITY warning Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: remaining CodeRabbit items in torque_inertia_profiler - extract_punch_ratios return type: [Vec<f64>; 3] -> [Vec<f64>; AXIS_COUNT] - ms-to-samples conversions: / 1000.0 -> / OPTIMAL_P_SECONDS_TO_MS_MULTIPLIER (constant already existed; now imported and used consistently) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: remove orphaned tests_optimal_p.rs Tests for FrameClass/TdTargetSpec/for_frame_inches — all removed with the prop-size approach in favour of the physics-derived torque-inertia profiler. File was not declared in mod.rs and was never compiled; deleted to avoid confusion. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: settle window is time-based (ms), not sample-count-based TORQUE_PROFILER_SETTLE_SAMPLES (fixed 5 samples) was only correct at 1 kHz (5 ms). At higher loop rates the settle period was far too short: - 3.2 kHz (BMI270): 1.56 ms — before most ESCs respond - 4 kHz: 1.25 ms - 8 kHz: 0.63 ms Premature measurement produces near-zero angular acceleration, underestimates torque_inertia_ratio, inflates the Td target, and biases recommendations toward Optimal/Decrease when P may need increasing. Replace with TORQUE_PROFILER_SETTLE_MS (5.0 ms) converted to samples at runtime using the actual log sample rate. Behaviour at 1 kHz is unchanged; high-rate logs now skip the correct number of samples to clear ESC/motor lag before measuring peak angular acceleration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: note over-P limitation in CV/reliability section of OVERVIEW The profiler may report Optimal when P is too high because an oscillatory step response produces a short Td that matches the physics formula for the current (excessive) P. CV/consistency warning is the indirect signal. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: high CV without LOW AUTHORITY as over-P investigative signal Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: document .headers.csv requirement for --estimate-optimal-p Add requirement note to OVERVIEW.md, README.md, and --help output. Without a .headers.csv file P gain is unavailable and optimal P estimation is skipped; all other analyses are unaffected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
This PR transitions the Optimal P estimation from hardcoded empirical tables to a physics-derived model that calculates aircraft-specific Td targets from torque-to-inertia ratios measured during throttle-punch events.
Changes
TD_TARGETStable.insufficient throttle dynamics).Prerequisites
fix/practical-tuning-thresholds.Testing
Requires logs with deliberate throttle punch sequences (at least 5 events) for accurate profiling.