Skip to content

plan 0011: Drag mode — distance runs without a track - #148

Open
TheAngryRaven wants to merge 1 commit into
BETAfrom
claude/drag-mode-planning-mggk0b
Open

plan 0011: Drag mode — distance runs without a track#148
TheAngryRaven wants to merge 1 commit into
BETAfrom
claude/drag-mode-planning-mggk0b

Conversation

@TheAngryRaven

Copy link
Copy Markdown
Owner

Summary

Adds Drag mode: main menu → Drag → pick a distance (1/8 Mile, 1000 ft, 1/4 Mile, 1/2 Mile, 1 Mile) and the session starts — no track file, no detection. The device stages at a standstill, starts the clock rollout-style (11.25 in of movement past a re-latching standstill anchor, ET start interpolated between 25 Hz fixes), and ends the run at the target distance with an interpolated ET + trap speed and a 0-60 mph split. It re-arms automatically when the car stops, so a whole day of passes lands in one DOVEX session (race_mode=DRAG, laps line = run ETs — same backwards-compatible trailing-column scheme as SPRINT; trap/0-60 stay out of the frozen header since the 25 Hz rows carry speed).

The run state machine is a new host-tested pure unit, drag_timer.{h,cpp}: staging (≤1 mph held 1 s, anchor = re-latching running mean so staging-lane GPS drift can't fake a launch), launch, chord-distance accumulation, and silent aborts (3 s mid-run standstill, ≥2 s fix gap) — which is also how queue-creep phantom launches self-cancel. The sketch keeps only sprint-style glue: dragTimer != nullptr IS drag mode, drag branch first in every activeTimer*() helper, run capture on the run-count edge, and the auto-idle grace re-arms on each completed run and each fresh stage so a staging queue never idles the session out. Design record in docs/plans/0011-drag-mode.md.

UI: new Drag main-menu row + PAGE_DRAG_DISTANCE picker (scrolling 3-row window, Back row); the Current Lap / Pace / Best Lap pages branch for drag (live ET, *staged*/*waiting*, trap/0-60 subtext, live 0-60 readout); the LED pace pip is suppressed between runs like sprint.

Follow-up outside this repo: the webapp will want a DRAG case in its race_mode loader (old viewers degrade gracefully to "unknown mode with a runs line").

Type of change

  • Bug fix (no user-visible behavior change beyond the fix)
  • New feature / behavior
  • Refactor (no behavior change)
  • Tests only
  • CI / tooling / docs
  • Breaking change (track files, log format, BLE protocol, or a removed mode)

How it was verified

  • Host unit tests pass (ctest --test-dir tests/build) — 523 cases including 14 new drag_timer cases (analytic launch/ET/trap/0-60 interpolation, anchor-drift and jitter immunity, abort paths, re-arm cycles, distance-table goldens)
  • clang-tidy clean — left to CI
  • Compiles for the XIAO nRF52840 Sense — left to CI (native sim build of the full firmware TU compiles clean)
  • Tested on real hardware — not yet; sim-verified: boot soak, determinism, golden fixtures (menu walk updated for the new row + a new picker fixture, goldens regenerated), both lap oracles, and two-session carryover all pass

Checklist

  • CHANGELOG.md updated under [Unreleased] (if user-visible)
  • ARCHITECTURE.md / CLAUDE.md updated (if a module or interface changed)
  • New testable logic has a matching test in tests/
  • Branch is focused — refactors / behavior / tests are not mixed together

Related issues

None — design record: docs/plans/0011-drag-mode.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BKa62h4fhn61pKn1LygrMK


Generated by Claude Code

Main menu -> Drag -> pick 1/8 Mile / 1000 ft / 1/4 Mile / 1/2 Mile /
1 Mile and the session starts: no track file, no detection. Stage at a
standstill, rollout-style launch (11.25 in past a re-latching standstill
anchor, ET start interpolated between 25 Hz fixes), run ends at the
target distance with interpolated ET + trap speed and a 0-60 split, then
re-arms automatically for the next pass. All passes land in one DOVEX
session with race_mode=DRAG (laps line = run ETs, same trailing-column
scheme as SPRINT).

- New host-tested pure unit drag_timer.{h,cpp}: the whole run state
  machine (ARMED/STAGED/LAUNCHED), the distance table, silent aborts
  (mid-run standstill, >=2 s fix gap — also how queue-creep phantom
  launches self-cancel), 14 doctest cases.
- Sketch glue mirrors sprint: dragTimer != nullptr IS drag mode, drag
  branch first in every activeTimer*() helper, run capture on the
  run-count edge, trackDetected latched, idle-grace re-arm on each run
  AND each fresh STAGED latch.
- UI: Drag main-menu row, PAGE_DRAG_DISTANCE picker (scrolling window),
  drag branches on the lap-time/pace/best pages (live ET, *staged*,
  trap/0-60 subtext, live 0-60 readout), LED pace pip suppressed between
  runs like sprint.
- Sim: golden walk updated for the new menu row + a picker fixture;
  goldens regenerated; soak/determinism/goldens/oracles all pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKa62h4fhn61pKn1LygrMK
@github-actions

Copy link
Copy Markdown

Coverage — host-testable units

📂 Overall coverage

Metric Coverage
Lines 🟢 1752/1778 (98.5%)
Functions 🟢 182/182 (100.0%)
Branches 🟡 1277/1429 (89.4%)

📄 File coverage

File Lines Functions Branches
BirdsEye/ble_stream.cpp 🟢 34/34 (100.0%) 🟢 8/8 (100.0%) 🟡 17/20 (85.0%)
BirdsEye/camera_fsm.cpp 🟢 238/246 (96.7%) 🟢 20/20 (100.0%) 🟡 142/160 (88.8%)
BirdsEye/course_creator.cpp 🟢 213/221 (96.4%) 🟢 21/21 (100.0%) 🟡 119/136 (87.5%)
BirdsEye/course_prune.cpp 🟢 37/37 (100.0%) 🟢 5/5 (100.0%) 🟢 47/50 (94.0%)
BirdsEye/crc32.cpp 🟢 30/30 (100.0%) 🟢 4/4 (100.0%) 🟢 24/24 (100.0%)
BirdsEye/crossing_pattern.cpp 🟢 15/15 (100.0%) 🟢 1/1 (100.0%) 🟢 12/12 (100.0%)
BirdsEye/dovex_header.cpp 🟢 106/107 (99.1%) 🟢 7/7 (100.0%) 🔴 62/88 (70.5%)
BirdsEye/drag_timer.cpp 🟢 140/143 (97.9%) 🟢 10/10 (100.0%) 🟡 64/80 (80.0%)
BirdsEye/filename_validator.cpp 🟢 14/14 (100.0%) 🟢 1/1 (100.0%) 🟢 30/30 (100.0%)
BirdsEye/gps_stats.cpp 🟢 25/25 (100.0%) 🟢 3/3 (100.0%) 🟢 8/8 (100.0%)
BirdsEye/gps_status_page.cpp 🟢 29/29 (100.0%) 🟢 4/4 (100.0%) 🟢 28/28 (100.0%)
BirdsEye/gps_time.cpp 🟢 45/45 (100.0%) 🟢 6/6 (100.0%) 🟢 30/32 (93.8%)
BirdsEye/gps_validation.cpp 🟢 24/24 (100.0%) 🟢 2/2 (100.0%) 🟢 66/66 (100.0%)
BirdsEye/haversine.cpp 🟢 8/8 (100.0%) 🟢 1/1 (100.0%) ⚫ 0/0 (0.0%)
BirdsEye/idle_policy.cpp 🟢 17/17 (100.0%) 🟢 2/2 (100.0%) 🟢 14/14 (100.0%)
BirdsEye/insta360_protocol.cpp 🟢 140/140 (100.0%) 🟢 16/16 (100.0%) 🟡 86/98 (87.8%)
BirdsEye/lap_format.cpp 🟢 18/18 (100.0%) 🟢 1/1 (100.0%) 🟢 9/9 (100.0%)
BirdsEye/led_animations.cpp 🟢 76/76 (100.0%) 🟢 5/5 (100.0%) 🟢 43/46 (93.5%)
BirdsEye/led_frame.cpp 🟢 21/21 (100.0%) 🟢 7/7 (100.0%) 🟢 6/6 (100.0%)
BirdsEye/led_modes.cpp 🟢 64/65 (98.5%) 🟢 5/5 (100.0%) 🟢 48/50 (96.0%)
BirdsEye/local_time.cpp 🟢 48/48 (100.0%) 🟢 6/6 (100.0%) 🟢 46/50 (92.0%)
BirdsEye/sat_bars.cpp 🟢 33/33 (100.0%) 🟢 2/2 (100.0%) 🟢 51/54 (94.4%)
BirdsEye/sd_access_policy.cpp 🟢 9/9 (100.0%) 🟢 3/3 (100.0%) 🟢 18/18 (100.0%)
BirdsEye/sd_format_page.cpp 🟢 25/25 (100.0%) 🟢 3/3 (100.0%) 🟢 25/26 (96.2%)
BirdsEye/sector_purple.cpp 🟢 51/51 (100.0%) 🟢 2/2 (100.0%) 🟡 42/50 (84.0%)
BirdsEye/sensoregg_protocol.cpp 🟢 44/45 (97.8%) 🟢 7/7 (100.0%) 🟢 33/34 (97.1%)
BirdsEye/sprint_select.cpp 🟢 25/25 (100.0%) 🟢 4/4 (100.0%) 🟢 46/48 (95.8%)
BirdsEye/tach_filter.cpp 🟢 93/93 (100.0%) 🟢 12/12 (100.0%) 🟡 74/84 (88.1%)
BirdsEye/track_json.cpp 🟢 116/120 (96.7%) 🟢 12/12 (100.0%) 🟡 67/88 (76.1%)
BirdsEye/wake_cause.cpp 🟢 14/14 (100.0%) 🟢 2/2 (100.0%) 🟢 20/20 (100.0%)

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.

2 participants