diff --git a/android/ConductorsPhone/app/src/main/java/de/tonifetzer/conductorswatch/Metronome.java b/android/ConductorsPhone/app/src/main/java/de/tonifetzer/conductorswatch/Metronome.java index f83404b..f2537e5 100644 --- a/android/ConductorsPhone/app/src/main/java/de/tonifetzer/conductorswatch/Metronome.java +++ b/android/ConductorsPhone/app/src/main/java/de/tonifetzer/conductorswatch/Metronome.java @@ -8,7 +8,6 @@ import java.util.TimerTask; /** * Created by toni on 20/12/17. */ - public class Metronome extends TimerTask { //private MediaPlayer mMediaPlayer; diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/WorkerFragment.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/WorkerFragment.java index 2f591a1..95db80c 100644 --- a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/WorkerFragment.java +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/WorkerFragment.java @@ -13,7 +13,7 @@ import android.widget.TextView; import java.util.Timer; 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.Metronome; diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/Estimator.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/Estimator.java similarity index 97% rename from android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/Estimator.java rename to android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/Estimator.java index efb966b..fec3b19 100644 --- a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/Estimator.java +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/Estimator.java @@ -1,4 +1,4 @@ -package de.tonifetzer.conductorswatch.bpmEstimation; +package de.tonifetzer.conductorswatch.estimation; import android.content.Context; import android.hardware.Sensor; @@ -12,6 +12,8 @@ import java.util.Timer; import java.util.TimerTask; 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.utilities.ByteStreamWriter; import de.tonifetzer.conductorswatch.utilities.Utils; diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/EstimatorAutoCorr.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/EstimatorAutoCorr.java similarity index 87% rename from android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/EstimatorAutoCorr.java rename to android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/EstimatorAutoCorr.java index 7828aa9..69bbc16 100644 --- a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/EstimatorAutoCorr.java +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/EstimatorAutoCorr.java @@ -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.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. @@ -28,7 +29,7 @@ public class EstimatorAutoCorr { //private SimpleKalman mKalman; - public EstimatorAutoCorr(AccelerometerWindowBuffer windowBuffer, double sampleRate_ms, int resetAfter_ms){ + EstimatorAutoCorr(AccelerometerWindowBuffer windowBuffer, double sampleRate_ms, int resetAfter_ms){ mBuffer = windowBuffer; mSampleRate_ms = sampleRate_ms; @@ -47,7 +48,7 @@ public class EstimatorAutoCorr { //mKalman = new SimpleKalman(); } - public double estimate(AccelerometerWindowBuffer fixedWindow){ + double estimate(AccelerometerWindowBuffer fixedWindow){ double sampleRate = mSampleRate_ms; if(sampleRate <= 0){ @@ -73,9 +74,9 @@ public class EstimatorAutoCorr { //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); + PeakDetector pX = new PeakDetector(xAutoCorr, peakWidth, 0.1f, 0, false); + PeakDetector pY = new PeakDetector(yAutoCorr, peakWidth, 0.1f, 0, false); + PeakDetector pZ = new PeakDetector(zAutoCorr, peakWidth, 0.1f, 0, false); mBpmHistory_X.add(pX.getBPM(sampleRate)); mBpmHistory_Y.add(pY.getBPM(sampleRate)); @@ -125,7 +126,7 @@ public class EstimatorAutoCorr { 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; double sumCorr = 0; //to prevent division by zero @@ -173,7 +174,7 @@ public class EstimatorAutoCorr { //no peaks, reject if(cntNumAxis == 0){ - //throw new IllegalArgumentException("All Peaks are empty! -> Reject Estimation"); + //throw new IllegalArgumentException("All PeakDetector are empty! -> Reject Estimation"); return -1; } diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/EstimatorDistCorr.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/EstimatorDistCorr.java new file mode 100644 index 0000000..6442ec4 --- /dev/null +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/EstimatorDistCorr.java @@ -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 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 :) + +} diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/AccelerometerData.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerData.java similarity index 92% rename from android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/AccelerometerData.java rename to android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerData.java index 1a0fb73..6361c94 100644 --- a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/AccelerometerData.java +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerData.java @@ -1,4 +1,4 @@ -package de.tonifetzer.conductorswatch.bpmEstimation; +package de.tonifetzer.conductorswatch.estimation.accelerometer; /** * Created by toni on 15/12/17. diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/AccelerometerInterpolator.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerInterpolator.java similarity index 58% rename from android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/AccelerometerInterpolator.java rename to android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerInterpolator.java index c3a68bd..044ac00 100644 --- a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/AccelerometerInterpolator.java +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerInterpolator.java @@ -1,7 +1,12 @@ -package de.tonifetzer.conductorswatch.bpmEstimation; +package de.tonifetzer.conductorswatch.estimation.accelerometer; 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. */ @@ -11,11 +16,12 @@ public class AccelerometerInterpolator { private double[] mY; private double[] mZ; private long[] mTsInterp; + private int mSize; public AccelerometerInterpolator(AccelerometerWindowBuffer ab, double sampleRate_ms){ - long size = (ab.getYongest().ts - (ab.getOldest().ts - (long) sampleRate_ms)) / (long) sampleRate_ms; - mTsInterp = new long[(int)size]; + mSize = (int) ((ab.getYongest().ts - (ab.getOldest().ts - (long) sampleRate_ms)) / (long) sampleRate_ms); + mTsInterp = new long[mSize]; int j = 0; for(long i = ab.getOldest().ts; i <= ab.getYongest().ts; i += sampleRate_ms){ mTsInterp[j++] = i; @@ -30,6 +36,18 @@ public class AccelerometerInterpolator { 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(){ return mX; } @@ -42,6 +60,10 @@ public class AccelerometerInterpolator { return mZ; } + public int size(){ + return mSize; + } + private static double[] interpLinear(double[] x, double[] y, double[] xi) throws IllegalArgumentException { if (x.length != y.length) { throw new IllegalArgumentException("X and Y must be the same length"); @@ -104,5 +126,34 @@ public class AccelerometerInterpolator { 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; + } } diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerWindow.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerWindow.java new file mode 100644 index 0000000..3b8a236 --- /dev/null +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerWindow.java @@ -0,0 +1,45 @@ +package de.tonifetzer.conductorswatch.estimation.accelerometer; + +import java.util.ArrayList; +import java.util.LinkedList; + +public class AccelerometerWindow extends ArrayList { + + public AccelerometerWindow(int size){ + super(size); + } + + public AccelerometerWindow(AccelerometerWindow other){ + super(other); + } + + public AccelerometerWindow(LinkedList 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(); + } +} \ No newline at end of file diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/AccelerometerWindowBuffer.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerWindowBuffer.java similarity index 98% rename from android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/AccelerometerWindowBuffer.java rename to android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerWindowBuffer.java index 5974d84..90cba4c 100644 --- a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/AccelerometerWindowBuffer.java +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerWindowBuffer.java @@ -1,4 +1,4 @@ -package de.tonifetzer.conductorswatch.bpmEstimation; +package de.tonifetzer.conductorswatch.estimation.accelerometer; import java.util.ArrayList; diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerWindowContainer.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerWindowContainer.java new file mode 100644 index 0000000..368c53f --- /dev/null +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/accelerometer/AccelerometerWindowContainer.java @@ -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 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 getRawData(){ + return mRawData; + } + + public ArrayList 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; + } + } + +} diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/AutoCorrelation.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/dsp/AutoCorrelation.java similarity index 97% rename from android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/AutoCorrelation.java rename to android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/dsp/AutoCorrelation.java index 1ca8856..390e7c9 100644 --- a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/AutoCorrelation.java +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/dsp/AutoCorrelation.java @@ -1,4 +1,4 @@ -package de.tonifetzer.conductorswatch.bpmEstimation; +package de.tonifetzer.conductorswatch.estimation.dsp; import org.jtransforms.fft.DoubleFFT_1D; import de.tonifetzer.conductorswatch.utilities.Utils; diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/dsp/DistanceCorrelation.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/dsp/DistanceCorrelation.java new file mode 100644 index 0000000..2da426f --- /dev/null +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/dsp/DistanceCorrelation.java @@ -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; + } +} + diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/Peaks.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/dsp/PeakDetector.java similarity index 96% rename from android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/Peaks.java rename to android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/dsp/PeakDetector.java index c2c540a..d293d48 100644 --- a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/bpmEstimation/Peaks.java +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/estimation/dsp/PeakDetector.java @@ -1,4 +1,4 @@ -package de.tonifetzer.conductorswatch.bpmEstimation; +package de.tonifetzer.conductorswatch.estimation.dsp; import de.tonifetzer.conductorswatch.utilities.Utils; @@ -7,7 +7,7 @@ import java.util.LinkedList; /** * Created by toni on 15/12/17. */ -public class Peaks { +public class PeakDetector { private LinkedList mPeaksIdx; //provide the idx within the given data array private LinkedList 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 * @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.mPeaksIdx = new LinkedList<>(); diff --git a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/utilities/Utils.java b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/utilities/Utils.java index 90023e9..972de76 100644 --- a/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/utilities/Utils.java +++ b/android/ConductorsWatch/app/src/main/java/de/tonifetzer/conductorswatch/utilities/Utils.java @@ -12,7 +12,27 @@ import java.util.List; //TODO: change from double to generic type public class Utils { 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) { @@ -120,6 +140,17 @@ public class Utils { 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){ int cnt = 0; boolean isSmallerValue = false;