Added average HR calculations to SdAlgHR - SdAlgHR still needs to be commissioned in SdDataSource class so it is used though.
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="uk.org.openseizuredetector"
|
package="uk.org.openseizuredetector"
|
||||||
android:versionCode="115"
|
android:versionCode="115"
|
||||||
android:versionName="4.1.3n">
|
android:versionName="4.1.3m">
|
||||||
<!-- android:allowBackup="false" -->
|
<!-- android:allowBackup="false" -->
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||||
|
|||||||
99
app/src/main/java/uk/org/openseizuredetector/CircBuf.java
Normal file
99
app/src/main/java/uk/org/openseizuredetector/CircBuf.java
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package uk.org.openseizuredetector;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class CircBuf {
|
||||||
|
/*
|
||||||
|
* A circular buffer used to calculate rolling averages
|
||||||
|
* Based loosely on https://gist.github.com/hardik-vala/dc2d19fa7c5108536fbbff96b4fcf105
|
||||||
|
*/
|
||||||
|
private final static String TAG = "CircBuf";
|
||||||
|
|
||||||
|
private double[] mBuff;
|
||||||
|
private double mErrVal;
|
||||||
|
private int mBuffLen;
|
||||||
|
private int mHead;
|
||||||
|
private int mTail;
|
||||||
|
private boolean mIsFull;
|
||||||
|
|
||||||
|
public CircBuf(int n, double errVal) {
|
||||||
|
/**
|
||||||
|
* Create a circular buffer of doubles, of length n members.
|
||||||
|
*/
|
||||||
|
Log.i(TAG, "CircBuf Constructor");
|
||||||
|
mBuff = new double[n];
|
||||||
|
mBuffLen = n;
|
||||||
|
mErrVal = errVal;
|
||||||
|
mHead = 0;
|
||||||
|
mTail = 0;
|
||||||
|
mIsFull = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(double val) {
|
||||||
|
/**
|
||||||
|
* Add value val to the circular buffer.
|
||||||
|
*/
|
||||||
|
Log.d(TAG,"add() - before: mHead="+mHead+", mTail="+mTail);
|
||||||
|
//System.out.println(TAG+" add() - before: mHead="+mHead+", mTail="+mTail);
|
||||||
|
if (mIsFull)
|
||||||
|
mHead = (mHead + 1) % mBuffLen;
|
||||||
|
|
||||||
|
mBuff[mTail] = val;
|
||||||
|
mTail = (mTail + 1) % mBuffLen;
|
||||||
|
if (mTail == mHead)
|
||||||
|
mIsFull = true;
|
||||||
|
Log.d(TAG,"add() - after: mHead="+mHead+", mTail="+mTail);
|
||||||
|
//System.out.println(TAG+" add() - before: mHead="+mHead+", mTail="+mTail);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumVals() {
|
||||||
|
int numElements;
|
||||||
|
if (mIsFull) {
|
||||||
|
numElements = mBuffLen;
|
||||||
|
} else {
|
||||||
|
// Not sure if this is necessary - why would mHead be greater than mTail if the buffer is not full?
|
||||||
|
if (mHead > mTail) {
|
||||||
|
numElements = (mTail + mBuffLen) - mHead;
|
||||||
|
} else {
|
||||||
|
numElements = mTail-mHead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numElements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a double array of buffer items in order of their insertion time
|
||||||
|
* @return double[] of buffer items
|
||||||
|
*/
|
||||||
|
public double[] getVals () {
|
||||||
|
int numElements = getNumVals();
|
||||||
|
System.out.println(TAG+" getVals() - numElements=" + numElements);
|
||||||
|
double[] retArr = new double[numElements];
|
||||||
|
for (int i = 0; i < numElements; i++) {
|
||||||
|
retArr[i] = mBuff[(mHead + i) % mBuffLen];
|
||||||
|
}
|
||||||
|
return retArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAverageVal() {
|
||||||
|
double hrSum = 0.;
|
||||||
|
int hrCount = 0;
|
||||||
|
double valArr[] = getVals();
|
||||||
|
double retVal;
|
||||||
|
for (int n=0; n<valArr.length; n++) {
|
||||||
|
if (valArr[n] != mErrVal) {
|
||||||
|
hrSum += valArr[n];
|
||||||
|
hrCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hrCount>0) {
|
||||||
|
retVal = hrSum / hrCount;
|
||||||
|
} else {
|
||||||
|
retVal = -1;
|
||||||
|
}
|
||||||
|
return(retVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -25,13 +25,15 @@ public class SdAlgHr {
|
|||||||
private double mAverageHrAlarmThreshMin;
|
private double mAverageHrAlarmThreshMin;
|
||||||
private double mAverageHrAlarmThreshMax;
|
private double mAverageHrAlarmThreshMax;
|
||||||
|
|
||||||
private ArrayList<Double> mHrHist;
|
private CircBuf mAdaptiveHrBuff;
|
||||||
|
private CircBuf mAverageHrBuff;
|
||||||
|
|
||||||
public SdAlgHr(Context context) {
|
public SdAlgHr(Context context) {
|
||||||
Log.i(TAG, "SdAlgHr Constructor");
|
Log.i(TAG, "SdAlgHr Constructor");
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mHrHist = new ArrayList<Double>();
|
|
||||||
updatePrefs();
|
updatePrefs();
|
||||||
|
mAdaptiveHrBuff = new CircBuf(mAdaptiveHrAlarmWindowDp, -1.0);
|
||||||
|
mAverageHrBuff = new CircBuf(mAverageHrAlarmWindowDp, -1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
@@ -93,15 +95,7 @@ public class SdAlgHr {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addToHist(double hrVal) {
|
|
||||||
/**
|
|
||||||
* Add value hrVal to the heart rate history list, truncating the list if it is
|
|
||||||
* longer than the required length.
|
|
||||||
*/
|
|
||||||
Log.d(TAG,"addToHist() - length before="+mHrHist.size());
|
|
||||||
mHrHist.add(hrVal);
|
|
||||||
Log.d(TAG,"addToHist() - length before="+mHrHist.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkSimpleHr(double hrVal) {
|
private boolean checkSimpleHr(double hrVal) {
|
||||||
/**
|
/**
|
||||||
@@ -118,33 +112,42 @@ public class SdAlgHr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkAdaptiveHr(double hrVal) {
|
private boolean checkAdaptiveHr(double hrVal) {
|
||||||
// FIXME Make this do something
|
boolean retVal;
|
||||||
return(false);
|
double hrThreshMin;
|
||||||
|
double hrThreshMax;
|
||||||
|
double avHr = mAdaptiveHrBuff.getAverageVal();
|
||||||
|
hrThreshMin = avHr - mAdaptiveHrAlarmThresh;
|
||||||
|
hrThreshMax = avHr + mAdaptiveHrAlarmThresh;
|
||||||
|
|
||||||
|
retVal = false;
|
||||||
|
if (hrVal < hrThreshMin) {
|
||||||
|
retVal = true;
|
||||||
|
}
|
||||||
|
if (hrVal > hrThreshMax) {
|
||||||
|
retVal = true;
|
||||||
|
}
|
||||||
|
Log.d(TAG, "checkAdaptiveHr() - hrVal="+hrVal+", avHr="+avHr+", thresholds=("+hrThreshMin+", "+hrThreshMax+"): Alarm="+retVal);
|
||||||
|
|
||||||
|
return(retVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkAverageHr(double hrVal) {
|
private boolean checkAverageHr(double hrVal) {
|
||||||
// FIXME Make this do something
|
boolean retVal;
|
||||||
return(false);
|
double avHr = mAdaptiveHrBuff.getAverageVal();
|
||||||
}
|
|
||||||
|
|
||||||
public double getAverageHrVal() {
|
retVal = false;
|
||||||
double hrSum = 0.;
|
if (hrVal < mAverageHrAlarmThreshMin) {
|
||||||
int hrCount = 0;
|
retVal = true;
|
||||||
double retVal;
|
|
||||||
for (int n=0; n<mHrHist.size(); n++) {
|
|
||||||
if (mHrHist.get(n) > -1) {
|
|
||||||
hrSum += mHrHist.get(n);
|
|
||||||
hrCount++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (hrCount>0) {
|
if (hrVal > mAverageHrAlarmThreshMax) {
|
||||||
retVal = hrSum / hrCount;
|
retVal = true;
|
||||||
} else {
|
|
||||||
retVal = -1;
|
|
||||||
}
|
}
|
||||||
|
Log.d(TAG, "checkAverageHr() - hrVal="+hrVal+", avHr="+avHr+", thresholds=("+mAverageHrAlarmThreshMin+", "+mAverageHrAlarmThreshMin+"): Alarm="+retVal);
|
||||||
return(retVal);
|
return(retVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public ArrayList<Boolean> checkHr(double hrVal) {
|
public ArrayList<Boolean> checkHr(double hrVal) {
|
||||||
/**
|
/**
|
||||||
* Checks the current Heart Rate reading hrVal against the
|
* Checks the current Heart Rate reading hrVal against the
|
||||||
@@ -153,7 +156,8 @@ public class SdAlgHr {
|
|||||||
* true=ALARM, false=OK.
|
* true=ALARM, false=OK.
|
||||||
*/
|
*/
|
||||||
Log.v(TAG, "checkHr("+hrVal+")");
|
Log.v(TAG, "checkHr("+hrVal+")");
|
||||||
addToHist(hrVal);
|
mAdaptiveHrBuff.add(hrVal);
|
||||||
|
mAverageHrBuff.add(hrVal);
|
||||||
ArrayList<Boolean> retVal = new ArrayList<Boolean>();
|
ArrayList<Boolean> retVal = new ArrayList<Boolean>();
|
||||||
retVal.add(checkSimpleHr(hrVal));
|
retVal.add(checkSimpleHr(hrVal));
|
||||||
retVal.add(checkAdaptiveHr(hrVal));
|
retVal.add(checkAdaptiveHr(hrVal));
|
||||||
|
|||||||
103
app/src/test/java/uk/org/openseizuredetector/CircBufTest.java
Normal file
103
app/src/test/java/uk/org/openseizuredetector/CircBufTest.java
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package uk.org.openseizuredetector;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public class CircBufTest extends TestCase {
|
||||||
|
private CircBuf mCb;
|
||||||
|
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
mCb = new CircBuf(10, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printArr(double[] arr) {
|
||||||
|
for (int n=0; n<arr.length; n++) {
|
||||||
|
System.out.println("arr["+n+"] = "+arr[n]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAdd() {
|
||||||
|
double[] retArr;
|
||||||
|
|
||||||
|
// Add a single value = we should get a single element array back.
|
||||||
|
mCb.add(1);
|
||||||
|
retArr = mCb.getVals();
|
||||||
|
assertEquals(1,retArr.length);
|
||||||
|
|
||||||
|
// Add 9 more elements - we should get a 10 element array back.
|
||||||
|
for (int i=0; i<9;i++) {
|
||||||
|
mCb.add(i);
|
||||||
|
}
|
||||||
|
retArr = mCb.getVals();
|
||||||
|
assertEquals(10,retArr.length);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetVals() {
|
||||||
|
double[] retArr;
|
||||||
|
// Add 10 more elements - we should get a 10 element array back.
|
||||||
|
for (int i=0; i<10;i++) {
|
||||||
|
mCb.add(i);
|
||||||
|
}
|
||||||
|
retArr = mCb.getVals();
|
||||||
|
assertEquals(0.0, retArr[0]);
|
||||||
|
assertEquals(9.0, retArr[9]);
|
||||||
|
|
||||||
|
//add one more element
|
||||||
|
mCb.add(10.0);
|
||||||
|
retArr = mCb.getVals();
|
||||||
|
printArr(retArr);
|
||||||
|
assertEquals(1.0, retArr[0]);
|
||||||
|
assertEquals(10.0, retArr[9]);
|
||||||
|
|
||||||
|
//add one more element
|
||||||
|
mCb.add(10.0);
|
||||||
|
retArr = mCb.getVals();
|
||||||
|
printArr(retArr);
|
||||||
|
assertEquals(2.0, retArr[0]);
|
||||||
|
assertEquals(10.0, retArr[9]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetAverageVal() {
|
||||||
|
double[] retArr;
|
||||||
|
double avVal;
|
||||||
|
|
||||||
|
// empty array;
|
||||||
|
avVal = mCb.getAverageVal();
|
||||||
|
assertEquals(-1.0, avVal, 1e-5);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// A trivial example
|
||||||
|
for (int i=0; i<10;i++) {
|
||||||
|
mCb.add(1.0);
|
||||||
|
}
|
||||||
|
retArr = mCb.getVals();
|
||||||
|
avVal = mCb.getAverageVal();
|
||||||
|
assertEquals(1.0, avVal,1e-5);
|
||||||
|
|
||||||
|
// Real calculation
|
||||||
|
double sum = 0.0;
|
||||||
|
for (int i=0; i<10;i++) {
|
||||||
|
mCb.add(i);
|
||||||
|
sum += i;
|
||||||
|
}
|
||||||
|
avVal = mCb.getAverageVal();
|
||||||
|
assertEquals(sum/10, avVal, 1e-5);
|
||||||
|
|
||||||
|
// Error value in array - should now be average of 9 values between 1 and 9 (because zero value is removed when -1 is added to end).
|
||||||
|
sum = 0.0;
|
||||||
|
for(int i=1;i<10;i++)
|
||||||
|
sum+= i;
|
||||||
|
|
||||||
|
mCb.add(-1.0);
|
||||||
|
avVal = mCb.getAverageVal();
|
||||||
|
assertEquals(sum/9, avVal, 1e-5);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user