I have two ways of getting the magnetic fields (strength, x, y, and z) using the iOS device's magnetometer.
1) Core Location
Used the CLHeading from CLLocationManagerDelegate
method locationManager:didUpdateHeading:
. This is similar to Apple's Teslameter sample app.
2) Core Motion
Used CMMagneticField
from CMMotionManager
's magnetometerData.magneticField
.
Questions:
a) What is the difference between the two? I am getting different values from both. I was expecting that they will return the same values.
The difference is most notable when I start the app from a resting position (face up in a table), and then lift the device up in to the air.
b) If there is a difference, when should I use the magnetic field from Core Location heading, and when should I use the magnetic field from Core Motion?
Note: I also am not sure if the "magnetic field" of Core Location and Core Motion refer to different magnetic field concepts.
Note: I computed strength as the square root of (x^2 + y^2 + z^2) for both approaches.
I think the magnetometerData.magneticField it telling you the acceleration, not the position (hence why you get large values moving from still to moving) whereas the location manager provides data on the direction the device is pointing in.
An important thing to remember when using the CLHeading.[x|y|z] values to calculate the local magnetic field strength is that the CLLocationManagerDelegate method
should be set to return YES. I have found that the magnetometer readings are never calibrated if this calibration warning is turned off, and that as a result the calculated field strength is very unstable when the orientation of the device is changed.
this answer is based on my interpretation of the documentation links below
http://developer.apple.com/library/ios/#documentation/CoreLocation/Reference/CLHeading_Class/Reference/Reference.html#//apple_ref/doc/c_ref/CLHeading
http://developer.apple.com/library/ios/#documentation/CoreMotion/Reference/CMMagnetometerData_Class/Reference/Reference.html#//apple_ref/doc/c_ref/CMMagnetometerData
a) CLHeading is "deviation from the magnetic field lines being tracked by the device" while magnetometerData.magneticField "is the total magnetic field observed by the device which is equal to the Earth’s geomagnetic field plus bias introduced from the device itself and its surroundings".
So CLHeading gives you filtered values with the Frame of Reference being the existing magnetic field of the earth. While magnetometerData gives you unfiltered values with the Frame of reference being the device.
b) if you were doing anything with location where you wanted to know where is magnetic or true north I'd recommend using CLHeading. If you wanted to create an app that responds to magnetic fields in the devices immediate vicinity or you had some specific sensor fusion you wanted to perform maybe try create an AHRS for instance then go with CMMagneticField.
To unravel this I've spent a bit too much time digging through the Apple docs.
There are three ways of obtaining magnetometer data
1/ Core Motion framework
CMMotionManagers's
CMMagnetometer
class2/ Core Motion framework
CMDeviceMotion
CMCalibratedMagneticField
property3 / Core Location framework
CLLocationManager's
CLHeading
1/ supplies 'raw' data from the magnetometer.
2/ and 3/ return 'derived' data. The numbers in both cases are similar (though not exactly the same).
Difference between Core Motion's CMMagnetometer and CMCalibratedMagneticField
1/ and 2/ - both from the Core Motion framework - differ as follows:
CMDeviceMotion Class Reference
CMMagnetometer gives us raw data, CMCalibratedMagneticField is adjusted data.
Difference between Core Motion's CMCalibratedMagneticField and Core Location's CLHeading
The docs are not immediately clear on the difference between 2/ and 3/, but they do generate different numbers so let's do some digging….
Core Location framework
CLHeading
From Location Awareness Programming Guide
Here are the relevant
CLHeading
'raw' propertiesI am not clear how a microtesla measurement can be 'normalized' (compressed? clipped?) to a range of +/-128 and still represent the unit it claims to measure. Perhaps that's why the sentence was removed from the docs. The units on an iPad mini do seem to conform to this kind of range, but the iPhone4S gives CMMagnetometer readings in higher ranges, eg 200-500.
The API clearly expects you to use the derived properties:
which give stable N/S E/W compass readings in degrees (0 = North, 180 = South etc). For the true heading, other Core Location services are required (geolocation) to obtain the deviation of magnetic from true north.
Here is a snippet from the
CLHeading
header fileCore Motion framework
CMDeviceMotion CMCalibratedMagneticField
CMMagnetometer
CMMagneticField
This is the struct that holds the vector.
It's the same for
CMDeviceMotion
's calibrated magnetic field andCMMagnetometer
's uncalibrated version:The difference between 2/ and 3/ are hinted at here:
Core Location CLHeading
Core Motion CMCalibratedMagneticField
So - according to the docs - we have:
1/ CMMagnetometer
Raw readings from the magnetometer
2/ CMDeviceMotion (CMCalibratedMagneticField*) magneticField
Magnetometer readings corrected for device bias (onboard magnetic fields)
3/ CLHeading [x|y|z]
Magnetometer readings corrected for device bias and filtered to eliminate local external magnetic fields (as detected by device movement - if the field moves with the device, ignore it; otherwise measure it)
Testing the theory
I have put a Magnet-O-Meter demo app on gitHub which displays some of these differences. It's quite revealing to wave a magnet around your device when the app is running and watching how the various APIs react:
CMMagnetometer doesn't react much to anything unless you pull a rare earth magnet up close. The onboard magnetic fields seem far more significant than local external fields or the earth's magnetic field. On my iPhone 4S it consistently points to the bottom left of the device; on the iPad mini it points usually to the top right.
CLHeading.[x|y|z] is the most vulnerable (responsive) to local external fields, whether moving or static relative to the device.
(CMDevice)CMCalibratedMagneticField is the most steady in the face of varying external fields, but otherwise tracks it's Core Location counterpart CLHeading.[x|y|z] pretty closely.
CLHeading.magneticHeading - Apple's recommendation for magnetic compass reading - is far more stable than any of these. It is using data from the other sensors to stabilise the magnetometer data. But you don't get a raw breakdown of x,y,z
yellow CMMagnetometer
green CLHeading.[x|y|z]
blue CMCalibratedMagneticField
red CLHeading.magneticHeading
This does seem to contradict the docs, which suggest that CLHeading.[x|y|z] should be less influenced by local external fields than CMCalibratedMagneticField.
What approach should you take? Based on my limited testing, I would suggest…
If you want a compass reading
CLHeading's
magneticHeading
andtrueHeading
will give you the most accurate and most stable compass reading.If you need to avoid Core Location
CMDeviceMotion's
CMCalibratedMagneticField
seems to be the next most desirable, although considerably less stable and accurate thanmagneticHeading
.If you are interested in local magnetic fields
CLHeading's 'raw' x y and z properties seem to be more sensitive to local magnetic fields.
If you want all of the data including onboard magnetic fields
Raw magnetometer data from CMMagnetometer. There is really not much point using this unless you are prepared to do tons of filtering, as it is hugely influenced by magnetic fields generated on the device itself.