closes #7 - sensoren könne ausgelesen werden closes #12

- bug gefixed! die letzte bekannte position des fingers auf dem display musste zurückgesetzt werden
ref #6 - angefangen matlab code zu portieren
- auto correlationsmethode implementiert
This commit is contained in:
toni
2017-11-25 18:09:16 +01:00
parent 4b21120d2b
commit d0f0d0aa0b
6 changed files with 172 additions and 26 deletions

View File

@@ -41,5 +41,5 @@ dependencies {
implementation 'com.android.support:recyclerview-v7:26.1.0'
implementation 'com.android.support:wear:26.1.0'
compileOnly 'com.google.android.wearable:wearable:2.1.0'
//compile 'com.sdsmdg.harjot:croller:1.0.7'
compile 'com.github.wendykierp:JTransforms:3.1'
}

View File

@@ -1,39 +1,101 @@
package de.tonifetzer.conductorswatch;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadLocalRandom;
import de.tonifetzer.conductorswatch.utilities.Utils;
/**
* Created by toni on 13/11/17.
*/
public class BpmEstimator implements Runnable {
//TODO: diesen estimator testen. kommen alle messungen? wie sind die messungen zeitlich voneinander verschieden?
//TODO: klapp das wirklich mit den 4ms. passt der buffer? gehen auch höhere zeiten?
//TODO: einfügen der logik autoCorr + FindPeaks
public class BpmEstimator implements SensorEventListener {
private volatile boolean mRunning = true;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private Context mContext;
private Utils.AccelerometerWindowBuffer mAccelerometerWindowBuffer;
@Override
public void run() {
while(mRunning){
int randomNum = ThreadLocalRandom.current().nextInt(0, 240 + 1);
private Handler mHandler;
private int mUpdaterate_ms = 100;
private boolean mSensorUpdateFlag = false;
for (OnBpmEstimatorListener listener:listeners) {
listener.onNewDataAvailable((float) randomNum);
private final Runnable mProcessSensors = new Runnable() {
@Override
public void run() {
if(mAccelerometerWindowBuffer.isNextWindowReady()){
//int randomNum = ThreadLocalRandom.current().nextInt(0, 240 + 1);
long diff = mAccelerometerWindowBuffer.getYongest().ts - mAccelerometerWindowBuffer.getOldest().ts;
for (OnBpmEstimatorListener listener:listeners) {
listener.onNewDataAvailable((float) diff);
}
}
try {
Thread.sleep(60000 / 60);
} catch (InterruptedException e) {
e.printStackTrace();
}
mSensorUpdateFlag = true;
// The Runnable is posted to run again here:
mHandler.postDelayed(this, mUpdaterate_ms);
}
};
public BpmEstimator(Context mContext){
this.mContext = mContext;
}
public void start() {
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
mAccelerometerWindowBuffer = new Utils.AccelerometerWindowBuffer(4096, 256);
mHandler = new Handler();
mHandler.post(mProcessSensors); //start runnable
}
public void stop() {
mRunning = false;
mHandler.removeCallbacks(mProcessSensors); //remove runnable
mSensorManager.unregisterListener(this);
}
@Override
public void onSensorChanged(SensorEvent se) {
// einkommentieren, falls die updaterate beschränkt werden soll. aktuell maximum speed
//if(mSensorUpdateFlag) {
//ca 200hz, every 5 to 6 ms we have an update
if (se.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
mAccelerometerWindowBuffer.add(new Utils.AccelerometerData(System.currentTimeMillis(), se.values[0], se.values[1], se.values[2]));
}
// mSensorUpdateFlag = false;
//}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
// do nothin
}
/**
* Interface for callback calculated bpm
*/

View File

@@ -121,6 +121,7 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
mHandler.removeCallbacks(mLongPressed);
if(mLongPressHandlerActivated){
mLongPressHandlerActivated = false;
mPreviousMovePoint = null;
return true;
}
return false;

View File

@@ -29,7 +29,7 @@ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeLis
private BpmEstimator mBpmEstimator;
private Metronome mMetronome;
private Thread mBpmThread;
//private Thread mBpmThread;
private Thread mMetronomeThread;
private Vibrator mVibrator;
@@ -58,9 +58,9 @@ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeLis
super.onCreate(savedInstanceState);
// init bpm estimator and listener
mBpmEstimator = new BpmEstimator();
mBpmEstimator = new BpmEstimator(getContext());
mBpmEstimator.add(this);
mBpmThread = new Thread(mBpmEstimator, "estThread");
//mBpmThread = new Thread(mBpmEstimator, "estThread");
mBpmList = new Vector<Float>();
@@ -99,7 +99,7 @@ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeLis
super.onStart();
// start the worker thread for bpm estimator
mBpmThread.start();
mBpmEstimator.start();
// start the worker thread for metronom
mMetronomeThread.start();
@@ -111,12 +111,12 @@ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeLis
// stop the worker thread for bpm estimator
mBpmEstimator.stop();
mBpmThread.interrupt();
/*mBpmThread.interrupt();
try {
mBpmThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}*/
// stop the worker thread for metronom
mMetronome.stop();

View File

@@ -2,12 +2,13 @@ package de.tonifetzer.conductorswatch.utilities;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import de.tonifetzer.conductorswatch.Croller;
import org.jtransforms.fft.DoubleFFT_1D;
import java.util.ArrayList;
import java.util.Queue;
public class Utils {
public static float getDistance(float x1, float y1, float x2, float y2) {
@@ -26,4 +27,86 @@ public class Utils {
return px / ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}
public static class AccelerometerData {
public float x,y,z;
public long ts;
public AccelerometerData(long ts, float x, float y, float z){
this.ts = ts;
this.x = x;
this.y = y;
this.z = z;
}
}
//TODO: implement methods providing x,y,z and ts as solo vectors
//TODO: implement sliding window counter
public static class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
private int mWindowSize;
private int mOverlapSize;
private int mOverlapCounter;
public AccelerometerWindowBuffer(int windowSize, int overlap){
this.mWindowSize = windowSize;
this.mOverlapSize = overlap;
mOverlapCounter = 1;
}
public boolean add(AccelerometerData ad){
boolean r = super.add(ad);
if (size() > mWindowSize){
removeRange(0, size() - mWindowSize);
}
++mOverlapCounter;
return r;
}
public boolean isNextWindowReady(){
if(size() == mWindowSize && mOverlapCounter > mOverlapSize){
mOverlapCounter = 1;
return true;
}
return false;
}
public AccelerometerData getYongest() {
return get(size() - 1);
}
public AccelerometerData getOldest() {
return get(0);
}
}
public static double sqr(double x) {
return x * x;
}
//TODO: implement maxLag as input
//TODO: implement positive and negative lag output
public void fftAutoCorrelation(double [] x, double [] ac) {
int n = x.length;
// Assumes n is even.
DoubleFFT_1D fft = new DoubleFFT_1D(n);
fft.realForward(x);
//ac[0] = sqr(x[0]); // For normal xcov
ac[0] = 0; // For statistical convention, zero out the mean
ac[1] = sqr(x[1]);
for (int i = 2; i < n; i += 2) {
ac[i] = sqr(x[i]) + sqr(x[i+1]);
ac[i+1] = 0;
}
DoubleFFT_1D ifft = new DoubleFFT_1D(n);
ifft.realInverse(ac, true);
//For statistical convention, normalize by dividing through with variance
for (int i = 1; i < n; i++){
ac[i] /= ac[0];
}
ac[0] = 1;
}
}

View File

@@ -6,7 +6,7 @@
%load file provided by the sensor readout app
%% SMARTWATCH LG WEAR ------> 100 hz - 1000hz
measurements = dlmread('../measurements/lgWear/PR_recording_80bpm_4-4_177596720.csv', ';'); %*
measurements = dlmread('../../measurements/lgWear/PR_recording_80bpm_4-4_177596720.csv', ';'); %*
%measurements = dlmread('../measurements/lgWear/recording_48bpm_4-4_176527527.csv', ';');
%measurements = dlmread('../measurements/lgWear/recording_48bpm_4-4_176606785.csv', ';');
%measurements = dlmread('../measurements/lgWear/recording_48bpm_4-4_176696356.csv', ';');