Fixed FFT analysis - close to working

This commit is contained in:
Graham Jones
2017-10-18 22:08:38 +01:00
parent 700abb0855
commit 85cd881c63

View File

@@ -48,6 +48,8 @@ import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import static java.lang.Math.sqrt;
/**
* Abstract class for a seizure detector data source. Subclasses include a pebble smart watch data source and a
@@ -56,6 +58,7 @@ import java.util.UUID;
public class SdDataSourceNetworkPassive extends SdDataSource {
private Handler mHandler = new Handler();
private Timer mStatusTimer;
private Timer mAlarmCheckTimer;
private Time mDataStatusTime;
private boolean mWatchAppRunningCheck = false;
private int mAppRestartTimeout = 10; // Timeout before re-starting watch app (sec) if we have not received
@@ -90,6 +93,8 @@ public class SdDataSourceNetworkPassive extends SdDataSource {
private short mFallThreshMax;
private short mFallWindow;
private int mAlarmCount;
// raw data storage for SD_MODE_RAW
private int MAX_RAW_DATA = 500;
public double[] mAccData = new double[MAX_RAW_DATA];
@@ -132,6 +137,20 @@ public class SdDataSourceNetworkPassive extends SdDataSource {
Log.v(TAG, "start(): status timer already running.");
mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.start() - status timer already running??");
}
if (mAlarmCheckTimer == null) {
Log.v(TAG, "start(): starting alarm check timer");
mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.start() - starting alarm check timer");
mAlarmCheckTimer = new Timer();
mAlarmCheckTimer.schedule(new TimerTask() {
@Override
public void run() {
alarmCheck();
}
}, 0, 1000);
} else {
Log.v(TAG, "start(): alarm check timer already running.");
mUtil.writeToSysLogFile("SdDataSourceNetworkPassive.start() - alarm check timer already running??");
}
}
/**
@@ -290,11 +309,13 @@ public class SdDataSourceNetworkPassive extends SdDataSource {
mAccData[i] = accelVals.getInt(i);
}
mNSamp = accelVals.length();
mWatchAppRunningCheck = true;
doAnalysis();
} else if (dataTypeStr.equals("settings")){
Log.v(TAG,"updateFromJSON - processing settings");
mSamplePeriod = (short)dataObject.getInt("analysisPeriod");
mSampleFreq = (short)dataObject.getInt("analysisPeriod");
mSampleFreq = (short)dataObject.getInt("sampleFreq");
Log.v(TAG,"updateFromJSON - mSamplePeriod="+mSamplePeriod+" mSampleFreq="+mSampleFreq);
mSdData.haveSettings = true;
mSdData.mSampleFreq = mSampleFreq;
}
@@ -303,6 +324,18 @@ public class SdDataSourceNetworkPassive extends SdDataSource {
}
}
/**
* Calculate the magnitude of entry i in the fft array fft
* @param fft
* @param i
* @return magnitude ( Re*Re + Im*Im )
*/
private double getMagnitude(double[] fft, int i) {
double mag;
mag = sqrt(fft[2*i]*fft[2*i] + fft[2*i + 1] * fft[2*i +1]);
return mag;
}
/**
* doAnalysis() - analyse the data if the accelerometer data array mAccData
* and populate the output data structure mSdData
@@ -322,15 +355,14 @@ public class SdDataSourceNetworkPassive extends SdDataSource {
DoubleFFT_1D fftDo = new DoubleFFT_1D(mNSamp);
double[] fft = new double[mNSamp * 2];
System.arraycopy(mAccData, 0, fft, 0, mNSamp);
fftDo.realForwardFull(fft);
fftDo.realForward(fft);
// Calculate the whole spectrum power (well a value equivalent to it that avoids suare root calculations
// and zero any readings that are above the frequency cutoff.
double specPower = 0;
for (int i = 1; i < mNSamp / 2; i++) {
if (i <= nFreqCutoff) {
specPower = specPower + fft[2 * i] + fft[2*i] + fft[2*i +1] * fft[2*i+1];
specPower = specPower + getMagnitude(fft,i);
} else {
fft[2*i] = 0.;
fft[2*i+1] = 0.;
@@ -341,7 +373,7 @@ public class SdDataSourceNetworkPassive extends SdDataSource {
// Calculate the Region of Interest power and power ratio.
double roiPower = 0;
for (int i=nMin;i<nMax;i++) {
roiPower = roiPower + fft[2 * i] + fft[2*i] + fft[2*i +1] * fft[2*i+1];
roiPower = roiPower + getMagnitude(fft,i);
}
roiPower = roiPower/(nMax - nMin);
double roiRatio = 10 * roiPower / specPower;
@@ -353,7 +385,7 @@ public class SdDataSourceNetworkPassive extends SdDataSource {
int binMax = (int)(1 + (ifreq+1)/freqRes);
simpleSpec[ifreq]=0;
for (int i=binMin;i<binMax;i++) {
simpleSpec[ifreq] = simpleSpec[ifreq] + fft[2 * i] + fft[2*i] + fft[2*i +1] * fft[2*i+1];
simpleSpec[ifreq] = simpleSpec[ifreq] + getMagnitude(fft,i);
}
simpleSpec[ifreq] = simpleSpec[ifreq] / (binMax-binMin);
}
@@ -372,6 +404,8 @@ public class SdDataSourceNetworkPassive extends SdDataSource {
for(int i=0;i<SIMPLE_SPEC_FMAX;i++) {
mSdData.simpleSpec[i] = (int)simpleSpec[i];
}
// Because we have received data, set flag to show watch app running.
mWatchAppRunningCheck = true;
}
@@ -420,6 +454,40 @@ public class SdDataSourceNetworkPassive extends SdDataSource {
}
}
/**
* alarmCheck - determines alarm state based on seizure detector data SdData. Called every second.
*/
private void alarmCheck() {
boolean inAlarm;
Log.v(TAG,"alarmCheck()");
if ((mSdData.roiPower > mAlarmThresh) && (10*(mSdData.roiPower/mSdData.specPower) > mAlarmRatioThresh)) {
inAlarm = true;
} else {
inAlarm = false;
}
if (inAlarm) {
mAlarmCount+=mSamplePeriod;
if (mAlarmCount>mAlarmTime) {
mSdData.alarmState = 2;
} else if (mAlarmCount>mWarnTime) {
mSdData.alarmState = 1;
}
} else {
// If we are in an ALARM state, revert back to WARNING, otherwise
// revert back to OK.
if (mSdData.alarmState == 2) {
mSdData.alarmState = 1;
} else {
mSdData.alarmState = 0;
mAlarmCount = 0;
}
}
Log.v(TAG,"inAlarm="+inAlarm+", alarmState = "+mSdData.alarmState+" alarmCount="+mAlarmCount);
}
}