dead reckoning (?) position issue
15:22 12 Feb 2026

I am trying to use my phone as a mock data input for sensors that I am later connecting through bluetooth. All I need it for is for setting up relative positioning when given the acceleration, but I have this recurring issue where after I stop moving my phone, the estimated position goes back to where I started the motion, and swiftly moving my phone causes it to become very inaccurate very fast. I am aware that dead reckoning is not exactly the most precise method, but I also don't exactly need it to be perfect.

private func startRandomizerTask() {
        guard motionManager.isDeviceMotionAvailable else { return }
        motionManager.deviceMotionUpdateInterval = 1.0 / 200.0
        motionManager.startDeviceMotionUpdates(using: .xArbitraryZVertical)
        
        lastMotionTimestamp = nil
        velocity = .init(0,0,0)
        position = .init(-3.00,  -9.25, 0)
        accelBias = .init(0,0,0)
        accelSmoothed = .init(0,0,0)
        
    
        let smallVelocityThreshold: Double = 0.01
        
        Task.detached { [weak self] in
            guard let self = self else { return }
            while !Task.isCancelled {
                await MainActor.run {
                    guard self.settings.values["useOnboardPositioning"] == true,
                          let dm = self.motionManager.deviceMotion else { return }
                    
                    let now = dm.timestamp
                    if let last = self.lastMotionTimestamp {
                        let dt = now - last
                        if dt <= 0 { self.lastMotionTimestamp = now; return }
                        
                        // device acceleration
                        var userAccDevice = SIMD3(dm.userAcceleration.x, dm.userAcceleration.z, dm.userAcceleration.y) * 9.81
                        
                        // rotate to world
                        var accWorldRaw = self.rotateToWorld(userAccDevice, attitude: dm.attitude)
                        
                        accWorldRaw = SIMD3(
                            max(min(accWorldRaw.x, self.accelClampThreshold), -self.accelClampThreshold),
                            max(min(accWorldRaw.y, self.accelClampThreshold), -self.accelClampThreshold),
                            max(min(accWorldRaw.z, self.accelClampThreshold), -self.accelClampThreshold)
                        )
                        self.velocity += accWorldRaw * dt
                    
                        let speed = sqrt(self.velocity.x*self.velocity.x + self.velocity.y*self.velocity.y + self.velocity.z*self.velocity.z)
                        if speed > 0 {
                            let dragFactor = self.dragCoefficient * speed
                            self.velocity -= self.velocity * dragFactor * dt
                        }
                        
                        self.velocity *= self.velocityDamping

                        // clamp velocity
                        self.velocity = SIMD3(
                            max(min(self.velocity.x, self.maxVelocity), -self.maxVelocity),
                            max(min(self.velocity.y, self.maxVelocity), -self.maxVelocity),
                            max(min(self.velocity.z, self.maxVelocity), -self.maxVelocity)
                        )

                        // try and get rid of small numbers
                        if abs(self.velocity.x) < smallVelocityThreshold { self.velocity.x = 0 }
                        if abs(self.velocity.y) < smallVelocityThreshold { self.velocity.y = 0 }
                        if abs(self.velocity.z) < smallVelocityThreshold { self.velocity.z = 0 }

                        
                        let newPosition = self.position + (self.velocity * dt * 20)
                        
                        // max distance
                        let vecFromElbow = newPosition - (self.Sensors["Left"]?["Elbow"])!
                        let dist = sqrt(vecFromElbow.x*vecFromElbow.x + vecFromElbow.y*vecFromElbow.y + vecFromElbow.z*vecFromElbow.z)
                        if dist > self.armLength && dist > 0 {
                            let scale = self.armLength / dist
                            self.position = (self.Sensors["Left"]?["Elbow"])! + vecFromElbow * scale
                        } else {
                            self.position = newPosition
                        }

                        
                        // update sensor position
                        if var left = self.Sensors["Left"] {
                            left["Wrist"] = Point3D(self.position.x, self.position.y, self.position.z)
                            self.Sensors["Left"] = left
                        }
                        
                        //print("dt:\(dt) accMag:\(accMag) spike:\(spikeDetected) isStationary:\(isStationary) vel:(\(self.velocity.x),\(self.velocity.y),\(self.velocity.z)) pos:(\(self.position.x),\(self.position.y),\(self.position.z))")
                        print("\(round(self.velocity.x*10000)/10000),\(round(self.velocity.y*10000)/10000),\(round(self.velocity.z*10000)/10000) | \(accWorldRaw.y < 0 ? "+" : "-")")
                    }
                    self.lastMotionTimestamp = now
                }
                
                do {
                    try await Task.sleep(nanoseconds: 1_000_000)
                }
                catch {
                    break
                }
            }
        }
    }
swift math swiftui