Question

I'm trying to get the device's declination from the magnetic North in degrees, by relying solely on the device's magnetometer. This is the code I've written but I just get 0 degrees.. What am I doing wrong ?

CMMotionManager *motionManager;

motionManager = [[CMMotionManager alloc] init];

[motionManager startDeviceMotionUpdates];

CMDeviceMotion *deviceMotion;

deviceMotion = [[CMDeviceMotion alloc] init];

while(!self.stopButtonPressed)
{
    double x = motionManager.deviceMotion.magneticField.field.x;

    double y = motionManager.deviceMotion.magneticField.field.y;

    double degrees = asin(y/sqrt(pow(x, 2.0) + pow(y, 2.0))) * 180.0 / M_PI ;

    int degreesRounded = (int)degrees;

    NSLog(@"Degrees : %i", degreesRounded);
}
Was it helpful?

Solution

It is likely that the simulator doesn't return normal values for these methods, so you will need to test on a real device.

The CLLocationManager's method, didUpdateHeading: doesn't work on the simulator, so you are probably experiencing something similar here.

Edit:

From the docs:

"The latest sample of device-motion data. (read-only)

@property(readonly) CMDeviceMotion *deviceMotion

Discussion If no device-motion data is available, the value of this property is nil. An application that is receiving device-motion data after calling startDeviceMotionUpdates periodically checks the value of this property and processes the device-motion data."

Check to see if that property of your motion manager is nil. If it is, then you would get 0 for the magnetic field property.

Edit 2:

Instead of using startDeviceMotionUpdates, you should be using startMagnetometerUpdatesToQueue:. The docs say this:

"Magnetometer. Set the magnetometerUpdateInterval property to specify an update interval. Call the startMagnetometerUpdatesToQueue:withHandler: method, passing a block of type CMMagnetometerHandler. Magnetic-field data is passed into the block as CMMagnetometerData objects."

Docs are here.

OTHER TIPS

What thread is the above code running on? If you're running it on the main thread, you probably won't get see updates to the device motion data over time. Use an NSTimer or similar mechanism to sample the motion over time, so the main thread is free to do other things (like service requests from the core motion sub-system).

Here is what I tested on a real device:

   CMMotionManager *myMotionManager= [[CMMotionManager alloc] init];
    myMotionManager.deviceMotionUpdateInterval = 1;
    [myMotionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXMagneticNorthZVertical  toQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
        double x = myMotionManager.deviceMotion.magneticField.field.x;
        double y = myMotionManager.deviceMotion.magneticField.field.y;
        double z = myMotionManager.deviceMotion.magneticField.field.z;
        NSLog(@"Field.x= %f; Field.y = %f; Field.z= %f",x,y,z);
    }];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top