In two previous posts we discussed how to calibrate magnetometer data in order to get accurate measurements, as well as how to acquire data in the first place. In this post we will go through the last major step, how to convert the data from units of Gauss into a true compass heading using the Pmod CMPS2 as an example. You can use an FPGA board like the Basys 3, Nexys A7, or Cora Z7 to host the Pmod.
Data Conversion
The Memsic MMC34160PJ magnetometer on the Pmod CMPS2 provides data for each axis in units of Gauss, but the information is generally more legible when presented as a compass heading. The method for converting Gauss units to a compass heading is provided below.
-
Calculate the real Gauss value for the X and Y axes from the amount of LSBs returned where the LSB value by default is 0.48828125 mG, resulting in 2048 LSBs per Gauss.xGaussData = xDataLSB∗0.48828125 mG
yGaussData = yDataLSB∗0.48828125 mG -
Calculate the direction D by first checking to see if the X Gauss data is equal to 0 to prevent divide by 0 zero errors in the future calculations. If the X Gauss data is 0, check to see if the Y Gauss data is less than 0. If Y is less than 0 Gauss, the direction D is 90 degrees; if Y is greater than or equal to 0 Gauss, the direction D is 0 degrees.
-
If the X Gauss data is not zero, calculate the arctangent of the Y Gauss and X Gauss data and convert from polar coordinates to degrees.D = arctan(yGaussData/xGaussData)∗(180/π)
-
If the direction D is greater than 360 degrees, subtract 360 degrees from that value.
-
If the direction D is less than 0 degrees, add 360 degrees to that value.
-
The compass heading can then be determined by the direction value D:
-
If D is greater than 337.25 degrees or less than 22.5 degrees – North
-
If D is between 292.5 degrees and 337.25 degrees – North-West
-
If D is between 247.5 degrees and 292.5 degrees – West
-
If D is between 202.5 degrees and 247.5 degrees – South-West
-
If D is between 157.5 degrees and 202.5 degrees – South
-
If D is between 112.5 degrees and 157.5 degrees – South-East
-
If D is between 67.5 degrees and 112.5 degrees – East
-
If D is between 0 degrees and 67.5 degrees – North-East
-
If you’d like to see how to acquire date initially or how to calibrate your magnetometer, you can visit the previous posts on theses topics, or go to the Pmod CMPS2 reference manual.
If you’d like to see some example code written in the Arduino IDE for how we acquire, calibrate and convert data from the Pmod CMPS2, see the Pmod CMPS2 Resource Center.
For questions or comments, please post in the comment section below or visit the Digilent Forum!
I used your code and it work at a certain point but there is a lack of data between 90 and 270. It switches from those two if the D data tries to goes in between those two numbers. Maybe you have an explanation to my problem!
Hi Philippe,
I would recommend posting your question on the Digilent Forum (link at the top of this page since it doesn’t look like I can provide a url link) where one of the Digilent engineers will be able to see and respond to your question in an environment much better suited to troubleshooting.
Thanks,
James Colvin
I think the problem is you are using the arctan function instead of the arctan2 function
Thanks!!!
this kindof works with a 9250 mpu but I have to hold the sensor upside down or I rarely get any eastern movement.. :}
//GET DEG HEADING
float heading = atan2(my, mx) * 180 / M_PI;
if(heading >360)
heading = heading -360;
else if(heading = 337.25 || heading = 292.5 && heading = 247.5 && heading = 202.5 && heading = 157.5 && heading = 112.5 && heading = 67.5 && heading = 22.5 && heading < 67.5 )
Mydirection = "North-East";
Serial.print(",HEADING:");
Serial.println(heading, 5);
Serial.print(",DIR:");
Serial.println(Mydirection);
if (xGaussData = 0); {
if (yGaussData 360)
d = d – 360;
else if (d < 0)
d = d + 360;
if (337.25 <= d < 22.5)
printf("North");
else if (292.5 <= d < 337.25)
printf("North-West");
else if (247.5 <= d < 292.5)
printf("West");
else if (202.5 <= d < 247.5)
printf("South-West");
else if (157.5 <= d < 202.5)
printf("South");
else if (112.5 <= d < 157.5)
printf("South-East");
else if (67.5 <= d < 112.5)
printf("East");
else if (0 <= d < 67.5)
printf("North-East");
Hey there,
you have a bug in your pseudo code:
If D is greater than 337.25 degrees or less than 22.5 degrees – North
If D is between 0 degrees and 67.5 degrees – North-East
Assuming D = anything between 0 and 22.5 then both conditions are true.
X/Y is a ratio, the units are irrelevant, so conversion to milliGauss is unnecessary – important in microcontrollers lacking an FPU or with an FPU but using an RTOS that may not preserve FPU registers.
Does resolving for X and Y only assume the compass is horizontal?
The expression D = arctan(yGaussData/xGaussData)∗(180/π) is ratio-metric, there is no useful reason for converting x and y to Gauss, ratios are unitless.
xDataLSB/yDataLSB == yGaussData/xGaussData