sensorRecorder.ts

Interfaces with the iOS CoreMotion coprocessor and pedometer for background-safe activity data

Purpose

This module provides background-safe access to motion data on iOS by reading from the CoreMotion motion coprocessor and the device pedometer. Because coprocessor recording operates independently of app state, this module is the primary data source for CPM computation during background task wakes.

Its responsibilities include:

  • Querying the system pedometer for step count over an arbitrary historical window
  • Computing CPM from pedometer step counts as a lightweight fallback
  • Starting a timed CoreMotion accelerometer recording session via the native module
  • Querying the most recent 1-minute CPM value from the native coprocessor buffer
  • Querying per-chunk CPM values across a longer coprocessor recording window
  • Averaging per-chunk CPM values into a single representative value
  • Logging all query results and errors with structured log entries

Invariants

iOS-Only Native Module

HeatSafeSensorRecorder is loaded conditionally: it is null on Android.

All functions that depend on it (startAccelerometerRecording, getLatestMinuteCPM, getCPMFromRecordedAccelerometer) return a safe null/false value immediately when the module is unavailable, without throwing.


Pedometer Availability Check

All pedometer-dependent operations call Pedometer.isAvailableAsync() before querying.

If the pedometer is unavailable, getCPMFromPedometer returns 0 and logs a WARN. It never throws.


CPM from Pedometer Zero-Guard

When the requested fromSeconds is 0 or negative, the CPM computation returns 0 rather than dividing by zero.


Minimum Data Guard — Coprocessor

getCPMFromRecordedAccelerometer returns null when the native module returns an empty chunks array.

This signals that fewer than 3 minutes of coprocessor data are available, which is enforced by the native side.

getLatestMinuteCPM returns null when the native module signals insufficient data (less than 1 minute available).


Error Containment

All native module calls are wrapped in try/catch.

Errors are logged at ERROR level and a safe default is returned (false, 0, or null depending on the function). No error propagates to the caller.


Variants

CPM Sources

The module exposes two distinct CPM data sources with different characteristics:

SourceFunctionAccuracyBackground-safe
Pedometer (step count)getCPMFromPedometerStep-based proxyYes
Coprocessor (raw accelerometer)getCPMFromRecordedAccelerometerFull AUC pipelineYes
Coprocessor (latest minute)getLatestMinuteCPMNative pipelineYes

Coprocessor Chunk Averaging

getCPMFromRecordedAccelerometer returns the mean of all per-chunk CPM values alongside the raw chunks array.

Individual chunks represent non-overlapping 60-second windows computed by the native module. The average field is the arithmetic mean across all returned chunks.


Exports

isSensorRecorderAvailable()

async function isSensorRecorderAvailable(): Promise<boolean>

Returns true if the device pedometer is available. Does not check native module availability separately.


getCPMFromPedometer(...)

async function getCPMFromPedometer(fromSeconds: number): Promise<number>

Queries the system pedometer for the step count over the last fromSeconds and returns the average CPM.

Parameters

ParameterDescription
fromSecondsHistorical window length in seconds

Returns

CPM as a number. Returns 0 if the pedometer is unavailable, the window is zero-length, or an error occurs.


startAccelerometerRecording(...)

async function startAccelerometerRecording(durationSeconds: number): Promise<boolean>

Asks the CoreMotion coprocessor to record raw accelerometer data for durationSeconds. Safe to call during a background wake. Returns false on Android or on any native error.


getLatestMinuteCPM()

async function getLatestMinuteCPM(): Promise<number | null>

Returns the CPM for the most recent 60-second coprocessor recording window, or null if fewer than 1 minute of data is available or the native module is absent.


getCPMFromRecordedAccelerometer(...)

async function getCPMFromRecordedAccelerometer(fromSeconds: number): Promise<AccelerometerCpmResult | null>

Returns per-chunk CPM values and their average for the last fromSeconds of coprocessor data, or null if fewer than 3 minutes of data are available.

Parameters

ParameterDescription
fromSecondsHistorical window to query from the coprocessor

AccelerometerCpmResult

type AccelerometerCpmResult = {
	average: number;
	chunks: number[];
};
FieldDescription
averageArithmetic mean CPM across all returned chunks
chunksPer-minute CPM values from the coprocessor pipeline