package org.deviceconnect.android.deviceplugin.theta.core.sensor;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.view.Surface;
import android.view.WindowManager;
import org.deviceconnect.android.deviceplugin.theta.utils.Quaternion;
import org.deviceconnect.android.deviceplugin.theta.utils.Vector3D;
import java.util.logging.Logger;
public class DefaultHeadTracker extends AbstractHeadTracker implements SensorEventListener {
private static final float NS2S = 1.0f / 1000000000.0f;
private long mLastEventTimestamp;
private final SensorManager mSensorMgr;
private Quaternion mCurrentRotation = new Quaternion(1, new Vector3D(0, 0, 0));
private float[] mCurrentGyroscope = new float[3];
private boolean mInitFlag = false;
private Logger mLogger = Logger.getLogger("theta.dplugin");
private final WindowManager mWindowMgr;
public DefaultHeadTracker(final Context context) {
mSensorMgr = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
mWindowMgr = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
}
@Override
public void start() {
// Reset current rotation.
mCurrentRotation = new Quaternion(1, new Vector3D(0, 0, 0));
Sensor sensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
if (sensor == null) {
mLogger.warning("Failed to start: Default GYROSCOPE sensor is NOT found.");
return;
}
mSensorMgr.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME);
mInitFlag = false;
for (int i = 0; i < mCurrentGyroscope.length; i++) {
mCurrentGyroscope[i] = 0.0f;
}
}
@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[] values = new float[3];
int displayOrientation = mWindowMgr.getDefaultDisplay().getRotation();
switch (displayOrientation) {
case Surface.ROTATION_0:
values[0] = event.values[0];
values[1] = event.values[1];
values[2] = event.values[2];
break;
case Surface.ROTATION_90:
values[0] = event.values[1] * -1;
values[1] = event.values[0];
values[2] = event.values[2];
break;
case Surface.ROTATION_180:
values[0] = event.values[0] * -1;
values[1] = event.values[1] * -1;
values[2] = event.values[2];
break;
case Surface.ROTATION_270:
values[0] = event.values[1];
values[1] = event.values[0] * -1;
values[2] = event.values[2];
break;
default:
break;
}
float epsilon = 0.000000001f;
float[] vGyroscope = new float[3];
float[] deltaVGyroscope = new float[4];
Quaternion qGyroscopeDelta;
float dT = (event.timestamp - mLastEventTimestamp) * NS2S;
final float alpha = 0.8f;
if (!mInitFlag) {
System.arraycopy(values, 0, vGyroscope, 0, vGyroscope.length);
System.arraycopy(values, 0, mCurrentGyroscope, 0, values.length);
mInitFlag = true;
} else {
vGyroscope[0] = alpha * mCurrentGyroscope[0] + (1.0f - alpha) * values[0];
vGyroscope[1] = alpha * mCurrentGyroscope[1] + (1.0f - alpha) * values[1];
vGyroscope[2] = alpha * mCurrentGyroscope[2] + (1.0f - alpha) * values[2];
System.arraycopy(vGyroscope, 0, mCurrentGyroscope, 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;
qGyroscopeDelta = new Quaternion(deltaVGyroscope[3], new Vector3D(deltaVGyroscope));
mCurrentRotation = qGyroscopeDelta.multiply(mCurrentRotation);
notifyHeadRotation(mCurrentRotation);
}
mLastEventTimestamp = event.timestamp;
}
@Override
public void reset() {
mCurrentRotation = new Quaternion(1, new Vector3D(0, 0, 0));
}
}