CPM Pipeline

Scope

This document defines the runtime CPM pipeline from accelerometer samples to prediction consumption.

Canonical Flow

  1. App.tsx starts startActivityService() on mount and calls stopActivityService() on cleanup.
  2. activityService starts expo-sensors accelerometer streaming at ~30 Hz.
  3. Sensor samples are buffered in memory (src/sensors/accelerometer.ts), converted from g to m/s^2, timestamped from the native sensor event, and pruned to the last ~75 seconds.
  4. activityService treats a CPM window as ready only when it spans nearly 60 seconds, allowing one configured sensor interval of tolerance.
  5. Every 60 seconds, activityService reads the most recent ready window and calls computeCpmFrom60s(...) in src/model/inferenceActivity.ts, which consumes the collected 30 Hz window directly.
  6. When CPM computation succeeds, one row is written to cpm_history in SQLite through src/db/cpmDb.ts.
  7. Callers such as predictionHeatRisk and PredictionCoreTempCard consume getActivityCpm40Average() from activityService.

Readiness Contract (getActivityCpm40Average)

  • Window: last 40 minutes.
  • Minimum history: at least 35 rows.
  • Freshness: latest row must be <= 2 minutes old.
  • Return: average CPM when ready, otherwise null.

Supporting References

Invariants

  • Model behavior lives in src/model/inferenceActivity.ts; orchestration should stay in activityService.
  • The model consumes the collected 30 Hz window directly; there is no separate 60 Hz downsampling stage.
  • Each successful minute tick produces at most one persisted CPM row.
  • Storage shape, retention, and query ordering live in the schema doc.