Reputation: 1412
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)")
}
}
Upvotes: 9
Views: 2735
Reputation: 51
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)
}
}
Upvotes: 1
Reputation: 1412
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.
Upvotes: 13