First go at adding support for O2 Saturation Data - it expects it to be included in the JSON data string as "O2Sat" in a similar way to heart rate.

This commit is contained in:
Graham Jones
2021-11-08 21:10:00 +00:00
parent eb2aff577f
commit d822fcf2e9
9 changed files with 139 additions and 10 deletions

Binary file not shown.

View File

@@ -10,8 +10,8 @@
{ {
"type": "SINGLE", "type": "SINGLE",
"filters": [], "filters": [],
"versionCode": 88, "versionCode": 90,
"versionName": "3.6.3a", "versionName": "3.7.0a",
"outputFile": "app-release.apk" "outputFile": "app-release.apk"
} }
] ]

View File

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="uk.org.openseizuredetector" package="uk.org.openseizuredetector"
android:versionCode="88" android:versionCode="90"
android:versionName="3.6.3a"> android:versionName="3.7.0a">
<!-- 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" />

View File

@@ -460,11 +460,12 @@ public class MainActivity extends AppCompatActivity {
// Pebble Connected Phrase - use for HR if active instead. // Pebble Connected Phrase - use for HR if active instead.
tv = (TextView) findViewById(R.id.pebbleTv); tv = (TextView) findViewById(R.id.pebbleTv);
if (mConnection.mSdServer.mSdData.mHRAlarmActive) { if (mConnection.mSdServer.mSdData.mHRAlarmActive) {
tv.setText(getString(R.string.HR_Equals) + mConnection.mSdServer.mSdData.mHR); tv.setText(getString(R.string.HR_Equals) + mConnection.mSdServer.mSdData.mHR +" bpm\n"
if (mConnection.mSdServer.mSdData.mHRAlarmStanding) { + "O2 Sat = " + mConnection.mSdServer.mSdData.mO2Sat + "%");
if (mConnection.mSdServer.mSdData.mHRAlarmStanding || mConnection.mSdServer.mSdData.mO2SatAlarmStanding) {
tv.setBackgroundColor(alarmColour); tv.setBackgroundColor(alarmColour);
tv.setTextColor(alarmTextColour); tv.setTextColor(alarmTextColour);
} else if (mConnection.mSdServer.mSdData.mHRFaultStanding) { } else if (mConnection.mSdServer.mSdData.mHRFaultStanding || mConnection.mSdServer.mSdData.mO2SatFaultStanding) {
tv.setBackgroundColor(warnColour); tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour); tv.setTextColor(warnTextColour);
} else { } else {
@@ -589,7 +590,7 @@ public class MainActivity extends AppCompatActivity {
tv.setTextColor(warnTextColour); tv.setTextColor(warnTextColour);
tv = (TextView) findViewById(R.id.pebbleTv); tv = (TextView) findViewById(R.id.pebbleTv);
tv.setText(getString(R.string.HR_Equals)+"---"); tv.setText(getString(R.string.HR_Equals)+" --- bpm\nO2 Sat = --- %");
tv.setBackgroundColor(warnColour); tv.setBackgroundColor(warnColour);
tv.setTextColor(warnTextColour); tv.setTextColor(warnTextColour);

View File

@@ -65,6 +65,13 @@ public class SdData implements Parcelable {
public boolean mHRNullAsAlarm = false; public boolean mHRNullAsAlarm = false;
public double mHRThreshMin = 40.0; public double mHRThreshMin = 40.0;
public double mHRThreshMax = 150.0; public double mHRThreshMax = 150.0;
/* Oxygen Saturation Alarm Settings */
public boolean mO2SatAlarmActive = false;
public boolean mO2SatNullAsAlarm = false;
public double mO2SatThreshMin = 80.0;
public double rawData[]; public double rawData[];
int mNsamp = 0; int mNsamp = 0;
@@ -87,6 +94,11 @@ public class SdData implements Parcelable {
public boolean mHRFaultStanding = false; public boolean mHRFaultStanding = false;
public double mHR = 0; public double mHR = 0;
public boolean mO2SatAlarmStanding = false;
public boolean mO2SatFaultStanding = false;
public double mO2Sat = 0;
public SdData() { public SdData() {
simpleSpec = new int[10]; simpleSpec = new int[10];
rawData = new double[N_RAW_DATA]; rawData = new double[N_RAW_DATA];
@@ -95,6 +107,7 @@ public class SdData implements Parcelable {
/* /*
* Intialise this SdData object from a JSON String * Intialise this SdData object from a JSON String
* FIXME - add O2saturation with checking in case it is not included in the data
*/ */
public boolean fromJSON(String jsonStr) { public boolean fromJSON(String jsonStr) {
Log.v(TAG, "fromJSON() - parsing jsonString - " + jsonStr); Log.v(TAG, "fromJSON() - parsing jsonString - " + jsonStr);
@@ -182,6 +195,10 @@ public class SdData implements Parcelable {
jsonObj.put("hrThreshMin",mHRThreshMin); jsonObj.put("hrThreshMin",mHRThreshMin);
jsonObj.put("hrThreshMax", mHRThreshMax); jsonObj.put("hrThreshMax", mHRThreshMax);
jsonObj.put("hr",mHR); jsonObj.put("hr",mHR);
jsonObj.put("o2SatAlarmActive", mO2SatAlarmActive);
jsonObj.put("o2SatAlarmStanding", mO2SatAlarmStanding);
jsonObj.put("o2SatThreshMin",mO2SatThreshMin);
jsonObj.put("o2Sat",mO2Sat);
JSONArray arr = new JSONArray(); JSONArray arr = new JSONArray();
for (int i = 0; i < simpleSpec.length; i++) { for (int i = 0; i < simpleSpec.length; i++) {
arr.put(simpleSpec[i]); arr.put(simpleSpec[i]);
@@ -220,6 +237,7 @@ public class SdData implements Parcelable {
retval = retval + ", " + mSampleFreq; retval = retval + ", " + mSampleFreq;
retval = retval + ", " + alarmPhrase; retval = retval + ", " + alarmPhrase;
retval = retval + ", " + mHR; retval = retval + ", " + mHR;
retval = retval + ", " + mO2Sat;
if (includeRawData) { if (includeRawData) {
for (int i = 0; i< mNsamp;i++) { for (int i = 0; i< mNsamp;i++) {
retval = retval + ", " + rawData[i]; retval = retval + ", " + rawData[i];

View File

@@ -270,6 +270,12 @@ public abstract class SdDataSource {
// if we get 'null' HR (For example if the heart rate is not working) // if we get 'null' HR (For example if the heart rate is not working)
mSdData.mHR = -1; mSdData.mHR = -1;
} }
try {
mSdData.mO2Sat = dataObject.getDouble("O2Sat");
} catch (JSONException e) {
// if we get 'null' O2 Saturation (For example if the oxygen sensor is not working)
mSdData.mO2Sat = -1;
}
try { try {
mMute = dataObject.getInt("Mute"); mMute = dataObject.getInt("Mute");
} catch (JSONException e) { } catch (JSONException e) {
@@ -455,6 +461,7 @@ public abstract class SdDataSource {
// Check this data to see if it represents an alarm state. // Check this data to see if it represents an alarm state.
alarmCheck(); alarmCheck();
hrCheck(); hrCheck();
o2SatCheck();
fallCheck(); fallCheck();
muteCheck(); muteCheck();
Log.v(TAG,"after fallCheck, mSdData.fallAlarmStanding="+mSdData.fallAlarmStanding); Log.v(TAG,"after fallCheck, mSdData.fallAlarmStanding="+mSdData.fallAlarmStanding);
@@ -544,9 +551,39 @@ public abstract class SdDataSource {
mSdData.mHRAlarmStanding = false; mSdData.mHRAlarmStanding = false;
} }
} }
}
/**
* hrCheck - check the Heart rate data in mSdData to see if it represents an alarm condition.
* Sets mSdData.mHRAlarmStanding
*/
public void o2SatCheck() {
Log.v(TAG, "o2SatCheck()");
/* Check Oxygen Saturation against alarm settings */
if (mSdData.mO2SatAlarmActive) {
if (mSdData.mO2Sat < 0) {
if (mSdData.mO2SatNullAsAlarm) {
Log.i(TAG, "Oxygen Saturation Null - Alarming");
mSdData.mO2SatFaultStanding = false;
mSdData.mO2SatAlarmStanding = true;
} else {
Log.i(TAG, "Oxygen Saturation Fault (O2Sat<0)");
mSdData.mO2SatFaultStanding = true;
mSdData.mO2SatAlarmStanding = false;
}
} else if (mSdData.mO2Sat < mSdData.mO2SatThreshMin) {
Log.i(TAG, "Oxygen Saturation Abnormal - " + mSdData.mO2Sat + " %");
mSdData.mO2SatFaultStanding = false;
mSdData.mO2SatAlarmStanding = true;
} else {
mSdData.mO2SatFaultStanding = false;
mSdData.mO2SatAlarmStanding = false;
}
}
} }
/**************************************************************** /****************************************************************
* Simple threshold analysis to chech for fall. * Simple threshold analysis to chech for fall.
* Called from clock_tick_handler() * Called from clock_tick_handler()
@@ -816,6 +853,19 @@ public abstract class SdDataSource {
Log.v(TAG, "updatePrefs() HRThreshMax = " + mSdData.mHRThreshMax); Log.v(TAG, "updatePrefs() HRThreshMax = " + mSdData.mHRThreshMax);
mUtil.writeToSysLogFile( "updatePrefs() HRThreshMax = " + mSdData.mHRThreshMax); mUtil.writeToSysLogFile( "updatePrefs() HRThreshMax = " + mSdData.mHRThreshMax);
mSdData.mO2SatAlarmActive = SP.getBoolean("O2SatAlarmActive", false);
Log.v(TAG, "updatePrefs() O2SatAlarmActive = " + mSdData.mO2SatAlarmActive);
mUtil.writeToSysLogFile( "updatePrefs() O2SatAlarmActive = " + mSdData.mO2SatAlarmActive);
mSdData.mO2SatNullAsAlarm = SP.getBoolean("O2SatNullAsAlarm", false);
Log.v(TAG, "updatePrefs() O2SatNullAsAlarm = " + mSdData.mO2SatNullAsAlarm);
mUtil.writeToSysLogFile( "updatePrefs() O2SatNullAsAlarm = " + mSdData.mO2SatNullAsAlarm);
prefStr = SP.getString("O2SatThreshMin", "SET_FROM_XML");
mSdData.mO2SatThreshMin = (short) Integer.parseInt(prefStr);
Log.v(TAG, "updatePrefs() O2SatThreshMin = " + mSdData.mO2SatThreshMin);
mUtil.writeToSysLogFile( "updatePrefs() O2SatThreshMin = " + mSdData.mO2SatThreshMin);
} else { } else {
Log.v(TAG, "updatePrefs() - prefStr is null - WHY????"); Log.v(TAG, "updatePrefs() - prefStr is null - WHY????");
mUtil.writeToSysLogFile("SDDataSource.updatePrefs() - prefStr is null - WHY??"); mUtil.writeToSysLogFile("SDDataSource.updatePrefs() - prefStr is null - WHY??");

View File

@@ -672,9 +672,43 @@ public class SdServer extends Service implements SdDataReceiver {
mUtil.showToast(getString(R.string.SMSAlarmDisabledNotSendingMsg)); mUtil.showToast(getString(R.string.SMSAlarmDisabledNotSendingMsg));
Log.v(TAG, "mSMSAlarm is false - not sending"); Log.v(TAG, "mSMSAlarm is false - not sending");
} }
} }
// Handle Oxygen Saturation alarm
if ((sdData.mO2SatAlarmActive) && (sdData.mO2SatAlarmStanding)) {
sdData.alarmPhrase = "Oxygen Saturation ABNORMAL";
if (mLogAlarms) {
Log.v(TAG, "***OXYGEN SATURATION*** - Logging to SD Card");
writeAlarmToSD();
} else {
Log.v(TAG, "***OXYGEN SATURATION***");
}
// Make alarm beep tone
alarmBeep();
showNotification(2);
// Display MainActvity
showMainActivity();
// Send SMS Alarm.
if (mSMSAlarm) {
Time tnow = new Time(Time.getCurrentTimezone());
tnow.setToNow();
// limit SMS alarms to one per minute
if ((tnow.toMillis(false)
- mSMSTime.toMillis(false))
> 60000) {
sendSMSAlarm();
mSMSTime = tnow;
} else {
mUtil.showToast("SMS Alarm already sent - not re-sending");
Log.v(TAG, "SMS Alarm already sent - not re-sending");
}
} else {
mUtil.showToast(getString(R.string.SMSAlarmDisabledNotSendingMsg));
Log.v(TAG, "mSMSAlarm is false - not sending");
}
}
// Fault // Fault
if ((sdData.alarmState) == 4 || (sdData.alarmState == 7) || (sdData.mHRFaultStanding)) { if ((sdData.alarmState) == 4 || (sdData.alarmState == 7) || (sdData.mHRFaultStanding)) {
sdData.alarmPhrase = "FAULT"; sdData.alarmPhrase = "FAULT";

View File

@@ -2,7 +2,8 @@
<resources> <resources>
<string name="app_name">OpenSeizureDetector</string> <string name="app_name">OpenSeizureDetector</string>
<string name="changelog"> <string name="changelog">
"V3.6.2 - Fix of issue with log file permissions on some Android 10 devices and added more translatable strings with polish translation. "V3.7.0 - Added support for Garmin Blood Oxygen Saturation measurements
\nV3.6.2 - Fix of issue with log file permissions on some Android 10 devices and added more translatable strings with polish translation.
\nV3.6.1 - Possible fix for issue with shutting down system and expanded Polish translation to all settings screens. \nV3.6.1 - Possible fix for issue with shutting down system and expanded Polish translation to all settings screens.
\nV3.6 - Added phone sensor data source for testing without a watches \nV3.6 - Added phone sensor data source for testing without a watches
\nV3.5 - Added support for SMS Annunciator App \nV3.5 - Added support for SMS Annunciator App
@@ -282,4 +283,11 @@
<string name="WarnTimeTitle">WarnTime (sec)</string> <string name="WarnTimeTitle">WarnTime (sec)</string>
<string name="AlarmTimeSummary">Time to wait before initiating alarm (Default = 10 sec)</string> <string name="AlarmTimeSummary">Time to wait before initiating alarm (Default = 10 sec)</string>
<string name="AlarmTimeTitle">AlarmTime (sec)</string> <string name="AlarmTimeTitle">AlarmTime (sec)</string>
<string name="O2SatSettingsTitle">Blood Oxygen Saturation Alarm Settigs</string>
<string name="O2Sat_enabled_summary">O2Sat_enabled_summary</string>
<string name="O2Sat_enabled_title">Enable O2 Saturation Alarm</string>
<string name="O2SatNullAlarmSummary">Treat an error condition (null value of oxygen saturation reading) as an alarm condition</string>
<string name="O2SatNullAlarmTitle">Treat Null Value as Alarm</string>
<string name="O2SatThreshMinTitle">Oxygen Saturation Low Alarm Level (%)</string>
<string name="O2SatThreshMinSummary">O2 Saturation Low Alarm Level (%)</string>
</resources> </resources>

View File

@@ -65,6 +65,24 @@
android:title="@string/HRThreshMaxTitle" /> android:title="@string/HRThreshMaxTitle" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/O2SatSettingsTitle">
<CheckBoxPreference
android:defaultValue="false"
android:key="O2SatAlarmActive"
android:summary="@string/O2Sat_enabled_summary"
android:title="@string/O2Sat_enabled_title" />
<CheckBoxPreference
android:defaultValue="false"
android:key="O2SatNullAsAlarm"
android:summary="@string/O2SatNullAlarmSummary"
android:title="@string/O2SatNullAlarmTitle" />
<EditTextPreference
android:defaultValue="80"
android:key="O2SatThreshMin"
android:summary="@string/O2SatThreshMinSummary"
android:title="@string/O2SatThreshMinTitle" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/fall_detect_title"> <PreferenceCategory android:title="@string/fall_detect_title">
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="false" android:defaultValue="false"