puhh. this is a big commit
- change the complete file sending / receiving process between phone and watch, we now use channels instead of simple messages. this is recommentad by google, due to some changes in google play services. - made some smaller changes in the ui, for file saving and saving of sensordata for evaluation purposes - edited the manifest and gradle script for play store. - made some change for a better performance for he huawei watch
@@ -11,8 +11,8 @@ android {
|
|||||||
minSdkVersion 24
|
minSdkVersion 24
|
||||||
targetSdkVersion 26
|
targetSdkVersion 26
|
||||||
//sdk 2 | product version 3 | build num 2 | multi-apk 2
|
//sdk 2 | product version 3 | build num 2 | multi-apk 2
|
||||||
versionCode 260120100
|
versionCode 260130300
|
||||||
versionName "0.1.2"
|
versionName "0.1.3.2"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -33,12 +33,12 @@ dependencies {
|
|||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'com.android.support.test:runner:1.0.1'
|
androidTestImplementation 'com.android.support.test:runner:1.0.1'
|
||||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
|
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
|
||||||
implementation 'com.google.android.support:wearable:2.1.0'
|
implementation 'com.google.android.support:wearable:2.2.0'
|
||||||
implementation 'com.google.android.gms:play-services-wearable:11.8.0'
|
implementation 'com.google.android.gms:play-services-wearable:11.8.0'
|
||||||
implementation 'com.android.support:percent:26.1.0'
|
implementation 'com.android.support:percent:26.1.0'
|
||||||
implementation 'com.android.support:animated-vector-drawable:26.1.0'
|
implementation 'com.android.support:animated-vector-drawable:26.1.0'
|
||||||
implementation 'com.android.support:appcompat-v7:26.1.0'
|
implementation 'com.android.support:appcompat-v7:26.1.0'
|
||||||
implementation 'com.android.support:support-v4:26.1.0'
|
implementation 'com.android.support:support-v4:26.1.0'
|
||||||
implementation 'com.android.support:recyclerview-v7:26.1.0'
|
implementation 'com.android.support:recyclerview-v7:26.1.0'
|
||||||
compileOnly 'com.google.android.wearable:wearable:2.1.0'
|
compileOnly 'com.google.android.wearable:wearable:2.2.0'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
package="de.tonifetzer.conductorswatch">
|
package="de.tonifetzer.conductorswatch">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 20 KiB |
@@ -9,8 +9,8 @@ android {
|
|||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 26
|
targetSdkVersion 26
|
||||||
//sdk 2 | product version 3 | build num 2 | multi-apk 2
|
//sdk 2 | product version 3 | build num 2 | multi-apk 2
|
||||||
versionCode 260120101
|
versionCode 260130301
|
||||||
versionName "0.1.2"
|
versionName "0.1.3.2"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@@ -22,6 +22,7 @@ android {
|
|||||||
wear1 {
|
wear1 {
|
||||||
dimension "minSdk"
|
dimension "minSdk"
|
||||||
// Use the defaultConfig value
|
// Use the defaultConfig value
|
||||||
|
minSdkVersion 23
|
||||||
}
|
}
|
||||||
wear2 {
|
wear2 {
|
||||||
dimension "minSdk"
|
dimension "minSdk"
|
||||||
@@ -36,8 +37,8 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
implementation 'com.google.android.support:wearable:2.1.0'
|
implementation 'com.google.android.support:wearable:2.2.0'
|
||||||
implementation 'com.google.android.gms:play-services-wearable:11.8.0'
|
implementation 'com.google.android.gms:play-services-wearable:11.8.0'
|
||||||
implementation 'com.android.support:percent:26.1.0'
|
implementation 'com.android.support:percent:26.1.0'
|
||||||
implementation 'com.android.support:animated-vector-drawable:26.1.0'
|
implementation 'com.android.support:animated-vector-drawable:26.1.0'
|
||||||
@@ -45,6 +46,6 @@ dependencies {
|
|||||||
implementation 'com.android.support:support-v4:26.1.0'
|
implementation 'com.android.support:support-v4:26.1.0'
|
||||||
implementation 'com.android.support:recyclerview-v7:26.1.0'
|
implementation 'com.android.support:recyclerview-v7:26.1.0'
|
||||||
implementation 'com.android.support:wear:26.1.0'
|
implementation 'com.android.support:wear:26.1.0'
|
||||||
compileOnly 'com.google.android.wearable:wearable:2.1.0'
|
compileOnly 'com.google.android.wearable:wearable:2.2.0'
|
||||||
compile 'com.github.wendykierp:JTransforms:3.1'
|
compile 'com.github.wendykierp:JTransforms:3.1'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class Estimator implements SensorEventListener {
|
|||||||
|
|
||||||
private SensorManager mSensorManager;
|
private SensorManager mSensorManager;
|
||||||
private Sensor mAccelerometer;
|
private Sensor mAccelerometer;
|
||||||
private Sensor mGyroscope;
|
private Sensor mRawAccelerometer;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private AccelerometerWindowBuffer mAccelerometerWindowBuffer;
|
private AccelerometerWindowBuffer mAccelerometerWindowBuffer;
|
||||||
private BpmEstimator mBpmEstimator;
|
private BpmEstimator mBpmEstimator;
|
||||||
@@ -50,11 +50,11 @@ public class Estimator implements SensorEventListener {
|
|||||||
|
|
||||||
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
|
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
|
||||||
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
|
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
|
||||||
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
|
mRawAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||||
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
|
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
|
||||||
mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_FASTEST);
|
mSensorManager.registerListener(this, mRawAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
|
||||||
|
|
||||||
mAccelerometerWindowBuffer = new AccelerometerWindowBuffer(1024, 256);
|
mAccelerometerWindowBuffer = new AccelerometerWindowBuffer(6000, 1500);
|
||||||
mBpmEstimator = new BpmEstimator(mAccelerometerWindowBuffer, 0, 5000);
|
mBpmEstimator = new BpmEstimator(mAccelerometerWindowBuffer, 0, 5000);
|
||||||
|
|
||||||
mTimer.scheduleAtFixedRate(new TimerTask() {
|
mTimer.scheduleAtFixedRate(new TimerTask() {
|
||||||
@@ -110,9 +110,8 @@ public class Estimator implements SensorEventListener {
|
|||||||
mByteStreamWriterAcc.reset();
|
mByteStreamWriterAcc.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (se.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
|
if (se.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||||
//TODO: Rename AccelerometerData to SensorData3D
|
mByteStreamWriterGyro.writeSensor3D(Sensor.TYPE_ACCELEROMETER, se.values[0], se.values[1], se.values[2]);
|
||||||
mByteStreamWriterGyro.writeSensor3D(Sensor.TYPE_GYROSCOPE, se.values[0], se.values[1], se.values[2]);
|
|
||||||
|
|
||||||
mStreamer.sendByteArray(mByteStreamWriterGyro.getByteArray());
|
mStreamer.sendByteArray(mByteStreamWriterGyro.getByteArray());
|
||||||
mByteStreamWriterGyro.reset();
|
mByteStreamWriterGyro.reset();
|
||||||
|
|||||||
@@ -12,35 +12,49 @@ public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
|||||||
private int mOverlapSize;
|
private int mOverlapSize;
|
||||||
private int mOverlapCounter;
|
private int mOverlapCounter;
|
||||||
|
|
||||||
public AccelerometerWindowBuffer(int windowSize, int overlap){
|
public AccelerometerWindowBuffer(int windowSize_ms, int overlap_ms){
|
||||||
mWindowSize = windowSize;
|
mWindowSize = windowSize_ms;
|
||||||
mOverlapSize = overlap;
|
mOverlapSize = overlap_ms;
|
||||||
mOverlapCounter = 1;
|
mOverlapCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: add exception handling. falseArgument if ad has no numeric x,y,z
|
//TODO: add exception handling. falseArgument if ad has no numeric x,y,z
|
||||||
public boolean add(AccelerometerData ad){
|
public boolean add(AccelerometerData ad){
|
||||||
synchronized (this){
|
synchronized (this){
|
||||||
|
|
||||||
//do not add duplicates!
|
//do not add duplicates!
|
||||||
if (!isEmpty() && getYongest().equals(ad)) {
|
if(!isEmpty() && getYongest().equals(ad)){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean r = super.add(ad);
|
// current - last to increment overlap time
|
||||||
if (size() > mWindowSize) {
|
if(!isEmpty()){
|
||||||
removeRange(0, size() - mWindowSize);
|
mOverlapCounter += ad.ts - getYongest().ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
++mOverlapCounter;
|
//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;
|
||||||
|
}
|
||||||
|
remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNextWindowReady(){
|
public boolean isNextWindowReady(){
|
||||||
if((size() > mWindowSize / 2) && mOverlapCounter > mOverlapSize){
|
if(!isEmpty()){
|
||||||
mOverlapCounter = 1;
|
if(((getYongest().ts - getOldest().ts) > mWindowSize / 2) && mOverlapCounter > mOverlapSize){
|
||||||
|
mOverlapCounter = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,13 +58,16 @@ public class BpmEstimator {
|
|||||||
//just look at the newest 512 samples
|
//just look at the newest 512 samples
|
||||||
//List<AccelerometerData> subBuffer = mBuffer.subList(mBuffer.size() - 512, mBuffer.size());
|
//List<AccelerometerData> subBuffer = mBuffer.subList(mBuffer.size() - 512, mBuffer.size());
|
||||||
|
|
||||||
double[] xAutoCorr = new AutoCorrelation(interp.getX(), 512).getCorr();
|
double[] xAutoCorr = new AutoCorrelation(interp.getX(), fixedWindow.size()).getCorr();
|
||||||
double[] yAutoCorr = new AutoCorrelation(interp.getY(), 512).getCorr();
|
double[] yAutoCorr = new AutoCorrelation(interp.getY(), fixedWindow.size()).getCorr();
|
||||||
double[] zAutoCorr = new AutoCorrelation(interp.getZ(), 512).getCorr();
|
double[] zAutoCorr = new AutoCorrelation(interp.getZ(), fixedWindow.size()).getCorr();
|
||||||
|
|
||||||
Peaks pX = new Peaks(xAutoCorr, 50, 0.1f, 0, false);
|
|
||||||
Peaks pY = new Peaks(yAutoCorr, 50, 0.1f, 0, false);
|
//find a peak within range of 250 ms
|
||||||
Peaks pZ = new Peaks(zAutoCorr, 50, 0.1f, 0, false);
|
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_X.add(pX.getBPM(sampleRate));
|
||||||
mBpmHistory_Y.add(pY.getBPM(sampleRate));
|
mBpmHistory_Y.add(pY.getBPM(sampleRate));
|
||||||
@@ -88,7 +91,7 @@ public class BpmEstimator {
|
|||||||
//mResetCounter = 0;
|
//mResetCounter = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int resetAfter = (int) Math.round(mResetLimit_ms / (mBuffer.getOverlapSize() * sampleRate));
|
int resetAfter = (int) Math.round(mResetLimit_ms / (mBuffer.getOverlapSize()));
|
||||||
if(++mResetCounter > resetAfter){
|
if(++mResetCounter > resetAfter){
|
||||||
mBpmHistory.clear();
|
mBpmHistory.clear();
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 12 KiB |
@@ -3,8 +3,11 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
maven {
|
||||||
|
url "https://maven.google.com"
|
||||||
|
}
|
||||||
jcenter()
|
jcenter()
|
||||||
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||||
@@ -17,8 +20,11 @@ buildscript {
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
maven {
|
||||||
|
url "https://maven.google.com"
|
||||||
|
}
|
||||||
jcenter()
|
jcenter()
|
||||||
|
google()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
# Specifies the JVM arguments used for the daemon process.
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
org.gradle.jvmargs=-Xmx1536m
|
org.gradle.jvmargs=-Xmx1536m
|
||||||
|
android.enableD8=true
|
||||||
|
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ import java.util.stream.IntStream;
|
|||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(String [ ] args) {
|
public static void main(String [ ] args) {
|
||||||
File folder = new File("/home/toni/Documents/programme/dirigent/measurements/wearR");
|
//File folder = new File("/home/toni/Documents/programme/dirigent/measurements/lgWear");
|
||||||
|
File folder = new File("/home/toni/Documents/programme/dirigent/measurements/peter_failed");
|
||||||
File[] listOfFiles = folder.listFiles();
|
File[] listOfFiles = folder.listFiles();
|
||||||
|
|
||||||
Utils.ShowPNG windowRaw = new Utils.ShowPNG();
|
Utils.ShowPNG windowRaw = new Utils.ShowPNG();
|
||||||
@@ -30,7 +31,8 @@ public class Main {
|
|||||||
for (File file : listOfFiles) {
|
for (File file : listOfFiles) {
|
||||||
if (file.isFile() && file.getName().contains(".csv")) {
|
if (file.isFile() && file.getName().contains(".csv")) {
|
||||||
|
|
||||||
AccelerometerWindowBuffer accWindowBuffer = new AccelerometerWindowBuffer(1024, 256);
|
//TODO: mach Fenster genau 6 sekunden groß. Egal wie viele Samples.
|
||||||
|
AccelerometerWindowBuffer accWindowBuffer = new AccelerometerWindowBuffer(6000, 1500);
|
||||||
BpmEstimator bpmEstimator = new BpmEstimator(accWindowBuffer, 0, 5000);
|
BpmEstimator bpmEstimator = new BpmEstimator(accWindowBuffer, 0, 5000);
|
||||||
|
|
||||||
//read the file line by line
|
//read the file line by line
|
||||||
@@ -40,7 +42,7 @@ public class Main {
|
|||||||
String[] measurement = line.split(";");
|
String[] measurement = line.split(";");
|
||||||
|
|
||||||
//if linear acc
|
//if linear acc
|
||||||
if(measurement[1].equals("2")){
|
if(measurement[1].equals("10")){
|
||||||
long ts = Long.parseLong(measurement[0]);
|
long ts = Long.parseLong(measurement[0]);
|
||||||
double x = Double.parseDouble(measurement[2]);
|
double x = Double.parseDouble(measurement[2]);
|
||||||
double y = Double.parseDouble(measurement[3]);
|
double y = Double.parseDouble(measurement[3]);
|
||||||
@@ -54,8 +56,9 @@ public class Main {
|
|||||||
double curBpm = bpmEstimator.estimate();
|
double curBpm = bpmEstimator.estimate();
|
||||||
//System.out.println("BPM: " + curBpm);
|
//System.out.println("BPM: " + curBpm);
|
||||||
|
|
||||||
|
double sampleRate = 20;
|
||||||
AccelerometerInterpolator acInterp = new AccelerometerInterpolator(accWindowBuffer, 5);
|
AccelerometerInterpolator acInterp = new AccelerometerInterpolator(accWindowBuffer, sampleRate);
|
||||||
|
int peakWidth = (int) Math.round(250 / sampleRate);
|
||||||
|
|
||||||
//print raw x,y,z
|
//print raw x,y,z
|
||||||
double[] dTs = IntStream.range(0, accWindowBuffer.getTs().length).mapToDouble(i -> accWindowBuffer.getTs()[i]).toArray();
|
double[] dTs = IntStream.range(0, accWindowBuffer.getTs().length).mapToDouble(i -> accWindowBuffer.getTs()[i]).toArray();
|
||||||
@@ -70,9 +73,9 @@ public class Main {
|
|||||||
windowRaw.set(plotRaw.draw());
|
windowRaw.set(plotRaw.draw());
|
||||||
|
|
||||||
//auto corr
|
//auto corr
|
||||||
double[] xAutoCorr = new AutoCorrelation(acInterp.getX(), 512).getCorr();
|
double[] xAutoCorr = new AutoCorrelation(acInterp.getX(), accWindowBuffer.size()).getCorr();
|
||||||
double[] yAutoCorr = new AutoCorrelation(acInterp.getY(), 512).getCorr();
|
double[] yAutoCorr = new AutoCorrelation(acInterp.getY(), accWindowBuffer.size()).getCorr();
|
||||||
double[] zAutoCorr = new AutoCorrelation(acInterp.getZ(), 512).getCorr();
|
double[] zAutoCorr = new AutoCorrelation(acInterp.getZ(), accWindowBuffer.size()).getCorr();
|
||||||
|
|
||||||
//print autocorr
|
//print autocorr
|
||||||
int[] tmp = IntStream.rangeClosed(-((xAutoCorr.length - 1)/2), ((xAutoCorr.length - 1)/2)).toArray();
|
int[] tmp = IntStream.rangeClosed(-((xAutoCorr.length - 1)/2), ((xAutoCorr.length - 1)/2)).toArray();
|
||||||
@@ -86,10 +89,11 @@ public class Main {
|
|||||||
|
|
||||||
windowAuto.set(plotCorr.draw());
|
windowAuto.set(plotCorr.draw());
|
||||||
|
|
||||||
Peaks pX = new Peaks(xAutoCorr, 50, 0.1f, 0, false);
|
Peaks pX = new Peaks(xAutoCorr, peakWidth, 0.1f, 0, false);
|
||||||
|
int xOffset = xAutoCorr.length / 2;
|
||||||
LinkedList<Integer> peaksX = pX.getPeaksIdx();
|
LinkedList<Integer> peaksX = pX.getPeaksIdx();
|
||||||
|
|
||||||
double[] dPeaksXX = IntStream.range(0, peaksX.size()).mapToDouble(i -> (peaksX.get(i) - 512)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
|
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();
|
double[] dPeaksXY = IntStream.range(0, peaksX.size()).mapToDouble(i -> (xAutoCorr[peaksX.get(i)])).toArray();
|
||||||
Plot plotPeaksX = Plot.plot(Plot.plotOpts().
|
Plot plotPeaksX = Plot.plot(Plot.plotOpts().
|
||||||
title("Peak Detection on X").
|
title("Peak Detection on X").
|
||||||
@@ -100,10 +104,11 @@ public class Main {
|
|||||||
|
|
||||||
windowPeaksX.set(plotPeaksX.draw());
|
windowPeaksX.set(plotPeaksX.draw());
|
||||||
|
|
||||||
Peaks pY = new Peaks(yAutoCorr, 50, 0.1f, 0, false);
|
Peaks pY = new Peaks(yAutoCorr, peakWidth, 0.1f, 0, false);
|
||||||
|
int yOffset = yAutoCorr.length / 2;
|
||||||
LinkedList<Integer> peaksY = pY.getPeaksIdx();
|
LinkedList<Integer> peaksY = pY.getPeaksIdx();
|
||||||
|
|
||||||
double[] dPeaksYX = IntStream.range(0, peaksY.size()).mapToDouble(i -> (peaksY.get(i) - 512)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
|
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();
|
double[] dPeaksYY = IntStream.range(0, peaksY.size()).mapToDouble(i -> (yAutoCorr[peaksY.get(i)])).toArray();
|
||||||
Plot plotPeaksY = Plot.plot(Plot.plotOpts().
|
Plot plotPeaksY = Plot.plot(Plot.plotOpts().
|
||||||
title("Peak Detection on Y").
|
title("Peak Detection on Y").
|
||||||
@@ -114,10 +119,11 @@ public class Main {
|
|||||||
|
|
||||||
windowPeaksY.set(plotPeaksY.draw());
|
windowPeaksY.set(plotPeaksY.draw());
|
||||||
|
|
||||||
Peaks pZ = new Peaks(zAutoCorr, 50, 0.1f, 0, false);
|
Peaks pZ = new Peaks(zAutoCorr, peakWidth, 0.1f, 0, false);
|
||||||
|
int zOffset = zAutoCorr.length / 2;
|
||||||
LinkedList<Integer> peaksZ = pZ.getPeaksIdx();
|
LinkedList<Integer> peaksZ = pZ.getPeaksIdx();
|
||||||
|
|
||||||
double[] dPeaksZX = IntStream.range(0, peaksZ.size()).mapToDouble(i -> (peaksZ.get(i) - 512)).toArray();//peaks.stream().mapToDouble(i->i).toArray();
|
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();
|
double[] dPeaksZY = IntStream.range(0, peaksZ.size()).mapToDouble(i -> (zAutoCorr[peaksZ.get(i)])).toArray();
|
||||||
Plot plotPeaksZ = Plot.plot(Plot.plotOpts().
|
Plot plotPeaksZ = Plot.plot(Plot.plotOpts().
|
||||||
title("Peak Detection on Z").
|
title("Peak Detection on Z").
|
||||||
@@ -142,10 +148,10 @@ public class Main {
|
|||||||
}
|
}
|
||||||
// line is not visible here.
|
// line is not visible here.
|
||||||
|
|
||||||
double meanBPM = bpmEstimator.getMeanBpm();
|
//double meanBPM = bpmEstimator.getMeanBpm();
|
||||||
double medianBPM = bpmEstimator.getMedianBPM();
|
//double medianBPM = bpmEstimator.getMedianBPM();
|
||||||
//System.out.println("MEAN BPM: " + Math.round(meanBPM));
|
//System.out.println("MEAN BPM: " + Math.round(meanBPM));
|
||||||
System.out.println("MEDIAN BPM: " + Math.round(medianBPM));
|
//System.out.println("MEDIAN BPM: " + Math.round(medianBPM));
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ import java.util.ArrayList;
|
|||||||
*/
|
*/
|
||||||
public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
||||||
|
|
||||||
private static int mWindowSize;
|
private static int mWindowSize; // in ms
|
||||||
private static int mOverlapSize;
|
private static int mOverlapSize; // in ms
|
||||||
private int mOverlapCounter;
|
private long mOverlapCounter;
|
||||||
|
|
||||||
public AccelerometerWindowBuffer(int windowSize, int overlap){
|
public AccelerometerWindowBuffer(int windowSize, int overlap){
|
||||||
mWindowSize = windowSize;
|
mWindowSize = windowSize;
|
||||||
mOverlapSize = overlap;
|
mOverlapSize = overlap;
|
||||||
mOverlapCounter = 1;
|
mOverlapCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: add exception handling. falseArgument if ad has no numeric x,y,z
|
//TODO: add exception handling. falseArgument if ad has no numeric x,y,z
|
||||||
@@ -26,20 +26,33 @@ public class AccelerometerWindowBuffer extends ArrayList<AccelerometerData> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean r = super.add(ad);
|
// current - last to increment overlap time
|
||||||
if (size() > mWindowSize){
|
if(!isEmpty()){
|
||||||
removeRange(0, size() - mWindowSize);
|
mOverlapCounter += ad.ts - getYongest().ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
++mOverlapCounter;
|
//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;
|
||||||
|
}
|
||||||
|
remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNextWindowReady(){
|
public boolean isNextWindowReady(){
|
||||||
if((size() > mWindowSize / 2) && mOverlapCounter > mOverlapSize){
|
if(!isEmpty()){
|
||||||
mOverlapCounter = 1;
|
if(((getYongest().ts - getOldest().ts) > mWindowSize / 2) && mOverlapCounter > mOverlapSize){
|
||||||
|
mOverlapCounter = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,13 +51,15 @@ public class BpmEstimator {
|
|||||||
|
|
||||||
AccelerometerInterpolator interp = new AccelerometerInterpolator(mBuffer, sampleRate);
|
AccelerometerInterpolator interp = new AccelerometerInterpolator(mBuffer, sampleRate);
|
||||||
|
|
||||||
double[] xAutoCorr = new AutoCorrelation(interp.getX(), 1024).getCorr();
|
double[] xAutoCorr = new AutoCorrelation(interp.getX(), mBuffer.size()).getCorr();
|
||||||
double[] yAutoCorr = new AutoCorrelation(interp.getY(), 1024).getCorr();
|
double[] yAutoCorr = new AutoCorrelation(interp.getY(), mBuffer.size()).getCorr();
|
||||||
double[] zAutoCorr = new AutoCorrelation(interp.getZ(), 1024).getCorr();
|
double[] zAutoCorr = new AutoCorrelation(interp.getZ(), mBuffer.size()).getCorr();
|
||||||
|
|
||||||
Peaks pX = new Peaks(xAutoCorr, 50, 0.1f, 0, false);
|
//find a peak within range of 250 ms
|
||||||
Peaks pY = new Peaks(yAutoCorr, 50, 0.1f, 0, false);
|
int peakWidth = (int) Math.round(250 / sampleRate);
|
||||||
Peaks pZ = new Peaks(zAutoCorr, 50, 0.1f, 0, false);
|
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_X.add(pX.getBPM(sampleRate));
|
||||||
mBpmHistory_Y.add(pY.getBPM(sampleRate));
|
mBpmHistory_Y.add(pY.getBPM(sampleRate));
|
||||||
@@ -80,9 +82,12 @@ public class BpmEstimator {
|
|||||||
mResetCounter = 0;
|
mResetCounter = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int resetAfter = (int) Math.round(mResetLimit_ms / (mBuffer.getOverlapSize() * sampleRate));
|
int resetAfter = (int) Math.round(mResetLimit_ms / (mBuffer.getOverlapSize()));
|
||||||
if(++mResetCounter > resetAfter){
|
if(++mResetCounter > resetAfter){
|
||||||
mBpmHistory.clear();
|
mBpmHistory.clear();
|
||||||
|
|
||||||
|
mBuffer.clear();
|
||||||
|
mMvg.clear();
|
||||||
mResetCounter = 0;
|
mResetCounter = 0;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -30,4 +30,9 @@ public class MovingFilter {
|
|||||||
public double getMedian() {
|
public double getMedian() {
|
||||||
return Utils.median(mSamples);
|
return Utils.median(mSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clear(){
|
||||||
|
mSamples.clear();
|
||||||
|
mTotal = 0d;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,8 @@
|
|||||||
|
|
||||||
|
|
||||||
%files = dir(fullfile('../../measurements/lgWear/', '*.csv'));
|
%files = dir(fullfile('../../measurements/lgWear/', '*.csv'));
|
||||||
files = dir(fullfile('../../measurements/wearR/', '*.csv'));
|
%files = dir(fullfile('../../measurements/wearR/', '*.csv'));
|
||||||
|
files = dir(fullfile('../../measurements/peter_failed/', '*.csv'));
|
||||||
|
|
||||||
for file = files'
|
for file = files'
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ for file = files'
|
|||||||
|
|
||||||
%draw the raw acc data
|
%draw the raw acc data
|
||||||
m_idx = [];
|
m_idx = [];
|
||||||
m_idx = (measurements(:,2)==2);
|
m_idx = (measurements(:,2)==10);
|
||||||
m = measurements(m_idx, :);
|
m = measurements(m_idx, :);
|
||||||
|
|
||||||
%Interpolate to generate a constant sample rate to 250hz (4ms per sample)
|
%Interpolate to generate a constant sample rate to 250hz (4ms per sample)
|
||||||
@@ -76,13 +77,15 @@ for file = files'
|
|||||||
figure(3);
|
figure(3);
|
||||||
plot(m(:,1),m(:,5)) %z
|
plot(m(:,1),m(:,5)) %z
|
||||||
legend("z", "location", "eastoutside");
|
legend("z", "location", "eastoutside");
|
||||||
|
|
||||||
|
waitforbuttonpress();
|
||||||
|
|
||||||
%save timestamps
|
%save timestamps
|
||||||
timestamps = m(:,1);
|
timestamps = m(:,1);
|
||||||
data = m(:,3); %only z
|
data = m(:,3); %only z
|
||||||
|
|
||||||
%TODO: Different window sizes for periods under 16.3 s
|
%TODO: Different window sizes for periods under 16.3 s
|
||||||
window_size = 4096; %about 2 seconds using 2000hz, 16.3 s using 250hz
|
window_size = 2048; %about 2 seconds using 2000hz, 16.3 s using 250hz
|
||||||
overlap = 256;
|
overlap = 256;
|
||||||
bpm_per_window_ms = [];
|
bpm_per_window_ms = [];
|
||||||
bpm_per_window = [];
|
bpm_per_window = [];
|
||||||
|
|||||||