package gestures.detectors;
import java.util.Arrays;
import android.hardware.SensorManager;
import gestures.PhoneGesture;
import gestures.PhoneGestureDetector;
import gestures.SensorData;
import gestures.StandardPhoneGesture;
/**
* The FullTurnDetector can be used to determine whether the player has
* performed a turn of roughly 360 degrees while holding the phone. Detection of
* this gesture is based on sampling of the compass coordinates. The gesture
* allows a certain slowness of the movement but resets itself if there has not
* been a large change in the compass direction for more than 1.5s.
*
* @author Rene Glebke <rene.glebke@rwth-aachen.de>
* @version 1.0pre - 2013-01-13
*
*/
public class FullTurnDetector implements PhoneGestureDetector {
/**
* Controls how sensitive the detector shall be.
*/
public static final float TIME_BEFORE_RESET_IN_MS = 1500;
public static final int NUMBER_OF_AZIMUTH_BINS = 42; // For detection
public static final int NUMBER_OF_COARSE_BINS = 8; // For resetting
private static final float ORIENTATION_DIVIDER = 360f / NUMBER_OF_AZIMUTH_BINS;
private static final float ORIENTATION_DIVIDER_COARSE = 360f / NUMBER_OF_COARSE_BINS;
/**
* Preallocated members for the matrices used by the SensorManager.
*/
private float rotationMatrix[] = new float[9];
private float orientationMatrix[] = new float[3];
/**
* The field of seen "binned" readings and of seen different readings.
*/
private boolean[] seenReadings = new boolean[NUMBER_OF_AZIMUTH_BINS];
private int seenDifferentReadings = 0;
/**
* Orientation changes.
*/
private int lastSeenCoarseOrientation = 0;
private long timeOfLastSeenCoarseOrientationChange = System
.currentTimeMillis();
@Override
public PhoneGesture getType() {
return StandardPhoneGesture.FULL_TURN;
}
@Override
public double getProbability() {
/*
* The probability of this event is the number of seen different
* readings.
*/
// Log.d("Full Turn Probability",
// Double.toString((double)this.seenDifferentReadings /
// NUMBER_OF_AZIMUTH_BINS));
return (double) this.seenDifferentReadings / NUMBER_OF_AZIMUTH_BINS;
}
public void reset() {
Arrays.fill(this.seenReadings, false);
this.seenDifferentReadings = 0;
this.lastSeenCoarseOrientation = 0;
this.timeOfLastSeenCoarseOrientationChange = System.currentTimeMillis();
}
@Override
public void feedSensorEvent(SensorData sensorData) {
// Step 1: Get the orientation according to the new (API >= 8) method
// (see Sensor class documentation)
SensorManager.getRotationMatrix(rotationMatrix, null,
sensorData.gravity, sensorData.mag);
SensorManager.getOrientation(rotationMatrix, orientationMatrix);
/*
* Step 2: The current azimuth is given by the 1st entry in the
* orientation matrix; we normalize it to the value range 0..360.
*/
float orientation = (float) Math.toDegrees(orientationMatrix[0]);
if (orientation < 0) {
orientation += 360;
}
/*
* Step 3: Check if the player is facing the same direction for a longer
* period of time. In this case, reset the instance (there is no motion
* going on at the moment).
*/
int orientationIntCoarse = (int) (orientation / ORIENTATION_DIVIDER_COARSE);
if ((Math.abs(this.lastSeenCoarseOrientation - orientationIntCoarse) % NUMBER_OF_COARSE_BINS) <= 1) {
if (System.currentTimeMillis()
- this.timeOfLastSeenCoarseOrientationChange >= FullTurnDetector.TIME_BEFORE_RESET_IN_MS) {
this.reset();
}
} else {
this.lastSeenCoarseOrientation = orientationIntCoarse;
this.timeOfLastSeenCoarseOrientationChange = System
.currentTimeMillis();
}
// Step 4: Set the bin of the seen reading to true
int orientationInt = (int) (orientation / ORIENTATION_DIVIDER);
if (!this.seenReadings[orientationInt]) {
++this.seenDifferentReadings;
this.seenReadings[orientationInt] = true;
}
}
}