MotorSignal-App/docs/DEBUGGING_MAP.md

7.8 KiB

MOTORSIGNAL DEBUGGING MAP

This document maps common symptoms in the MotorSignal app to their specific root causes in the GNSS/Telemetry/Speedometer subsystems. Use this guide to jump directly to the responsible layer during triage.

1. Symptom: "Speedometer shows 0 while moving"

Possible Causes & Investigation:

  • Permissions Issue: If OS-level location permissions were denied or revoked, LocationService stream does not initialize.
    • Check: LocationService.hasPermissions()
  • Deadband Overreach: If the rider is moving slower than 1.3 MPH (0.6 m/s), LocationService._applyEmaFilter enforces 0.0 to prevent stationary jitter.
    • Check: LocationService._applyEmaFilter
  • Validation Rejection: The OS is sending fixes with terrible accuracy (> 80m-100m) or physically impossible acceleration, causing LocationService._isValidPosition to silently drop the fix instead of updating the EMA.
    • Check: Look for Accuracy poor or Impossible acceleration logs from _emitGpsDiagnostic.

2. Symptom: "Speedometer freezes at a high speed during a tunnel/dead zone"

Possible Causes & Investigation:

  • Signal Loss Freeze: When GPS fixes stop arriving, LocationService stops emitting new positions. However, TelemetryService._emitSample() continues to fire at 20Hz using _latestPosition (which contains the last known high speed).
    • Status (RESOLVED):
      1. Recording Health (Phase 1D): RideStateMachine._recordTelemetryEvidence() checks _lastSeenGpsTimestamp and only refreshes GPS freshness when a genuinely newer GPS fix arrives. After evidenceStaleAfterMs (4000 ms) without a new fix, RideStateMachine truthfully transitions recording health to DEGRADED.
      2. Speed Presentation (Phase 1E): TelemetrySample now exposes isSpeedFresh and freshSpeed, evaluating the GNSS fix age against the canonical 4000 ms staleness bound. SpeedReadout consumes freshSpeed and truthfully presents -- when GPS is stale or unavailable, preventing frozen speeds from being displayed while avoiding fabricated zero-speed measurements. When fresh GPS fixes resume, numerical speed presentation automatically recovers.

3. Symptom: "Speedometer lags significantly behind real-world braking/acceleration"

Possible Causes & Investigation:

  • EMA Tuning Conflict: LocationService._applyEmaFilter uses a fast dynamic alpha (0.90) for accelerating/braking and 0.75 for cruising. If the delta threshold (> 0.4 m/s) isn't being tripped cleanly due to noisy GPS, it may stick to the slower 0.75 cruising alpha.
    • Check: LocationService._applyEmaFilter

4. Symptom: "Leaning the bike on the kickstand records a new 'Max Lean Angle' record"

Possible Causes & Investigation:

  • Low-Speed Gate Failure: RideStateMachine._processStats restricts dynamic metrics (lean, accel, braking) to speeds >= 3.0 MPH. If a false GPS speed spike momentarily registers > 3.0 MPH while the bike is tilted on the kickstand, it can permanently update state.maxLeanLeft or state.maxLeanRight.
    • Check: RideStateMachine._processStats (isDynamicallyMoving = sample.speed! >= 3.0).

5. Symptom: "App constantly auto-starts a ride while parked in the driveway"

Possible Causes & Investigation:

  • Auto-Start Jitter: RideStateMachine._onTelemetryUpdate promotes a ready state to riding if sample.speed! >= 1.5. If LocationService fails to enforce the 0.6 m/s (1.3 MPH) deadband correctly, GPS multipath bounce in a driveway can trigger a false start.
    • Check: LocationService._applyEmaFilter deadband logic.

6. Symptom: "Dashboard flickers or UI lags severely during high-speed rides"

Possible Causes & Investigation:

  • Provider Granularity Issue: The dashboard is supposed to isolate high-frequency (20Hz) UI updates entirely within SpeedReadout (which uses .select on the telemetry provider). If an outer widget (like ResponsiveDashboardLayout or DashboardScreen) accidentally watches the root telemetryStreamProvider without .select, it will trigger 20 rebuilds per second of the entire screen.
    • Check: EvolutionPortraitDashboard and ResponsiveDashboardLayout usage of ref.watch.

7. Symptom: "GPS Weak / Degraded indicator won't go away even with clear skies"

Possible Causes & Investigation:

  • Pre-commit DB Failure or Watchdog Staleness: The dashboard claims RECORDING or DEGRADED based entirely on the primary RecordingHealthContract in RideStateMachine. If _lastDbCommitMonotonicMs stops advancing (due to a SQLite deadlock) or _lastAcceptedTelemetryTime stops advancing, health defaults to degraded.
    • Check: Database flush interval in RideStateMachine._persistOwnedTelemetrySnapshot and the _evaluateRecordingHealth watchdog loop.

8. Symptom: "Trip distance or moving time inflated after riding through tunnel/dead zone"

Possible Causes & Investigation:

  • Stale GNSS Integration (RESOLVED in Phase 1F): When GPS fixes stop advancing, TelemetryService emits cached positions at 20Hz. Previously, _movingTimeAccumulator continued to increment based on the cached speed, and upon GPS recovery, the distance between pre-outage coordinates and post-outage coordinates could be added as an artificial catch-up jump.
    • Resolution: RideStateMachine._processStats gates moving/stopped time on sample.isSpeedFresh && _lastSample!.isSpeedFresh. Distance and elevation require hasFreshGps = sample.isSpeedFresh && (_lastSample?.isSpeedFresh ?? false). When GPS is lost, time and distance cease accumulating; upon recovery, the first fresh fix resets baseline coordinates without integrating across the outage gap.
    • Check: RideStateMachine._processStats time accumulation, distance, and elevation gating.

9. Symptom: "Performance launch timer records anomalous 0-60 time or finishes without GPS"

Possible Causes & Investigation:

  • Stale Speed in Performance State Machine (RESOLVED in Phase 1F): If GPS fixes stop during an active launch or countdown, the state machine previously retained cached speeds, allowing runs to complete across signal loss or trigger high-speed launch anomalies upon recovery.
    • Resolution: PerformanceLaunchStateMachine and PerformanceTimerService inspect sample.isSpeedFresh. In launched or countdown, active runs abort immediately with PerformanceAbortReason.gpsLost. In ready, the state machine reverts to idle, preventing high-speed launch anomalies when fresh GPS resumes.
    • Check: PerformanceLaunchStateMachine._onTelemetryUpdate and PerformanceTimerService._onTelemetryUpdate.

10. Symptom: "Compass/Heading indicator remains stuck pointing in old direction after turning into tunnel/garage"

Possible Causes & Investigation:

  • Stale GNSS Heading Override (RESOLVED in Phase 1G): When GPS fixes stopped advancing while moving, TelemetryService continued to use the cached _latestPosition.heading, overriding live compass heading indefinitely.
    • Resolution: TelemetryService._emitSample() gates GPS heading selection on isGpsFresh (4000 ms bound). When GPS fix age exceeds 4000 ms, heading immediately falls back to live hardware compass.
    • Check: TelemetryService._emitSample() heading selection and CompassService.headingStream.

11. Symptom: "Map marker shows vehicle at active location when GPS signal is lost"

Possible Causes & Investigation:

  • Unindicated Stale Map Coordinate (RESOLVED in Phase 1G): DashboardMapPanel rendered the vehicle marker identically whether coordinates were current or stale.
    • Resolution: DashboardMapPanel watches s.value?.isGpsFresh. When stale, the marker is dimmed and a subtle LAST KNOWN LOCATION badge is displayed, preserving the last-known position truthfully without fabricating position.
    • Check: DashboardMapPanel and TelemetrySample.isGpsFresh.