Guide du code de l'outil de réglage Betaflight

VérifiéSûr

Guide opérationnel complet pour le code de l'outil de réglage du contrôleur de vol Betaflight. Inclut la carte des fichiers, les commandes et les modèles de code.

Spar Skills Guide Bot
DeveloppementDébutant
3025/07/2026
Claude CodeCursorWindsurfCopilotCodex
#codebase-guide#project-commands#file-map#developer-reference

Recommandé pour

Notre avis

Ce guide opérationnel complet décrit l'architecture, les commandes et la cartographie des fichiers d'un projet d'analyse de logs Blackbox pour Betaflight.

Points forts

  • Tableau de correspondance clair entre besoins et emplacements des fichiers.
  • Liste exhaustive des commandes de développement avec descriptions.
  • Mise en garde sur la nécessité de maintenir le fichier à jour.

Limites

  • Spécifique à un projet unique (analyseur de logs Betaflight).
  • Ne couvre pas les détails d'implémentation ou les API internes.
  • Peut devenir obsolète si le projet évolue.
Quand l'utiliser

Lorsque vous devez comprendre rapidement la structure du projet, exécuter des commandes ou localiser des fichiers.

Quand l'éviter

Pour des tâches de développement générales ne concernant pas ce projet spécifique.

Analyse de sécurité

Sûr
Score qualité95/100

The skill is a static documentation file containing only informational content about a codebase. There are no executable instructions, dangerous commands, or security risks.

Aucun point d'attention détecté

Exemples

List development commands
What are the available development commands in this project?
Find file for adding a React component
Where should I add a new React component according to the codebase guide?
Locate file for adding a tuning rule
I need to add a new tuning rule. Which directory should I look in?

Codebase Skill — Complete Operational Guide

KEEP THIS FILE UP TO DATE. When architectural decisions change, files move, new patterns are adopted, or conventions shift, update this file immediately. Stale guidance is worse than no guidance. After any structural change (new store, renamed file, new dependency, changed workflow), check if this file needs updating.


Quick Reference Commands

pnpm dev              # Dev server → localhost:5173
pnpm build            # tsc && vite build → dist/
pnpm lint             # ESLint (--max-warnings 0, zero tolerance)
pnpm test             # Playwright E2E (full suite, ~10 min)
pnpm test:headed      # E2E with visible browser
pnpm test:ui          # E2E Playwright UI debug mode
pnpm test:unit        # Vitest unit tests (src/**/*.test.ts)
npx tsx scripts/take-screenshots.ts      # All 8 showcase screenshots
npx tsx scripts/take-screenshots.ts 2    # Single screenshot by index
npx tsx scripts/take-screenshots.ts composite  # Re-composite from existing raws

File Map — Where to Find Everything

By Purpose

| I need to... | Look here | |---------------------------------------|-----------------------------------------------------| | Add/change a React component | src/components/ | | Add/change chart logic | src/components/LogChart.tsx + src/components/logChart/ | | Add/change chart styles | src/components/LogChart.styles.ts | | Add a new tuning rule | src/domain/rules/ (create new file, register in RuleEngine) | | Change how analysis works | src/domain/engine/RuleEngine.ts | | Add/change a Betaflight parameter | src/domain/types/Analysis.ts (BetaflightParameter type) | | Map parameter to CLI command | src/domain/utils/CliExport.ts | | Add CLI option metadata/ranges | src/lib/betaflight/cliOptions.ts | | Change how .bbl/.bfl files parse | src/domain/blackbox/ | | Change how .txt/.csv files parse | src/workers/logParser.worker.ts | | Add/change a MobX store | src/stores/ | | Wire a new store into the app | src/stores/RootStore.ts | | Add/change theme colors | src/theme/lightTheme.ts, src/theme/darkTheme.ts | | Add/change theme type | src/theme/types.ts (ThemeColors interface) | | Add global CSS animations | src/theme/GlobalStyles.tsx | | Add/change MobX reactive primitives | src/lib/mobx-reactivity/ | | Add/change serial (USB) communication | src/serial/ | | Add/change quad profile thresholds | src/domain/profiles/quadProfiles.ts | | Add/change issue descriptions on chart| src/domain/utils/issueChartDescriptions.ts | | Update changelog | src/data/changelog.ts | | Add/change glossary terms | src/data/glossary.ts | | Change build/deploy | vite.config.ts, .github/workflows/deploy.yml | | Change virtual:changelog module | vite-plugins/changelogPlugin.ts | | Add/change E2E tests | e2e/ (specs + helpers) | | Add/change unit tests | Co-located with source: src/**/*.test.ts | | Change Playwright config | playwright.config.ts | | Change ESLint rules | eslint.config.js | | Check TS config | tsconfig.json (useDefineForClassFields: false) |

Key Files (Sorted by Importance)

| File | Lines | Role | |------|-------|------| | src/App.tsx | ~613 | Root layout, panel resize, drag-drop, modal orchestration | | src/stores/RootStore.ts | ~96 | Composes all stores, provides React context + hooks | | src/stores/AnalysisStore.ts | ~200 | Analysis results, issue/recommendation selection, reanalyze | | src/stores/LogStore.ts | ~130 | Parsed frames, metadata, worker communication | | src/stores/UIStore.ts | ~294 | Axis, zoom, panel state, modals, mobile layout, toasts | | src/stores/SettingsStore.ts | ~200 | Imported Betaflight settings, pending/accepted values | | src/stores/SerialStore.ts | ~200 | USB serial connection, read/write to FC | | src/stores/FlashDownloadStore.ts | ~160 | Download logs from FC flash memory | | src/domain/engine/RuleEngine.ts | ~500 | Orchestrates analysis: segment → detect → dedup → recommend | | src/domain/types/Analysis.ts | ~245 | DetectedIssue, Recommendation, ParameterChange, BetaflightParameter | | src/domain/types/LogFrame.ts | ~167 | LogFrame, LogMetadata, AxisData interfaces | | src/domain/types/TuningRule.ts | ~60 | TuningRule interface (condition, detect, recommend) | | src/components/RecommendationsPanel.tsx | ~1429 | Tuning recommendations UI (tabs, CLI export, accept tune) | | src/components/LogChart.tsx | ~385 | Recharts line chart with issue markers | | src/components/logChart/useChartData.ts | ~100 | Downsample frames → chart data points | | src/components/logChart/useChartInteractions.ts | ~330 | Mouse drag/click handlers, issue selection on chart click | | src/components/logChart/useIssueLabels.ts | ~135 | Compute label positions, stacking, severity sorting | | src/components/logChart/useIssuePopover.ts | ~245 | Hover/forced popover HTML, glow effect on selection | | src/components/LogChart.styles.ts | ~230 | Styled components for chart, labels, popover, overlays | | src/components/FileUpload.tsx | ~770 | Drag-drop upload, parse progress, metadata display | | src/workers/logParser.worker.ts | ~400 | Web Worker: parses .bbl/.bfl/.txt/.csv in background | | src/domain/utils/CliExport.ts | ~384 | Map recommendations → Betaflight CLI set commands | | src/domain/utils/FrequencyAnalysis.ts | ~299 | Cooley-Tukey FFT, RMS, band energy | | src/domain/utils/SignalAnalysis.ts | ~391 | Bounceback detection, settling time analysis | | src/lib/betaflight/cliOptions.ts | ~1000 | Betaflight 4.5 CLI param definitions, ranges, enums |


Architecture at a Glance

User uploads .bbl/.bfl/.txt/.csv file
    ↓
LogStore.uploadFile() → spawns Web Worker
    ↓
Worker parses binary/text → postMessage({ type: 'complete', frames, metadata })
    ↓
LogStore sets frames + metadata → triggers AnalysisStore.analyze()
    ↓
RuleEngine.analyzeLog():
  1. Segment log into 100ms windows (50% overlap)
  2. Classify flight phase per window (idle/hover/cruise/punch/propwash/flip/roll)
  3. Run each rule: condition() → detect() → recommend()
  4. Temporal dedup issues (100ms gap merge, then collapse by type+axis)
  5. Cross-axis correlation (annotate patterns, generate hardware recs)
  6. Frequency issue merge (collapse same-freq frameResonance/bearingNoise across axes)
  7. Temporal progression analysis (annotate trends, generate meta-issues)
  8. Generate recommendations (settings-aware, with currentValue populated)
  9. Dedup recommendations (key on parameter:axis, not title)
  10. Generate summary + flight segments
    ↓
AnalysisStore.result populated → observer() components re-render
    ↓
LogChart shows traces + issue markers
RecommendationsPanel shows issues + recommendations + CLI export

Layer Rules

  • Domain (src/domain/): Pure TypeScript. NO React imports, NO MobX imports. Testable in isolation.
  • Stores (src/stores/): MobX makeAutoObservable. Business logic orchestration. Can reference domain layer.
  • Components (src/components/): React + observer(). Access stores via hooks. Emotion styled components.
  • Workers (src/workers/): Background threads. Can import domain layer. Communicate via postMessage.

How to Implement Common Tasks

New Tuning Rule

  1. Create src/domain/rules/YourRule.ts conforming to TuningRule interface:
    export const YourRule: TuningRule = {
      id: 'your-rule',
      name: 'Your Rule',
      description: '...',
      baseConfidence: 0.7,
      issueTypes: ['yourIssueType'],
      applicableAxes: ['roll', 'pitch', 'yaw'],
      condition(window, frames) { return /* should this window be checked? */ },
      detect(window, frames, profile) { return /* DetectedIssue[] */ },
      recommend(issues, frames, profile) { return /* Recommendation[] */ },
    }
    
  2. Add issue type to IssueType union in src/domain/types/Analysis.ts
  3. Register rule in src/domain/engine/RuleEngine.ts constructor: this.registerRule(YourRule)
  4. Add chart description in src/domain/utils/issueChartDescriptions.ts
  5. If new parameters: add to BetaflightParameter union in Analysis.ts, map in CliExport.ts
  6. Add thresholds per quad profile in src/domain/profiles/quadProfiles.ts

New React Component

import { observer } from 'mobx-react-lite'
import styled from '@emotion/styled'
import { useStores } from '../stores/RootStore'
import { useObservableState, useComputed, useAutorun } from '../lib/mobx-reactivity'

const Wrapper = styled.div`
  color: ${p => p.theme.colors.text.primary};
`

export const MyComponent = observer(() => {
  const { uiStore, analysisStore } = useStores()
  const [localState, setLocalState] = useObservableState(false)
  const derived = useComputed(() => analysisStore.issues.filter(i => i.severity === 'high'))

  useAutorun(() => {
    if (analysisStore.selectedIssue) {
      // side effect reacting to observable change
    }
  })

  return <Wrapper>...</Wrapper>
})

Rules:

  • ALWAYS wrap with observer()
  • NEVER use useState, useEffect, useMemo, useCallback
  • ALWAYS use useObservableState, useComputed, useAutorun
  • ALWAYS use Emotion styled components (no inline styles, no CSS files, no Tailwind)
  • Keep under 300 lines — split by domain/concern

New MobX Store

import { makeAutoObservable, runInAction } from 'mobx'

export class MyStore {
  publicField: string = ''
  private privateField: SomeType | null = null

  constructor() {
    makeAutoObservable<this, 'privateField'>(this, {
      privateField: false,  // exclude from observation
    })
  }

  get computed(): string {
    return this.publicField.toUpperCase()
  }

  someAction = (value: string): void => {
    this.publicField = value
  }

  someAsyncAction = async (): Promise<void> => {
    const result = await fetch(...)
    runInAction(() => {
      this.publicField = result
    })
  }

  reset = (): void => {
    this.publicField = ''
  }
}

Then wire in src/stores/RootStore.ts:

  1. Add field: myStore: MyStore
  2. Instantiate in constructor: this.myStore = new MyStore()
  3. Add hook: export function useMyStore() { return useStores().myStore }
  4. Add to reset() if applicable

New Betaflight Parameter

  1. Add to BetaflightParameter union in src/domain/types/Analysis.ts
  2. Add CLI mapping in src/domain/utils/CliExport.ts:
    • Per-axis → PER_AXIS_PARAMS map
    • Global → GLOBAL_PARAM_MAP
  3. Add display name in PARAMETER_DISPLAY_NAMES
  4. Add CLI option metadata in src/lib/betaflight/cliOptions.ts
  5. Add value lookup in getPidValue() or getGlobalValue()

New Theme Color

  1. Add to ThemeColors interface in src/theme/types.ts
  2. Add values in src/theme/lightTheme.ts and src/theme/darkTheme.ts
  3. Use in styled components: ${p => p.theme.colors.your.new.color}

New Modal

  1. Add boolean toggle to UIStore: yourModalOpen = false
  2. Add open/close actions in UIStore
  3. Create component in src/components/YourModal.tsx
  4. Render conditionally in src/App.tsx based on uiStore.yourModalOpen

Store Access Hooks

useStores()              // Full RootStore (all stores)
useLogStore()            // Parsed frames, metadata, parse status
useAnalysisStore()       // Analysis results, issues, recommendations, selection
useUIStore()             // Axis, zoom, panel state, modals, toasts
useThemeStore()          // Dark/light mode, theme object
useSettingsStore()       // Imported Betaflight settings
useSerialStore()         // USB serial connection state
useFlashDownloadStore()  // Flash download progress

8 Registered Tuning Rules

| Rule | File | Issue Type | Detects | |------|------|-----------|---------| | BouncebackRule | src/domain/rules/BouncebackRule.ts | bounceback | Overshoot after stick release | | PropwashRule | src/domain/rules/PropwashRule.ts | propwash | Low-throttle descent oscillation | | WobbleRule | src/domain/rules/WobbleRule.ts | midThrottleWobble | Mid-throttle cruise wobble | | TrackingQualityRule | src/domain/rules/TrackingQualityRule.ts | lowFrequencyOscillation | Gyro-setpoint tracking error | | MotorSaturationRule | src/domain/rules/MotorSaturationRule.ts | motorSaturation | Motors hitting max output | | DTermNoiseRule | src/domain/rules/DTermNoiseRule.ts | dtermNoise | D-term amplifying noise | | HighThrottleOscillationRule | src/domain/rules/HighThrottleOscillationRule.ts | highThrottleOscillation | High-throttle vibration | | GyroNoiseRule | src/domain/rules/GyroNoiseRule.ts | gyroNoise | Gyro noise floor elevation |


Testing Guide

Unit Tests (Vitest)

  • Co-located with source: src/**/*.test.ts
  • Run: pnpm test:unit
  • Key test files:
    • src/domain/blackbox/BblParser.test.ts — Binary parser
    • src/domain/engine/RuleEngine.test.ts — Deduplication, segmentation
    • src/domain/engine/RuleEngineProfiles.test.ts — Profile thresholds
    • src/domain/rules/TrackingQualityRule.test.ts — Specific rule
    • src/domain/utils/CliExport.test.ts — CLI generation
    • src/components/logChart/useIssueLabels.test.ts — Label collision

E2E Tests (Playwright)

  • Located in e2e/*.spec.ts
  • Helpers: e2e/helpers.ts, e2e/data-verification-helpers.ts
  • Sample log: test-logs/shortLog.BFL
  • Run: pnpm test (full), pnpm test:headed (visible browser)
  • Config: playwright.config.ts — Chromium only, 1920x1080, 60s timeout
  • Selectors: Use data-testid attributes
  • No mocking: Real file uploads, real parsing, real analysis
  • Upload helper: uploadAndAnalyze(page, filePath?) — uploads BFL and waits for analysis

Key test files: | File | Tests | |------|-------| | file-upload.spec.ts | Drag-drop, file selection, parse progress | | analysis-summary.spec.ts | Summary panel, issue counts | | analysis-details.spec.ts | Issue details, severity, metrics | | chart-rendering.spec.ts | Lines, grid, tooltips | | chart-interaction.spec.ts | Zoom, pan, scroll | | issue-*.spec.ts | Issue detection, stacking, popover | | navigation-*.spec.ts | Panel resize, axis switch, segments | | settings-review.spec.ts | Import/export settings | | data-verification-*.spec.ts | Correct issues/recommendations detected |

When to run tests:

  • After changing domain logic → run unit tests + targeted E2E
  • After changing UI → run targeted E2E spec
  • Don't re-run ALL tests unless there's a strong reason they might fail

Commit Workflow

Writing Commit Messages

  • Plain language for non-technical users
  • One concern per commit — split unrelated changes
  • Good: "Show build time in user's timezone on What's New modal"
  • Bad: "Emit full ISO datetime from changelogPlugin and add formatBuildDate"

After Committing

If the change is user-facing, add an entry to src/data/changelog.ts:

{
  hash: 'abc1234',      // git short hash
  date: '2026-02-19',   // ISO date
  message: 'What the user sees changed',
  category: 'feature' | 'improvement' | 'fix',
}

CI/CD Pipeline

Push to main triggers .github/workflows/deploy.yml:

  1. buildtsc && vite build
  2. unit-testspnpm test:unit
  3. integration-tests — Playwright across 16 shards
  4. deploy — GitHub Pages (all 3 above must pass)

Key Deduplication Logic

Understanding this prevents confusion when working on analysis:

Issue Deduplication (RuleEngine)

  1. Temporal merge: Issues of same type+axis within 100ms gap are merged into one
  2. Collapse: Multiple occurrences become occurrences[] array with count
  3. Result: One DetectedIssue per type+axis with occurrence count

Recommendation Deduplication (RuleEngine)

  1. Key: parameter:axis from changes[] array (NOT title string)
  2. Multiple rules recommending same parameter change are merged
  3. Conflicting changes use weighted merge based on confidence

Dependencies

| Package | Version | Role | |---------|---------|------| | react | 18.2.0 | UI framework | | react-dom | 18.2.0 | DOM rendering | | mobx | 6.12.0 | Reactive state management | | mobx-react-lite | 4.0.5 | observer() HOC | | @emotion/react | 11.14.0 | CSS-in-JS (css prop) | | @emotion/styled | 11.14.1 | Styled components | | recharts | 2.10.3 | SVG chart library | | @playwright/test | 1.58.2 | E2E testing | | vitest | 4.0.18 | Unit testing | | vite | 5.0.8 | Build tool | | vite-plugin-pwa | 1.2.0 | PWA manifest + service worker | | typescript | 5.3.3 | Type checking |


Critical Constraints

| Constraint | Reason | |-----------|--------| | useDefineForClassFields: false in tsconfig | MobX makeAutoObservable needs getter/setter pattern | | No React hooks (useState, useEffect, etc.) | MobX reactive primitives replace them | | All components wrapped in observer() | MobX reactivity requires it | | Emotion styled components only | Theme-aware, dynamic dark/light mode | | Files under 300 lines | Maintainability, split by domain | | No any in domain layer | Type safety, ESLint warning-level | | Chart adaptive downsampling (300–2500 pts) | Progressive formula + FPS feedback loop in useChartData.ts | | FFT capped at 2048 samples | Avoid slowdown on large logs | | Web Worker for parsing | Prevent main thread blocking on large files | | No backend | Everything client-side, works offline (PWA) |


Zoom System

Zoom is percentage-based (0–100%). UIStore.zoomStart / zoomEnd define the visible window.

Minimum zoom window is enforced in 3 places — all must agree:

| Location | What it controls | |----------|-----------------| | UIStore.setZoom() | Safety floor when start ≥ end (0.01% absolute minimum) | | useChartInteractions.ts (wheel handler) | Scroll-to-zoom on the chart area | | RangeSlider.tsx (minWindow prop) | Handle drag + scroll-to-zoom on the slider |

The minimum is dynamic based on log duration so that full zoom always shows a 0.2s window:

const minZoomPct = (0.2 / totalDuration) * 100

LogChart.tsx computes this and passes it to RangeSlider via the minWindow prop. The chart scroll handler in useChartInteractions computes the same value from logStore.duration.


Mobile Layout

  • Breakpoint: max-width: 1599px → mobile layout
  • UIStore.isMobileLayout observable (media query listener)
  • Mobile: 3-tab bottom bar (Upload / Chart / Tune) via BottomTabBar
  • Desktop: 3-panel layout (left / chart / right) with drag-to-resize
  • Touch targets: minimum 36x36px, 18px font on pointer: coarse
  • No serial options on mobile (WebSerial is desktop Chrome/Edge only)

Serial Communication (USB)

  • src/serial/SerialPort.ts — WebSerial API wrapper
  • src/serial/MspProtocol.ts — MSP protocol (read FC state)
  • src/serial/CliProtocol.ts — Enter/exit CLI, read/write settings
  • src/serial/DataflashReader.ts — Download blackbox from FC flash memory
  • Chrome/Edge only (WebSerial API)
  • Flow: connect → enter CLI → dump settings → parse → show in SettingsImportModal

Vite Plugins

changelogPlugin (vite-plugins/changelogPlugin.ts)

  • Provides virtual:changelog module
  • Injects: entries[], buildDate (ISO), buildHash (git short hash)
  • Watches src/data/changelog.ts for HMR
  • Used by ChangelogModal for "What's New" feature

Panel Resize Pattern (Drag Performance)

When resizing panels adjacent to the chart (expensive Recharts component):

  1. Freeze inner chart wrapper at current pixel width on drag start
  2. During drag: only change panel width via direct DOM (no MobX, no React re-renders)
  3. On mouseup: clear inline styles, commit final width to MobX in single runInAction
  4. Result: zero ResizeObserver fires, zero React re-renders during drag

Quad Profiles

5 profiles in src/domain/profiles/quadProfiles.ts:

| Profile | Multiplier Range | Notes | |---------|-----------------|-------| | Whoop (65-85mm) | 1.5-2.5x (relaxed) | More noise tolerance | | 3" (micro) | 1.1-1.3x | Balanced | | 5" (baseline) | 1.0x | All thresholds calibrated here | | 7" (long-range) | 0.7-1.3x | Varies per issue | | X-Class (10"+) | 0.6-1.5x | More propwash, less noise |

actualThreshold = baseThreshold * profile.thresholds[issueType]

Skills similaires