대략적인 개념이기 때문에 실제 사용할 수 있는 나침반을 만들려면 다른 변수들도 반영니 되야 겠지만 가속도 센서(TYPE_ACCELEROMETER)자기장 센서(TYPE_MAGNETIC_FIELD)를 이용하여 단순한compass를 만들어보겠습니다구현 방법은 구글에서 이미 함수를 만들어서 제공을 하기 때문에 그걸 그대로 사용합니다. 사용되는 함수는 getRotationMatrix() getOrientation() 입니다.



1. 함수

 

1.1 getRotationMatrix


 public static boolean getRotationMatrix (float[] R,

                float[] I,

                float[] gravity,

                float[] geomagnetic)


Type

 Parameter

 설명

 float[9]

 R

 회전 매트릭스 (Rotation matrix)

 float[9]

 I

 경사도 매트릭스 (Inclination matrix)

 float[3]

 gravity

 장치 좌표계의 gravity vector(TYPE_ACCELEROMETER)

 float[3]

 geomagnetic

 장치 좌표계의 geomagnetic vector(TYPE_MAGNETIC_FIELD)


getRotationMatrix()는 경사도와 회전 매트릭스를 구하는 함수로 지구에 대한 세계 좌표계(World Coordinate System)를 기준으로 핸드폰 장치의 좌표계의 변화하는 값을 구합니다세계 좌표계에서 서로 직교하는 3개의 축은

x : 지표면에 접하는 동쪽 방향의 벡터

y : 지표면에 접하는 북극 방향의 벡터

z : 지표면과 직각을 이루는 상공을 향하는 벡터

으로 설명 할 수 있습니다구글에서 설명하는 그림을 참고하면 아래와 같습니다.


android_compass


나침반을 만들 때에는 경사도 매트릭스를 사용하지 않습니다.


2.2 getOrientation


getOrientation() 함수는 rotation matrix를 이용하여 장치의 방향을 구하는 함수입니다. getRotationMatrix() 함수를 통해 구한 첫 번째 값인 회전 매트릭스를 사용해서 방향을 구할 수 있습니다.


public static float[] getOrientation (float[] R,

                float[] values)


Parameter

 설명

 R

 otation matrix : getRotationMatrix(float[], float[], float[], float[])

 values

 float[3] :

values[0]: Azimuth. z축에 대한 회전 방위각으로 이게 필요하죠.

values[1]: Pitch. x축에 대한 회전 방위각

values[2]: Roll. y축에 대한 회전 방위각


설명에도 기술 하였지만 getRotationMatrix()에서 회전 매트릭스 (Rotation matrix)를 구하여 getOrientation()에 입력하면 values[0] (Azimuth : z축에 대한 회전 방위각) 구하게 됩니다 값이 바로 북을 향하는 각도가 됩니다.

 

 

2. 프로젝트 생성

 

CompassTest 라는 이름으로 프로젝트를 생성 합니다.


android_compass


Activityempty로 선택합니다.


android_compass


나머지는 그냥 기본 설정 값으로 놓고 Next 버튼을 클릭 합니다.


3. 코딩

 

3.1 layout

 

구해진 회전각을 출력 할 수 있는 TextView를 하나 생성하고 나침반 이미지를 위해서 ImageView를 추가 합니다.


<TextView
    android:id="@+id/DegreeTV"
    android:layout_width="wrap_content"
    android:layout_height="18dp"
    android:layout_marginBottom="8dp"
    android:layout_marginTop="8dp"
    android:text="Heading : 0.0"
    app:layout_constraintBottom_toTopOf="@+id/pointer"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.027"
    tools:layout_editor_absoluteX="127dp" />

<ImageView
    android:id="@+id/pointer"
    android:layout_width="match_parent"
    android:layout_height="437dp"
    android:src="@drawable/compass_icon"
    tools:layout_editor_absoluteX="0dp"
    tools:layout_editor_absoluteY="56dp" />


이미지는 인터넷에서 무료 이미지를 하나 줏어왔습니다. 배경이 투명이 아니라 돌아갈때 커다란네모가 돌면서 글씨를 가리는 군요전에 작업한 투명이미지 만들기를 보면서 수정했습니다.


[Pixlr] Adobe Photoshop 없이 무료 투명 배경 이미지 만들기


android_compass


3.2 방위각 구하기


getRotationMatrix() 함수를 이용하여 회전 매트릭스(mR)를 구합니다.


SensorManager.getRotationMatrix(mR, null, mLastAccelerometer, mLastMagnetometer);


mR을 이용하여 getOrientation()에서 방위각을 구합니다.


SensorManager.getOrientation(mR, mOrientation);


3.3 이미지 회전


z 축의 회전각으로 RotateAnimation()을 이용하여 이미지를 회전 시킵니다.

RotateAnimation ra = new RotateAnimation(
mCurrentDegree,
-azimuthinDegress,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
);

이미지 회전 설명은 [Android] 안드로이드 RotateAnimation 함수 이용 이미지 회전 을 참조 하세요.



3. 결과


android_compass


 

4. 전체 소스

 

activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/DegreeTV"
        android:layout_width="wrap_content"
        android:layout_height="18dp"
        android:layout_marginBottom="8dp"
        android:layout_marginTop="8dp"
        android:text="Heading : 0.0"
        app:layout_constraintBottom_toTopOf="@+id/pointer"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.027"
        tools:layout_editor_absoluteX="127dp" />

    <ImageView
        android:id="@+id/pointer"
        android:layout_width="match_parent"
        android:layout_height="437dp"
        android:src="@drawable/compass_icon"
        tools:layout_editor_absoluteX="0dp"
        tools:layout_editor_absoluteY="56dp" />
</android.support.constraint.ConstraintLayout>


MainActivity.java


package copycoding.tistory.com.compasstest;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity 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;
    TextView DegreeTV;

    @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);
        DegreeTV = (TextView)findViewById(R.id.DegreeTV);

    }

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

    @Override
    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);
            float azimuthinDegress  = (int) ( Math.toDegrees( SensorManager.getOrientation( mR, mOrientation)[0] ) + 360 ) % 360;
            DegreeTV.setText("Heading : " + Float.toString(azimuthinDegress) + " degrees");
            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) {

    }

}



+ Recent posts