added magnitude
refactored plotting refactored some code
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import bpmEstimation.*;
|
||||
import uk.me.berndporr.iirj.Butterworth;
|
||||
import utilities.Plot;
|
||||
import utilities.Utils;
|
||||
|
||||
import java.awt.*;
|
||||
@@ -6,6 +8,7 @@ import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@@ -16,33 +19,46 @@ import java.util.stream.IntStream;
|
||||
public class Main {
|
||||
|
||||
public static void main(String [ ] args) {
|
||||
//File folder = new File("/home/toni/Documents/programme/dirigent/measurements/lgWear");
|
||||
File folder = new File("/home/toni/Documents/programme/dirigent/measurements/peter_failed");
|
||||
//File folder = new File("/home/toni/Documents/programme/dirigent/measurements/2017.06/lgWear");
|
||||
//File folder = new File("/home/toni/Documents/programme/dirigent/measurements/peter_failed");
|
||||
File folder = new File("/home/toni/Documents/programme/dirigent/measurements/2018.06/frank/mSensor");
|
||||
File[] listOfFiles = folder.listFiles();
|
||||
Arrays.sort(listOfFiles);
|
||||
|
||||
Utils.ShowPNG windowRaw = new Utils.ShowPNG();
|
||||
Utils.ShowPNG windowAuto = new Utils.ShowPNG();
|
||||
Utils.ShowPNG windowPeaksX = new Utils.ShowPNG();
|
||||
Utils.ShowPNG windowPeaksY = new Utils.ShowPNG();
|
||||
Utils.ShowPNG windowPeaksZ = new Utils.ShowPNG();
|
||||
|
||||
// //TODO: write debug class, that is able to simply draw images...
|
||||
// Utils.ShowPNG windowRaw = new Utils.ShowPNG();
|
||||
// Utils.ShowPNG windowAuto = new Utils.ShowPNG();
|
||||
// Utils.ShowPNG windowAutoButter = new Utils.ShowPNG();
|
||||
// Utils.ShowPNG windowPeaksX = new Utils.ShowPNG();
|
||||
// Utils.ShowPNG windowPeaksY = new Utils.ShowPNG();
|
||||
// Utils.ShowPNG windowPeaksZ = new Utils.ShowPNG();
|
||||
|
||||
|
||||
// iterate trough files in measurements folder
|
||||
for (File file : listOfFiles) {
|
||||
if (file.isFile() && file.getName().contains(".csv")) {
|
||||
|
||||
//TODO: mach Fenster genau 6 sekunden groß. Egal wie viele Samples.
|
||||
AccelerometerWindowBuffer accWindowBuffer = new AccelerometerWindowBuffer(6000, 1500);
|
||||
//TODO: Die Raw Sensordaten sollte man vielleicht etwas glätten. Sieh Aufnahmen von Frank!
|
||||
AccelerometerWindowBuffer accWindowBuffer = new AccelerometerWindowBuffer(6000, 750);
|
||||
BpmEstimator bpmEstimator = new BpmEstimator(accWindowBuffer, 0, 5000);
|
||||
Butterworth butterLowpass = new Butterworth();
|
||||
|
||||
//read the file line by line
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
|
||||
|
||||
//read the first three lines and print out what file it is!
|
||||
String comment = br.readLine();
|
||||
br.readLine();
|
||||
br.readLine();
|
||||
System.out.println(comment);
|
||||
|
||||
for (String line; (line = br.readLine()) != null; ) {
|
||||
// process the line.
|
||||
String[] measurement = line.split(";");
|
||||
|
||||
//if linear acc
|
||||
if(measurement[1].equals("10")){
|
||||
if(measurement[1].equals("3")){
|
||||
long ts = Long.parseLong(measurement[0]);
|
||||
double x = Double.parseDouble(measurement[2]);
|
||||
double y = Double.parseDouble(measurement[3]);
|
||||
@@ -53,86 +69,132 @@ public class Main {
|
||||
//do calculation stuff
|
||||
if(accWindowBuffer.isNextWindowReady()){
|
||||
|
||||
double curBpm = bpmEstimator.estimate();
|
||||
//System.out.println("BPM: " + curBpm);
|
||||
LinkedList<Double> bpmList = new LinkedList<>();
|
||||
|
||||
double sampleRate = 20;
|
||||
AccelerometerInterpolator acInterp = new AccelerometerInterpolator(accWindowBuffer, sampleRate);
|
||||
int peakWidth = (int) Math.round(250 / sampleRate);
|
||||
// Calculate the BPM for different window sizes
|
||||
double bpm60 = bpmEstimator.estimate();
|
||||
double bpm85 = bpmEstimator.estimate(3500, 750);
|
||||
double bpm110 = bpmEstimator.estimate(2600, 750);
|
||||
double bpm135 = bpmEstimator.estimate(2000, 750);
|
||||
double bpm160 = bpmEstimator.estimate(1600,750);
|
||||
double bpm200 = bpmEstimator.estimate(1200, 750);
|
||||
|
||||
//print raw x,y,z
|
||||
double[] dTs = IntStream.range(0, accWindowBuffer.getTs().length).mapToDouble(i -> accWindowBuffer.getTs()[i]).toArray();
|
||||
double[] dTsInterp = IntStream.range(0, acInterp.getTs().length).mapToDouble(i -> acInterp.getTs()[i]).toArray();
|
||||
Plot plotRaw = Plot.plot(Plot.plotOpts().
|
||||
title("Raw Acc Data").
|
||||
legend(Plot.LegendFormat.BOTTOM)).
|
||||
series("x", Plot.data().xy(dTsInterp, acInterp.getX()), Plot.seriesOpts().color(Color.RED)).
|
||||
series("y", Plot.data().xy(dTsInterp, acInterp.getY()), Plot.seriesOpts().color(Color.BLUE)).
|
||||
series("z", Plot.data().xy(dTsInterp, acInterp.getZ()), Plot.seriesOpts().color(Color.GREEN));
|
||||
System.out.println("--------------------------------------------------");
|
||||
|
||||
windowRaw.set(plotRaw.draw());
|
||||
bpmList.add(bpm60);
|
||||
bpmList.add(bpm85);
|
||||
bpmList.add(bpm110);
|
||||
bpmList.add(bpm135);
|
||||
bpmList.add(bpm160);
|
||||
bpmList.add(bpm200);
|
||||
|
||||
//auto corr
|
||||
double[] xAutoCorr = new AutoCorrelation(acInterp.getX(), accWindowBuffer.size()).getCorr();
|
||||
double[] yAutoCorr = new AutoCorrelation(acInterp.getY(), accWindowBuffer.size()).getCorr();
|
||||
double[] zAutoCorr = new AutoCorrelation(acInterp.getZ(), accWindowBuffer.size()).getCorr();
|
||||
|
||||
//print autocorr
|
||||
int[] tmp = IntStream.rangeClosed(-((xAutoCorr.length - 1)/2), ((xAutoCorr.length - 1)/2)).toArray();
|
||||
double[] rangeAuto = IntStream.range(0, tmp.length).mapToDouble(i -> tmp[i]).toArray();
|
||||
Plot plotCorr = Plot.plot(Plot.plotOpts().
|
||||
title("Auto Correlation").
|
||||
legend(Plot.LegendFormat.BOTTOM)).
|
||||
series("x", Plot.data().xy(rangeAuto, xAutoCorr), Plot.seriesOpts().color(Color.RED)).
|
||||
series("y", Plot.data().xy(rangeAuto, yAutoCorr), Plot.seriesOpts().color(Color.BLUE)).
|
||||
series("z", Plot.data().xy(rangeAuto, zAutoCorr), Plot.seriesOpts().color(Color.GREEN));
|
||||
|
||||
windowAuto.set(plotCorr.draw());
|
||||
|
||||
Peaks pX = new Peaks(xAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
int xOffset = xAutoCorr.length / 2;
|
||||
LinkedList<Integer> peaksX = pX.getPeaksIdx();
|
||||
|
||||
double[] dPeaksXX = IntStream.range(0, peaksX.size()).mapToDouble(i -> (peaksX.get(i) - xOffset)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
|
||||
double[] dPeaksXY = IntStream.range(0, peaksX.size()).mapToDouble(i -> (xAutoCorr[peaksX.get(i)])).toArray();
|
||||
Plot plotPeaksX = Plot.plot(Plot.plotOpts().
|
||||
title("Peak Detection on X").
|
||||
legend(Plot.LegendFormat.BOTTOM)).
|
||||
series("x", Plot.data().xy(rangeAuto, xAutoCorr), Plot.seriesOpts().color(Color.RED)).
|
||||
series("Peaks", Plot.data().xy(dPeaksXX, dPeaksXY), Plot.seriesOpts().color(Color.CYAN).
|
||||
marker(Plot.Marker.DIAMOND).line(Plot.Line.NONE));
|
||||
|
||||
windowPeaksX.set(plotPeaksX.draw());
|
||||
|
||||
Peaks pY = new Peaks(yAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
int yOffset = yAutoCorr.length / 2;
|
||||
LinkedList<Integer> peaksY = pY.getPeaksIdx();
|
||||
|
||||
double[] dPeaksYX = IntStream.range(0, peaksY.size()).mapToDouble(i -> (peaksY.get(i) - yOffset)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
|
||||
double[] dPeaksYY = IntStream.range(0, peaksY.size()).mapToDouble(i -> (yAutoCorr[peaksY.get(i)])).toArray();
|
||||
Plot plotPeaksY = Plot.plot(Plot.plotOpts().
|
||||
title("Peak Detection on Y").
|
||||
legend(Plot.LegendFormat.BOTTOM)).
|
||||
series("x", Plot.data().xy(rangeAuto, yAutoCorr), Plot.seriesOpts().color(Color.RED)).
|
||||
series("Peaks", Plot.data().xy(dPeaksYX, dPeaksYY), Plot.seriesOpts().color(Color.CYAN).
|
||||
marker(Plot.Marker.DIAMOND).line(Plot.Line.NONE));
|
||||
|
||||
windowPeaksY.set(plotPeaksY.draw());
|
||||
|
||||
Peaks pZ = new Peaks(zAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
int zOffset = zAutoCorr.length / 2;
|
||||
LinkedList<Integer> peaksZ = pZ.getPeaksIdx();
|
||||
|
||||
double[] dPeaksZX = IntStream.range(0, peaksZ.size()).mapToDouble(i -> (peaksZ.get(i) - zOffset)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
|
||||
double[] dPeaksZY = IntStream.range(0, peaksZ.size()).mapToDouble(i -> (zAutoCorr[peaksZ.get(i)])).toArray();
|
||||
Plot plotPeaksZ = Plot.plot(Plot.plotOpts().
|
||||
title("Peak Detection on Z").
|
||||
legend(Plot.LegendFormat.BOTTOM)).
|
||||
series("x", Plot.data().xy(rangeAuto, zAutoCorr), Plot.seriesOpts().color(Color.RED)).
|
||||
series("Peaks", Plot.data().xy(dPeaksZX, dPeaksZY), Plot.seriesOpts().color(Color.CYAN).
|
||||
marker(Plot.Marker.DIAMOND).line(Plot.Line.NONE));
|
||||
|
||||
windowPeaksZ.set(plotPeaksZ.draw());
|
||||
// //TODO: plot the different autocorrelation of different window sizes
|
||||
// // Draw the data for the complete 6 second window
|
||||
// double sampleRate = 4;
|
||||
// AccelerometerInterpolator acInterp = new AccelerometerInterpolator(accWindowBuffer, sampleRate);
|
||||
// int peakWidth = (int) Math.round(250 / sampleRate);
|
||||
//
|
||||
// //print raw x,y,z
|
||||
// double[] dTs = IntStream.range(0, accWindowBuffer.getTs().length).mapToDouble(i -> accWindowBuffer.getTs()[i]).toArray();
|
||||
// double[] dTsInterp = IntStream.range(0, acInterp.getTs().length).mapToDouble(i -> acInterp.getTs()[i]).toArray();
|
||||
// Plot plotRaw = Plot.plot(Plot.plotOpts().
|
||||
// title("Raw Acc Data").
|
||||
// legend(Plot.LegendFormat.BOTTOM)).
|
||||
// series("x", Plot.data().xy(dTsInterp, acInterp.getX()), Plot.seriesOpts().color(Color.RED)).
|
||||
// series("y", Plot.data().xy(dTsInterp, acInterp.getY()), Plot.seriesOpts().color(Color.BLUE)).
|
||||
// series("z", Plot.data().xy(dTsInterp, acInterp.getZ()), Plot.seriesOpts().color(Color.GREEN));
|
||||
//
|
||||
// windowRaw.set(plotRaw.draw());
|
||||
//
|
||||
// //auto corr
|
||||
// double[] xAutoCorr = new AutoCorrelation(acInterp.getX(), accWindowBuffer.size()).getCorr();
|
||||
// double[] yAutoCorr = new AutoCorrelation(acInterp.getY(), accWindowBuffer.size()).getCorr();
|
||||
// double[] zAutoCorr = new AutoCorrelation(acInterp.getZ(), accWindowBuffer.size()).getCorr();
|
||||
//
|
||||
// //print autocorr raw
|
||||
// int[] tmp = IntStream.rangeClosed(-((xAutoCorr.length - 1)/2), ((xAutoCorr.length - 1)/2)).toArray();
|
||||
// double[] rangeAuto = IntStream.range(0, tmp.length).mapToDouble(i -> tmp[i]).toArray();
|
||||
// Plot plotCorr = Plot.plot(Plot.plotOpts().
|
||||
// title("Auto Correlation").
|
||||
// legend(Plot.LegendFormat.BOTTOM)).
|
||||
// series("x", Plot.data().xy(rangeAuto, xAutoCorr), Plot.seriesOpts().color(Color.RED)).
|
||||
// series("y", Plot.data().xy(rangeAuto, yAutoCorr), Plot.seriesOpts().color(Color.BLUE)).
|
||||
// series("z", Plot.data().xy(rangeAuto, zAutoCorr), Plot.seriesOpts().color(Color.GREEN));
|
||||
//
|
||||
// windowAuto.set(plotCorr.draw());
|
||||
//
|
||||
// //print autocorr butter
|
||||
// // butterworth lowpass filter, cutoff at 3 hz (~180 bpm)
|
||||
// butterLowpass.lowPass(1,(1/(sampleRate / 1000))/2, 1);
|
||||
//
|
||||
// int n = acInterp.getX().length;
|
||||
// double[] xButter = new double[n];
|
||||
// double[] yButter = new double[n];;
|
||||
// double[] zButter = new double[n];;
|
||||
// for(int i = 0; i < acInterp.getX().length; ++i){
|
||||
// //xButter[i] = butterLowpass.filter(acInterp.getX()[i]);
|
||||
// //yButter[i] = butterLowpass.filter(acInterp.getY()[i]);
|
||||
// zButter[i] = butterLowpass.filter(acInterp.getZ()[i]);
|
||||
// }
|
||||
//
|
||||
// //double[] xAutoCorrButter = new AutoCorrelation(xButter, accWindowBuffer.size()).getCorr();
|
||||
// //double[] yAutoCorrButter = new AutoCorrelation(yButter, accWindowBuffer.size()).getCorr();
|
||||
// double[] zAutoCorrButter = new AutoCorrelation(zButter, accWindowBuffer.size()).getCorr();
|
||||
//
|
||||
// Plot plotCorrButter = Plot.plot(Plot.plotOpts().
|
||||
// title("Auto Correlation Butter").
|
||||
// legend(Plot.LegendFormat.BOTTOM)).
|
||||
// //series("x", utilities.Plot.data().xy(rangeAuto, xAutoCorrButter), utilities.Plot.seriesOpts().color(Color.RED)).
|
||||
// //series("y", utilities.Plot.data().xy(rangeAuto, yAutoCorrButter), utilities.Plot.seriesOpts().color(Color.BLUE)).
|
||||
// series("z", Plot.data().xy(rangeAuto, zAutoCorrButter), Plot.seriesOpts().color(Color.GREEN));
|
||||
//
|
||||
// windowAutoButter.set(plotCorrButter.draw());
|
||||
//
|
||||
// //Print peaks
|
||||
// Peaks pX = new Peaks(xAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
// int xOffset = xAutoCorr.length / 2;
|
||||
// LinkedList<Integer> peaksX = pX.getPeaksIdx();
|
||||
//
|
||||
// double[] dPeaksXX = IntStream.range(0, peaksX.size()).mapToDouble(i -> (peaksX.get(i) - xOffset)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
|
||||
// double[] dPeaksXY = IntStream.range(0, peaksX.size()).mapToDouble(i -> (xAutoCorr[peaksX.get(i)])).toArray();
|
||||
// Plot plotPeaksX = Plot.plot(Plot.plotOpts().
|
||||
// title("Peak Detection on X").
|
||||
// legend(Plot.LegendFormat.BOTTOM)).
|
||||
// series("x", Plot.data().xy(rangeAuto, xAutoCorr), Plot.seriesOpts().color(Color.RED)).
|
||||
// series("Peaks", Plot.data().xy(dPeaksXX, dPeaksXY), Plot.seriesOpts().color(Color.CYAN).
|
||||
// marker(Plot.Marker.DIAMOND).line(Plot.Line.NONE));
|
||||
//
|
||||
// windowPeaksX.set(plotPeaksX.draw());
|
||||
//
|
||||
// Peaks pY = new Peaks(yAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
// int yOffset = yAutoCorr.length / 2;
|
||||
// LinkedList<Integer> peaksY = pY.getPeaksIdx();
|
||||
//
|
||||
// double[] dPeaksYX = IntStream.range(0, peaksY.size()).mapToDouble(i -> (peaksY.get(i) - yOffset)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
|
||||
// double[] dPeaksYY = IntStream.range(0, peaksY.size()).mapToDouble(i -> (yAutoCorr[peaksY.get(i)])).toArray();
|
||||
// Plot plotPeaksY = Plot.plot(Plot.plotOpts().
|
||||
// title("Peak Detection on Y").
|
||||
// legend(Plot.LegendFormat.BOTTOM)).
|
||||
// series("x", Plot.data().xy(rangeAuto, yAutoCorr), Plot.seriesOpts().color(Color.RED)).
|
||||
// series("Peaks", Plot.data().xy(dPeaksYX, dPeaksYY), Plot.seriesOpts().color(Color.CYAN).
|
||||
// marker(Plot.Marker.DIAMOND).line(Plot.Line.NONE));
|
||||
//
|
||||
// windowPeaksY.set(plotPeaksY.draw());
|
||||
//
|
||||
// Peaks pZ = new Peaks(zAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
// int zOffset = zAutoCorr.length / 2;
|
||||
// LinkedList<Integer> peaksZ = pZ.getPeaksIdx();
|
||||
//
|
||||
// double[] dPeaksZX = IntStream.range(0, peaksZ.size()).mapToDouble(i -> (peaksZ.get(i) - zOffset)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
|
||||
// double[] dPeaksZY = IntStream.range(0, peaksZ.size()).mapToDouble(i -> (zAutoCorr[peaksZ.get(i)])).toArray();
|
||||
// Plot plotPeaksZ = Plot.plot(Plot.plotOpts().
|
||||
// title("Peak Detection on Z").
|
||||
// legend(Plot.LegendFormat.BOTTOM)).
|
||||
// series("x", Plot.data().xy(rangeAuto, zAutoCorr), Plot.seriesOpts().color(Color.RED)).
|
||||
// series("Peaks", Plot.data().xy(dPeaksZX, dPeaksZY), Plot.seriesOpts().color(Color.CYAN).
|
||||
// marker(Plot.Marker.DIAMOND).line(Plot.Line.NONE));
|
||||
//
|
||||
// windowPeaksZ.set(plotPeaksZ.draw());
|
||||
|
||||
//fill hols improve peaks
|
||||
|
||||
@@ -141,8 +203,6 @@ public class Main {
|
||||
//System.out.println("BPM-Y: " + pY.getBPM(bpmEstimator.getSampleRate_ms()));
|
||||
//System.out.println("BPM-Z: " + pZ.getBPM(bpmEstimator.getSampleRate_ms()));
|
||||
|
||||
//todo: kleiner fenstergrößen testen. so ist doch etwas langsam auf der Uhr.
|
||||
|
||||
int dummyForBreakpoint = 0;
|
||||
}
|
||||
}
|
||||
@@ -158,6 +218,12 @@ public class Main {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
System.in.read();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,14 +2,15 @@ package bpmEstimation;
|
||||
|
||||
import utilities.Utils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by toni on 15/12/17.
|
||||
*/
|
||||
public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
||||
|
||||
private static int mWindowSize; // in ms
|
||||
private static int mOverlapSize; // in ms
|
||||
private final int mWindowSize; // in ms
|
||||
private final int mOverlapSize; // in ms
|
||||
private long mOverlapCounter;
|
||||
|
||||
public AccelerometerWindowBuffer(int windowSize, int overlap){
|
||||
@@ -18,37 +19,55 @@ public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
||||
mOverlapCounter = 0;
|
||||
}
|
||||
|
||||
public AccelerometerWindowBuffer(List<AccelerometerData> list, int overlap){
|
||||
mWindowSize = list.size();
|
||||
mOverlapSize = overlap;
|
||||
mOverlapCounter = 0;
|
||||
super.addAll(list);
|
||||
}
|
||||
|
||||
//TODO: add exception handling. falseArgument if ad has no numeric x,y,z
|
||||
public boolean add(AccelerometerData ad){
|
||||
synchronized (this){
|
||||
|
||||
//do not add duplicates!
|
||||
if(!isEmpty() && getYongest().equals(ad)){
|
||||
return false;
|
||||
//do not add duplicates!
|
||||
if(!isEmpty() && getYongest().equals(ad)){
|
||||
return false;
|
||||
}
|
||||
|
||||
// current - last to increment overlap time
|
||||
if(!isEmpty()){
|
||||
mOverlapCounter += ad.ts - getYongest().ts;
|
||||
}
|
||||
|
||||
//add element
|
||||
boolean r = super.add(ad);
|
||||
removeOldElements();
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// current - last to increment overlap time
|
||||
if(!isEmpty()){
|
||||
mOverlapCounter += ad.ts - getYongest().ts;
|
||||
}
|
||||
private void removeOldElements(){
|
||||
synchronized (this) {
|
||||
if (!isEmpty()) {
|
||||
if ((getYongest().ts - getOldest().ts) > mWindowSize) {
|
||||
|
||||
//add element
|
||||
boolean r = super.add(ad);
|
||||
if ((getYongest().ts - getOldest().ts) > mWindowSize){
|
||||
|
||||
long oldestTime = getYongest().ts - mWindowSize;
|
||||
for(int i = 0; i < size(); ++i) {
|
||||
if (get(i).ts > oldestTime) {
|
||||
break;
|
||||
long oldestTime = getYongest().ts - mWindowSize;
|
||||
for (int i = 0; i < size(); ++i) {
|
||||
if (get(i).ts > oldestTime) {
|
||||
break;
|
||||
}
|
||||
super.remove(i);
|
||||
}
|
||||
}
|
||||
remove(i);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -57,6 +76,31 @@ public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static AccelerometerWindowBuffer getNewInstance(AccelerometerWindowBuffer buffer, int size, int overlap) {
|
||||
|
||||
double sampleRate = ((buffer.getYongest().ts - buffer.getOldest().ts) / buffer.size());
|
||||
|
||||
//if current size is smaller then wanted size, start at 0 and provide smaller list
|
||||
int start = 0;
|
||||
if ((buffer.getYongest().ts - buffer.getOldest().ts) > size) {
|
||||
start = (int) Math.round(buffer.size() - (size / sampleRate));
|
||||
}
|
||||
|
||||
// start should not be negative, this can happen due to rounding errors.
|
||||
start = start < 0 ? 0 : start;
|
||||
|
||||
List<AccelerometerData> syncList;
|
||||
synchronized (buffer) {
|
||||
syncList = buffer.subList(start, buffer.size());
|
||||
}
|
||||
|
||||
return new AccelerometerWindowBuffer(syncList, overlap);
|
||||
}
|
||||
|
||||
public static AccelerometerWindowBuffer getNewInstance(AccelerometerWindowBuffer buffer) {
|
||||
return new AccelerometerWindowBuffer(buffer, buffer.getOverlapSize());
|
||||
}
|
||||
|
||||
public AccelerometerData getYongest() {
|
||||
return get(size() - 1);
|
||||
}
|
||||
@@ -84,4 +128,6 @@ public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
||||
public int getOverlapSize(){
|
||||
return mOverlapSize;
|
||||
}
|
||||
|
||||
public int getWindowSize(){return mWindowSize; }
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
package bpmEstimation;
|
||||
|
||||
import static utilities.Utils.DEBUG_MODE;
|
||||
|
||||
import utilities.MovingFilter;
|
||||
import utilities.SimpleKalman;
|
||||
import utilities.Utils;
|
||||
|
||||
import uk.me.berndporr.iirj.*;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
@@ -17,6 +21,7 @@ public class BpmEstimator {
|
||||
private LinkedList<Double> mBpmHistory_X;
|
||||
private LinkedList<Double> mBpmHistory_Y;
|
||||
private LinkedList<Double> mBpmHistory_Z;
|
||||
private LinkedList<Double> mBpmHistory_Mag;
|
||||
|
||||
private LinkedList<Double> mBpmHistory;
|
||||
private int mResetCounter;
|
||||
@@ -25,6 +30,13 @@ public class BpmEstimator {
|
||||
private MovingFilter mMvg;
|
||||
//private SimpleKalman mKalman;
|
||||
|
||||
private Butterworth mButter_X;
|
||||
private Butterworth mButter_Y;
|
||||
private Butterworth mButter_Z;
|
||||
private Butterworth mButter_Mag;
|
||||
|
||||
//Debugging stuff
|
||||
private Utils.DebugPlotter plotter;
|
||||
|
||||
public BpmEstimator(AccelerometerWindowBuffer windowBuffer, double sampleRate_ms, int resetAfter_ms){
|
||||
mBuffer = windowBuffer;
|
||||
@@ -33,39 +45,107 @@ public class BpmEstimator {
|
||||
mBpmHistory_X = new LinkedList<>();
|
||||
mBpmHistory_Y = new LinkedList<>();
|
||||
mBpmHistory_Z = new LinkedList<>();
|
||||
mBpmHistory_Mag = new LinkedList<>();
|
||||
|
||||
mBpmHistory = new LinkedList<>();
|
||||
mResetCounter = 0;
|
||||
mResetLimit_ms = resetAfter_ms;
|
||||
|
||||
mMvg = new MovingFilter(10);
|
||||
mMvg = new MovingFilter(2);
|
||||
//mKalman = new SimpleKalman();
|
||||
|
||||
mButter_X = new Butterworth();
|
||||
mButter_Y = new Butterworth();
|
||||
mButter_Z = new Butterworth();
|
||||
mButter_Mag = new Butterworth();
|
||||
|
||||
if(DEBUG_MODE){
|
||||
plotter = new Utils.DebugPlotter();
|
||||
}
|
||||
}
|
||||
|
||||
public double estimate(){
|
||||
return estimate(mBuffer.getWindowSize(), mBuffer.getOverlapSize());
|
||||
}
|
||||
|
||||
public double estimate(int length_ms, int overlap_ms){
|
||||
|
||||
AccelerometerWindowBuffer tmpBuffer = AccelerometerWindowBuffer.getNewInstance(mBuffer, length_ms, overlap_ms);
|
||||
|
||||
double sampleRate = mSampleRate_ms;
|
||||
if(sampleRate <= 0){
|
||||
sampleRate = Math.round(Utils.mean(Utils.diff(mBuffer.getTs())));
|
||||
sampleRate = Math.round(Utils.mean(Utils.diff(tmpBuffer.getTs())));
|
||||
}
|
||||
|
||||
AccelerometerInterpolator interp = new AccelerometerInterpolator(mBuffer, sampleRate);
|
||||
assert sampleRate != 0 : "samplerate is zero";
|
||||
|
||||
double[] xAutoCorr = new AutoCorrelation(interp.getX(), mBuffer.size()).getCorr();
|
||||
double[] yAutoCorr = new AutoCorrelation(interp.getY(), mBuffer.size()).getCorr();
|
||||
double[] zAutoCorr = new AutoCorrelation(interp.getZ(), mBuffer.size()).getCorr();
|
||||
// interpolate
|
||||
AccelerometerInterpolator interp = new AccelerometerInterpolator(tmpBuffer, sampleRate);
|
||||
double[] magRaw = Utils.magnitude(interp);
|
||||
|
||||
//todo: aufräumen. funktion die eine achse bekommt und aus ihr dann die peaks zurück gibt. für debuggen gleich zeichenfunktionen dazu.
|
||||
|
||||
// butterworth lowpass filter, cutoff at 3 hz (~180 bpm)
|
||||
mButter_X.lowPass(1,(1/(sampleRate / 1000))/2, 1);
|
||||
mButter_Y.lowPass(1,(1/(sampleRate / 1000))/2, 1);
|
||||
mButter_Z.lowPass(1,(1/(sampleRate / 1000))/2, 1);
|
||||
|
||||
int n = interp.getX().length;
|
||||
double[] xButter = new double[n];
|
||||
double[] yButter = new double[n];;
|
||||
double[] zButter = new double[n];;
|
||||
for(int i = 0; i < interp.getX().length; ++i){
|
||||
xButter[i] = mButter_X.filter(interp.getX()[i]);
|
||||
yButter[i] = mButter_Y.filter(interp.getY()[i]);
|
||||
zButter[i] = mButter_Z.filter(interp.getZ()[i]);
|
||||
}
|
||||
|
||||
//TODO: compare with own mButter_Mag.filter
|
||||
double[] magButter = Utils.magnitude(xButter, yButter, zButter);
|
||||
|
||||
//auto correlation
|
||||
double[] xAutoCorr = new AutoCorrelation(xButter, tmpBuffer.size()).getCorr();
|
||||
double[] yAutoCorr = new AutoCorrelation(yButter, tmpBuffer.size()).getCorr();
|
||||
double[] zAutoCorr = new AutoCorrelation(zButter, tmpBuffer.size()).getCorr();
|
||||
double[] magAutoCorr = new AutoCorrelation(magRaw, tmpBuffer.size()).getCorr();
|
||||
|
||||
//find a peak within range of 250 ms
|
||||
int peakWidth = (int) Math.round(250 / sampleRate);
|
||||
Peaks pX = new Peaks(xAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
Peaks pY = new Peaks(yAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
Peaks pZ = new Peaks(zAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
Peaks xPeaks = new Peaks(xAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
Peaks yPeaks = new Peaks(yAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
Peaks zPeaks = new Peaks(zAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
Peaks magPeaks = new Peaks(magAutoCorr, peakWidth, 0.1f, 0, false);
|
||||
|
||||
mBpmHistory_X.add(pX.getBPM(sampleRate));
|
||||
mBpmHistory_Y.add(pY.getBPM(sampleRate));
|
||||
mBpmHistory_Z.add(pZ.getBPM(sampleRate));
|
||||
mBpmHistory_X.add(xPeaks.getBPM(sampleRate));
|
||||
mBpmHistory_Y.add(yPeaks.getBPM(sampleRate));
|
||||
mBpmHistory_Z.add(zPeaks.getBPM(sampleRate));
|
||||
mBpmHistory_Mag.add(magPeaks.getBPM(sampleRate));
|
||||
|
||||
double estimatedBPM = getBestBpmEstimation(pX, pY, pZ);
|
||||
|
||||
if(DEBUG_MODE){
|
||||
plotter.setPlotRawX(interp.getTs(), interp.getX());
|
||||
plotter.setPlotRawY(interp.getTs(), interp.getY());
|
||||
plotter.setPlotRawZ(interp.getTs(), interp.getZ());
|
||||
plotter.setPlotRawMag(interp.getTs(), magRaw);
|
||||
|
||||
plotter.setPlotButterX(interp.getTs(), xButter);
|
||||
plotter.setPlotButterY(interp.getTs(), yButter);
|
||||
plotter.setPlotButterZ(interp.getTs(), zButter);
|
||||
plotter.setPlotButterMag(interp.getTs(), magButter);
|
||||
|
||||
plotter.setPlotCorrX(xAutoCorr, xPeaks);
|
||||
plotter.setPlotCorrY(yAutoCorr, yPeaks);
|
||||
plotter.setPlotCorrZ(zAutoCorr, zPeaks);
|
||||
plotter.setPlotCorrMag(magAutoCorr, magPeaks);
|
||||
|
||||
//printout the current BPM
|
||||
System.out.println(length_ms + "; x: " + mBpmHistory_X.getLast()
|
||||
+ "; y: " + mBpmHistory_Y.getLast()
|
||||
+ "; z: " + mBpmHistory_Z.getLast()
|
||||
+ "; mag: " + mBpmHistory_Mag.getLast());
|
||||
}
|
||||
|
||||
double estimatedBPM = getBestBpmEstimation(xPeaks, yPeaks, zPeaks);
|
||||
if(estimatedBPM != -1){
|
||||
|
||||
//moving avg (lohnt dann, wenn wir viele daten haben)
|
||||
@@ -86,7 +166,8 @@ public class BpmEstimator {
|
||||
if(++mResetCounter > resetAfter){
|
||||
mBpmHistory.clear();
|
||||
|
||||
mBuffer.clear();
|
||||
//TODO: send signal to clear from outside this function should just return the bpm
|
||||
//mBuffer.clear();
|
||||
mMvg.clear();
|
||||
mResetCounter = 0;
|
||||
}
|
||||
@@ -105,6 +186,8 @@ public class BpmEstimator {
|
||||
return Utils.median(mBpmHistory);
|
||||
}
|
||||
|
||||
//TODO: die einzelnen achsen cleverer kombinieren. bspw. bei Peter brauchen wir zwei Achsen in einer Bewegung.
|
||||
//TODO: Vielleicht die einzelnen Kombinationen / Magnitudes der Achsen noch mit einbeziehen. Also xy, xz, yz
|
||||
private double getBestBpmEstimation(Peaks peaksX, Peaks peaksY, Peaks peaksZ) throws IllegalArgumentException {
|
||||
|
||||
int cntNumAxis = 0;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package utilities;
|
||||
|
||||
import bpmEstimation.AccelerometerInterpolator;
|
||||
import bpmEstimation.AccelerometerWindowBuffer;
|
||||
import bpmEstimation.Peaks;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -9,9 +11,13 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
//TODO: change from double to generic type
|
||||
public class Utils {
|
||||
|
||||
public static final boolean DEBUG_MODE = true;
|
||||
|
||||
public static double getDistance(double x1, double y1, double x2, double y2) {
|
||||
return (double) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
|
||||
}
|
||||
@@ -164,6 +170,37 @@ public class Utils {
|
||||
return newArray;
|
||||
}
|
||||
|
||||
public static double magnitude(double x, double y, double z){
|
||||
return Math.sqrt((x*x) + (y*y) + (z*z));
|
||||
}
|
||||
|
||||
public static double[] magnitude(double[] arrayX, double[] arrayY, double[] arrayZ){
|
||||
|
||||
//check of all arrays have the same size, if not crop to smallest one
|
||||
int n = arrayZ.length;
|
||||
if(arrayX.length < arrayY.length && arrayX.length < arrayZ.length){
|
||||
n = arrayX.length;
|
||||
} else if (arrayY.length < arrayZ.length){
|
||||
n = arrayY.length;
|
||||
}
|
||||
|
||||
double[] output = new double[n];
|
||||
for(int i = 0; i < n; ++i){
|
||||
output[i] = magnitude(arrayX[i], arrayY[i], arrayZ[i]);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static double[] magnitude(AccelerometerInterpolator interpolator){
|
||||
return magnitude(interpolator.getX(), interpolator.getY(), interpolator.getZ());
|
||||
}
|
||||
|
||||
//TODO: this could be added to AccelerometerData.. saves some time, better design.
|
||||
public static double[] magnitude(AccelerometerWindowBuffer buffer){
|
||||
return magnitude(buffer.getX(), buffer.getY(), buffer.getZ());
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class ShowPNG extends JFrame
|
||||
{
|
||||
@@ -178,7 +215,55 @@ public class Utils {
|
||||
this.setVisible(true);
|
||||
}
|
||||
|
||||
public void set(BufferedImage bi){
|
||||
public void plotData(String title, String name, long[] ts, double[] data){
|
||||
|
||||
double[] dTs = IntStream.range(0, ts.length).mapToDouble(i -> ts[i]).toArray();
|
||||
Plot plot = Plot.plot(Plot.plotOpts().
|
||||
title(title).
|
||||
legend(Plot.LegendFormat.BOTTOM)).
|
||||
series(name, Plot.data().xy(dTs, data), Plot.seriesOpts().color(Color.RED));
|
||||
|
||||
this.set(plot.draw());
|
||||
}
|
||||
|
||||
public void plotCorr(String title, String name, double[] data, int scale){
|
||||
|
||||
int[] tmp = IntStream.rangeClosed(-((data.length - 1)/scale), ((data.length - 1)/scale)).toArray();
|
||||
double[] range = IntStream.range(0, tmp.length).mapToDouble(i -> tmp[i]).toArray();
|
||||
Plot plot = Plot.plot(Plot.plotOpts().
|
||||
title(title).
|
||||
legend(Plot.LegendFormat.BOTTOM)).
|
||||
series(name, Plot.data().xy(range, data), Plot.seriesOpts().color(Color.RED));
|
||||
|
||||
this.set(plot.draw());
|
||||
}
|
||||
|
||||
public void plotCorrWithPeaks(String title, String name, double[] corr, int scale, Peaks peaks, boolean isCorrelation){
|
||||
|
||||
int[] tmp = IntStream.rangeClosed(-((corr.length - 1)/scale), ((corr.length - 1)/scale)).toArray();
|
||||
double[] range= IntStream.range(0, tmp.length).mapToDouble(i -> tmp[i]).toArray();
|
||||
|
||||
//if we have a correlation, shift the peaks into negative by half the correlation size
|
||||
int tmpOffset = 0;
|
||||
if(isCorrelation){
|
||||
tmpOffset = corr.length / 2;
|
||||
}
|
||||
int offset = tmpOffset;
|
||||
|
||||
LinkedList<Integer> peaksZ = peaks.getPeaksIdx();
|
||||
double[] dPeaksZX = IntStream.range(0, peaksZ.size()).mapToDouble(i -> (peaksZ.get(i) - offset)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
|
||||
double[] dPeaksZY = IntStream.range(0, peaksZ.size()).mapToDouble(i -> (corr[peaksZ.get(i)])).toArray();
|
||||
Plot plot = Plot.plot(Plot.plotOpts().
|
||||
title(title).
|
||||
legend(Plot.LegendFormat.BOTTOM)).
|
||||
series(name, Plot.data().xy(range, corr), Plot.seriesOpts().color(Color.RED)).
|
||||
series("Peaks", Plot.data().xy(dPeaksZX, dPeaksZY), Plot.seriesOpts().color(Color.CYAN).
|
||||
marker(Plot.Marker.DIAMOND).line(Plot.Line.NONE));
|
||||
|
||||
this.set(plot.draw());
|
||||
}
|
||||
|
||||
private void set(BufferedImage bi){
|
||||
|
||||
mIcon = new ImageIcon(bi);
|
||||
mLabel.setVisible(false);
|
||||
@@ -187,4 +272,87 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static class DebugPlotter {
|
||||
|
||||
//what we want to draw
|
||||
private Utils.ShowPNG plotRawX;
|
||||
private Utils.ShowPNG plotRawY;
|
||||
private Utils.ShowPNG plotRawZ;
|
||||
private Utils.ShowPNG plotRawMag;
|
||||
private Utils.ShowPNG plotButterX;
|
||||
private Utils.ShowPNG plotButterY;
|
||||
private Utils.ShowPNG plotButterZ;
|
||||
private Utils.ShowPNG plotButterMag;
|
||||
private Utils.ShowPNG plotCorrX;
|
||||
private Utils.ShowPNG plotCorrY;
|
||||
private Utils.ShowPNG plotCorrZ;
|
||||
private Utils.ShowPNG plotCorrMag;
|
||||
|
||||
public DebugPlotter(){
|
||||
|
||||
plotRawX = new Utils.ShowPNG();
|
||||
plotRawY = new Utils.ShowPNG();
|
||||
plotRawZ = new Utils.ShowPNG();
|
||||
plotRawMag = new Utils.ShowPNG();
|
||||
plotButterX = new Utils.ShowPNG();
|
||||
plotButterY = new Utils.ShowPNG();
|
||||
plotButterZ = new Utils.ShowPNG();
|
||||
plotButterMag = new Utils.ShowPNG();
|
||||
plotCorrX = new Utils.ShowPNG();
|
||||
plotCorrY = new Utils.ShowPNG();
|
||||
plotCorrZ = new Utils.ShowPNG();
|
||||
plotCorrMag = new Utils.ShowPNG();
|
||||
}
|
||||
|
||||
public void setPlotRawX(long[] ts, double[] data){
|
||||
plotRawX.plotData("Raw Data X", "x", ts, data);
|
||||
}
|
||||
|
||||
public void setPlotRawY(long[] ts, double[] data){
|
||||
plotRawY.plotData("Raw Data Y", "y", ts, data);
|
||||
}
|
||||
|
||||
public void setPlotRawZ(long[] ts, double[] data){
|
||||
plotRawZ.plotData("Raw Data Z", "z", ts, data);
|
||||
}
|
||||
|
||||
public void setPlotRawMag(long[] ts, double[] data){
|
||||
plotRawMag.plotData("Raw Data Magnitude", "mag", ts, data);
|
||||
}
|
||||
|
||||
public void setPlotButterX(long[] ts, double[] data){
|
||||
plotButterX.plotData("Butter Data X", "x", ts, data);
|
||||
}
|
||||
|
||||
public void setPlotButterY(long[] ts, double[] data){
|
||||
plotButterY.plotData("Butter Data Y", "z", ts, data);
|
||||
}
|
||||
|
||||
public void setPlotButterZ(long[] ts, double[] data){
|
||||
plotButterZ.plotData("Butter Data Z", "y", ts, data);
|
||||
}
|
||||
|
||||
public void setPlotButterMag(long[] ts, double[] data){
|
||||
plotButterMag.plotData("Butter Data Mag", "mag", ts, data);
|
||||
}
|
||||
|
||||
public void setPlotCorrX(double[] data, Peaks peaks){
|
||||
plotCorrX.plotCorrWithPeaks("Autocorr X", "x", data,2, peaks, true);
|
||||
}
|
||||
|
||||
public void setPlotCorrY(double[] data, Peaks peaks){
|
||||
plotCorrY.plotCorrWithPeaks("Autocorr Y", "y", data,2, peaks, true);
|
||||
}
|
||||
|
||||
public void setPlotCorrZ(double[] data, Peaks peaks){
|
||||
plotCorrZ.plotCorrWithPeaks("Autocorr Z", "z", data,2, peaks, true);
|
||||
}
|
||||
|
||||
public void setPlotCorrMag(double[] data, Peaks peaks){
|
||||
plotCorrMag.plotCorrWithPeaks("Autocorr Mag", "mag", data,2, peaks, true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user