package edu.mit.media.funf.probe.builtin;
import java.util.HashMap;
import android.hardware.SensorManager;
import android.util.Log;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import edu.mit.media.funf.Schedule;
import edu.mit.media.funf.config.Configurable;
import edu.mit.media.funf.json.IJsonObject;
import edu.mit.media.funf.probe.Probe;
import edu.mit.media.funf.probe.Probe.Base;
import edu.mit.media.funf.probe.Probe.ContinuousProbe;
import edu.mit.media.funf.probe.Probe.PassiveProbe;
import edu.mit.media.funf.probe.Probe.RequiredFeatures;
import edu.mit.media.funf.probe.Probe.RequiredProbes;
import edu.mit.media.funf.probe.builtin.ProbeKeys.PedometerKeys;
import edu.mit.media.funf.util.LogUtil;
@Schedule.DefaultSchedule(interval=120, duration=15)
@RequiredFeatures("android.hardware.sensor.accelerometer")
@RequiredProbes(AccelerometerSensorProbe.class)
public class PedometerProbe extends Base implements ContinuousProbe, PassiveProbe, PedometerKeys{
@Configurable
private String sensitivityLevel = PedometerKeys.SENSITIVITY_LEVEL_LOW;
// private final int[] sensitivities = new int[9];
private final static String TAG = "PedometerProbe";
// 1.97(extra high) 2.96(very high) 4.44(high) 6.66(higher) 10.00(medium) 15.00(lower. works best for me)
// 22.50(low) 33.75(very low) 50.62(extra low)
// private float mScale[] = new float[2];
private float mScale;
private float mYOffset;
private float mLastValues[] = new float[3*2];
private float mLastDirections[] = new float[3*2];
private float mLastExtremes[][] = { new float[3*2], new float[3*2] };
private float mLastDiff[] = new float[3*2];
private int mLastMatch = -1;
private float mLimit = 10;
private static final HashMap<String, Float> sensitivities = new HashMap<String, Float>();
static{ // some kind of magic number
sensitivities.put(PedometerKeys.SENSITIVITY_LEVEL_EXTRA_HIGH, new Float(1.97));
sensitivities.put(PedometerKeys.SENSITIVITY_LEVEL_VERY_HIGH, new Float(2.96));
sensitivities.put(PedometerKeys.SENSITIVITY_LEVEL_HIGH, new Float(4.44));
sensitivities.put(PedometerKeys.SENSITIVITY_LEVEL_HIGHER, new Float(6.66));
sensitivities.put(PedometerKeys.SENSITIVITY_LEVEL_MEDIUM, new Float(10.00));
sensitivities.put(PedometerKeys.SENSITIVITY_LEVEL_LOWER, new Float(15.00));
sensitivities.put(PedometerKeys.SENSITIVITY_LEVEL_LOW, new Float(22.50));
sensitivities.put(PedometerKeys.SENSITIVITY_LEVEL_VERY_LOW, new Float(33.75));
sensitivities.put(PedometerKeys.SENSITIVITY_LEVEL_EXTRA_LOW, new Float(50.62));
}
private StepCounter stepCounter = new StepCounter();
@Override
protected void onEnable() {
super.onEnable();
int h = 480;
mYOffset = h * 0.5f;
// mScale[0] = - (h * 0.5f * (1.0f / (SensorManager.STANDARD_GRAVITY * 2)));
mScale = - (h * 0.5f * (1.0f / (SensorManager.MAGNETIC_FIELD_EARTH_MAX)));
mLimit = sensitivities.get(sensitivityLevel);
getAccelerometerProbe().registerPassiveListener(stepCounter);
}
@Override
protected void onStart() {
super.onStart();
getAccelerometerProbe().registerListener(stepCounter);
}
@Override
protected void onStop() {
super.onStop();
getAccelerometerProbe().unregisterListener(stepCounter);
}
@Override
protected void onDisable() {
super.onDisable();
getAccelerometerProbe().unregisterPassiveListener(stepCounter);
}
private AccelerometerSensorProbe getAccelerometerProbe() {
return getGson().fromJson(DEFAULT_CONFIG, AccelerometerSensorProbe.class);
}
private void onStep(float diff){
JsonObject data = new JsonObject();
//data.addProperty(PedometerKeys.SENSITIVITY_LEVEL, sensitivity);
data.addProperty(PedometerKeys.RAW_VALUE, diff);
sendData(data);
}
private class StepCounter implements DataListener{
@Override
public void onDataReceived(IJsonObject probeConfig, IJsonObject data) {
// TODO Auto-generated method stub
double timestamp = data.get(TIMESTAMP).getAsDouble();
Log.d(LogUtil.TAG, "RECEIVED:" + timestamp);
/*
* Codes here below taken from http://code.google.com/p/pedometer/ (StepDetector.java)
* Copyright (C) 2009 Levente Bagi, under GPL license
*/
float x = data.get(AccelerometerSensorProbe.X).getAsFloat();
float y = data.get(AccelerometerSensorProbe.Y).getAsFloat();
float z = data.get(AccelerometerSensorProbe.Z).getAsFloat();
float vSum = 0;
vSum = (mYOffset + x * mScale) + (mYOffset + y * mScale) + (mYOffset + z * mScale);
int k = 0;
float v = vSum / 3;
float direction = (v > mLastValues[k] ? 1 : (v < mLastValues[k] ? -1 : 0));
if (direction == - mLastDirections[k]) {
// Direction changed
int extType = (direction > 0 ? 0 : 1); // minumum or maximum?
mLastExtremes[extType][k] = mLastValues[k];
float diff = Math.abs(mLastExtremes[extType][k] - mLastExtremes[1 - extType][k]);
if (diff > mLimit) {
boolean isAlmostAsLargeAsPrevious = diff > (mLastDiff[k]*2/3);
boolean isPreviousLargeEnough = mLastDiff[k] > (diff/3);
boolean isNotContra = (mLastMatch != 1 - extType);
if (isAlmostAsLargeAsPrevious && isPreviousLargeEnough && isNotContra) {
Log.i(TAG, "step");
// detect walking one step. send data here
onStep(diff);
mLastMatch = extType;
}
else {
mLastMatch = -1;
}
}
mLastDiff[k] = diff;
}
mLastDirections[k] = direction;
mLastValues[k] = v;
}
@Override
public void onDataCompleted(IJsonObject probeConfig,
JsonElement checkpoint) {
// TODO Auto-generated method stub
// Do nothing
}
}
}