No Result
View All Result
No Result
View All Result
No Result
View All Result

Pro tip: Create your own magnetic compass using Android’s internal sensors

king by king
December 28, 2022
in Android
0
Pro tip: Create your own magnetic compass using Android’s internal sensors

Ever wondered how a compass works? I never gave it a lot of thought until last week when my son and I were hiking in a national park, and I couldn’t get a GPS signal.

When I finally got back to civilization, I started combing the Android forums for sample digital compass code and found that most of the examples freely available used the now deprecated Sensor.TYPE_ORIENTATION. A little more digging and I discovered that Sensor.TYPE_ORIENTATION was actually never a real hardware sensor but a software service that composited the values of the accelerometer and the magnetic field sensor.

So pulling from multiple online sources including a Wikipedia article that compared radians to degrees, I managed to create a simple magnetic compass. If you are interested in turning your Android device into a compass, you can follow along with the tutorial below, or download and import the project directly into Eclipse.

1. Create a new Android project in Eclipse. Target SDK 14 (ICS) or better.

2. In your AndroidManifest.xml file, request permissions for fine and coarse locations. In this instance I am also locking the orientation to portrait mode.

AndroidManifest.xml

3. You’re going to need a pointer for your compass. I chose the image below and stuck in the /res/drawable-xhdpi folder.

4. The modified activity_main.xml file in the /res/layout folder is simply an image view centered in a relative layout.

activity_main.xml

5. We can code up our compass in the /src/MainActivity.java file. I’m not going to attempt to explain the math in the on sensor changed event because it is beyond the scope of this article and because my trigonometry is rusty and just getting it to work made my head hurt. I have, however, compared my compass with Google Maps, and it seems to consistently point north, which I consider a win. One important thing to keep in mind for the sake of preserving the device’s battery is the registering and unregistering of the sensors in the on resume and on pause, respectively.

MainActivity.java
package com.authorwjf.whichwayisup;

import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;

public class MainActivity extends Activity implements SensorEventListener {

private ImageView mPointer;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private Sensor mMagnetometer;
private float[] mLastAccelerometer = new float[3];
private float[] mLastMagnetometer = new float[3];
private boolean mLastAccelerometerSet = false;
private boolean mLastMagnetometerSet = false;
private float[] mR = new float[9];
private float[] mOrientation = new float[3];
private float mCurrentDegree = 0f;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
mPointer = (ImageView) findViewById(R.id.pointer);
}

protected void onResume() {
super.onResume();
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME);
}

protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this, mAccelerometer);
mSensorManager.unregisterListener(this, mMagnetometer);
}

@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor == mAccelerometer) {
System.arraycopy(event.values, 0, mLastAccelerometer, 0, event.values.length);
mLastAccelerometerSet = true;
} else if (event.sensor == mMagnetometer) {
System.arraycopy(event.values, 0, mLastMagnetometer, 0, event.values.length);
mLastMagnetometerSet = true;
}
if (mLastAccelerometerSet && mLastMagnetometerSet) {
SensorManager.getRotationMatrix(mR, null, mLastAccelerometer, mLastMagnetometer);
SensorManager.getOrientation(mR, mOrientation);
float azimuthInRadians = mOrientation[0];
float azimuthInDegress = (float)(Math.toDegrees(azimuthInRadians)+360)%360;
RotateAnimation ra = new RotateAnimation(
mCurrentDegree,
-azimuthInDegress,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF,
0.5f);

ra.setDuration(250);

ra.setFillAfter(true);

mPointer.startAnimation(ra);
mCurrentDegree = -azimuthInDegress;
}
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub

}

}

This app isn’t all that impressive on the emulator, so if you’re interested in seeing it in action go ahead and load it to an actual Android device.

Notes about accuracy

  • The compass points to magnetic north, not true north. To determine the latter, you’d need to pull in your location via GPS or Wi-Fi and then calculate the offset.
  • Because we are using the magnetic field sensor there is quite a bit of noise that I am not attempting to dampen.
  • You’ll find this simple example works best on a level surface, preferably outdoors.

If any developers out there are savvy when it comes to trig and graphing, I’d love to read your comments on how to improve accuracy.

Share this:

  • Facebook
  • Tweet
  • WhatsApp

Related posts:

Netflix Games on Android

Enhanced Security Algorithms on the Pixel 6

Google Brings Android Games to Windows 10 and Windows 11

Tags: actionaddadsAndroidAndroidManifest.xmlAndroidsappcodecomparecompassconsistentCreatedevdevelopdeveloperdevicediscoveredEclipseemulatorfilefinalfindfreeGoogleGPShardwarehurtinternallayoutlevellocationlocklockinglovemagneticmanageMapsmodemultiplenoiseOnepermissionpermissionspointportportraitProprotectpublicraterealridsensorsensorsSetshowshowssidesimplesimplysoftwarestartedstepstextThetiptooltoolsversionViewwidgetwinwork
Previous Post

LibreOffice is coming to Android

Next Post

Android is winning the platform race

Next Post
Android is winning the platform race

Android is winning the platform race

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

No Result
View All Result

Recent Posts

  • Netflix Games on Android
  • Enhanced Security Algorithms on the Pixel 6
  • Google Brings Android Games to Windows 10 and Windows 11
  • Oppo’s Find N Foldable: Next Generation of Smartphones
  • Huawei’s P50 Pocket: The Ultimate Portable Device

Categories

  • Android
  • Review
  • Tutorial
MicroG

  • About Us
  • Contact
  • Disclaimer
  • Privacy Policy
  • Terms and Condition

Follow Us

No Result
View All Result
  • About Us
  • Contact
  • Disclaimer
  • Privacy Policy
  • Terms and Condition