Monday, 11 July 2011

How to use the magnetic and orientation sensors to build the compass

If you have read the previous posts, you will recall I want to display directions and distance from my current location to a place of interest.

I will need the following sensor information:

  • an idea of where I am, ie my current location in latitude and longitude as given by either my network location or, better, by the embarked GPS
  • the orientation of my phone with regards to the true North. Your phone will give you the Magnetic North direction but you can easily derive the true North by adding the Declination, see for instance this Wikipedia article.
To the two sensor information above I will add a third one:
  • the magnetic field value. This will help me find out if the orientation is not biased by some magnetic object around.
To collect up to date information from these three sensors, you will need to use the SensorManager class. You can register a listener to all supported sensors. Do not forget to unregister, sensor listening is very battery consuming.


listener = new SensorEventListener() {

         public void onSensorChanged(SensorEvent event) {
             float[] values = event.values;
             if (compassView != null) {
              compassView.setNorth(- values[0]);
              compassView.invalidate();
             }
         }

         public void onAccuracyChanged(Sensor sensor, int accuracy) {
         }
     };
     
    magneticFieldListener = new SensorEventListener() {

          public void onSensorChanged(SensorEvent event) {
            float[] values = event.values;
            if(values.length < 3) {
              return;
            }
            if(Math.sqrt(values[0] * values[0] + values[1] * values[1] + values[2] * values[2]) > SensorManager.MAGNETIC_FIELD_EARTH_MAX) {
              if(alertDisplayed == false) {
               alert(Compastic.this, "Abnormal magnetic field! check for magnets or iron objects around you and recalibrate the device by doing an '8 shape' pattern with your phone.");
               alertDisplayed = true;
              } else {
                if(toastDisplayed == false) {
                   toast(Compastic.this, "Abnormal magnetic field! ");
                   toastDisplayed = true;
                }
              }
            } else {
              // field back to normal
              toastDisplayed = false;
            }
         }

         public void onAccuracyChanged(Sensor sensor, int accuracy) {
         }
     };
     

Both listeners are fields so I can unregister them and register them again when the activity stops and resumes.

The orientation listener updates the compass view (more on that later) when the orientation has changed. The change event contains a 3 dimensional float vector. The only part we are interested in here is value[0] which represents the Magnetic North direction. We update the compass view drawing by changing the angle value and invalidating its content to force the refresh.

The Magnetic field listener is used to check the actual value of the field. The value in micro Tesla is calculated with the L2 norm. We know that in any case the field cannot be greater than the MAGNETIC_FIELD_EARTH_MAX. If it is then we display an alert dialog. For instance my phone comes with a case that holds a magnet, ie to close the cover. This Magnet is way stronger than the Earth field. If I move the magnet around the phone, I can see the arrow following the magnet.

For the location now, we use the LocationManager class. We decide to listen to both the GPS and the network location for location updates.

locationService.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 25, locationListener);
locationService.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000, 25, locationListener);
The location listener is basically handling the location changed event from the location service by updating the compass view with the new location.

We are missing one last parameter: the place of interest. If you remember the screenshots from the design post, there is a top bar that enables the user to select a place of interest. The top bar is made of a Spinner with its own adapter. I will come back onto that later on. Bottom line is you can choose the place of interest from that spinner. At init the place of interest is the first item in the spinner list. This place contains all necessary location information that we need to construct a Location object. The compass view is updated with this value at init and any time the place of interest changes.

To sum it up, the compass view itself has up to date information about the magnetic north angle and the current location of the phone as well as the place of interest. With all these parameters we can now display the compass graphics to the user.

The next post will brush the compass view display using the android Canvas and many other goodies.

No comments:

Post a Comment