Getting to the Point

The SpherAware GeoGraphics Library for Android is a package of GIS components for use by other developers.  It includes a nice compass control which provides a steady true north reading.  In developing it, I followed the same basic approach offered in several online tutorials.  Those made the task seem fairly easy, but I encountered several interesting and unexpected conundrums. 

In the beginning, I assumed that Android's magnetometer would provide all of the necessary input.  It does detect the presence of Earth's magnetic field but that alone isn't enough to build a useful display.  You also need to know how the device is oriented with respect to gravity.  Is it right side up or upside down?  Is it tilted forward or to the side and at what angle?  And, of course, the device itself will reorient the display from portrait to landscape or back depending on which way it thinks is up.

To deal with that, you also need to consult the device's accelerometer.  Later versions of Android include high-level sensor fusion capabilities that help with some of this but I wanted to support older devices, too, if at all possible.  I relied on the work of others to get started and soon arrived at a working version that usually did what it was supposed to do: point to magnetic north.  I say "usually" because an accelerometer is quite sensitive to vibration, shocks and changes in temperature.  That happens a lot to mobile devices so they should be recalibrated before using any compass app by rotating them several times in all directions.  This re-calibrates the accelerometer by forcing it to calculate through its full range of motion on each axis.

Another complication that I didn't anticipate is the fact that the magnetometer responds to all magnetics fields, not just the Earth's.  Electronic equipment generates detectable fields and there's quite a bit of it around my desk.  Many aspects of android development can be done with an emulator (a software surrogate), but debugging this stuff really requires a physical device.  Sure enough, with a cell phone on a short data cable laying next to the keyboard, my new compass app dutifully pointed to my computer!

After sorting that out by logging the critical variables in a compiled version that I could run while well away from my attractive equipment rack, I moved on to the next challenge: true north.  Magnetic north is the direction to the north magnetic pole and can differ from true north by a significant amount.  This varies from about -18º to +18º across the continental US.  To compensate, you need to add this value to the magnetic bearing.  Fortunately, the Android GPS sensor provides the magnetic declination with each coordinate position.  I built that capability into the control by always saving the most recent declination value.  Once the magnetometer has been re-calibrated and the GPS has a good fix, the compass is right on.

The final dilemma was flutter.  Even under ideal conditions, the magnetometer output constantly varies by a few radians.  It's not clear to me whether this is due to limited hardware precision, magnetic background noise or simply the fact that the sensor chatters away with a new output value every few milliseconds.  Whatever the reason, it's fairly annoying.  To smooth this out, I wrote an algorithm that returns a running average of the 10 most recent azimuth values with a half-degree tolerance.  Of course that gets tricky, too, because the average of 0.1º and 355.9º is exactly wrong.  So, to keep it simple, dramatic output swings are ignored until they are confirmed by subsequent inputs. 

This experience confirmed one of my favorite software development axioms: what seems difficult is often surprisingly easy — and what seems easy is often surprisingly difficult.  Perhaps difficult is overstating it a bit since this project was really kind of fun.  The listed resource should help anyone who wants more technical detail.  They all provide good example code and helpful comments.


Posted in Android, Techniques on Mar 13, 2017