CMDeviceMotion returns 0 values for magnetic field

2.5k views Asked by At

I am developing iOS app with compass functionality. I have tried to use CMMagnetometerData updates which give uncalibrated, but normal results.

After that I tried to get CMDeviceMotion updates which turned out to give always zero magnetic field with CMMagneticFieldCalibrationAccuracyUncalibrated accuracy. The only device I have is an iPad, so can't test on others.

May be field is zero because sensor is not calibrated, but I could not find any way to perform calibration.

How to fix that?

UPDATE:

Here is suggested to use startDeviceMotionUpdatesUsingReferenceFrame:toQueue:withHandler:, however it didn't work for me.

Here is suggested to set showsDeviceMovementDisplay to true. However it didn't work either, calibration windows is just not popping up.

Finally, SOLVED. According to my observations:

1) Use startDeviceMotionUpdatesUsingReferenceFrame:toQueue:withHandler: with referenceFrame NOT equal to allZeros or XArbitraryZVertical.

2) Set showsDeviceMovementDisplay to true.

After few zero-value updates with accuracy CMMagneticFieldCalibrationAccuracyUncalibrated it will normalise.

CODE:

...
motionManager.deviceMotionUpdateInterval = 0.05
motionManager.showsDeviceMovementDisplay = true 
motionManager.startDeviceMotionUpdatesUsingReferenceFrame(CMAttitudeReferenceFrame.XArbitraryCorrectedZVertical, toQueue: NSOperationQueue.mainQueue(), withHandler:handleUpdate)
...
private func handleUpdate(data: CMDeviceMotion!, error: NSError!) {
    if data != nil {
        let field = data.magneticField.field
        println("\(field.x), \(field.y), \(field.z)")
    }
}
2

There are 2 answers

1
Tzoiker On BEST ANSWER

Finally, according to my own observations:

1) Use startDeviceMotionUpdatesUsingReferenceFrame:toQueue:withHandler:with referenceFrame NOT equal to allZeros or XArbitraryZVertical.

2) Set showsDeviceMovementDisplay to true.

After few zero-value updates with accuracy CMMagneticFieldCalibrationAccuracyUncalibrated it will normalise.

1
Felix Nie On

Just to update this solution to fit Swift 5.6.1.

The following code also shows how to read other motion data altogether.

data!.magneticField.accuracy.rawValue will return something else rather than -1.

CODE:

// MARK: gyroscope acceleration magnitude orientation gravity all-at-once
motionManager.deviceMotionUpdateInterval = 0.01
// for calibrated magnetic field
motionManager.showsDeviceMovementDisplay = true
motionManager.startDeviceMotionUpdates(using: CMAttitudeReferenceFrame.xArbitraryCorrectedZVertical, to: OperationQueue.main) { (data, error) in
    // handle device motion updates
    if isPlaying {
        if timeT.isEmpty {
            startTime = data!.timestamp
        }
        timeT.append(data!.timestamp - startTime)
        // get gyroscope sensor data
        gyroX.append(data!.rotationRate.x)
        gyroY.append(data!.rotationRate.y)
        gyroZ.append(data!.rotationRate.z)
        // get accelerometer sensor data
        accX.append(data!.userAcceleration.x)
        accY.append(data!.userAcceleration.y)
        accZ.append(data!.userAcceleration.z)
        // get magnetometer sensor data
        // data!.magneticField.accuracy
        magX.append(data!.magneticField.field.x)
        magY.append(data!.magneticField.field.y)
        magZ.append(data!.magneticField.field.z)
        // get attitude orientation
        oriPitch.append(data!.attitude.pitch)
        oriYaw.append(data!.attitude.yaw)
        oriRoll.append(data!.attitude.roll)
        // get gravity vector
        // motion.gravity.x
        // motion.gravity.y
        // motion.gravity.z
        
        // debug
        print(data!.timestamp - startTime)
        print(data!.rotationRate)
        print(data!.userAcceleration)
        print(data!.magneticField.accuracy.rawValue)
        print(data!.magneticField.field)
        print(data!.attitude)
    }
}