CoreLocation compass heading is wrong

We receive a complaint from a user that the compass heading in our paragliding app differs from the heading in Compass app (preinstalled on iOS). During our research, it was found that third-party apps show the wrong compass heading.

We get the compass heading according to the documentation (https://developer.apple.com/documentation/corelocation/getting-heading-and-course-information#Get-the-current-heading):

func locationManager(_ manager: CLLocationManager, didUpdateHeading heading: CLHeading) {
    magneticHeading = heading.magneticHeading
    trueHeading = heading.trueHeading
}

The video linked below shows our app and the third-party app getting the compass heading of 270 degrees, and Compass app (preinstalled) getting the compass heading of 30 degrees. https://drive.google.com/file/d/1HPMRWWq1E_bFYZVyCeqB2Fo-AfG4q9J7/view?usp=share_link

This problem appears to the user unpredictably and the correct compass heading is shown by Compass app (preinstalled). He has iPhone 15 Pro Max and iOS 17.4.1.

The presence of this problem is very critical as it can cause fatal accidents.

Answered by Engineer in 790794022

A couple things here. First of all, you are showing here is that you are grabbing the heading values from the CLHeading object. But:

  • which value (magnetic or true) are you showing as incorrect in your video?
  • which value (magnetic or true north) is the Compass app set to use? If you are comparing the two, make sure they are displaying the same thing
  • what is the .headingOrientation set to for CLLocationManager before you call startUpdatingHeading()? The device orientation needs to be set correctly for accurate results
  • have you been checking the .headingAccuracy property of the CLHeading object to determine if the reported heading is considered accurate or some error is expected. Heading accuracy will depend on factors like GPS location accuracy, the speed of movement, and so on. You may want to correct the heading you are displaying based on this value as well
  • furthermore what have you set the CLLocationManager's .activityType property? For your use case you would want to set it to .airborne so the system does not try to make corrections not appropriate to the motion patterns which might occur in the air

Alternatively, you can look into the CMDeviceMotion class, and obtain heading and magnetic field information using the startDeviceMotionUpdates() call, and see if the results will be more suitable for your use case.

It is important to understand that there are a lot of factors into determining the heading, and a number of things to set and check to get results suitable for each use case. Just using the .heading value from a heading update may not get you the results you expect, and in a complicated use case like paragliding, where you may or may not have any information of the device orientation and other factors, some additional engineering might be required on the app side.

A couple things here. First of all, you are showing here is that you are grabbing the heading values from the CLHeading object. But:

  • which value (magnetic or true) are you showing as incorrect in your video?
  • which value (magnetic or true north) is the Compass app set to use? If you are comparing the two, make sure they are displaying the same thing
  • what is the .headingOrientation set to for CLLocationManager before you call startUpdatingHeading()? The device orientation needs to be set correctly for accurate results
  • have you been checking the .headingAccuracy property of the CLHeading object to determine if the reported heading is considered accurate or some error is expected. Heading accuracy will depend on factors like GPS location accuracy, the speed of movement, and so on. You may want to correct the heading you are displaying based on this value as well
  • furthermore what have you set the CLLocationManager's .activityType property? For your use case you would want to set it to .airborne so the system does not try to make corrections not appropriate to the motion patterns which might occur in the air

Alternatively, you can look into the CMDeviceMotion class, and obtain heading and magnetic field information using the startDeviceMotionUpdates() call, and see if the results will be more suitable for your use case.

It is important to understand that there are a lot of factors into determining the heading, and a number of things to set and check to get results suitable for each use case. Just using the .heading value from a heading update may not get you the results you expect, and in a complicated use case like paragliding, where you may or may not have any information of the device orientation and other factors, some additional engineering might be required on the app side.

Thank you for your quick reply!

They are both incorrect.

which value (magnetic or true) are you showing as incorrect in your video?

I don't know what value is set to use in the Compass app. But in our app, two values ​​​​are displayed on the compass: "M:" - magnetic and "T:" - true north. In the video you can see a difference of about 120 degrees between our app (as well as third-party) and the Compass app.

which value (magnetic or true north) is the Compass app set to use? If you are comparing the two, make sure they are displaying the same thing

We use the default value, that is, according to the documentation (https://developer.apple.com/documentation/corelocation/cllocationmanager/headingorientation) this is .portrait, which corresponds to our expectations.

what is the .headingOrientation set to for CLLocationManager before you call startUpdatingHeading()? The device orientation needs to be set correctly for accurate results

We do not correct the heading value based on .headingAccuracy property.

have you been checking the .headingAccuracy property of the CLHeading object to determine if the reported heading is considered accurate or some error is expected. Heading accuracy will depend on factors like GPS location accuracy, the speed of movement, and so on. You may want to correct the heading you are displaying based on this value as well

It was the default value - .other.

furthermore what have you set the CLLocationManager's .activityType property? For your use case you would want to set it to .airborne so the system does not try to make corrections not appropriate to the motion patterns which might occur in the air

.headingAccuracy property of the CLHeading class (https://developer.apple.com/documentation/corelocation/clheading/headingaccuracy) is the maximum deviation (measured in degrees) between the reported heading and the true geomagnetic heading.

  • With the help of the CMDeviceMotion class we get this true geomagnetic heading or not?
  • Is there a way to just get the same data as the Compass app?
  • And also, can we somehow make sure that the problem is on the side of our app (and other third-party ones as well), and not in an iOS / device bug?

Alternatively, you can look into the CMDeviceMotion class, and obtain heading and magnetic field information using the startDeviceMotionUpdates() call, and see if the results will be more suitable for your use case.

There is no way to be sure if the issue is with your app or an iOS device bug.

All I can do is point out the necessary elements you need to add to your code in order to get better results, based on the few lines of code you have shown me. I pointed out some missing settings (which you confirm) and made recommendations.

With the help of the CMDeviceMotion class we get this true geomagnetic heading or not?

I also explained you need to look at the accuracy values and make necessary calculations. There is no guarantee that a single value from the CL or CM frameworks will give the results you want without adding the necessary calculations yourself for your use case.

I you believe this is a system bug that needs to be fixed, please file a bug report using http://feedbackassistant.apple.com

.activityType property? It was the default value - .other.

You should definitely fix that - otherwise, you may find that locations get snapped to roads or otherwise messed with!

If your app supports orientations other than portrait you definitely need to use .headingOrientation.

Regarding true vs. magnetic, my recommendation is to get the magnetic data from iOS and do the conversion to true yourself (e.g. using GeographicLib). We don't know how Apple is doing that conversion.

None of those things explain what your user is seeing, though. The important question is, which is right, the Compass app or your app?

If you'd like to share the identities of either your app or the 3rd app, I'll compare them with mine.

CoreLocation compass heading is wrong
 
 
Q