From 2bac4a95229dec140ec03c1c268fd32025ecc333 Mon Sep 17 00:00:00 2001 From: Graham Jones Date: Fri, 7 Apr 2023 21:35:16 +0100 Subject: [PATCH] Added average HR calculations to SdAlgHR - SdAlgHR still needs to be commissioned in SdDataSource class so it is used though. --- app/src/main/AndroidManifest.xml | 2 +- .../uk/org/openseizuredetector/CircBuf.java | 99 +++++++++++++++++ .../uk/org/openseizuredetector/SdAlgHr.java | 64 ++++++----- .../org/openseizuredetector/CircBufTest.java | 103 ++++++++++++++++++ 4 files changed, 237 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/uk/org/openseizuredetector/CircBuf.java create mode 100644 app/src/test/java/uk/org/openseizuredetector/CircBufTest.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9b84410..2c0bf3b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,7 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" package="uk.org.openseizuredetector" android:versionCode="115" - android:versionName="4.1.3n"> + android:versionName="4.1.3m"> diff --git a/app/src/main/java/uk/org/openseizuredetector/CircBuf.java b/app/src/main/java/uk/org/openseizuredetector/CircBuf.java new file mode 100644 index 0000000..27cb597 --- /dev/null +++ b/app/src/main/java/uk/org/openseizuredetector/CircBuf.java @@ -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; n0) { + retVal = hrSum / hrCount; + } else { + retVal = -1; + } + return(retVal); + } + +} \ No newline at end of file diff --git a/app/src/main/java/uk/org/openseizuredetector/SdAlgHr.java b/app/src/main/java/uk/org/openseizuredetector/SdAlgHr.java index e6b7ed9..8edf04f 100644 --- a/app/src/main/java/uk/org/openseizuredetector/SdAlgHr.java +++ b/app/src/main/java/uk/org/openseizuredetector/SdAlgHr.java @@ -25,13 +25,15 @@ public class SdAlgHr { private double mAverageHrAlarmThreshMin; private double mAverageHrAlarmThreshMax; - private ArrayList mHrHist; + private CircBuf mAdaptiveHrBuff; + private CircBuf mAverageHrBuff; public SdAlgHr(Context context) { Log.i(TAG, "SdAlgHr Constructor"); mContext = context; - mHrHist = new ArrayList(); updatePrefs(); + mAdaptiveHrBuff = new CircBuf(mAdaptiveHrAlarmWindowDp, -1.0); + mAverageHrBuff = new CircBuf(mAverageHrAlarmWindowDp, -1.0); } 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) { /** @@ -118,33 +112,42 @@ public class SdAlgHr { } private boolean checkAdaptiveHr(double hrVal) { - // FIXME Make this do something - return(false); + boolean retVal; + 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) { - // FIXME Make this do something - return(false); - } + boolean retVal; + double avHr = mAdaptiveHrBuff.getAverageVal(); - public double getAverageHrVal() { - double hrSum = 0.; - int hrCount = 0; - double retVal; - for (int n=0; n -1) { - hrSum += mHrHist.get(n); - hrCount++; - } + retVal = false; + if (hrVal < mAverageHrAlarmThreshMin) { + retVal = true; } - if (hrCount>0) { - retVal = hrSum / hrCount; - } else { - retVal = -1; + if (hrVal > mAverageHrAlarmThreshMax) { + retVal = true; } + Log.d(TAG, "checkAverageHr() - hrVal="+hrVal+", avHr="+avHr+", thresholds=("+mAverageHrAlarmThreshMin+", "+mAverageHrAlarmThreshMin+"): Alarm="+retVal); return(retVal); } + + public ArrayList checkHr(double hrVal) { /** * Checks the current Heart Rate reading hrVal against the @@ -153,7 +156,8 @@ public class SdAlgHr { * true=ALARM, false=OK. */ Log.v(TAG, "checkHr("+hrVal+")"); - addToHist(hrVal); + mAdaptiveHrBuff.add(hrVal); + mAverageHrBuff.add(hrVal); ArrayList retVal = new ArrayList(); retVal.add(checkSimpleHr(hrVal)); retVal.add(checkAdaptiveHr(hrVal)); diff --git a/app/src/test/java/uk/org/openseizuredetector/CircBufTest.java b/app/src/test/java/uk/org/openseizuredetector/CircBufTest.java new file mode 100644 index 0000000..32573d7 --- /dev/null +++ b/app/src/test/java/uk/org/openseizuredetector/CircBufTest.java @@ -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