ref #27 -new gradle and sdk version
- added faster bpm estimation by using multiple windows now - implemented a simple z score based outlier detection - some ugly heuristics to prefend to many -1 on the screen NOTE: this is a very unstable version of the code, very protoype
This commit is contained in:
@@ -7,7 +7,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
classpath 'com.android.tools.build:gradle:3.1.2'
|
||||
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#Tue Dec 19 11:59:51 CET 2017
|
||||
#Fri Apr 27 11:02:05 CEST 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||
|
||||
@@ -6,10 +6,14 @@ import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@@ -21,6 +25,7 @@ import de.tonifetzer.conductorswatch.bpmEstimation.BpmEstimator;
|
||||
import de.tonifetzer.conductorswatch.network.SensorDataFileSender;
|
||||
import de.tonifetzer.conductorswatch.network.SensorDataFileStreamer;
|
||||
import de.tonifetzer.conductorswatch.utilities.ByteStreamWriter;
|
||||
import de.tonifetzer.conductorswatch.utilities.Utils;
|
||||
|
||||
/**
|
||||
* Created by toni on 13/11/17.
|
||||
@@ -34,6 +39,7 @@ public class Estimator implements SensorEventListener {
|
||||
private Context mContext;
|
||||
private AccelerometerWindowBuffer mAccelerometerWindowBuffer;
|
||||
private BpmEstimator mBpmEstimator;
|
||||
private double mCurrentBpm;
|
||||
|
||||
private ByteStreamWriter mByteStreamWriterAcc;
|
||||
private ByteStreamWriter mByteStreamWriterGyro;
|
||||
@@ -44,6 +50,7 @@ public class Estimator implements SensorEventListener {
|
||||
|
||||
public Estimator(Context mContext){
|
||||
this.mContext = mContext;
|
||||
this.mCurrentBpm = -1;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
@@ -54,7 +61,8 @@ public class Estimator implements SensorEventListener {
|
||||
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
|
||||
mSensorManager.registerListener(this, mRawAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
|
||||
|
||||
mAccelerometerWindowBuffer = new AccelerometerWindowBuffer(6000, 1500);
|
||||
|
||||
mAccelerometerWindowBuffer = new AccelerometerWindowBuffer(6000, 750);
|
||||
mBpmEstimator = new BpmEstimator(mAccelerometerWindowBuffer, 0, 5000);
|
||||
|
||||
mTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
@@ -62,11 +70,89 @@ public class Estimator implements SensorEventListener {
|
||||
public void run() {
|
||||
|
||||
if (mAccelerometerWindowBuffer.isNextWindowReady()) {
|
||||
double bpm = mBpmEstimator.estimate(mAccelerometerWindowBuffer.getFixedWindow());
|
||||
|
||||
LinkedList<Double> bpmList = new LinkedList<>();
|
||||
|
||||
//todo: wie viele dieser Klassen kann ich wegwerfen um das Ergebnis nich schlechter zu machen?
|
||||
double bpm60 = mBpmEstimator.estimate(mAccelerometerWindowBuffer.getFixedSizedWindow());
|
||||
double bpm85 = mBpmEstimator.estimate(mAccelerometerWindowBuffer.getFixedSizedWindow(3500, 750));
|
||||
double bpm110 = mBpmEstimator.estimate(mAccelerometerWindowBuffer.getFixedSizedWindow(2600, 750));
|
||||
double bpm135 = mBpmEstimator.estimate(mAccelerometerWindowBuffer.getFixedSizedWindow(2000, 750));
|
||||
double bpm160 = mBpmEstimator.estimate(mAccelerometerWindowBuffer.getFixedSizedWindow(1600,750));
|
||||
double bpm200 = mBpmEstimator.estimate(mAccelerometerWindowBuffer.getFixedSizedWindow(1200, 750));
|
||||
|
||||
//add to list
|
||||
//todo: make this cool...
|
||||
//vielleicht einen weighted mean?
|
||||
bpmList.add(bpm60);
|
||||
bpmList.add(bpm85);
|
||||
bpmList.add(bpm110); //110, 135, 160 langen auch schon
|
||||
bpmList.add(bpm135);
|
||||
bpmList.add(bpm160);
|
||||
bpmList.add(bpm200);
|
||||
|
||||
//Log.d("BPM: ", bpmList.toString());
|
||||
|
||||
//remove all -1 and calc bpmMean
|
||||
while(bpmList.remove(Double.valueOf(-1))) {}
|
||||
|
||||
//remove outliers
|
||||
//todo: aktuell wird die liste hier sortiert.. eig net so schön.
|
||||
Utils.removeOutliersZScore(bpmList, 3.4);
|
||||
//Utils.removeOutliersHeuristic();
|
||||
|
||||
//Log.d("BPM: ", bpmList.toString());
|
||||
|
||||
double bpm = -1;
|
||||
if(!bpmList.isEmpty()) {
|
||||
double bpmMean = Utils.mean(bpmList);
|
||||
double bpmMedian = Utils.median(bpmList);
|
||||
|
||||
double bpmDiffSlowFast = bpmList.getFirst() - bpmList.getLast();
|
||||
if (Math.abs(bpmDiffSlowFast) > 25) {
|
||||
|
||||
double tmpBPM = bpmMean + 25;
|
||||
|
||||
while(bpm == -1){
|
||||
if (tmpBPM < 60) {
|
||||
bpm = bpm60;
|
||||
if(bpm == -1){
|
||||
bpm = bpmMean;
|
||||
}
|
||||
}
|
||||
else if (tmpBPM < 85) {
|
||||
bpm = bpm85;
|
||||
} else if (tmpBPM < 110) {
|
||||
bpm = bpm110;
|
||||
} else if (tmpBPM < 135) {
|
||||
bpm = bpm135;
|
||||
} else if (tmpBPM < 160) {
|
||||
bpm = bpm160;
|
||||
} else {
|
||||
bpm = bpm200;
|
||||
}
|
||||
|
||||
tmpBPM -= 5;
|
||||
}
|
||||
|
||||
//Log.d("BPM: ", "CHANGE");
|
||||
|
||||
} else {
|
||||
|
||||
bpm = bpmMean;
|
||||
//Log.d("BPM: ", "STAY");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
for (OnBpmEstimatorListener listener : listeners) {
|
||||
listener.onNewDataAvailable(bpm);
|
||||
listener.onNewDataAvailable(bpm); //135 gibt gute ergebnisse!
|
||||
}
|
||||
|
||||
//update the windowSize and updaterate depending on current bpm
|
||||
//updateWindowSizeAndOverlap(bpm);
|
||||
}
|
||||
}
|
||||
}, 0, 100);
|
||||
@@ -134,4 +220,39 @@ public class Estimator implements SensorEventListener {
|
||||
private List<OnBpmEstimatorListener> listeners = new CopyOnWriteArrayList<OnBpmEstimatorListener>();
|
||||
public void add(OnBpmEstimatorListener listener){listeners.add(listener);}
|
||||
public void remove(OnBpmEstimatorListener listener){listeners.remove(listener);}
|
||||
|
||||
/**
|
||||
* Simple function that sets die windowSize and Overlap time to a specific value
|
||||
* depending on the currentBPM. Nothing rly dynamical. However, should work out
|
||||
* for our purposes.
|
||||
* @param bpm
|
||||
*/
|
||||
private void updateWindowSizeAndOverlap(double bpm){
|
||||
|
||||
//round to nearest tenner. this limits the number of windowsize and overlap changes.
|
||||
int newBpmRounded = (int) Math.round(bpm / 10.0) * 10;
|
||||
int curBpmRounded = (int) Math.round(mCurrentBpm / 10.0) * 10;
|
||||
|
||||
//TODO: i guess this is not the best method.. if the default sizes always produces -1, we run into problems
|
||||
if(bpm != -1 && curBpmRounded != newBpmRounded){
|
||||
|
||||
int overlap_ms = 60000 / newBpmRounded;
|
||||
int window_ms = overlap_ms * 5;
|
||||
|
||||
//idea: wenn man mehrere fenster parallel laufen lässt und beobachtet, müsste das kleinste fenster die tempowechsel eigentlich am
|
||||
// besten mitbekommen. dieses fenster dann bestimmen lassen?
|
||||
|
||||
mAccelerometerWindowBuffer.setWindowSize(window_ms);
|
||||
mAccelerometerWindowBuffer.setOverlapSize(overlap_ms);
|
||||
|
||||
} else if (bpm == -1){
|
||||
//if bpm is -1 due to a non-classification, reset to default.
|
||||
|
||||
//idea: anstatt auf einen festen wert zu setzen, könnte man das fenster dann auch einfach ein wenig größer / kleiner machen.
|
||||
mAccelerometerWindowBuffer.setWindowSize(3000);
|
||||
mAccelerometerWindowBuffer.setOverlapSize(750);
|
||||
}
|
||||
|
||||
mCurrentBpm = bpm;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,6 +307,8 @@ public class MainActivity extends WearableActivity implements WorkerFragment.OnF
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
||||
mSender.wakeUpPhoneCall();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -144,14 +144,14 @@ public class WorkerFragment extends Fragment implements Metronome.OnMetronomeLis
|
||||
public void onNewDataAvailable(double bpm) {
|
||||
|
||||
//TODO: what if multiple threads access mBpmList? put into synchronized? @frank fragen :D
|
||||
//TODO: send this to smartphone
|
||||
if(mWorkerRunning){
|
||||
|
||||
mBpmList.add(bpm);
|
||||
|
||||
if(bpm == -1){
|
||||
//to stuff with UI. Write Text or make XX or something like that
|
||||
}
|
||||
|
||||
mBpmList.add(bpm);
|
||||
|
||||
// we need this here, since ui elements can only be changed within activity thread and
|
||||
// onNewDataAvailable lives inside the bpm estimation thread.
|
||||
// TODO: is this really okay? also synchronized?
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.tonifetzer.conductorswatch.bpmEstimation;
|
||||
|
||||
import de.tonifetzer.conductorswatch.utilities.Utils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by toni on 15/12/17.
|
||||
@@ -34,23 +35,37 @@ public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
||||
|
||||
//add element
|
||||
boolean r = super.add(ad);
|
||||
if ((getYongest().ts - getOldest().ts) > mWindowSize){
|
||||
removeOldElements();
|
||||
|
||||
long oldestTime = getYongest().ts - mWindowSize;
|
||||
for(int i = 0; i < size(); ++i) {
|
||||
if (get(i).ts > oldestTime) {
|
||||
break;
|
||||
}
|
||||
remove(i);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeOldElements(){
|
||||
synchronized (this) {
|
||||
if (!isEmpty()) {
|
||||
if ((getYongest().ts - getOldest().ts) > mWindowSize) {
|
||||
|
||||
long oldestTime = getYongest().ts - mWindowSize;
|
||||
for (int i = 0; i < size(); ++i) {
|
||||
if (get(i).ts > oldestTime) {
|
||||
break;
|
||||
}
|
||||
super.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNextWindowReady(long lastWindowTS, int overlapSize){
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isNextWindowReady(){
|
||||
|
||||
if(!isEmpty()){
|
||||
if(((getYongest().ts - getOldest().ts) > mWindowSize / 2) && mOverlapCounter > mOverlapSize){
|
||||
if(((getYongest().ts - getOldest().ts) > mWindowSize / 4) && mOverlapCounter > mOverlapSize){
|
||||
mOverlapCounter = 0;
|
||||
|
||||
return true;
|
||||
@@ -59,22 +74,40 @@ public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
||||
return false;
|
||||
}
|
||||
|
||||
public AccelerometerWindowBuffer getFixedWindow(){
|
||||
AccelerometerWindowBuffer other = new AccelerometerWindowBuffer(mWindowSize, mOverlapSize);
|
||||
synchronized (this){
|
||||
for(AccelerometerData data : this){
|
||||
other.add(new AccelerometerData(data));
|
||||
}
|
||||
public AccelerometerWindowBuffer getFixedSizedWindow(int size, int overlap){
|
||||
AccelerometerWindowBuffer other = new AccelerometerWindowBuffer(size, overlap);
|
||||
|
||||
double sampleRate = ((getYongest().ts - getOldest().ts) / super.size());
|
||||
|
||||
//if current size is smaller then wanted size, start at 0 and provide smaller list
|
||||
int start = 0;
|
||||
if((getYongest().ts - getOldest().ts) > size){
|
||||
start = (int) Math.round(super.size() - (size / sampleRate));
|
||||
}
|
||||
|
||||
// start should not be negative, this can happen due to rounding errors.
|
||||
start = start < 0? 0 : start;
|
||||
|
||||
synchronized (this) {
|
||||
other.addAll(super.subList(start, super.size()));
|
||||
}
|
||||
|
||||
return other;
|
||||
}
|
||||
|
||||
public AccelerometerWindowBuffer getFixedSizedWindow(){
|
||||
return getFixedSizedWindow(mWindowSize, mOverlapSize);
|
||||
}
|
||||
|
||||
//TODO: check if list is empty! this causes indexoutofbounce
|
||||
public AccelerometerData getYongest() {
|
||||
return get(size() - 1);
|
||||
synchronized (this){
|
||||
return super.get(size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public AccelerometerData getOldest() {
|
||||
return get(0);
|
||||
return super.get(0);
|
||||
}
|
||||
|
||||
public double[] getX(){
|
||||
@@ -96,4 +129,22 @@ public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
||||
public int getOverlapSize(){
|
||||
return mOverlapSize;
|
||||
}
|
||||
|
||||
public void setWindowSize(int size_ms){
|
||||
this.mWindowSize = size_ms;
|
||||
removeOldElements(); // need to call this here, to remove too old elements, if the windowSize gets smaller.
|
||||
}
|
||||
|
||||
public void setOverlapSize(int size_ms){
|
||||
this.mOverlapSize = size_ms;
|
||||
this.mOverlapCounter = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(){
|
||||
synchronized (this){
|
||||
super.clear();
|
||||
this.mOverlapCounter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,9 @@ public class BpmEstimator {
|
||||
mResetCounter = 0;
|
||||
mResetLimit_ms = resetAfter_ms;
|
||||
|
||||
|
||||
//TODO: this is to easy. since the new dyn. windowsize produces smaller update times, we need to consider something, that
|
||||
//TODO: holds more values, if they are similar, an resets the history if not.
|
||||
mMvg = new MovingFilter(2);
|
||||
//mKalman = new SimpleKalman();
|
||||
}
|
||||
@@ -52,6 +55,10 @@ public class BpmEstimator {
|
||||
sampleRate = Math.round(Utils.mean(Utils.diff(fixedWindow.getTs())));
|
||||
}
|
||||
|
||||
if(sampleRate == 0){
|
||||
int breakhere = 0;
|
||||
}
|
||||
|
||||
AccelerometerInterpolator interp = new AccelerometerInterpolator(fixedWindow, sampleRate);
|
||||
|
||||
//are we conducting?
|
||||
@@ -80,6 +87,8 @@ public class BpmEstimator {
|
||||
mMvg.add(estimatedBPM);
|
||||
mBpmHistory.add(mMvg.getAverage());
|
||||
|
||||
//mBpmHistory.add(estimatedBPM);
|
||||
|
||||
//moving median (lohnt nur bei konstantem tempo, da wir nur ein tempo damit gut halten können.)
|
||||
//mMvg.add(estimatedBPM);
|
||||
//mBpmHistory.add(mMvg.getMedian());
|
||||
@@ -96,7 +105,7 @@ public class BpmEstimator {
|
||||
mBpmHistory.clear();
|
||||
|
||||
//TODO: send signal to clear.
|
||||
mBuffer.clear();
|
||||
//mBuffer.clear();
|
||||
mMvg.clear();
|
||||
mResetCounter = 0;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
//TODO: change from double to generic type
|
||||
public class Utils {
|
||||
@@ -37,7 +39,7 @@ public class Utils {
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static double sum (LinkedList<Double> data){
|
||||
public static double sum (List<Double> data){
|
||||
double sum = 0;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
sum += data.get(i).doubleValue();
|
||||
@@ -81,11 +83,11 @@ public class Utils {
|
||||
return (double) sum(data) / (double) data.length;
|
||||
}
|
||||
|
||||
public static double mean(LinkedList<Double> data){
|
||||
public static double mean(List<Double> data){
|
||||
return sum(data) / data.size();
|
||||
}
|
||||
|
||||
public static double median(LinkedList<Double> data){
|
||||
public static double median(List<Double> data){
|
||||
data.sort(Comparator.naturalOrder());
|
||||
|
||||
double median;
|
||||
@@ -97,6 +99,18 @@ public class Utils {
|
||||
return median;
|
||||
}
|
||||
|
||||
public static double mad(List<Double> data){
|
||||
|
||||
double median = median(data);
|
||||
|
||||
List<Double> tmpList = new ArrayList<>();
|
||||
for(double value : data){
|
||||
tmpList.add(Math.abs(value - median));
|
||||
}
|
||||
|
||||
return median(tmpList);
|
||||
}
|
||||
|
||||
//TODO: Could be slow.. faster method?
|
||||
public static double geometricMean(double[] data) {
|
||||
double sum = data[0];
|
||||
@@ -173,4 +187,17 @@ public class Utils {
|
||||
return px / ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
|
||||
}
|
||||
|
||||
public static void removeOutliersZScore(List<Double> data, double score) {
|
||||
|
||||
if(!data.isEmpty()){
|
||||
double median = median(data);
|
||||
double mad = mad(data);
|
||||
|
||||
for(Iterator<Double> it = data.iterator(); it.hasNext(); ){
|
||||
|
||||
double M = Math.abs((0.6745 * (it.next() - median)) / mad);
|
||||
if (M > score){ it.remove(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
classpath 'com.android.tools.build:gradle:3.1.2'
|
||||
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#Mon Nov 13 10:12:40 CET 2017
|
||||
#Fri Apr 27 14:10:48 CEST 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
%measurements = dlmread('../measurements/wearR/recording_180bpm_4-4_177064915.csv', ';'); *
|
||||
|
||||
|
||||
%files = dir(fullfile('../../measurements/lgWear/', '*.csv'));
|
||||
files = dir(fullfile('../../measurements/lgWear/', '*.csv'));
|
||||
%files = dir(fullfile('../../measurements/wearR/', '*.csv'));
|
||||
files = dir(fullfile('../../measurements/peter_failed/', '*.csv'));
|
||||
%files = dir(fullfile('../../measurements/peter_failed/', '*.csv'));
|
||||
|
||||
for file = files'
|
||||
|
||||
@@ -52,7 +52,7 @@ for file = files'
|
||||
|
||||
%draw the raw acc data
|
||||
m_idx = [];
|
||||
m_idx = (measurements(:,2)==10);
|
||||
m_idx = (measurements(:,2)==2); %Android App: 10, Normal Data: 2
|
||||
m = measurements(m_idx, :);
|
||||
|
||||
%Interpolate to generate a constant sample rate to 250hz (4ms per sample)
|
||||
@@ -71,13 +71,19 @@ for file = files'
|
||||
legend("x", "location", "eastoutside");
|
||||
|
||||
figure(2);
|
||||
plot(m(:,1),m(:,4)) %y
|
||||
plot(m(:,1),m(:,4)) %yt
|
||||
legend("y", "location", "eastoutside");
|
||||
|
||||
figure(3);
|
||||
plot(m(:,1),m(:,5)) %z
|
||||
legend("z", "location", "eastoutside");
|
||||
|
||||
%magnitude
|
||||
magnitude = sqrt(sum(m(:,3:5).^2,2));
|
||||
figure(5);
|
||||
plot(m(:,1), magnitude);
|
||||
legend("magnitude", "location", "eastoutside");
|
||||
|
||||
waitforbuttonpress();
|
||||
|
||||
%save timestamps
|
||||
@@ -98,26 +104,39 @@ for file = files'
|
||||
[corr_x, lag_x] = xcov(m(i-window_size:i,3), (window_size/4), "coeff");
|
||||
[corr_y, lag_y] = xcov(m(i-window_size:i,4), (window_size/4), "coeff");
|
||||
[corr_z, lag_z] = xcov(m(i-window_size:i,5), (window_size/4), "coeff");
|
||||
[corr_mag, lag_mag] = xcov(magnitude(i-window_size:i), (window_size/4), "coeff");
|
||||
|
||||
|
||||
%autocorrelation of the autocorrelation?!
|
||||
%[corr_corr_x, lag_lag_x] = xcov(corr_x, length(corr_x), "coeff");
|
||||
%[corr_corr_y, lag_lag_y] = xcov(corr_y, length(corr_x), "coeff");
|
||||
%[corr_corr_z, lag_lag_z] = xcov(corr_z, length(corr_x), "coeff");
|
||||
|
||||
|
||||
corr_x_pos = corr_x;
|
||||
corr_y_pos = corr_y;
|
||||
corr_z_pos = corr_z;
|
||||
corr_mag_pos = corr_mag;
|
||||
|
||||
corr_x_pos(corr_x_pos<0)=0;
|
||||
corr_y_pos(corr_y_pos<0)=0;
|
||||
corr_z_pos(corr_z_pos<0)=0;
|
||||
corr_mag_pos(corr_mag_pos<0)=0;
|
||||
|
||||
[peak_x, idx_x_raw] = findpeaks(corr_x_pos, 'MinPeakHeight', 0.1,'MinPeakDistance', 50, 'MinPeakProminence', 0.1);
|
||||
[peak_y, idx_y_raw] = findpeaks(corr_y_pos, 'MinPeakHeight', 0.1,'MinPeakDistance', 50, 'MinPeakProminence', 0.1);
|
||||
[peak_z, idx_z_raw] = findpeaks(corr_z_pos, 'MinPeakHeight', 0.1,'MinPeakDistance', 50, 'MinPeakProminence', 0.1);
|
||||
[peak_mag, idx_mag_raw] = findpeaks(corr_mag_pos, 'MinPeakHeight', 0.1,'MinPeakDistance', 50, 'MinPeakProminence', 0.1);
|
||||
|
||||
idx_x_raw = sort(idx_x_raw);
|
||||
idx_y_raw = sort(idx_y_raw);
|
||||
idx_z_raw = sort(idx_z_raw);
|
||||
idx_mag_raw = sort(idx_mag_raw);
|
||||
|
||||
idx_x = findFalseDetectedPeaks(idx_x_raw, lag_x, corr_x);
|
||||
idx_y = findFalseDetectedPeaks(idx_y_raw, lag_y, corr_y);
|
||||
idx_z = findFalseDetectedPeaks(idx_z_raw, lag_z, corr_z);
|
||||
idx_mag = findFalseDetectedPeaks(idx_mag_raw, lag_mag, corr_mag);
|
||||
|
||||
Xwindow = m(i-window_size:i,3);
|
||||
Xwindow_mean_ts_diff = mean(diff(lag_x(idx_x) * sample_rate_ms)); %2.5 ms is the time between two samples at 400hz
|
||||
@@ -155,6 +174,20 @@ for file = files'
|
||||
title(strcat(" ", m_label_ms, " ", m_label_bpm));
|
||||
hold ("off");
|
||||
|
||||
%magnitude
|
||||
Mwindow = magnitude(i-window_size:i);
|
||||
Mwindow_mean_ts_diff = mean(diff(lag_mag(idx_mag)* sample_rate_ms));
|
||||
Mwindow_mean_bpm = (60000 / (Mwindow_mean_ts_diff));
|
||||
|
||||
figure(14);
|
||||
plot(lag_mag, corr_mag, lag_mag(idx_mag), corr_mag(idx_mag), 'r*', lag_mag(idx_mag_raw), corr_mag(idx_mag_raw), 'g*') %z
|
||||
hold ("on")
|
||||
m_label_ms = strcat(" mean ms: ", num2str(Mwindow_mean_ts_diff));
|
||||
m_label_bpm = strcat(" mean bpm: ", num2str(Mwindow_mean_bpm));
|
||||
title(strcat(" ", m_label_ms, " ", m_label_bpm));
|
||||
hold ("off");
|
||||
|
||||
|
||||
|
||||
%breakpoints dummy for testing
|
||||
if(length(idx_x) > length(idx_x_raw))
|
||||
|
||||
Reference in New Issue
Block a user