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));
}
}