package gestures.detectors;
import gestures.PhoneGesture;
import gestures.PhoneGestureDetector;
import gestures.SensorData;
import gestures.StandardPhoneGesture;
import util.Vec;
public class UppercutDetector implements PhoneGestureDetector {
private static final float VELOCITY_DECAY = 0.98f;
private static final float MINIMUM_VELOCITY = 0.2f;
private static final float PROBABILITY_DECAY = 0.8f;
private Vec velocity = new Vec();
private long lastTimestamp = 0;
private double probability = 0;
private double angle = 0;
@Override
public PhoneGesture getType() {
return StandardPhoneGesture.UPPERCUT;
}
@Override
public double getProbability() {
return Math.min(1, probability);
}
@Override
public void feedSensorEvent(SensorData sensorData) {
// We need at least two events to calculate velocity, the very first
// event is only used to obtain an initial timestamp.
if (lastTimestamp == 0) {
lastTimestamp = sensorData.timestamp;
return;
}
Vec linearAcceleration = new Vec(
(float) sensorData.linearAcceleration[0],
(float) sensorData.linearAcceleration[1],
(float) sensorData.linearAcceleration[2]);
Vec gravity = new Vec((float) sensorData.gravity[0],
(float) sensorData.gravity[1], (float) sensorData.gravity[2]);
// Integrate to obtain velocity vector. We divide by 1,000,000,000 in
// order to obtain the velocity in m/s instead of m/ns.
velocity.add(Vec.mult(
(sensorData.timestamp - lastTimestamp) / 1000000000f,
linearAcceleration));
// Let the velocity decay minimally to avoid problems of
// sensor drifting.
velocity.mult(VELOCITY_DECAY);
// Calculate angle between velocity and gravity vector to determine
// gesture probability.
double scalarProduct = gravity.x * velocity.x + gravity.y * velocity.y
+ gravity.z * velocity.z;
double currentAngle = Math.acos(scalarProduct
/ (gravity.getLength() * velocity.getLength()));
this.angle = 0.2 * this.angle + 0.8 * currentAngle;
if (velocity.getLength() > MINIMUM_VELOCITY) {
// Push the value through a sigmoid function (which might overshoot
// a little bit for perfect vertical movements (which is rarely the
// case, though). Nevertheless, that's why a min function is used in
// getProbability().
probability = 1.5 / (1 + Math.exp(-10
* (this.angle / Math.PI - 0.7)));
} else {
probability *= PROBABILITY_DECAY;
}
lastTimestamp = sensorData.timestamp;
}
}