CMMotionManager level meter inaccurate

I want to add a "bubble horizon" to a camera application to show if the user is keeping their phone level.

For this, I'm using the Motion Attitude functionality of CMMotionManager.

However, the output I'm getting is very inaccurate. I'm comparing it with Apple's own Measure app which is dead accurate, so the sensors are working fine. My own readings seem to be several degrees off.

Am I missing some calibration step or something?

- (void)processDeviceMotion:(CMDeviceMotion *)motion {

    // use quaternions to avoid Gimbal Lock
    CMQuaternion quat = motion.attitude.quaternion;

    // calculate roll in degrees
    double roll = atan2( 2 * ( quat.w * quat.x + quat.y * quat.z ), 1 -  2 * ( quat.x * quat.x + quat.y * quat.y ) );
    roll = radiansToDegrees( roll );

    NSLog( @"Roll: %f", roll );
}
CMMotionManager level meter inaccurate
 
 
Q