package org.deviceconnect.android.deviceplugin.theta.core.sensor; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.view.Surface; import org.deviceconnect.android.deviceplugin.theta.utils.Quaternion; import org.deviceconnect.android.deviceplugin.theta.utils.Vector3D; /** * Head tracker which uses a gyro sensor only. */ public class GyroHeadTracker extends AbstractHeadTracker implements SensorEventListener { private static final float NS2S = 1.0f / 1000000000.0f; private static final long EXPIRE_INTERVAL = 10 * 1000; private long mLastEventTimestamp; private float mEventInterval; private int mDisplayRotation = Surface.ROTATION_0; private Quaternion mCurrentRotation = new Quaternion(1, new Vector3D(0, 0, 0)); private final SensorManager mSensorMgr; public GyroHeadTracker(final SensorManager sensorMgr) { mSensorMgr = sensorMgr; } @Override public void start() { mCurrentRotation = new Quaternion(1, new Vector3D(0, 0, 0)); Sensor gyroSensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_GYROSCOPE); if (gyroSensor != null) { mSensorMgr.registerListener(this, gyroSensor, SensorManager.SENSOR_DELAY_NORMAL); } } @Override public void stop() { mSensorMgr.unregisterListener(this); } @Override public void onAccuracyChanged(final Sensor sensor, final int accuracy) { // Nothing to do. } @Override public void onSensorChanged(final SensorEvent event) { if (mLastEventTimestamp != 0) { float EPSILON = 0.000000001f; float[] vGyroscope = new float[3]; float[] deltaVGyroscope = new float[4]; Quaternion qGyroscopeDelta; float dT = (event.timestamp - mLastEventTimestamp) * NS2S; System.arraycopy(event.values, 0, vGyroscope, 0, vGyroscope.length); float tmp = vGyroscope[2]; vGyroscope[2] = vGyroscope[0] * -1; vGyroscope[0] = tmp; float magnitude = (float) Math.sqrt(Math.pow(vGyroscope[0], 2) + Math.pow(vGyroscope[1], 2) + Math.pow(vGyroscope[2], 2)); if (magnitude > EPSILON) { vGyroscope[0] /= magnitude; vGyroscope[1] /= magnitude; vGyroscope[2] /= magnitude; } float thetaOverTwo = magnitude * dT / 2.0f; float sinThetaOverTwo = (float) Math.sin(thetaOverTwo); float cosThetaOverTwo = (float) Math.cos(thetaOverTwo); deltaVGyroscope[0] = sinThetaOverTwo * vGyroscope[0]; deltaVGyroscope[1] = sinThetaOverTwo * vGyroscope[1]; deltaVGyroscope[2] = sinThetaOverTwo * vGyroscope[2]; deltaVGyroscope[3] = cosThetaOverTwo; float[] delta = new float[3]; switch (mDisplayRotation) { case Surface.ROTATION_0: delta[0] = deltaVGyroscope[0]; delta[1] = deltaVGyroscope[1]; delta[2] = deltaVGyroscope[2]; break; case Surface.ROTATION_90: delta[0] = deltaVGyroscope[0]; delta[1] = deltaVGyroscope[2] * -1; delta[2] = deltaVGyroscope[1]; break; case Surface.ROTATION_180: delta[0] = deltaVGyroscope[0]; delta[1] = deltaVGyroscope[1] * -1; delta[2] = deltaVGyroscope[2]; break; case Surface.ROTATION_270: delta[0] = deltaVGyroscope[0]; delta[1] = deltaVGyroscope[2]; delta[2] = deltaVGyroscope[1] * -1; break; default: break; } qGyroscopeDelta = new Quaternion(deltaVGyroscope[3], new Vector3D(delta)); mCurrentRotation = qGyroscopeDelta.multiply(mCurrentRotation); notifyHeadRotation(mCurrentRotation); } mLastEventTimestamp = event.timestamp; } @Override public void reset() { mCurrentRotation = new Quaternion(1, new Vector3D(0, 0, 0)); } }