package edu.mit.media.funf.probe.builtin;
import java.io.DataOutputStream;
import java.util.Arrays;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import edu.mit.media.funf.FFT;
import edu.mit.media.funf.Utils;
import edu.mit.media.funf.Window;
import edu.mit.media.funf.probe.Probe;
import edu.mit.media.funf.probe.builtin.ProbeKeys.AccelerometerFeaturesKeys;
public class AccelerometerFeaturesProbe extends Probe implements SensorEventListener, AccelerometerFeaturesKeys {
private static String STREAM_NAME = "hdl_accel";
private static final int SENSOR_TYPE = Sensor.TYPE_ACCELEROMETER;
private static final int SENSOR_RATE = SensorManager.SENSOR_DELAY_FASTEST;
private static final int STREAM_FEATURES = 26;
private static final double SENSOR_FRAME_DURATION = 1.0; // Frame length in seconds
private static final double SENSOR_MAX_RATE = 100.0; // Assumed maximum accelerometer sampling rate
// private static final double EVENT_SYS_TIME_ADJ = 1316877824.0d; // Discrepancy between system and event time?
private static int FFT_SIZE = 128;
private static double[] FREQ_BANDEDGES = {0,1,3,6,10};
private SensorManager sensorManager = null;
private Sensor sensor = null;
private DataOutputStream sensorStreamRaw = null;
private DataOutputStream sensorStreamFeatures = null;
private double prevSecs;
private double prevFrameSecs;
private double frameTimer = 0;
private double[][] frameBuffer = null;
private double[] fftBufferR = null;
private double[] fftBufferI = null;
private int frameSamples = 0;
private int frameBufferSize = 0;
private FFT featureFFT = null;
private Window featureWin = null;
private static int[] freqBandIdx = null;
@Override
public Parameter[] getAvailableParameters() {
return new Parameter[] {
new Parameter(Parameter.Builtin.DURATION, 30L),
new Parameter(Parameter.Builtin.PERIOD, 300L),
new Parameter(Parameter.Builtin.START, 0L),
new Parameter(Parameter.Builtin.END, 0L)
};
}
@Override
public String[] getRequiredFeatures() {
return new String[]{
"android.hardware.sensor.accelerometer"
};
}
@Override
public String[] getRequiredPermissions() {
return null;
}
@Override
protected void onEnable() {
sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(SENSOR_TYPE);
//openLogTextFile(STREAM_NAME, getStringPref(Globals.PREF_KEY_ROOT_PATH));
//writeLogTextLine("Created " + this.getClass().getName() + " instance");
//writeLogTextLine("Raw streaming: " + getBooleanPref(Globals.PREF_KEY_RAW_STREAMS_ENABLED));
// Allocate frame buffer, assuming a maximum sampling rate
frameBufferSize = (int)Math.ceil(SENSOR_MAX_RATE/SENSOR_FRAME_DURATION);
frameBuffer = new double[frameBufferSize][3];
//writeLogTextLine("Accelerometer maximum frame size (samples): " + frameBufferSize);
//writeLogTextLine("Accelerometer maximum frame duation (secs): " + SENSOR_FRAME_DURATION);
//allocateFrameFeatureBuffer(STREAM_FEATURES);
featureFFT = new FFT(FFT_SIZE);
featureWin = new Window(frameBufferSize);
fftBufferR = new double[FFT_SIZE];
fftBufferI = new double[FFT_SIZE];
freqBandIdx = new int[FREQ_BANDEDGES.length];
for (int i = 0; i < FREQ_BANDEDGES.length; i ++)
{
freqBandIdx[i] = Math.round((float)FREQ_BANDEDGES[i]*((float)FFT_SIZE/(float)SENSOR_MAX_RATE));
//writeLogTextLine("Frequency band edge " + i + ": " + Integer.toString(freqBandIdx[i]));
}
sensorManager.registerListener(this, sensor, SENSOR_RATE);
}
@Override
protected void onDisable() {
sensorManager.unregisterListener(this);
sensorManager = null;
sensor = null;
}
@Override
protected void onRun(Bundle params) {
prevSecs = 0;
// prevSecs = ((double)System.currentTimeMillis())/1000.0d;
//writeLogTextLine("prevSecs: " + prevSecs);
prevFrameSecs = 0;
frameTimer = 0;
frameSamples = 0;
// Ensure frame buffer is cleared
for (double[] row: frameBuffer)
Arrays.fill(row, 0);
// Create new stream file(s)
//String timeStamp = timeString(startTime);
//if (getBooleanPref(Globals.PREF_KEY_RAW_STREAMS_ENABLED))
//{
// sensorStreamRaw = openStreamFile(STREAM_NAME, timeStamp, Globals.STREAM_EXTENSION_RAW);
//}
//sensorStreamFeatures = openStreamFile(STREAM_NAME, timeStamp, Globals.STREAM_EXTENSION_BIN);
//isRecording = true;
//writeLogTextLine("Accelerometry recording started");
}
@Override
protected void onStop() {
prevSecs = 0;
// prevSecs = ((double)System.currentTimeMillis())/1000.0d;
//writeLogTextLine("prevSecs: " + prevSecs);
prevFrameSecs = 0;
frameTimer = 0;
frameSamples = 0;
// Ensure frame buffer is cleared
for (double[] row: frameBuffer)
Arrays.fill(row, 0);
// Create new stream file(s)
//String timeStamp = timeString(startTime);
//if (getBooleanPref(Globals.PREF_KEY_RAW_STREAMS_ENABLED))
//{
// sensorStreamRaw = openStreamFile(STREAM_NAME, timeStamp, Globals.STREAM_EXTENSION_RAW);
//}
//sensorStreamFeatures = openStreamFile(STREAM_NAME, timeStamp, Globals.STREAM_EXTENSION_BIN);
//isRecording = true;
//writeLogTextLine("Accelerometry recording started");
}
@Override
public void onSensorChanged(SensorEvent event)
{
if (isRunning())
{
double currentSecs = ((double)event.timestamp)/1000000000.0d;
if (prevSecs == 0)
{
prevSecs = currentSecs;
}
double diffSecs = currentSecs - prevSecs;
prevSecs = currentSecs;
double X = event.values[0];
double Y = event.values[1];
double Z = event.values[2];
// Write out raw accelerometry data, if enabled
//if (getBooleanPref(Globals.PREF_KEY_RAW_STREAMS_ENABLED))
//{
// double[] accData = new double[4];
// accData[0] = diffSecs;
// accData[1] = X;
// accData[2] = Y;
// accData[3] = Z;
// writeFeatureFrame(accData, sensorStreamRaw, OUTPUT_FORMAT_FLOAT);
//}
// Store measurement in frame buffer
frameBuffer[frameSamples][0] = X;
frameBuffer[frameSamples][1] = Y;
frameBuffer[frameSamples][2] = Z;
frameSamples ++;
frameTimer += diffSecs;
// Frame complete?
if ((frameTimer >= SENSOR_FRAME_DURATION) || (frameSamples == (frameBufferSize - 1)))
{
Bundle data = new Bundle();
double fN = (double)frameSamples;
if (prevFrameSecs == 0)
{
prevFrameSecs = currentSecs;
}
double diffFrameSecs = currentSecs - prevFrameSecs;
prevFrameSecs = currentSecs;
data.putDouble(DIFF_FRAME_SECS, diffFrameSecs);
data.putDouble(NUM_FRAME_SAMPLES, frameSamples);
// Calculate accelerometry features for X,Y,Z
for (int i = 0; i < 3; i ++)
{
// Mean
double mean = 0;
for (int j = 0; j < frameSamples; j ++)
mean += frameBuffer[j][i];
mean /= fN;
data.putDouble(MEAN, mean);
double accum;
// Absolute central moment
accum = 0;
for (int j = 0; j < frameSamples; j ++)
accum += Math.abs(frameBuffer[j][i] - mean);
data.putDouble(ABSOLUTE_CENTRAL_MOMENT, accum/fN);
// Standard deviation
accum = 0;
for (int j = 0; j < frameSamples; j ++)
accum += (frameBuffer[j][i] - mean)*(frameBuffer[j][i] - mean);
data.putDouble(STANDARD_DEVIATION, Math.sqrt(accum/fN));
// Max deviation
accum = 0;
for (int j = 0; j < frameSamples; j ++)
accum = Math.max(Math.abs(frameBuffer[j][i] - mean),accum);
data.putDouble(MAX_DEVIATION, accum);
// Frequency analysis with zero-padding
Arrays.fill(fftBufferR, 0);
Arrays.fill(fftBufferI, 0);
// Drop accel. values into FFT buffer
for (int j = 0; j < frameSamples; j++)
{
fftBufferR[j] = frameBuffer[j][i] - mean;
}
// In-place windowing
featureWin.applyWindow(fftBufferR);
// In-place FFT
featureFFT.fft(fftBufferR, fftBufferI);
// Get PSD across frequency band ranges
double[] psdAcrossFrequencyBands = new double[FREQ_BANDEDGES.length - 1];
for (int b = 0; b < (FREQ_BANDEDGES.length - 1); b ++)
{
int j = freqBandIdx[b];
int k = freqBandIdx[b+1];
accum = 0;
for (int h = j; h < k; h ++)
{
accum += fftBufferR[h]*fftBufferR[h] + fftBufferI[h]*fftBufferI[h];
}
psdAcrossFrequencyBands[b] = accum/((double)(k - j));
}
data.putDoubleArray(PSD_ACROSS_FREQUENCY_BANDS, psdAcrossFrequencyBands);
}
// Write out features
sendProbeData(Utils.getTimestamp(), data);
//writeFeatureFrame(featureBuffer, sensorStreamFeatures, OUTPUT_FORMAT_FLOAT);
// Reset frame buffer counters
frameSamples = 0;
frameTimer = 0;
// Ensure buffer is zero-padded
for (double[] row: frameBuffer)
Arrays.fill(row, 0);
}
}
}
@Override
public void sendProbeData() {
// TODO Auto-generated method stub
}
@Override
public void onAccuracyChanged(Sensor arg0, int arg1) {
// TODO Auto-generated method stub
}
}