Produced some unit tests for the seizure detection algorithm
This commit is contained in:
@@ -43,7 +43,7 @@ public class SdAnalyser {
|
||||
private double mWarnTime;
|
||||
private double mAlarmTime;
|
||||
private double mAlarmThresh;
|
||||
private double mAlarmRatioThresh;
|
||||
double mAlarmRatioThresh;
|
||||
private double mFreqRes;
|
||||
private int mAlarmCount;
|
||||
private double mFreqCutoff;
|
||||
@@ -79,13 +79,10 @@ public class SdAnalyser {
|
||||
}
|
||||
|
||||
/**
|
||||
* doAnalysis() - analyse the data if the accelerometer data array mAccData
|
||||
* and populate the output data structure mSdData
|
||||
* getSpecPower(rawData): Calculate the average bin power in the spectrum of rawData
|
||||
* between 0 and freqCutoff Hz, excluding the DC component
|
||||
*/
|
||||
void calculateSpectralPowers(double[] rawData) {
|
||||
// Set the frequency bounds for the analysis in fft output bin numbers.
|
||||
int nMin = freq2fftBin(mAlarmFreqMin);
|
||||
int nMax = freq2fftBin(mAlarmFreqMax);
|
||||
double getSpecPower(double[] rawData) {
|
||||
int nFreqCutoff = freq2fftBin(mFreqCutoff);
|
||||
|
||||
DoubleFFT_1D fftDo = new DoubleFFT_1D(mNSamp);
|
||||
@@ -104,7 +101,24 @@ public class SdAnalyser {
|
||||
fft[2 * i + 1] = 0.;
|
||||
}
|
||||
}
|
||||
specPower = specPower / (mNSamp / 2);
|
||||
specPower = specPower / mNSamp / 2;
|
||||
return(specPower);
|
||||
}
|
||||
|
||||
/**
|
||||
* getRoiPower(rawData): Calculate the average bin power in the spectrum of rawData
|
||||
* between alarmFreqMin and alarmFreqMax Hz.
|
||||
*/
|
||||
double getRoiPower(double[] rawData) {
|
||||
// Set the frequency bounds for the analysis in fft output bin numbers.
|
||||
int nMin = freq2fftBin(mAlarmFreqMin);
|
||||
int nMax = freq2fftBin(mAlarmFreqMax);
|
||||
int nFreqCutoff = freq2fftBin(mFreqCutoff);
|
||||
|
||||
DoubleFFT_1D fftDo = new DoubleFFT_1D(mNSamp);
|
||||
double[] fft = new double[mNSamp * 2];
|
||||
System.arraycopy(rawData, 0, fft, 0, mNSamp);
|
||||
fftDo.realForward(fft);
|
||||
|
||||
// Calculate the Region of Interest power and power ratio.
|
||||
double roiPower = 0;
|
||||
@@ -112,10 +126,49 @@ public class SdAnalyser {
|
||||
roiPower = roiPower + getMagnitude(fft, i);
|
||||
}
|
||||
roiPower = roiPower / (nMax - nMin);
|
||||
double roiRatio = 10 * roiPower / specPower;
|
||||
|
||||
return(roiPower);
|
||||
}
|
||||
|
||||
/**
|
||||
* getSpectrumRatio(rawData) - return the ratio of the ROI power to the whole spectrum power.
|
||||
* @param rawData
|
||||
* @return
|
||||
*/
|
||||
double getSpectrumRatio(double rawData[]) {
|
||||
double specPower;
|
||||
double roiPower;
|
||||
double specRatio;
|
||||
|
||||
specPower = getSpecPower(rawData);
|
||||
roiPower = getRoiPower(rawData);
|
||||
|
||||
if (specPower > mAlarmThresh) {
|
||||
specRatio = 10.0 * roiPower / specPower;
|
||||
} else {
|
||||
specRatio = 0.0;
|
||||
}
|
||||
return(specRatio);
|
||||
}
|
||||
|
||||
/**
|
||||
* getAlarmState(rawData) - determines the alarm state associated with the snapshot of raw
|
||||
* acceleration data rawData[]
|
||||
* @return the alarm state (0=ok, 1 = alarm)
|
||||
*/
|
||||
int getAlarmState(double rawData[]) {
|
||||
int alarmState;
|
||||
|
||||
double alarmRatio = getSpectrumRatio(rawData);
|
||||
|
||||
if (alarmRatio <= mAlarmRatioThresh) {
|
||||
alarmState = 0;
|
||||
} else {
|
||||
alarmState = 1;
|
||||
}
|
||||
return(alarmState);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the magnitude of entry i in the fft array fft
|
||||
*
|
||||
@@ -129,67 +182,34 @@ public class SdAnalyser {
|
||||
return mag;
|
||||
}
|
||||
|
||||
// Force the data stored in this datasource to update in line with the JSON string encoded data provided.
|
||||
// Used by webServer to update the GarminDatasource.
|
||||
// Returns a message string that is passed back to the watch.
|
||||
public String updateFromJSON(String jsonStr) {
|
||||
double[] getAccelDataFromJSON(String jsonStr) {
|
||||
String retVal = "undefined";
|
||||
Log.v(TAG,"updateFromJSON - "+jsonStr);
|
||||
double[] rawData = new double[mNSamp];
|
||||
Log.v(TAG,"getAccelDataFromJSON - "+jsonStr);
|
||||
|
||||
try {
|
||||
JSONObject mainObject = new JSONObject(jsonStr);
|
||||
//JSONObject dataObject = mainObject.getJSONObject("dataObj");
|
||||
JSONObject mainObject = null;
|
||||
mainObject = new JSONObject(jsonStr);
|
||||
JSONObject dataObject = mainObject;
|
||||
String dataTypeStr = dataObject.getString("dataType");
|
||||
Log.v(TAG,"updateFromJSON - dataType="+dataTypeStr);
|
||||
Log.v(TAG,"getAccelDataFromJSON - dataType="+dataTypeStr);
|
||||
if (dataTypeStr.equals("raw")) {
|
||||
Log.v(TAG,"updateFromJSON - processing raw data");
|
||||
try {
|
||||
mSdData.mHR = dataObject.getDouble("HR");
|
||||
} catch (JSONException e) {
|
||||
// if we get 'null' HR (For example if the heart rate is not working)
|
||||
mSdData.mHR = -1;
|
||||
}
|
||||
try {
|
||||
mMute = dataObject.getInt("Mute");
|
||||
} catch (JSONException e) {
|
||||
// if we get 'null' HR (For example if the heart rate is not working)
|
||||
mMute = 0;
|
||||
}
|
||||
Log.v(TAG, "getAccelDataFromJSON - processing raw data");
|
||||
JSONArray accelVals = dataObject.getJSONArray("data");
|
||||
Log.v(TAG, "Received " + accelVals.length() + " acceleration values");
|
||||
int i;
|
||||
for (i = 0; i < accelVals.length(); i++) {
|
||||
mSdData.rawData[i] = accelVals.getInt(i);
|
||||
rawData[i] = accelVals.getInt(i);
|
||||
}
|
||||
mSdData.mNsamp = accelVals.length();
|
||||
//mNSamp = accelVals.length();
|
||||
mWatchAppRunningCheck = true;
|
||||
doAnalysis();
|
||||
if (mSdData.haveSettings == false) {
|
||||
retVal = "sendSettings";
|
||||
} else {
|
||||
retVal = "OK";
|
||||
}
|
||||
} else if (dataTypeStr.equals("settings")){
|
||||
Log.v(TAG,"updateFromJSON - processing settings");
|
||||
mSamplePeriod = (short)dataObject.getInt("analysisPeriod");
|
||||
mSampleFreq = (short)dataObject.getInt("sampleFreq");
|
||||
mSdData.batteryPc = (short)dataObject.getInt("battery");
|
||||
Log.v(TAG,"updateFromJSON - mSamplePeriod="+mSamplePeriod+" mSampleFreq="+mSampleFreq);
|
||||
mSdData.haveSettings = true;
|
||||
mSdData.mSampleFreq = mSampleFreq;
|
||||
mWatchAppRunningCheck = true;
|
||||
retVal = "OK";
|
||||
} else {
|
||||
Log.e(TAG,"updateFromJSON - unrecognised dataType "+dataTypeStr);
|
||||
Log.e(TAG,"getAccelDataFromJSON - unrecognised dataType "+dataTypeStr);
|
||||
retVal = "ERROR";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG,"updateFromJSON - Error Parsing JSON String - "+e.toString());
|
||||
Log.e(TAG,"getAccelDataFromJSON - Error Parsing JSON String - "+e.toString());
|
||||
e.printStackTrace();
|
||||
retVal = "ERROR";
|
||||
}
|
||||
return(retVal);
|
||||
return(rawData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,17 @@ package uk.org.openseizuredetector;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class SdAnalyserTest {
|
||||
public SdAnalyser sda;
|
||||
|
||||
public String alarmJSON = "{ dataType: 'raw', " +
|
||||
"data: [" +
|
||||
public String alarmJSON = "{ 'dataType': 'raw', " +
|
||||
"'data': [" +
|
||||
"1644, 1316, 1144, 1332, 1716, 1716, 1392, 1148, 1276, 1660, " +
|
||||
"1716, 1496, 1196, 1232, 1572, 1684, 1552, 1236, 1228, 1528, " +
|
||||
"1648, 1572, 1268, 1208, 1492, 1680, 1596, 1272, 1192, 1424, " +
|
||||
@@ -24,13 +27,13 @@ public class SdAnalyserTest {
|
||||
"1188, 1360, 1680, 1724, 1424, 1192, 1224, 1556, 1744, 1588, " +
|
||||
"1260, 1220, 1472, 1692, 1608, 1328, 1192, 1412, 1668, 1656, " +
|
||||
"1356, 1216, 1304, 1636, 1712], " +
|
||||
"HR:54, " +
|
||||
"Mute:0 " +
|
||||
"'HR':54, " +
|
||||
"'Mute':0 " +
|
||||
"}";
|
||||
|
||||
private String okJSON = "{ " +
|
||||
"dataType: 'raw', " +
|
||||
"data: [" +
|
||||
"\"dataType\": \"raw\", " +
|
||||
"\"data\": [" +
|
||||
"1140, 1188, 1144, 1172, 1228, 1212, 1236, 1236, 1256, 1320, " +
|
||||
"1316, 1280, 1240, 1280, 1324, 1284, 1292, 1268, 1284, 1276, " +
|
||||
"1296, 1324, 1308, 1288, 1304, 1276, 1304, 1304, 1276, 1296, " +
|
||||
@@ -44,8 +47,8 @@ public class SdAnalyserTest {
|
||||
"1268, 1292, 1292, 1304, 1288, 1284, 1280, 1276, 1288, 1280, " +
|
||||
"1300, 1288, 1320, 1268, 1288, 1280, 1304, 1280, 1280, 1288, " +
|
||||
"1292, 1308, 1268, 1292, 1280], " +
|
||||
"HR:57, " +
|
||||
"Mute:0 " +
|
||||
"\"HR\":57, " +
|
||||
"\"Mute\":0 " +
|
||||
"}";
|
||||
|
||||
@Before
|
||||
@@ -65,7 +68,7 @@ public class SdAnalyserTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void freq2fftBin() {
|
||||
public void testFreq2fftBin() {
|
||||
int n;
|
||||
n = sda.freq2fftBin(0.0);
|
||||
assertEquals(0,n);
|
||||
@@ -74,7 +77,7 @@ public class SdAnalyserTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMagnitude() {
|
||||
public void testGetMagnitude() {
|
||||
double[] fft = {1, 1,
|
||||
2, 1,
|
||||
2, 2};
|
||||
@@ -87,4 +90,54 @@ public class SdAnalyserTest {
|
||||
assertEquals(8.0, m, m * 1e-4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccelDataFromJson() {
|
||||
double[] retVal;
|
||||
|
||||
retVal = sda.getAccelDataFromJSON(okJSON);
|
||||
assertNotNull(retVal);
|
||||
assertEquals(125,retVal.length);
|
||||
assertEquals(1140,retVal[0],0.001);
|
||||
assertEquals(1280,retVal[124],0.001);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetSpectrumRatio() {
|
||||
double[] okRawVals;
|
||||
double[] alarmRawVals;
|
||||
okRawVals = sda.getAccelDataFromJSON(okJSON);
|
||||
alarmRawVals = sda.getAccelDataFromJSON(alarmJSON);
|
||||
|
||||
double okRatio;
|
||||
double alarmRatio;
|
||||
|
||||
okRatio = sda.getSpectrumRatio(okRawVals);
|
||||
alarmRatio = sda.getSpectrumRatio(alarmRawVals);
|
||||
|
||||
assertTrue("Check Spectrum Ratio for OK data "+okRatio+" is <="+sda.mAlarmRatioThresh,
|
||||
okRatio <= sda.mAlarmRatioThresh);
|
||||
assertTrue("Check Spectrum Ratio for ALARM data "+alarmRatio+" is >"+sda.mAlarmRatioThresh,
|
||||
alarmRatio > sda.mAlarmRatioThresh);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testgetAlarmState() {
|
||||
double[] okRawVals;
|
||||
double[] alarmRawVals;
|
||||
okRawVals = sda.getAccelDataFromJSON(okJSON);
|
||||
alarmRawVals = sda.getAccelDataFromJSON(alarmJSON);
|
||||
|
||||
int okAlarmState;
|
||||
int alarmAlarmState;
|
||||
|
||||
okAlarmState = sda.getAlarmState(okRawVals);
|
||||
alarmAlarmState = sda.getAlarmState(alarmRawVals);
|
||||
|
||||
assertEquals("check OK start detected from raw data",0, okAlarmState);
|
||||
assertEquals("check alarm state detected from raw data",1, alarmAlarmState);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user