# Sensor acquisition decisions

Checked Apple documentation and Xcode-beta iOS 27 SDK headers on 2026-10-01. [CMMotionManager](https://developer.apple.com/documentation/coremotion/cmmotionmanager) supports separate simultaneous raw/fused callbacks; this establishes API support, not unchanged hardware sampling. One shared manager supplies callbacks to one serial off-main sensor operation queue; a separate serial dispatch queue owns controls and capture state. Sensor callbacks copy immutable Core Motion values and callback receipt timestamps before asynchronously enqueueing state work. Each stream requests 0.01 seconds and retains delivered timestamp/order without UI throttling, interpolation, or filtering. Fused-only is the default; combined and raw-only are diagnostic selections. Raw-only must not enable normal recording/calibration.

[CMLogItem timestamp](https://developer.apple.com/documentation/coremotion/cmlogitem/timestamp) is seconds since boot; the installed header explicitly identifies Mach absolute time. Receipt/event time uses `mach_absolute_time` scaled by `mach_timebase_info`, with wall-clock anchors separately. Mapping records zero offset and clock names, and remains unvalidated until ten ordered observations satisfy nonnegative age below five seconds and host/sensor progression agreement within one second. Any failed check invalidates the mapping for the rest of that capture. These permissive sanity checks expose gross mismatches; sustained device/interrupt checks remain necessary. Receipt age is delivery age, not physical latency or device synchronization.

[Heading](https://developer.apple.com/documentation/coremotion/cmdevicemotion/heading) is invalid for xArbitraryZVertical; preserve its returned value converted from degrees to radians with invalid status. [Heading accuracy](https://developer.apple.com/documentation/coremotion/cmdevicemotion/headingaccuracy) is runtime gated at iOS 27 in this SDK; older systems record unavailable. Magnetic field retains vector and raw calibration accuracy; absent magnetometer is unavailable and uncalibrated readings invalid. Stream availability/callback errors retain explicit reasons.

Raw attitude quaternion/matrix remain unchanged. Canonical analysis is active device-to-reference. Direction is determined by comparing identity versus conjugate quaternion gravity residuals against reference (0,0,-1), with matrix/quaternion basis-vector agreement in either a direct or transposed representation. One relationship must remain consistent across the capture; a switch or mismatch invalidates it. The iPhone physical sample captured on 2026-10-01 confirmed a transposed matrix with identity quaternion-to-canonical mapping. Quaternion mapping and matrix relationship are recorded separately. Ten nontrivial tilt observations, winning mean residual below 0.03 and losing residual above 0.1, permit a verified mapping. Identity/yaw-only inputs cannot establish verification. Any quaternion/matrix mismatch or nonfinite attitude, gravity, rotation-rate, or acceleration vector immediately invalidates prior verification for the rest of that capture, even when the sample is otherwise an ambiguous neutral pose. Physical rider sign checks are still mandatory. A snapshot exposes the current convention during calibration.

Start, snapshot, stop, and accepted record/diagnostic callbacks run on the serial control/state dispatch queue. The stop boundary is execution of that queued control block: records enqueued ahead of it finish entering the consumer; callbacks processed afterward are suppressed. A receipt timestamp can precede stop while copying/handoff races place acceptance after the boundary; that callback is outside the accepted recording. Receipt timestamps remain copied at callback arrival. Accepted counts reconcile accepted rows, not every hardware-delivered callback, and do not establish zero delivery losses. Stop advances the capture generation before stopping sensors, so a late callback from an earlier start cannot enter a restarted capture or alter its clock/convention. Sensor callbacks never synchronously wait for the control queue, avoiding a callback/stop wait cycle. The summary reconciles accepted counts and the stop anchor. The installed CMMotionManager.h (lines 38–40) states that stopping updates cancels all operations in the supplied operation queue; this SDK contract is the reason controls and persistence must stay off the sensor queue. Queued restart, snapshot, and repeated-stop completions therefore survive sensor cancellation. Persistence uses its own writer queue and drains after the summary. This is a contract-based correction, not a claimed hardware reproduction.

Simulator fixtures are compiled only under `targetEnvironment(simulator)` and invoked explicitly. They are synthetic workflow evidence, never a physical capture path. The app must visibly label fixture mode.

Statistics use (N−1)/duration, reciprocal median positive interval, population SD, nearest-rank p95/p99. Original order is retained, nonpositive/nonfinite intervals flagged and excluded from interval summaries. Gaps exceed 2.5 median intervals; missing-interval estimates are rounded gap/median minus one and are distinct from observed writer losses. Delivery ages require validated mapping and matched receipt counts.

Pending hardware gate: three matched 30-second stationary/movement captures each in fused-only and combined with UI/writer active; retain rates, p99, delivery ages, backlog/loss and noise. Provisional guardrails are ≤5% fused mean decrease, ≤20% p99 increase, zero writer loss. Investigate overlapping variance; retain raw-only diagnostics if combined fails. Apple Watch support/runtime/clock mapping requires separate device investigation.

The boot-time privacy manifest declares 35F9.1 for elapsed in-app timers and 8FFB.1 for the absolute timestamps of capture, marker and cue events occurring inside this app, including user-directed export. [Apple's required-reason API documentation](https://developer.apple.com/documentation/bundleresources/app-privacy-configuration/nsprivacyaccessedapitypes/nsprivacyaccessedapitype) permits this event-timestamp purpose. The app does not use boot time for fingerprinting. SessionClock now locks mapping reads and validation/reset mutations together so main-thread metadata creation copies a consistent value while the capture queue validates timestamps.
