In addition to the accelerometer, the iPhone 3GS has a magnetometer that acts as a digital
compass. Combining the heading (yaw) information (see Figure 1) returned by this
device with the roll and pitch information returned by the accelerometer
will let you determine the true orientation of the iPhone in real
time.
You should be aware that the magnetometer is measuring the strength
of the magnetic field surrounding the device. In the absence of any strong
local fields, these measurements will be of Earth’s ambient magnetic
field, allowing the device to determine its “heading” with respect to the
geomagnetic North Pole. The geomagnetic heading and true heading, relative
to the geographical North Pole, can vary widely (by several tens of
degrees depending on your location).
As well as reporting the current location, the CLLocationManager
class can, in the case where the device’s hardware supports
it, report the current heading of the device. The following code will
create an instance of the class, and will send both location and heading
update messages to the designated delegate class:
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
if( locationManager.locationServicesEnabled &&
locationManager.headingAvailable)
{
[locationManager startUpdatingLocation];
[locationManager startUpdatingHeading];
} else {
NSLog(@"Can't report heading");
}
We can filter these update messages based on an angular filter.
Changes in heading of less than this amount will not generate an update
message to the delegate:
locationManager.headingFilter = 5; // 5 degrees
The default value of this property is kCLHeadingFilterNone. Use
this value if you want to be notified of all heading updates.
The CLLocationManagerDelegate
protocol offers a method that is called when the heading is
updated:
- (void)locationManager:(CLLocationManager*)manager
didUpdateHeading:(CLHeading*)newHeading
{
// If the accuracy is valid, process the event.
if (newHeading.headingAccuracy > 0)
{
CLLocationDirection theHeading = newHeading.magneticHeading;
// Do something with the event data.
}
}
If location updates are also enabled, the location manager returns
both true heading and magnetic heading values. If location updates are not
enabled, the location manager returns only the magnetic heading
value:
CLLocationDirection trueHeading = newHeading.trueHeading;
As I mentioned previously, the magnetometer readings will be
affected by local magnetic fields, so the CLLocationManager will attempt to calibrate its
heading readings by displaying a heading calibration panel before it
starts to issue update messages. However, before it does so, it will call
the locationManagerShouldDisplayHeadingCalibration: delegate method:
- (BOOL)locationManagerShouldDisplayHeadingCalibration:
(CLLocationManager *)manager {
... code not shown ...
}
If you return YES from this
method, the CLLocationManager will
proceed to display the device calibration panel on top of the current
window. The calibration panel prompts the user to move the device in a
figure-eight pattern so that Core Location can distinguish between Earth’s
magnetic field and any local magnetic fields. The panel will remain
visible until calibration is complete or until you dismiss it by calling
the dismissHeadingCalibrationDisplay: method in
the CLLocationManager class.