Mencari Arah Dengan Kompas Digital Di Android


Pada artikel Mencari Lokasi Dengan GPS Di Android, saya menggunakan GPS untuk menentukan posisi perangkat Android di peta. Pada artikel ini, saya akan menambahkan sebuah informasi lain berupa arah perangkat pada saat posisi tersebut diambil. Beberapa (tidak semua!) perangkat Android dilengkapi dengan sensor yang disebut sebagai magnetometer atau sering disebut sebagai kompas digital yang mengukur nilai dalam satuan micro-Tesla (uT).

Sama seperti pada kompas analog, sensor ini pada dasarnya dipengaruhi oleh medan magnet disekitarnya, terutama oleh medan magnet bumi. Akan tetapi, berbeda pada kompas analog yang hanya mengukur pada satu arah, perangkat Android biasanya menggunakan vector magnetometer yang mengukur medan magnet pada masing-masing 3 arah (x, y, dan z). Untuk menunjukkannya, saya akan menampilkan medan magnet yang dibaca pada 3 vector tersebut dengan menambahkan kode program berikut ini di LokasiActivity:

public class LokasiActivity extends Activity implements LocationListener, SensorEventListener {

    ...

    private float magnetX, magnetY, magnetZ;
    private SensorManager sensorManager;

    @Override
    protected void onStart() {
        ...
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        Sensor magnetometer = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        if (magnetometer != null) {
            sensorManager.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_UI);
        }
    }

    @Override
    public void onLocationChanged(Location location) {
        ...
        String strOutput = "DMS: " + lokasiTerakhir.koordinatDMS() + "n" +
            "DD: " + lokasiTerakhir.koordinatDD() + "n" +
            "Altitude: " + lokasiTerakhir.getAltitude() + " mn" +
            "Akurasi: " + lokasiTerakhir.getAkurasi() + " mn" +
            "Magnet X: " + magnetX + " uTn" +
            "Magnet Y: " + magnetY + " uTn" +
            "Magnet Z: " + magnetZ + " uTn";
        output.setText(strOutput);
        ...
    }

    @Override
    protected void onPause() {
        ...
        sensorManager.unregisterListener(this);
    }

    ...

    @Override
    public void onSensorChanged(SensorEvent event) {
        magnetX = event.values[0];
        magnetY = event.values[1];
        magnetZ = event.values[2];
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

}

Pada saat aplikasi dijalankan, saya akan memperoleh nilai besarnya medan magnet pada sumbu x, y, dan z. Walaupun demikian, saya tidak bisa memperoleh sesuatu yang bisa dimengerti atau berguna secara langsung dari nilai ini. Yang saya butuhkan adalah sebuah nilai tunggal seperti azimuth. Nilai azimuth adalah derajat yang menunjukkan jarak dari utara searah dengan jarum jam (misalnya, nilai azimuth untuk posisi di utara adalah 0 derajat, posisi timur 90 derajat, posisi selatan 180 derajat dan posisi barat 270 derajat).

Beruntungnya, Android Sensor API sudah menyediakan method yang akan melakukan kalkulasi rumit bagi saya. Langkah pertama yang saya perlu saya lakukan adalah mendapatkan nilai rotation matrix (R) yang berguna untuk memetakan koordinat perangkat ke koordinat dunia nyata. Koordinat dunia nyata adalah koordinat dimana X selalu menghadap timur, Y selalu menghadap kutub utara dan Z selalu menghadap ke langit, tanpa peduli pada orientasi perangkat. Untuk mendapatkan nilai R, saya perlu memperoleh nilai dari sensor accelerometer dan magnetometer. Nilai yang diperoleh dari sensor akan dilewatkan sebagai parameter pada method getRotationMatrix() milik SensorManager. Sebagai contoh, saya membatalkan perubahan sebelumnya dan mengubah kode program saya menjadi seperti berikut ini:

public class LokasiActivity extends Activity implements LocationListener, SensorEventListener {

    ...

    private float[] magnet, percepatan;
    private SensorManager sensorManager;

    @Override
    protected void onStart() {
        ...
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        Sensor magnetometer = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        if (magnetometer != null) {
            sensorManager.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_UI);
        }
        Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        if (accelerometer != null) {
            sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI);
        }
    }

    @Override
    public void onLocationChanged(Location location) {
        // Hitung rotasi
        if ((magnet!=null) && (percepatan!=null)) {
            float R[] = new float[9];
            float I[] = new float[9];
            if (SensorManager.getRotationMatrix(R, I, percepatan, magnet)) {
                // Proses disini nanti!
            }
        }
        ...
    }

    ...

    @Override
    protected void onPause() {
        super.onPause();
        locationManager.removeUpdates(this);
        sensorManager.unregisterListener(this);
    }


    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            percepatan = event.values;
        } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
            magnet = event.values;
        }
    }

    ...
}

Pada kode program di atas, saya sudah memperoleh matrix R yang mewakili rotation matrix. Sekarang, saya bisa menggunakan nilai tersebut untuk dipakai pada method getOrientation() di SensorManager, seperti pada contoh berikut ini:

...
@Override
public void onLocationChanged(Location location) {
  // Hitung rotasi
  float azimuth = 0;
  if ((magnet!=null) && (percepatan!=null)) {
    float R[] = new float[9];
    float I[] = new float[9];
    if (SensorManager.getRotationMatrix(R, I, percepatan, magnet)) {
      float orientation[] = new float[3];
      azimuth = orientation[0];
    }
  }
  ...
}
....

Nilai azimuth yang dikembalikan oleh getOrientation() berada dalam satuan radian dimana nilai positif mewakili arah berlawanan dengan jarum jam. Untuk menerjemahkan nilai ini ke dalam derajat, saya menambahkan kode program berikut ini:

...
azimuth = (float) Math.toDegrees(orientation[0]);
if (orientation[0] < 0) {
    azimuth += 360;
}
...

Setelah itu, saya bisa menambahkan property baru pada Lokasi untuk menampung informasi azimuth. Saya juga perlu mengubah sebuah constructor pada class tersebut sehingga dapat menerima nilai azimuth. Ssebagai langkah terakhir, saya tinggal menampilkan nilai azimuth dari Lokasi ke dalam TextView (bersamaan dengan nilai yang diperoleh dari GPS).

Nilai azimuth yang diperoleh adalah azimuth compass yang relatif terhadap magnetic north bukan terhadap true north. Walaupun mendekati true north, nilai magnetic north selalu memiliki selisih tergantung pada wilayah dan waktu dimana pengguna berada. Selisih ini disebut sebagai magnetic declination. Struktur wilayah, jumlah besi yang terkandung di dalam tanah dan faktor lainnya merupakan contoh faktor yang mempengaruhi magnetic declination. Selain itu, metal dan magnet di sekitar perangkat bisa mempengaruhi nilai yang dibaca oleh magnetometer. Kelemahan lainnya adalah nilai azimuth hanya akurat bila perangkat berada dalam keadaan mendatar terhadap bumi dimana layar menghadap ke atas (sama seperti layaknya saat kompas digunakan). Bila perangkat berada dalam keadaan vertikal, gimbal lock akan menyebabkan hasil perhitungan getOrientation() milik SensorManager menjadi tidak akurat sehingga dibutuhkan perhitungan yang lebih kompleks untuk memperoleh azimuth yang diharapkan.

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: