Fix iOS Motion accel: match Android units + sign convention#70
Merged
Conversation
`Mob.Motion` documents `accel` as m/s² with gravity included, but the iOS NIF emitted `userAcceleration + gravity` straight from CoreMotion — which is in G (~1.0, not ~9.81) AND uses iOS's own sign convention: the gravity vector points down, so at rest the up-axis reads -g. Android's SensorManager reports specific force (proper acceleration), a_coord - g_field, so at rest the up-axis reads +g. The two were off by both a scale factor and a sign, so any tilt- or shake-driven UI (e.g. a mascot whose pupils follow gravity) barely moved on iOS and moved backwards when it did. Emit `(userAcceleration - gravity) * 9.80665` instead. That is exactly Android's a_coord - g_field: +g on the up-axis at rest, m/s², correct for both the static tilt term and the dynamic linear term (not merely a rest-time negation). gyro (rad/s) and mag (µT) already matched and are unchanged. Also document the `accel` convention explicitly in the moduledoc so it's a stated cross-platform contract, not folklore. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The accel normalization is ObjC inside the CoreMotion callback — no host seam to unit-test, and the iOS Simulator delivers no motion data, so it's not integration-testable off a physical device. Guard it the way Mob.NifDeclarationTest guards the NIF tables: parse ios/mob_nif.m and assert each accel axis SUBTRACTS gravity (Android specific-force sign, not iOS's inverted `+ gravity`) and scales G→m/s². Fails on a revert to the old form. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GenericJam
added a commit
that referenced
this pull request
Jul 11, 2026
Ships #70: Mob.Motion iOS `accel` now matches Android's m/s² units and specific-force sign convention — (userAcceleration - gravity) * 9.80665 instead of the raw CoreMotion userAcceleration + gravity (G, inverted). Fixes tilt/shake UIs that barely moved and moved backwards on iOS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Mob.Motiondocumentsaccelas m/s² with gravity included, but the iOS NIF was emitting CoreMotion'suserAcceleration + gravityverbatim. That value is:SensorManager.TYPE_ACCELEROMETER; anda_coord − g_field, so at rest the up-axis reads +g.So iOS
accelwas off from Android by both a scale factor and a sign. Any tilt- or shake-driven UI barely moved on iOS, and moved backwards when it did (found via a mascot whose pupils follow gravity — nearly still on iOS, then inverted once scaled).Fix
Emit
(userAcceleration − gravity) × 9.80665. That is exactly Android'sa_coord − g_field: +g on the up-axis at rest, m/s², correct for both the static tilt term and the dynamic linear term — not merely a rest-time negation of the old value.gyro(rad/s) andmag(µT) already matched Android and are untouched.Verified against known rest orientations:
(0, 0, +9.81)az = (0 − (−1.0))×9.81 = +9.81✓(0, +9.81, 0)ay = (0 − (−1.0))×9.81 = +9.81✓The underlying diagnosis (units + sign) was confirmed on a physical iPhone via an app-level patch that made a tilt UI track correctly on both axes; iOS Simulator has no motion hardware, so device verification of this exact NIF happens when a consuming app rebuilds
--nativeagainst a release carrying it.Also documents the
accelconvention explicitly in theMob.Motionmoduledoc so it's a stated cross-platform contract.Note on
mix.lockMaster's committed
mix.lockis out of sync withmix.exs— the dev-tooling deps (recon,ex_ast,reach,sourceror) are declared but weren't locked, somix format/mix compile(and the pre-push hook) can't run on a fresh checkout of master. The 4-linemix.lockaddition here is justmix deps.getre-locking them; it's required for this branch to build and is unrelated to the accel change.