small refactoring
This commit is contained in:
@@ -16,7 +16,7 @@ public class Metronome extends TimerTask {
|
||||
boolean loaded = false;
|
||||
private int soundID;
|
||||
|
||||
public Metronome(Context ){
|
||||
public Metronome(Context context){
|
||||
soundPool = new SoundPool.Builder().setMaxStreams(10).build();
|
||||
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
|
||||
@Override
|
||||
|
||||
@@ -1,250 +0,0 @@
|
||||
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 java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import de.tonifetzer.conductorswatch.bpmEstimation.AccelerometerData;
|
||||
import de.tonifetzer.conductorswatch.bpmEstimation.AccelerometerWindowBuffer;
|
||||
import de.tonifetzer.conductorswatch.bpmEstimation.BpmEstimator;
|
||||
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.
|
||||
*/
|
||||
|
||||
public class Estimator implements SensorEventListener {
|
||||
|
||||
private SensorManager mSensorManager;
|
||||
private Sensor mAccelerometer;
|
||||
private Sensor mRawAccelerometer;
|
||||
private Context mContext;
|
||||
private AccelerometerWindowBuffer mAccelerometerWindowBuffer;
|
||||
private BpmEstimator mBpmEstimator;
|
||||
private double mCurrentBpm;
|
||||
|
||||
private ByteStreamWriter mByteStreamWriterAcc;
|
||||
private ByteStreamWriter mByteStreamWriterGyro;
|
||||
private SensorDataFileStreamer mStreamer;
|
||||
|
||||
private Timer mTimer = new Timer();
|
||||
|
||||
|
||||
public Estimator(Context mContext){
|
||||
this.mContext = mContext;
|
||||
this.mCurrentBpm = -1;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
|
||||
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
|
||||
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
|
||||
mRawAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
|
||||
mSensorManager.registerListener(this, mRawAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
|
||||
|
||||
|
||||
mAccelerometerWindowBuffer = new AccelerometerWindowBuffer(6000, 750);
|
||||
mBpmEstimator = new BpmEstimator(mAccelerometerWindowBuffer, 0, 5000);
|
||||
|
||||
mTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (mAccelerometerWindowBuffer.isNextWindowReady()) {
|
||||
|
||||
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); //135 gibt gute ergebnisse!
|
||||
}
|
||||
|
||||
//update the windowSize and updaterate depending on current bpm
|
||||
//updateWindowSizeAndOverlap(bpm);
|
||||
}
|
||||
}
|
||||
}, 0, 100);
|
||||
|
||||
mByteStreamWriterAcc = new ByteStreamWriter();
|
||||
mByteStreamWriterGyro = new ByteStreamWriter();
|
||||
|
||||
mStreamer = new SensorDataFileStreamer(mContext);
|
||||
new Thread(mStreamer).start();
|
||||
}
|
||||
|
||||
|
||||
public void stop() {
|
||||
|
||||
mTimer.cancel();
|
||||
mTimer.purge();
|
||||
|
||||
mSensorManager.unregisterListener(this);
|
||||
|
||||
//send data and close the ByteStreamWriter
|
||||
for (OnBpmEstimatorListener listener : listeners) {
|
||||
//listener.onEstimationStopped(mByteStreamWriter.getByteArray());
|
||||
}
|
||||
mByteStreamWriterAcc.close();
|
||||
mByteStreamWriterGyro.close();
|
||||
|
||||
mStreamer.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent se) {
|
||||
|
||||
//TODO: at the moment this runs in main thread... since worker fragment runs also in main thread
|
||||
|
||||
//ca 200hz, every 5 to 6 ms we have an update
|
||||
if (se.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
|
||||
mAccelerometerWindowBuffer.add(new AccelerometerData(System.currentTimeMillis(), se.values[0], se.values[1], se.values[2]));
|
||||
mByteStreamWriterAcc.writeSensor3D(Sensor.TYPE_LINEAR_ACCELERATION, se.values[0], se.values[1], se.values[2]);
|
||||
|
||||
mStreamer.sendByteArray(mByteStreamWriterAcc.getByteArray());
|
||||
mByteStreamWriterAcc.reset();
|
||||
}
|
||||
|
||||
if (se.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||
mByteStreamWriterGyro.writeSensor3D(Sensor.TYPE_ACCELEROMETER, se.values[0], se.values[1], se.values[2]);
|
||||
|
||||
mStreamer.sendByteArray(mByteStreamWriterGyro.getByteArray());
|
||||
mByteStreamWriterGyro.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int i) {
|
||||
// do nothin
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for callback calculated bpm
|
||||
*/
|
||||
public interface OnBpmEstimatorListener {
|
||||
void onNewDataAvailable(double bpm);
|
||||
void onEstimationStopped(byte[] sensorData);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package de.tonifetzer.conductorswatch;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Vibrator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Created by toni on 13/11/17.
|
||||
*/
|
||||
|
||||
public class Metronome extends TimerTask {
|
||||
|
||||
private Vibrator mVibrator;
|
||||
|
||||
Metronome(Context context){
|
||||
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
mVibrator.vibrate(10);
|
||||
|
||||
for (OnMetronomeListener listener:listeners) {
|
||||
listener.onNewClick();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for callback metronome clicks
|
||||
* I know that java has observable.. but this why it is more clean and easy
|
||||
*/
|
||||
public interface OnMetronomeListener {
|
||||
void onNewClick();
|
||||
}
|
||||
|
||||
private List<OnMetronomeListener> listeners = new CopyOnWriteArrayList<OnMetronomeListener>();
|
||||
public void add(OnMetronomeListener listener){listeners.add(listener);}
|
||||
public void remove(OnMetronomeListener listener){listeners.remove(listener);}
|
||||
}
|
||||
@@ -13,7 +13,9 @@ import android.widget.TextView;
|
||||
import java.util.Timer;
|
||||
import java.util.Vector;
|
||||
|
||||
import de.tonifetzer.conductorswatch.bpmEstimation.Estimator;
|
||||
import de.tonifetzer.conductorswatch.ui.Croller;
|
||||
import de.tonifetzer.conductorswatch.ui.Metronome;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,7 +75,7 @@ public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
||||
public AccelerometerWindowBuffer getFixedSizedWindow(int size, int overlap){
|
||||
AccelerometerWindowBuffer other = new AccelerometerWindowBuffer(size, overlap);
|
||||
|
||||
double sampleRate = ((getYongest().ts - getOldest().ts) / super.size());
|
||||
double sampleRate = (double) ((getYongest().ts - getOldest().ts) / super.size());
|
||||
|
||||
//if current size is smaller then wanted size, start at 0 and provide smaller list
|
||||
int start = 0;
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
package de.tonifetzer.conductorswatch.bpmEstimation;
|
||||
|
||||
import de.tonifetzer.conductorswatch.utilities.MovingFilter;
|
||||
import de.tonifetzer.conductorswatch.utilities.SimpleKalman;
|
||||
import de.tonifetzer.conductorswatch.utilities.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by toni on 17/12/17.
|
||||
*/
|
||||
public class BpmEstimator {
|
||||
|
||||
private AccelerometerWindowBuffer mBuffer;
|
||||
private double mSampleRate_ms;
|
||||
|
||||
private LinkedList<Double> mBpmHistory_X;
|
||||
private LinkedList<Double> mBpmHistory_Y;
|
||||
private LinkedList<Double> mBpmHistory_Z;
|
||||
|
||||
private LinkedList<Double> mBpmHistory;
|
||||
private int mResetCounter;
|
||||
private int mResetLimit_ms;
|
||||
|
||||
private MovingFilter mMvg;
|
||||
//private SimpleKalman mKalman;
|
||||
|
||||
|
||||
public BpmEstimator(AccelerometerWindowBuffer windowBuffer, double sampleRate_ms, int resetAfter_ms){
|
||||
mBuffer = windowBuffer;
|
||||
mSampleRate_ms = sampleRate_ms;
|
||||
|
||||
mBpmHistory_X = new LinkedList<>();
|
||||
mBpmHistory_Y = new LinkedList<>();
|
||||
mBpmHistory_Z = new LinkedList<>();
|
||||
|
||||
mBpmHistory = new LinkedList<>();
|
||||
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();
|
||||
}
|
||||
|
||||
//TODO: we use the buffer from outside.. this buffer is continuously updated.. not good!
|
||||
public double estimate(AccelerometerWindowBuffer fixedWindow){
|
||||
|
||||
double sampleRate = mSampleRate_ms;
|
||||
if(sampleRate <= 0){
|
||||
sampleRate = Math.round(Utils.mean(Utils.diff(fixedWindow.getTs())));
|
||||
}
|
||||
|
||||
if(sampleRate == 0){
|
||||
int breakhere = 0;
|
||||
}
|
||||
|
||||
AccelerometerInterpolator interp = new AccelerometerInterpolator(fixedWindow, sampleRate);
|
||||
|
||||
//are we conducting?
|
||||
//just look at the newest 512 samples
|
||||
//List<AccelerometerData> subBuffer = mBuffer.subList(mBuffer.size() - 512, mBuffer.size());
|
||||
|
||||
double[] xAutoCorr = new AutoCorrelation(interp.getX(), fixedWindow.size()).getCorr();
|
||||
double[] yAutoCorr = new AutoCorrelation(interp.getY(), fixedWindow.size()).getCorr();
|
||||
double[] zAutoCorr = new AutoCorrelation(interp.getZ(), fixedWindow.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);
|
||||
|
||||
mBpmHistory_X.add(pX.getBPM(sampleRate));
|
||||
mBpmHistory_Y.add(pY.getBPM(sampleRate));
|
||||
mBpmHistory_Z.add(pZ.getBPM(sampleRate));
|
||||
|
||||
double estimatedBPM = getBestBpmEstimation(pX, pY, pZ);
|
||||
if(estimatedBPM != -1){
|
||||
|
||||
//moving avg (lohnt dann, wenn wir viele daten haben)
|
||||
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());
|
||||
|
||||
//kalman filter (lohnt dann, wenn wir konstantes tempo haben, mit startangabe!)
|
||||
|
||||
//standard last element
|
||||
//mBpmHistory.add(estimatedBPM);
|
||||
//mResetCounter = 0;
|
||||
}
|
||||
else {
|
||||
int resetAfter = (int) Math.round(mResetLimit_ms / (mBuffer.getOverlapSize()));
|
||||
if(++mResetCounter > resetAfter){
|
||||
mBpmHistory.clear();
|
||||
|
||||
//TODO: send signal to clear.
|
||||
//mBuffer.clear();
|
||||
mMvg.clear();
|
||||
mResetCounter = 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//last element
|
||||
return mBpmHistory.getLast();
|
||||
}
|
||||
|
||||
public double getMeanBpm(){
|
||||
return Utils.mean(mBpmHistory);
|
||||
}
|
||||
|
||||
public double getMedianBPM(){
|
||||
return Utils.median(mBpmHistory);
|
||||
}
|
||||
|
||||
private double getBestBpmEstimation(Peaks peaksX, Peaks peaksY, Peaks peaksZ) throws IllegalArgumentException {
|
||||
|
||||
int cntNumAxis = 0;
|
||||
double sumCorr = 0; //to prevent division by zero
|
||||
double sumRms = 0;
|
||||
double sumNumInter = 0;
|
||||
|
||||
double corrMeanX = 0, corrRmsX = 0;
|
||||
int corrNumInterX = 0;
|
||||
if(peaksX.hasPeaks()){
|
||||
corrMeanX = Utils.geometricMean(peaksX.getPeaksValueWithoutZeroIdxAndNegativeValues());
|
||||
corrRmsX = Utils.rms(peaksX.getPeaksValueWithoutZeroIdx());
|
||||
corrNumInterX = Utils.intersectionNumber(peaksX.getData(), 0.2f);
|
||||
|
||||
++cntNumAxis;
|
||||
sumCorr += corrMeanX;
|
||||
sumRms += corrRmsX;
|
||||
sumNumInter += corrNumInterX;
|
||||
}
|
||||
|
||||
double corrMeanY = 0, corrRmsY = 0;
|
||||
int corrNumInterY = 0;
|
||||
if(peaksY.hasPeaks()){
|
||||
corrMeanY = Utils.geometricMean(peaksY.getPeaksValueWithoutZeroIdxAndNegativeValues());
|
||||
corrRmsY = Utils.rms(peaksY.getPeaksValueWithoutZeroIdx());
|
||||
corrNumInterY = Utils.intersectionNumber(peaksY.getData(), 0.2f);
|
||||
|
||||
++cntNumAxis;
|
||||
sumCorr += corrMeanY;
|
||||
sumRms += corrRmsY;
|
||||
sumNumInter += corrNumInterY;
|
||||
}
|
||||
|
||||
double corrMeanZ = 0, corrRmsZ = 0;
|
||||
int corrNumInterZ = 0;
|
||||
if(peaksZ.hasPeaks()){
|
||||
corrMeanZ = Utils.geometricMean(peaksZ.getPeaksValueWithoutZeroIdxAndNegativeValues());
|
||||
corrRmsZ = Utils.rms(peaksZ.getPeaksValueWithoutZeroIdx());
|
||||
corrNumInterZ = Utils.intersectionNumber(peaksZ.getData(), 0.2f);
|
||||
|
||||
++cntNumAxis;
|
||||
sumCorr += corrMeanZ;
|
||||
sumRms += corrRmsZ;
|
||||
sumNumInter += corrNumInterZ;
|
||||
}
|
||||
|
||||
//no peaks, reject
|
||||
if(cntNumAxis == 0){
|
||||
//throw new IllegalArgumentException("All Peaks are empty! -> Reject Estimation");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
System.out.println("RMS-X: " + corrRmsX);
|
||||
System.out.println("GEO-X: " + corrMeanX);
|
||||
System.out.println("INTER-X: " + corrNumInterX);
|
||||
|
||||
System.out.println("RMS-Y: " + corrRmsY);
|
||||
System.out.println("GEO-Y: " + corrMeanY);
|
||||
System.out.println("INTER-Y: " + corrNumInterY);
|
||||
|
||||
System.out.println("RMS-Z: " + corrRmsZ);
|
||||
System.out.println("GEO-Z: " + corrMeanZ);
|
||||
System.out.println("INTER-Z: " + corrNumInterZ);
|
||||
*/
|
||||
|
||||
//values to low, reject
|
||||
//TODO: this is a pretty simple assumption. first shot!
|
||||
if(corrRmsX < 0.25 && corrRmsY < 0.25 && corrRmsZ < 0.25){
|
||||
return -1;
|
||||
}
|
||||
|
||||
double quantityX = ((corrMeanX / sumCorr) + (corrRmsX / sumRms) + (corrNumInterX / sumNumInter)) / cntNumAxis;
|
||||
double quantityY = ((corrMeanY / sumCorr) + (corrRmsY / sumRms) + (corrNumInterY / sumNumInter)) / cntNumAxis;
|
||||
double quantityZ = ((corrMeanZ / sumCorr) + (corrRmsZ / sumRms) + (corrNumInterZ / sumNumInter)) / cntNumAxis;
|
||||
|
||||
//get best axis by quantity and estimate bpm
|
||||
if(quantityX > quantityY && quantityX > quantityZ){
|
||||
return mBpmHistory_X.getLast();
|
||||
}
|
||||
else if(quantityY > quantityZ){
|
||||
return mBpmHistory_Y.getLast();
|
||||
}
|
||||
else {
|
||||
return mBpmHistory_Z.getLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user