aufräumen und refactoren
This commit is contained in:
@@ -8,7 +8,6 @@ import java.util.TimerTask;
|
|||||||
/**
|
/**
|
||||||
* Created by toni on 20/12/17.
|
* Created by toni on 20/12/17.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class Metronome extends TimerTask {
|
public class Metronome extends TimerTask {
|
||||||
|
|
||||||
//private MediaPlayer mMediaPlayer;
|
//private MediaPlayer mMediaPlayer;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import android.widget.TextView;
|
|||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import de.tonifetzer.conductorswatch.bpmEstimation.Estimator;
|
import de.tonifetzer.conductorswatch.estimation.Estimator;
|
||||||
import de.tonifetzer.conductorswatch.ui.Croller;
|
import de.tonifetzer.conductorswatch.ui.Croller;
|
||||||
import de.tonifetzer.conductorswatch.ui.Metronome;
|
import de.tonifetzer.conductorswatch.ui.Metronome;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.tonifetzer.conductorswatch.bpmEstimation;
|
package de.tonifetzer.conductorswatch.estimation;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.hardware.Sensor;
|
import android.hardware.Sensor;
|
||||||
@@ -12,6 +12,8 @@ import java.util.Timer;
|
|||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
import de.tonifetzer.conductorswatch.estimation.accelerometer.AccelerometerData;
|
||||||
|
import de.tonifetzer.conductorswatch.estimation.accelerometer.AccelerometerWindowBuffer;
|
||||||
import de.tonifetzer.conductorswatch.network.SensorDataFileStreamer;
|
import de.tonifetzer.conductorswatch.network.SensorDataFileStreamer;
|
||||||
import de.tonifetzer.conductorswatch.utilities.ByteStreamWriter;
|
import de.tonifetzer.conductorswatch.utilities.ByteStreamWriter;
|
||||||
import de.tonifetzer.conductorswatch.utilities.Utils;
|
import de.tonifetzer.conductorswatch.utilities.Utils;
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
package de.tonifetzer.conductorswatch.bpmEstimation;
|
package de.tonifetzer.conductorswatch.estimation;
|
||||||
|
|
||||||
|
import de.tonifetzer.conductorswatch.estimation.accelerometer.AccelerometerWindowBuffer;
|
||||||
|
import de.tonifetzer.conductorswatch.estimation.accelerometer.AccelerometerInterpolator;
|
||||||
|
import de.tonifetzer.conductorswatch.estimation.dsp.AutoCorrelation;
|
||||||
|
import de.tonifetzer.conductorswatch.estimation.dsp.PeakDetector;
|
||||||
import de.tonifetzer.conductorswatch.utilities.MovingFilter;
|
import de.tonifetzer.conductorswatch.utilities.MovingFilter;
|
||||||
import de.tonifetzer.conductorswatch.utilities.SimpleKalman;
|
|
||||||
import de.tonifetzer.conductorswatch.utilities.Utils;
|
import de.tonifetzer.conductorswatch.utilities.Utils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by toni on 17/12/17.
|
* Created by toni on 17/12/17.
|
||||||
@@ -28,7 +29,7 @@ public class EstimatorAutoCorr {
|
|||||||
//private SimpleKalman mKalman;
|
//private SimpleKalman mKalman;
|
||||||
|
|
||||||
|
|
||||||
public EstimatorAutoCorr(AccelerometerWindowBuffer windowBuffer, double sampleRate_ms, int resetAfter_ms){
|
EstimatorAutoCorr(AccelerometerWindowBuffer windowBuffer, double sampleRate_ms, int resetAfter_ms){
|
||||||
mBuffer = windowBuffer;
|
mBuffer = windowBuffer;
|
||||||
mSampleRate_ms = sampleRate_ms;
|
mSampleRate_ms = sampleRate_ms;
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ public class EstimatorAutoCorr {
|
|||||||
//mKalman = new SimpleKalman();
|
//mKalman = new SimpleKalman();
|
||||||
}
|
}
|
||||||
|
|
||||||
public double estimate(AccelerometerWindowBuffer fixedWindow){
|
double estimate(AccelerometerWindowBuffer fixedWindow){
|
||||||
|
|
||||||
double sampleRate = mSampleRate_ms;
|
double sampleRate = mSampleRate_ms;
|
||||||
if(sampleRate <= 0){
|
if(sampleRate <= 0){
|
||||||
@@ -73,9 +74,9 @@ public class EstimatorAutoCorr {
|
|||||||
|
|
||||||
//find a peak within range of 250 ms
|
//find a peak within range of 250 ms
|
||||||
int peakWidth = (int) Math.round(250 / sampleRate);
|
int peakWidth = (int) Math.round(250 / sampleRate);
|
||||||
Peaks pX = new Peaks(xAutoCorr, peakWidth, 0.1f, 0, false);
|
PeakDetector pX = new PeakDetector(xAutoCorr, peakWidth, 0.1f, 0, false);
|
||||||
Peaks pY = new Peaks(yAutoCorr, peakWidth, 0.1f, 0, false);
|
PeakDetector pY = new PeakDetector(yAutoCorr, peakWidth, 0.1f, 0, false);
|
||||||
Peaks pZ = new Peaks(zAutoCorr, peakWidth, 0.1f, 0, false);
|
PeakDetector pZ = new PeakDetector(zAutoCorr, peakWidth, 0.1f, 0, false);
|
||||||
|
|
||||||
mBpmHistory_X.add(pX.getBPM(sampleRate));
|
mBpmHistory_X.add(pX.getBPM(sampleRate));
|
||||||
mBpmHistory_Y.add(pY.getBPM(sampleRate));
|
mBpmHistory_Y.add(pY.getBPM(sampleRate));
|
||||||
@@ -125,7 +126,7 @@ public class EstimatorAutoCorr {
|
|||||||
return Utils.median(mBpmHistory);
|
return Utils.median(mBpmHistory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double getBestBpmEstimation(Peaks peaksX, Peaks peaksY, Peaks peaksZ) throws IllegalArgumentException {
|
private double getBestBpmEstimation(PeakDetector peaksX, PeakDetector peaksY, PeakDetector peaksZ) throws IllegalArgumentException {
|
||||||
|
|
||||||
int cntNumAxis = 0;
|
int cntNumAxis = 0;
|
||||||
double sumCorr = 0; //to prevent division by zero
|
double sumCorr = 0; //to prevent division by zero
|
||||||
@@ -173,7 +174,7 @@ public class EstimatorAutoCorr {
|
|||||||
|
|
||||||
//no peaks, reject
|
//no peaks, reject
|
||||||
if(cntNumAxis == 0){
|
if(cntNumAxis == 0){
|
||||||
//throw new IllegalArgumentException("All Peaks are empty! -> Reject Estimation");
|
//throw new IllegalArgumentException("All PeakDetector are empty! -> Reject Estimation");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package de.tonifetzer.conductorswatch.estimation;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import de.tonifetzer.conductorswatch.estimation.accelerometer.AccelerometerWindowBuffer;
|
||||||
|
import de.tonifetzer.conductorswatch.utilities.MovingFilter;
|
||||||
|
|
||||||
|
public class EstimatorDistCorr {
|
||||||
|
|
||||||
|
private AccelerometerWindowBuffer mBuffer;
|
||||||
|
private double mSampleRate_ms;
|
||||||
|
private LinkedList<Double> mBpmHistory;
|
||||||
|
private int mResetCounter;
|
||||||
|
private int mResetLimit_ms;
|
||||||
|
|
||||||
|
private MovingFilter mMvg;
|
||||||
|
|
||||||
|
EstimatorDistCorr(AccelerometerWindowBuffer windowBuffer, double sampleRate_ms, int resetAfter_ms){
|
||||||
|
|
||||||
|
mBuffer = windowBuffer;
|
||||||
|
mSampleRate_ms = sampleRate_ms;
|
||||||
|
mBpmHistory = new LinkedList<>();
|
||||||
|
mResetCounter = 0;
|
||||||
|
mResetLimit_ms = resetAfter_ms;
|
||||||
|
|
||||||
|
mMvg = new MovingFilter(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//hier gehts weiter :)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.tonifetzer.conductorswatch.bpmEstimation;
|
package de.tonifetzer.conductorswatch.estimation.accelerometer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by toni on 15/12/17.
|
* Created by toni on 15/12/17.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
package de.tonifetzer.conductorswatch.bpmEstimation;
|
package de.tonifetzer.conductorswatch.estimation.accelerometer;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import de.tonifetzer.conductorswatch.estimation.accelerometer.AccelerometerData;
|
||||||
|
import de.tonifetzer.conductorswatch.estimation.accelerometer.AccelerometerWindow;
|
||||||
|
import de.tonifetzer.conductorswatch.estimation.accelerometer.AccelerometerWindowBuffer;
|
||||||
|
import de.tonifetzer.conductorswatch.utilities.Utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by toni on 16/12/17.
|
* Created by toni on 16/12/17.
|
||||||
*/
|
*/
|
||||||
@@ -11,11 +16,12 @@ public class AccelerometerInterpolator {
|
|||||||
private double[] mY;
|
private double[] mY;
|
||||||
private double[] mZ;
|
private double[] mZ;
|
||||||
private long[] mTsInterp;
|
private long[] mTsInterp;
|
||||||
|
private int mSize;
|
||||||
|
|
||||||
public AccelerometerInterpolator(AccelerometerWindowBuffer ab, double sampleRate_ms){
|
public AccelerometerInterpolator(AccelerometerWindowBuffer ab, double sampleRate_ms){
|
||||||
|
|
||||||
long size = (ab.getYongest().ts - (ab.getOldest().ts - (long) sampleRate_ms)) / (long) sampleRate_ms;
|
mSize = (int) ((ab.getYongest().ts - (ab.getOldest().ts - (long) sampleRate_ms)) / (long) sampleRate_ms);
|
||||||
mTsInterp = new long[(int)size];
|
mTsInterp = new long[mSize];
|
||||||
int j = 0;
|
int j = 0;
|
||||||
for(long i = ab.getOldest().ts; i <= ab.getYongest().ts; i += sampleRate_ms){
|
for(long i = ab.getOldest().ts; i <= ab.getYongest().ts; i += sampleRate_ms){
|
||||||
mTsInterp[j++] = i;
|
mTsInterp[j++] = i;
|
||||||
@@ -30,6 +36,18 @@ public class AccelerometerInterpolator {
|
|||||||
return mTsInterp;
|
return mTsInterp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getX(int idx){
|
||||||
|
return mX[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getY(int idx){
|
||||||
|
return mY[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getZ(int idx){
|
||||||
|
return mZ[idx];
|
||||||
|
}
|
||||||
|
|
||||||
public double[] getX(){
|
public double[] getX(){
|
||||||
return mX;
|
return mX;
|
||||||
}
|
}
|
||||||
@@ -42,6 +60,10 @@ public class AccelerometerInterpolator {
|
|||||||
return mZ;
|
return mZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int size(){
|
||||||
|
return mSize;
|
||||||
|
}
|
||||||
|
|
||||||
private static double[] interpLinear(double[] x, double[] y, double[] xi) throws IllegalArgumentException {
|
private static double[] interpLinear(double[] x, double[] y, double[] xi) throws IllegalArgumentException {
|
||||||
if (x.length != y.length) {
|
if (x.length != y.length) {
|
||||||
throw new IllegalArgumentException("X and Y must be the same length");
|
throw new IllegalArgumentException("X and Y must be the same length");
|
||||||
@@ -104,5 +126,34 @@ public class AccelerometerInterpolator {
|
|||||||
return interpLinear(xd, y, xid);
|
return interpLinear(xd, y, xid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AccelerometerWindow interpolate(AccelerometerWindow data){
|
||||||
|
|
||||||
|
//calculate samplerate
|
||||||
|
double[] rawTimestamps = data.getTs_D();
|
||||||
|
double sampleRate_ms = Math.round(Utils.mean(Utils.diff(rawTimestamps)));
|
||||||
|
|
||||||
|
//create interpolated timestamps
|
||||||
|
double[] interpolatedTimestamps;
|
||||||
|
int size = (int) ((data.getLast().ts - (data.getFirst().ts - (long) sampleRate_ms)) / (long) sampleRate_ms);
|
||||||
|
interpolatedTimestamps = new double[size];
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
for(double i = data.getFirst().ts; i <= data.getLast().ts; i += sampleRate_ms){
|
||||||
|
interpolatedTimestamps[j++] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
//interpolate the single axis separately
|
||||||
|
double[] interpolatedX = interpLinear(rawTimestamps, data.getX(), interpolatedTimestamps);
|
||||||
|
double[] interpolatedY = interpLinear(rawTimestamps, data.getY(), interpolatedTimestamps);
|
||||||
|
double[] interpolatedZ = interpLinear(rawTimestamps, data.getZ(), interpolatedTimestamps);
|
||||||
|
|
||||||
|
//merge everything back into an ArrayList
|
||||||
|
AccelerometerWindow interpolatedData = new AccelerometerWindow(size);
|
||||||
|
for(int i = 0; i < size; ++i){
|
||||||
|
interpolatedData.add(new AccelerometerData((long) rawTimestamps[i], interpolatedX[i], interpolatedY[i], interpolatedZ[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return interpolatedData;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package de.tonifetzer.conductorswatch.estimation.accelerometer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
public class AccelerometerWindow extends ArrayList<AccelerometerData> {
|
||||||
|
|
||||||
|
public AccelerometerWindow(int size){
|
||||||
|
super(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccelerometerWindow(AccelerometerWindow other){
|
||||||
|
super(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccelerometerWindow(LinkedList<AccelerometerData> other){
|
||||||
|
super(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccelerometerData getLast() {
|
||||||
|
//TODO: check if list is empty! this causes indexoutofbounce
|
||||||
|
synchronized (this){
|
||||||
|
return super.get(size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public AccelerometerData getFirst() {
|
||||||
|
return super.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] getX(){
|
||||||
|
return this.stream().mapToDouble(d -> d.x).toArray();
|
||||||
|
}
|
||||||
|
public double[] getY(){
|
||||||
|
return this.stream().mapToDouble(d -> d.y).toArray();
|
||||||
|
}
|
||||||
|
public double[] getZ(){
|
||||||
|
return this.stream().mapToDouble(d -> d.z).toArray();
|
||||||
|
}
|
||||||
|
public long[] getTs_L(){
|
||||||
|
return this.stream().mapToLong(d -> d.ts).toArray();
|
||||||
|
}
|
||||||
|
public double[] getTs_D(){
|
||||||
|
return this.stream().mapToDouble(d -> d.ts).toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.tonifetzer.conductorswatch.bpmEstimation;
|
package de.tonifetzer.conductorswatch.estimation.accelerometer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package de.tonifetzer.conductorswatch.estimation.accelerometer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
public class AccelerometerWindowContainer {
|
||||||
|
|
||||||
|
private int mWindowSize;
|
||||||
|
private int mOverlapSize;
|
||||||
|
private int mOverlapCounter;
|
||||||
|
|
||||||
|
//data arrays that provide fixed windows for calculation
|
||||||
|
private AccelerometerWindow mRawData;
|
||||||
|
private AccelerometerWindow mInterpolatedData;
|
||||||
|
|
||||||
|
//buffer that is permanently updated through incoming sensor data
|
||||||
|
private LinkedList<AccelerometerData> mBuffer;
|
||||||
|
|
||||||
|
public AccelerometerWindowContainer(int windowSize_ms, int overlap_ms){
|
||||||
|
mWindowSize = windowSize_ms;
|
||||||
|
mOverlapSize = overlap_ms;
|
||||||
|
mOverlapCounter = 0;
|
||||||
|
|
||||||
|
mBuffer = new LinkedList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean add(AccelerometerData ad){
|
||||||
|
//TODO: add exception handling. falseArgument if ad has no numeric x,y,z
|
||||||
|
|
||||||
|
synchronized (this){
|
||||||
|
|
||||||
|
//do not add duplicates!
|
||||||
|
if(!mBuffer.isEmpty() && mBuffer.getLast().equals(ad)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// current - last to increment overlap time
|
||||||
|
if(!mBuffer.isEmpty()){
|
||||||
|
mOverlapCounter += ad.ts - mBuffer.getLast().ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
//add element
|
||||||
|
boolean r = mBuffer.add(ad);
|
||||||
|
removeOldElements();
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeOldElements(){
|
||||||
|
synchronized (this) {
|
||||||
|
if (!mBuffer.isEmpty()) {
|
||||||
|
if ((mBuffer.getLast().ts - mBuffer.getFirst().ts) > mWindowSize) {
|
||||||
|
|
||||||
|
while(mBuffer.getFirst().ts < mBuffer.getLast().ts - mWindowSize){
|
||||||
|
mBuffer.remove(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNextWindowReady(){
|
||||||
|
|
||||||
|
if(!mBuffer.isEmpty()){
|
||||||
|
if(((mBuffer.getFirst().ts - mBuffer.getLast().ts) > mWindowSize / 4) && mOverlapCounter > mOverlapSize){
|
||||||
|
mOverlapCounter = 0;
|
||||||
|
|
||||||
|
//fill the data arrays - we use a shallow copy, as the values are not touched.
|
||||||
|
mRawData = new AccelerometerWindow(mBuffer);
|
||||||
|
mInterpolatedData = AccelerometerInterpolator.interpolate(mRawData);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<AccelerometerData> getRawData(){
|
||||||
|
return mRawData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<AccelerometerData> getInterpolatedData(){
|
||||||
|
return mInterpolatedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear(){
|
||||||
|
synchronized (this){
|
||||||
|
mBuffer.clear();
|
||||||
|
mRawData.clear();
|
||||||
|
mInterpolatedData.clear();
|
||||||
|
this.mOverlapCounter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.tonifetzer.conductorswatch.bpmEstimation;
|
package de.tonifetzer.conductorswatch.estimation.dsp;
|
||||||
|
|
||||||
import org.jtransforms.fft.DoubleFFT_1D;
|
import org.jtransforms.fft.DoubleFFT_1D;
|
||||||
import de.tonifetzer.conductorswatch.utilities.Utils;
|
import de.tonifetzer.conductorswatch.utilities.Utils;
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package de.tonifetzer.conductorswatch.estimation.dsp;
|
||||||
|
|
||||||
|
import de.tonifetzer.conductorswatch.estimation.accelerometer.AccelerometerWindow;
|
||||||
|
import de.tonifetzer.conductorswatch.utilities.Utils;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.DoubleSummaryStatistics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by toni on 06/12/18.
|
||||||
|
*/
|
||||||
|
public class DistanceCorrelation {
|
||||||
|
|
||||||
|
//TODO: remove bad peaks found at the very beginning and end of the signal
|
||||||
|
private static int mMaxLag;
|
||||||
|
private double[] mCorr;
|
||||||
|
|
||||||
|
public DistanceCorrelation(AccelerometerWindow data, int maxLag){
|
||||||
|
|
||||||
|
mMaxLag = maxLag;
|
||||||
|
mCorr = calc(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] getCorr(){
|
||||||
|
return mCorr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double[] calc(AccelerometerWindow data){
|
||||||
|
|
||||||
|
if(mMaxLag < 1){
|
||||||
|
throw new RuntimeException("maxlag has to be greater 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
//init
|
||||||
|
int n = data.size();
|
||||||
|
int lag_size = Math.min(mMaxLag, n - 1);
|
||||||
|
double[] corr = new double[lag_size + 1];
|
||||||
|
|
||||||
|
//do the math
|
||||||
|
for(int j = 0; j <= lag_size; ++j){
|
||||||
|
double[] dist = new double[n - Math.abs(j)];
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
for (int i = j; i < n; ++i){
|
||||||
|
dist[idx] = Utils.getDistance(data.get(i).x, data.get(i).y, data.get(i).z, data.get(i-j).x, data.get(i-j).y, data.get(i-j).z);
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
corr[j] = Utils.geometricMeanLog(dist);
|
||||||
|
}
|
||||||
|
|
||||||
|
//to [0, 1]
|
||||||
|
DoubleSummaryStatistics corrStat = Arrays.stream(corr).summaryStatistics();
|
||||||
|
double corMaxVal = corrStat.getMax();
|
||||||
|
for(int k = 0; k < corr.length; ++k){
|
||||||
|
corr[k] = ((corr[k] * (-1)) / corMaxVal) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mirror corr(2:512) and put it in front
|
||||||
|
double[] output = new double[(2 * lag_size) + 1];
|
||||||
|
System.arraycopy(corr, 0, output, lag_size, lag_size + 1); // +1 to place the 1.0 in the middle of correlation
|
||||||
|
Utils.reverse(corr);
|
||||||
|
System.arraycopy(corr, 0, output, 0, lag_size);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package de.tonifetzer.conductorswatch.bpmEstimation;
|
package de.tonifetzer.conductorswatch.estimation.dsp;
|
||||||
|
|
||||||
import de.tonifetzer.conductorswatch.utilities.Utils;
|
import de.tonifetzer.conductorswatch.utilities.Utils;
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ import java.util.LinkedList;
|
|||||||
/**
|
/**
|
||||||
* Created by toni on 15/12/17.
|
* Created by toni on 15/12/17.
|
||||||
*/
|
*/
|
||||||
public class Peaks {
|
public class PeakDetector {
|
||||||
|
|
||||||
private LinkedList<Integer> mPeaksIdx; //provide the idx within the given data array
|
private LinkedList<Integer> mPeaksIdx; //provide the idx within the given data array
|
||||||
private LinkedList<Integer> mPeaksPos; //the real position within the data-rang e.g. lag -1024 to 1024
|
private LinkedList<Integer> mPeaksPos; //the real position within the data-rang e.g. lag -1024 to 1024
|
||||||
@@ -24,7 +24,7 @@ public class Peaks {
|
|||||||
* @param isRelative minimum value of peaks is relative to local average
|
* @param isRelative minimum value of peaks is relative to local average
|
||||||
* @return array of peaks
|
* @return array of peaks
|
||||||
*/
|
*/
|
||||||
public Peaks(double[] data, int width, double threshold, double decayRate, boolean isRelative){
|
public PeakDetector(double[] data, int width, double threshold, double decayRate, boolean isRelative){
|
||||||
|
|
||||||
this.mData = data;
|
this.mData = data;
|
||||||
this.mPeaksIdx = new LinkedList<>();
|
this.mPeaksIdx = new LinkedList<>();
|
||||||
@@ -12,7 +12,27 @@ import java.util.List;
|
|||||||
//TODO: change from double to generic type
|
//TODO: change from double to generic type
|
||||||
public class Utils {
|
public class Utils {
|
||||||
public static double getDistance(double x1, double y1, double x2, double y2) {
|
public static double getDistance(double x1, double y1, double x2, double y2) {
|
||||||
return (double) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
|
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getDistance(double x1, double y1, double z1, double x2, double y2, double z2) {
|
||||||
|
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void reverse(double[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
int j = array.length - 1;
|
||||||
|
double tmp;
|
||||||
|
while (j > i) {
|
||||||
|
tmp = array[j];
|
||||||
|
array[j] = array[i];
|
||||||
|
array[i] = tmp;
|
||||||
|
j--;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double sqr(double x) {
|
public static double sqr(double x) {
|
||||||
@@ -120,6 +140,17 @@ public class Utils {
|
|||||||
return Math.pow(sum, 1.0 / data.length);
|
return Math.pow(sum, 1.0 / data.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static double geometricMeanLog(double[] data){
|
||||||
|
double GM_log = 0.0d;
|
||||||
|
for (int i = 0; i < data.length; ++i) {
|
||||||
|
if (data[i] == 0.0d) {
|
||||||
|
return 0.0d;
|
||||||
|
}
|
||||||
|
GM_log += Math.log(data[i]);
|
||||||
|
}
|
||||||
|
return Math.exp(GM_log / data.length);
|
||||||
|
}
|
||||||
|
|
||||||
public static int intersectionNumber(double[] signal, double border){
|
public static int intersectionNumber(double[] signal, double border){
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
boolean isSmallerValue = false;
|
boolean isSmallerValue = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user