Showing the WP7 Magnetometer in 3D with XNA/Silverlight

In my last post I came up with a simple compass application and how to correct for sensor misalignments. In that application I had a blue arrow that always points up and a yellow arrow that always points North. Now I wanted to add another arrow showing the raw magnetometer readings. For the simple compass application, we just needed to use the Motion Sensor API. To get access to the raw magnetometer readings, we need to use the Compass API to get to the MagnetometerReading property.

To set up the compass I used the Compass Sensor example as reference. In the documentation, it says that the MagnetometerReading property (which is a Vector3) returns the magnetometer reading in microteslas. However, I found that it actually returns a normalized vector, so we really only know the direction of the magnetic field and not the strength. It would be nice to know the strength, but for now we’ll just have to do with the direction. UPDATE: Please see this blog for an update.

The normalized magnetometer values are aligned with the phone with the X-axis pointing to the right, the Y-axis pointing out the top, and the Z-axis pointing out the face of the phone.

Building upon my simple compass application, I added a red arrow to show the direction of the magnetic field. In my application, by default, the arrows orient to the Y-axis. To get our new red arrow to line up with the magnetometer vector, we need to do some Matrix multiplication to get a rotational matrix.

In the following diagram, the dashed line is the magnetometer vector with the value x,y,z. Angle xrot is the angle of the vector off of the XY plane.

The first step is to rotate the arrow about the X-axis to get the correct pitch. This is easily done with the following code

 

var xrot = Math.Atan2(rawMagnetometerReading.Z, rawMagnetometerReading.Y);
Matrix redMatrix = Matrix.CreateRotationX((float)xrot);

 

We use the Atan2 function and pass in the values for the Z and Y to get the angle (xrot) of the vector relative to the Y-axis. Go here if you need a refresher on why we use the Atan2 function. We then create a rotational axis from that angle. Now we only need to make one more axis rotation to get the arrow to line up with the magnetometer vector.

This one requires a bit more math. In this diagram (looking down at the face of the phone with the top of the phone pointing to the right), the angle zrot is between the YZ plane and the magnetometer vector.

Because we’ve already rotated the arrow about the X-axis, we need to find the magnitude of the magnetometer vector in the YZ plane. We can then use that magnitude in combination with the x value of the magnetometer vector to calculate the value of the zrot angle. Now we can do our next rotation. The final code snippet is as follows.

 

var xrot = Math.Atan2(rawMagnetometerReading.Z, rawMagnetometerReading.Y);
var yzmag = Math.Sqrt(Math.Pow(rawMagnetometerReading.Y, 2) + 
     Math.Pow(rawMagnetometerReading.Z, 2));
var zrot = Math.Atan2(rawMagnetometerReading.X, yzmag);
 
Matrix redMatrix = Matrix.CreateRotationX((float)xrot);
redMatrix = Matrix.CreateRotationZ((float)-zrot) * redMatrix;
redArrow.World = redMatrix;


We first calculate the rotation about the X-axis (xrot), then the magnitude of the magnetometer vector in the YZ plane (yzmag), and finally the angle between the YZ plane and the magnetometer vector (zrot). With the xrot and zrot angles we can now do our matrix rotations and apply the resulting rotational matrix to our red arrow.

The result is the following.

 

In this new application, the blue arrow always points up, the yellow arrow always points North, and the red arrow lines up with the earth’s magnetic field (assuming you’re not around any other strong magnetic fields). In the screen shot above I tried to get the yellow and red arrows lined up in the plane of the phone’s screen to show the magnetic inclination at my location. This value should be around 65 degrees down from the horizontal for the Texas panhandle, which looks to be spot on.

One other interesting thing to notice is that the yellow arrow, which indicates true north, is slightly off from the red arrow, which indicates the magnetic field. In this screen shot, which I took when the phone was flat and pointing north, you can see that the red arrow is pointing slightly to the right from the yellow arrow.

This offset is called the magnetic declination. This value should be around 6 degrees in the Texas panhandle, which again looks to be spot on.

What about making the same alignment correction as I did in my previous blog? It’s not really necessary in this case because the magnetometer sensor is a different sensor from the accelerometer used in the Motion API to determine what direction is up. Unless you have a highly specialized area where you absolutely know the strength and direction of the magnetic field, you can’t really make any magnetometer alignment corrections. In any case, with the errors we’re talking about, it’s not worth the effort.

The final product is very good at following the magnetic fields, as you can see in this video. In the last part of the video, you can see how the field changes drastically when rotating a magnet near the phone.

WP7 Magnetometer Application
comments powered by Disqus