Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 164 additions & 56 deletions catroid/src/org/catrobat/catroid/formulaeditor/SensorHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,32 +37,55 @@
import org.catrobat.catroid.facedetection.FaceDetectionHandler;

public final class SensorHandler implements SensorEventListener, SensorCustomEventListener {
public static final float RADIAN_TO_DEGREE_CONST = 180f / (float) Math.PI;
private static final String TAG = SensorHandler.class.getSimpleName();
private static SensorHandler instance = null;
private static BluetoothDeviceService btService = ServiceProvider.getService(CatroidService.BLUETOOTH_DEVICE_SERVICE);
private SensorManagerInterface sensorManager = null;
private Sensor linearAccelerationSensor = null;
private Sensor accelerometerSensor = null;
private Sensor magneticFieldSensor = null;
private Sensor rotationVectorSensor = null;
private float[] rotationMatrix = new float[16];
private float[] rotationVector = new float[3];
public static final float RADIAN_TO_DEGREE_CONST = 180f / (float) Math.PI;

private float linearAcceleartionX = 0f;
private float linearAcceleartionY = 0f;
private float linearAcceleartionZ = 0f;

private float[] accelerationXYZ = new float[3];
private float signAccelerationZ = 0f;
private float[] gravity = new float[] { 0f, 0f, 0f };
private boolean useLinearAccelerationFallback = false;
private boolean useRotationVectorFallback = false;
private float linearAccelerationX = 0f;
private float linearAccelerationY = 0f;
private float linearAccelerationZ = 0f;
private float loudness = 0f;
private float faceDetected = 0f;
private float faceSize = 0f;
private float facePositionX = 0f;
private float facePositionY = 0f;

private static BluetoothDeviceService btService = ServiceProvider.getService(CatroidService.BLUETOOTH_DEVICE_SERVICE);

private SensorHandler(Context context) {
sensorManager = new SensorManager(
(android.hardware.SensorManager) context.getSystemService(Context.SENSOR_SERVICE));
accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
linearAccelerationSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
if (linearAccelerationSensor == null) {
useLinearAccelerationFallback = true;
}

rotationVectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
if (rotationVectorSensor == null) {
useRotationVectorFallback = true;
}

if (useRotationVectorFallback) {
accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
magneticFieldSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
} else if (useLinearAccelerationFallback) {
accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}

Log.d(TAG, "*** LINEAR_ACCELERATION SENSOR: " + linearAccelerationSensor);
Log.d(TAG, "*** ACCELEROMETER SENSOR: " + accelerometerSensor);
Log.d(TAG, "*** ROTATION_VECTOR SENSOR: " + rotationVectorSensor);
Log.d(TAG, "*** MAGNETIC_FIELD SENSOR: " + magneticFieldSensor);
}

public static void startSensorListener(Context context) {
Expand All @@ -72,10 +95,9 @@ public static void startSensorListener(Context context) {
}
instance.sensorManager.unregisterListener((SensorEventListener) instance);
instance.sensorManager.unregisterListener((SensorCustomEventListener) instance);
instance.sensorManager.registerListener(instance, instance.accelerometerSensor,
android.hardware.SensorManager.SENSOR_DELAY_NORMAL);
instance.sensorManager.registerListener(instance, instance.rotationVectorSensor,
android.hardware.SensorManager.SENSOR_DELAY_NORMAL);

instance.registerListener(instance);

instance.sensorManager.registerListener(instance, Sensors.LOUDNESS);
FaceDetectionHandler.registerOnFaceDetectedListener(instance);
FaceDetectionHandler.registerOnFaceDetectionStatusListener(instance);
Expand All @@ -85,10 +107,26 @@ public static void registerListener(SensorEventListener listener) {
if (instance == null) {
return;
}
instance.sensorManager.registerListener(listener, instance.accelerometerSensor,
android.hardware.SensorManager.SENSOR_DELAY_NORMAL);
instance.sensorManager.registerListener(listener, instance.rotationVectorSensor,
android.hardware.SensorManager.SENSOR_DELAY_NORMAL);

if (!instance.useLinearAccelerationFallback) {
instance.sensorManager.registerListener(listener, instance.linearAccelerationSensor,
android.hardware.SensorManager.SENSOR_DELAY_GAME);
}

if (!instance.useRotationVectorFallback) {
instance.sensorManager.registerListener(listener, instance.rotationVectorSensor,
android.hardware.SensorManager.SENSOR_DELAY_GAME);
}

if (instance.useLinearAccelerationFallback || instance.useRotationVectorFallback) {
instance.sensorManager.registerListener(listener, instance.accelerometerSensor,
android.hardware.SensorManager.SENSOR_DELAY_GAME);
}

if (instance.useRotationVectorFallback && instance.magneticFieldSensor != null) {
instance.sensorManager.registerListener(listener, instance.magneticFieldSensor,
android.hardware.SensorManager.SENSOR_DELAY_GAME);
}
}

public static void unregisterListener(SensorEventListener listener) {
Expand Down Expand Up @@ -117,50 +155,89 @@ public static Double getSensorValue(Sensors sensor) {
switch (sensor) {

case X_ACCELERATION:
return (double) instance.linearAcceleartionX;
return (double) instance.linearAccelerationX;

case Y_ACCELERATION:
return (double) instance.linearAcceleartionY;
return (double) instance.linearAccelerationY;

case Z_ACCELERATION:
return (double) instance.linearAcceleartionZ;
return (double) instance.linearAccelerationZ;

case COMPASS_DIRECTION:
float[] orientations = new float[3];
android.hardware.SensorManager.getRotationMatrixFromVector(instance.rotationMatrix,
instance.rotationVector);
if (!instance.useRotationVectorFallback) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens here if useRotationVectorFallback is true? correct me if I'm wrong, but if there is no getRotationMatrixFromVector then the rotationMatrix is not initialised and hence you won't get any reasonable results anyway.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi!
If the useRotatitonVectorFallback is true - then the rotatiton-matrix is NOT obtained from the RotationVectorSensor (because this does not exist in this phone) but determined by means of the getRotationMatrix(float[],float[],float[],float[]) with accelerometer data AND macgnetic field data in line 303 in the onSensorChanged(...) method - when the MagneticField Sensor is present.
If NEITHER rotation-matrix sensor NOR magnetic field sensor are present compass direction does not work on this phone.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all right! got it! obviously I hadn't understood it properly before, thank you for clearing that up.

android.hardware.SensorManager.getRotationMatrixFromVector(instance.rotationMatrix,
instance.rotationVector);
}
android.hardware.SensorManager.getOrientation(instance.rotationMatrix, orientations);
sensorValue = (double) orientations[0];
return sensorValue * RADIAN_TO_DEGREE_CONST * -1f;

case X_INCLINATION:

orientations = new float[3];
android.hardware.SensorManager.getRotationMatrixFromVector(instance.rotationMatrix,
instance.rotationVector);
android.hardware.SensorManager.getOrientation(instance.rotationMatrix, orientations);
sensorValue = (double) orientations[2];
return sensorValue * RADIAN_TO_DEGREE_CONST * -1f;

if (instance.useRotationVectorFallback) {
float rawInclinationX = RADIAN_TO_DEGREE_CONST * (float) (Math.acos(instance.accelerationXYZ[0]));
float correctedInclinationX = 0;

if (rawInclinationX >= 90 && rawInclinationX <= 180) {
if (instance.signAccelerationZ > 0) {
correctedInclinationX = -(rawInclinationX - 90);
} else {
correctedInclinationX = -(180 + (90 - rawInclinationX));
}
} else if (rawInclinationX >= 0 && rawInclinationX < 90) {
if (instance.signAccelerationZ > 0) {
correctedInclinationX = (90 - rawInclinationX);
} else {
correctedInclinationX = (90 + rawInclinationX);
}
}
return (double) correctedInclinationX;
} else {
orientations = new float[3];
android.hardware.SensorManager.getRotationMatrixFromVector(instance.rotationMatrix,
instance.rotationVector);
android.hardware.SensorManager.getOrientation(instance.rotationMatrix, orientations);
sensorValue = (double) orientations[2];
return sensorValue * RADIAN_TO_DEGREE_CONST * -1f;
}
case Y_INCLINATION:
orientations = new float[3];
android.hardware.SensorManager.getRotationMatrixFromVector(instance.rotationMatrix,
instance.rotationVector);
android.hardware.SensorManager.getOrientation(instance.rotationMatrix, orientations);

float xInclinationUsedToExtendRangeOfRoll = orientations[2] * RADIAN_TO_DEGREE_CONST * -1f;
if (instance.useRotationVectorFallback) {
float rawInclinationY = RADIAN_TO_DEGREE_CONST * (float) (Math.acos(instance.accelerationXYZ[1]));
float correctedInclinationY = 0;
if (rawInclinationY >= 90 && rawInclinationY <= 180) {
if (instance.signAccelerationZ > 0) {
correctedInclinationY = -(rawInclinationY - 90);
} else {
correctedInclinationY = -(180 + (90 - rawInclinationY));
}
} else if (rawInclinationY >= 0 && rawInclinationY < 90) {
if (instance.signAccelerationZ > 0) {
correctedInclinationY = (90 - rawInclinationY);
} else {
correctedInclinationY = (90 + rawInclinationY);
}
}
return (double) correctedInclinationY;
} else {
orientations = new float[3];
android.hardware.SensorManager.getRotationMatrixFromVector(instance.rotationMatrix,
instance.rotationVector);
android.hardware.SensorManager.getOrientation(instance.rotationMatrix, orientations);

sensorValue = (double) orientations[1];
float xInclinationUsedToExtendRangeOfRoll = orientations[2] * RADIAN_TO_DEGREE_CONST * -1f;

if (Math.abs(xInclinationUsedToExtendRangeOfRoll) <= 90f) {
return sensorValue * RADIAN_TO_DEGREE_CONST * -1f;
} else {
float uncorrectedYInclination = sensorValue.floatValue() * RADIAN_TO_DEGREE_CONST * -1f;
sensorValue = (double) orientations[1];

if (uncorrectedYInclination > 0f) {
return (double) 180f - uncorrectedYInclination;
if (Math.abs(xInclinationUsedToExtendRangeOfRoll) <= 90f) {
return sensorValue * RADIAN_TO_DEGREE_CONST * -1f;
} else {
return (double) -180f - uncorrectedYInclination;
float uncorrectedYInclination = sensorValue.floatValue() * RADIAN_TO_DEGREE_CONST * -1f;

if (uncorrectedYInclination > 0f) {
return (double) 180f - uncorrectedYInclination;
} else {
return (double) -180f - uncorrectedYInclination;
}
}
}
case FACE_DETECTED:
Expand Down Expand Up @@ -201,17 +278,45 @@ public static Double getSensorValue(Sensors sensor) {
return 0d;
}

public static void clearFaceDetectionValues() {
if (instance != null) {
instance.faceDetected = 0f;
instance.faceSize = 0f;
instance.facePositionX = 0f;
instance.facePositionY = 0f;
}
}

@Override
public void onAccuracyChanged(Sensor arg0, int arg1) {
}

@Override
public void onSensorChanged(SensorEvent event) {
switch (event.sensor.getType()) {
case Sensor.TYPE_MAGNETIC_FIELD:
float[] tempMagneticFieldXYZ = event.values.clone();
float[] tempInclinationMatrix = new float[9];
android.hardware.SensorManager.getRotationMatrix(rotationMatrix, tempInclinationMatrix, accelerationXYZ,
tempMagneticFieldXYZ); //http://goo.gl/wo6QK5
break;
case Sensor.TYPE_ACCELEROMETER:
accelerationXYZ = event.values.clone();
if (useLinearAccelerationFallback) {
determinePseudoLinearAcceleration(accelerationXYZ.clone());
}
double normOfG = Math.sqrt(accelerationXYZ[0] * accelerationXYZ[0]
+ accelerationXYZ[1] * accelerationXYZ[1]
+ accelerationXYZ[2] * accelerationXYZ[2]);
accelerationXYZ[0] = (float) (accelerationXYZ[0] / normOfG);
accelerationXYZ[1] = (float) (accelerationXYZ[1] / normOfG);
accelerationXYZ[2] = (float) (accelerationXYZ[2] / normOfG);
signAccelerationZ = Math.signum(event.values[2]);
break;
case Sensor.TYPE_LINEAR_ACCELERATION:
linearAcceleartionX = event.values[0];
linearAcceleartionY = event.values[1];
linearAcceleartionZ = event.values[2];
linearAccelerationX = event.values[0];
linearAccelerationY = event.values[1];
linearAccelerationZ = event.values[2];
break;
case Sensor.TYPE_ROTATION_VECTOR:
rotationVector[0] = event.values[0];
Expand All @@ -223,6 +328,18 @@ public void onSensorChanged(SensorEvent event) {
}
}

private void determinePseudoLinearAcceleration(float[] input) {
float alpha = 0.8f;

gravity[0] = alpha * gravity[0] + ((1 - alpha) * input[0]);
gravity[1] = alpha * gravity[1] + ((1 - alpha) * input[1]);
gravity[2] = alpha * gravity[2] + ((1 - alpha) * input[2]);

linearAccelerationX = -1f * (input[0] - gravity[0]);
linearAccelerationY = -1f * (input[1] - gravity[1]);
linearAccelerationZ = -1f * (input[2] - gravity[2]);
}

@Override
public void onCustomSensorChanged(SensorCustomEvent event) {
switch (event.sensor) {
Expand All @@ -245,13 +362,4 @@ public void onCustomSensorChanged(SensorCustomEvent event) {
Log.v(TAG, "Unhandled sensor: " + event.sensor);
}
}

public static void clearFaceDetectionValues() {
if (instance != null) {
instance.faceDetected = 0f;
instance.faceSize = 0f;
instance.facePositionX = 0f;
instance.facePositionY = 0f;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,7 @@ public void onAccuracyChanged(Sensor sensor, int accuracy) {

@Override
public void onSensorChanged(SensorEvent event) {
switch (event.sensor.getType()) {
case Sensor.TYPE_LINEAR_ACCELERATION:
showFormulaResult();
break;
case Sensor.TYPE_ROTATION_VECTOR:
showFormulaResult();
break;
}
showFormulaResult();
}

private void setDialogTextView(final String newString) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ public void testSensors() throws SecurityException, IllegalArgumentException, No
SimulatedSensorManager simulatedSensorManager = new SimulatedSensorManager();
Reflection.setPrivateField(sensorHandler, "sensorManager", simulatedSensorManager);

Sensor accelerometerSensor = (Sensor) Reflection.getPrivateField(sensorHandler, "accelerometerSensor");
Sensor accelerometerSensor = (Sensor) Reflection.getPrivateField(sensorHandler,
"linearAccelerationSensor");
Sensor rotationVectorSensor = (Sensor) Reflection.getPrivateField(sensorHandler, "rotationVectorSensor");
SensorHandler.startSensorListener(getInstrumentation().getTargetContext());

Expand All @@ -222,9 +223,9 @@ public void testSensors() throws SecurityException, IllegalArgumentException, No

float expectedLoudness = (Float) Reflection.getPrivateField(sensorHandler, "loudness");

float expectedXAcceleration = (Float) Reflection.getPrivateField(sensorHandler, "linearAcceleartionX");
float expectedYAcceleration = (Float) Reflection.getPrivateField(sensorHandler, "linearAcceleartionY");
float expectedZAcceleration = (Float) Reflection.getPrivateField(sensorHandler, "linearAcceleartionZ");
float expectedXAcceleration = (Float) Reflection.getPrivateField(sensorHandler, "linearAccelerationX");
float expectedYAcceleration = (Float) Reflection.getPrivateField(sensorHandler, "linearAccelerationY");
float expectedZAcceleration = (Float) Reflection.getPrivateField(sensorHandler, "linearAccelerationZ");

float[] rotationMatrix = new float[16];
float[] rotationVector = (float[]) Reflection.getPrivateField(sensorHandler, "rotationVector");
Expand Down