From 18fc074a457a3ea5c749c167fced9185c4a44648 Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Sun, 22 Mar 2020 20:00:09 -0400
Subject: [PATCH 01/15] Add all known PRS1 settings to the parsed event stream,
in particular backup breath settings.
Also add more value and range checks for settings based on initial small
sample set. These will be dialed back once tested against all test data.
---
oscar/SleepLib/loader_plugins/prs1_loader.cpp | 202 +++++++++++++-----
1 file changed, 154 insertions(+), 48 deletions(-)
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index 88d21958..b3e00dc8 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -1185,6 +1185,7 @@ enum PRS1ParsedSettingType
PRS1_SETTING_FLEX_MODE,
PRS1_SETTING_FLEX_LEVEL,
PRS1_SETTING_RISE_TIME,
+ PRS1_SETTING_RISE_TIME_LOCK,
PRS1_SETTING_RAMP_TYPE,
PRS1_SETTING_RAMP_TIME,
PRS1_SETTING_RAMP_PRESSURE,
@@ -1196,6 +1197,7 @@ enum PRS1ParsedSettingType
PRS1_SETTING_MASK_RESIST_SETTING,
PRS1_SETTING_MASK_RESIST_STATUS,
PRS1_SETTING_HOSE_DIAMETER,
+ PRS1_SETTING_TUBING_LOCK,
PRS1_SETTING_AUTO_ON,
PRS1_SETTING_AUTO_OFF,
PRS1_SETTING_APNEA_ALARM,
@@ -1737,6 +1739,7 @@ static QString parsedSettingTypeName(PRS1ParsedSettingType t)
ENUMSTRING(PRS1_SETTING_FLEX_MODE);
ENUMSTRING(PRS1_SETTING_FLEX_LEVEL);
ENUMSTRING(PRS1_SETTING_RISE_TIME);
+ ENUMSTRING(PRS1_SETTING_RISE_TIME_LOCK);
ENUMSTRING(PRS1_SETTING_RAMP_TYPE);
ENUMSTRING(PRS1_SETTING_RAMP_TIME);
ENUMSTRING(PRS1_SETTING_RAMP_PRESSURE);
@@ -1748,6 +1751,7 @@ static QString parsedSettingTypeName(PRS1ParsedSettingType t)
ENUMSTRING(PRS1_SETTING_MASK_RESIST_SETTING);
ENUMSTRING(PRS1_SETTING_MASK_RESIST_STATUS);
ENUMSTRING(PRS1_SETTING_HOSE_DIAMETER);
+ ENUMSTRING(PRS1_SETTING_TUBING_LOCK);
ENUMSTRING(PRS1_SETTING_AUTO_ON);
ENUMSTRING(PRS1_SETTING_AUTO_OFF);
ENUMSTRING(PRS1_SETTING_APNEA_ALARM);
@@ -2016,7 +2020,8 @@ bool PRS1DataChunk::ParseEventsF5V3(void)
this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos-1, size+1));
break;
default:
- qWarning() << "Unknown event:" << code << "in" << this->sessionid << "at" << startpos-1;
+ DUMP_EVENT();
+ UNEXPECTED_VALUE(code, "known event code");
this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos-1, size+1));
break;
}
@@ -3299,7 +3304,8 @@ bool PRS1DataChunk::ParseEventsF3V6(void)
// case 0x0e?
// case 0x0f?
default:
- qWarning() << "Unknown event:" << code << "in" << this->sessionid << "at" << startpos-1;
+ DUMP_EVENT();
+ UNEXPECTED_VALUE(code, "known event code");
this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos-1, size+1));
break;
}
@@ -4091,7 +4097,8 @@ bool PRS1DataChunk::ParseEventsF0V6()
this->AddEvent(new PRS1HypopneaEvent(t - duration, 0));
break;
default:
- qWarning() << "Unknown event:" << code << "in" << this->sessionid << "at" << startpos-1;
+ DUMP_EVENT();
+ UNEXPECTED_VALUE(code, "known event code");
this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos-1, size+1));
break;
}
@@ -4235,6 +4242,7 @@ bool PRS1Import::ImportCompliance()
//session->settings[PRS1_ShowAHI] = (bool) e->m_value;
break;
case PRS1_SETTING_FLEX_LOCK:
+ case PRS1_SETTING_TUBING_LOCK:
case PRS1_SETTING_RAMP_TYPE:
//TODO: define and add new channels for any of these that we want to import
break;
@@ -4492,8 +4500,8 @@ bool PRS1DataChunk::ParseSettingsF0V23(const unsigned char* data, int /*size*/)
cpapmode = PRS1_MODE_AUTOBILEVEL;
break;
default:
- qWarning() << this->sessionid << "unknown cpap mode" << data[0x02];
- return false;
+ UNEXPECTED_VALUE(data[0x02], "known device mode");
+ break;
}
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_CPAP_MODE, (int) cpapmode));
@@ -4583,7 +4591,7 @@ bool PRS1DataChunk::ParseSettingsF0V4(const unsigned char* data, int /*size*/)
cpapmode = PRS1_MODE_CPAPCHECK;
break;
default:
- UNEXPECTED_VALUE(data[0x02], "known mode");
+ UNEXPECTED_VALUE(data[0x02], "known device mode");
break;
}
@@ -4613,8 +4621,10 @@ bool PRS1DataChunk::ParseSettingsF0V4(const unsigned char* data, int /*size*/)
CHECK_VALUE(max_ps, 0);
}
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE, min_ps));
- this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE_MIN, min_pressure));
- this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE_MAX, max_pressure));
+ // TODO: Once OSCAR can handle more modes, we can include these settings; right now including
+ // these settings makes it think this is AutoCPAP.
+ //this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE_MIN, min_pressure));
+ //this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE_MAX, max_pressure));
} else if (cpapmode == PRS1_MODE_BILEVEL) {
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_EPAP, min_pressure));
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP, max_pressure));
@@ -4623,7 +4633,7 @@ bool PRS1DataChunk::ParseSettingsF0V4(const unsigned char* data, int /*size*/)
CHECK_VALUE(max_ps, 0); // this seems to be unused on fixed bilevel
} else if (cpapmode == PRS1_MODE_AUTOBILEVEL) {
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_EPAP_MIN, min_pressure));
- this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_EPAP_MAX, max_pressure));
+ this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_EPAP_MAX, max_pressure - min_ps));
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MIN, min_pressure + min_ps));
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MAX, max_pressure));
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PS_MIN, min_ps));
@@ -4647,7 +4657,7 @@ bool PRS1DataChunk::ParseSettingsF0V4(const unsigned char* data, int /*size*/)
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_LOCK, (data[0x0d] & 0x40) != 0));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, resist_level));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_HOSE_DIAMETER, (data[0x0d] & 0x01) ? 15 : 22));
- //this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TUBING_LOCK, (data[0x0d] & 0x02) != 0)); // TODO: add this internal setting
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TUBING_LOCK, (data[0x0d] & 0x02) != 0));
CHECK_VALUE(data[0x0d] & (0x80|0x04), 0);
CHECK_VALUE(data[0x0e], 1);
@@ -4657,7 +4667,7 @@ bool PRS1DataChunk::ParseSettingsF0V4(const unsigned char* data, int /*size*/)
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_ALERT, (data[0x0f] & 0x04) != 0));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_SHOW_AHI, (data[0x0f] & 0x02) != 0));
CHECK_VALUE(data[0x0f] & (0xA0 | 0x08), 0);
- //CHECK_VALUE(data[0x0f] & 0x01, 0); // What is bit 1? It's sometimes set.
+ //CHECK_VALUE(data[0x0f] & 0x01, 0); // TODO: What is bit 1? It's sometimes set.
CHECK_VALUE(data[0x10], 0);
if (cpapmode == PRS1_MODE_AUTOTRIAL) {
@@ -5064,7 +5074,6 @@ void PRS1DataChunk::ParseHumidifierSettingF3V3(unsigned char humid1, unsigned ch
if (tubetemp > 5) UNEXPECTED_VALUE(tubetemp, "<= 5");
if (this->sessionid != 1) CHECK_VALUE(humid2 & 0x60, 0); // Only seen on 1-second session 1 of several machines, no humidifier data on chart.
- CHECK_VALUE(humid2 & ~(0x80|0x60|0x10|8|7), 0); // 0x60 is unknown but checked above
bool humidclassic = (humid2 & 0x80) != 0; // Set on classic mode reports; evidently ignored (sometimes set!) when tube is present
//bool no_tube? = (humid2 & 0x20) != 0; // Something tube related: whenever it is set, tube is never present (inverse is not true)
bool no_data = (humid2 & 0x60) == 0x60; // As described in chart, settings still show up
@@ -5167,34 +5176,69 @@ bool PRS1DataChunk::ParseSettingsF3V3(const unsigned char* data, int /*size*/)
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MAX, max_ipap));
// TODO: calculte PS or min/max PS? Create IPAP event when not AVAPS?
- // TODO: import rise time, bi-flex level
if (flexmode == FLEX_None || flexmode == FLEX_AVAPS) {
- //CHECK_VALUE(data[0xa], 1, 3); // 1 = Rise Time Setting 1, 2 = Rise Time Setting 2, 3 = Rise Time Setting 3
- CHECK_VALUES(data[0xb], 0, 1); // 1 = Rise Time Lock (in "None" and AVAPS flex mode)
+ int rise_time = data[0xa]; // 1 = Rise Time Setting 1, 2 = Rise Time Setting 2, 3 = Rise Time Setting 3
+ if (cpapmode == PRS1_MODE_CPAP) {
+ CHECK_VALUE(rise_time, 0);
+ } else {
+ if (rise_time < 1 || rise_time > 6) UNEXPECTED_VALUE(rise_time, "1-6"); // TODO: what is 0?
+ CHECK_VALUES(data[0xb], 0, 1); // 1 = Rise Time Lock (in "None" and AVAPS flex mode)
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RISE_TIME, rise_time));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RISE_TIME_LOCK, data[0xb] == 1));
+ }
} else {
- CHECK_VALUES(data[0xa], 0, 3); // May also be Bi-Flex 3? But how is this different from [0xc] below?
+ CHECK_VALUES(data[0xa], 0, 3); // TODO: May also be Bi-Flex 3? But how is this different from [0xc] below?
CHECK_VALUE(data[0xb], 0);
}
if (flexmode == FLEX_BiFlex) {
- //CHECK_VALUE(data[0xc], 0, 3); // 3 = Bi-Flex 3 (in bi-flex mode)
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LEVEL, data[0xc])); // 3 = Bi-Flex 3 (in bi-flex mode)
+ CHECK_VALUE(data[0xc], 3);
CHECK_VALUE(data[0x0a], data[0xc]);
} else {
CHECK_VALUE(data[0xc], 0);
}
CHECK_VALUE(data[0xd], 0);
- if (flexmode != FLEX_AVAPS && cpapmode != PRS1_MODE_CPAP) CHECK_VALUE(data[0xe], 0x14); // 0x14 = ???
+ if (flexmode != FLEX_AVAPS && cpapmode != PRS1_MODE_CPAP) CHECK_VALUE(data[0xe], 0x14); // 0x14 = ??? in S and non-AVAPS S/T and PC
if (cpapmode == PRS1_MODE_CPAP) CHECK_VALUE(data[0xe], 0);
- //CHECK_VALUES(data[0xe], 0x14, 0x3C); // 0x14 = ???; 0x3C = Tidal Volume 600, 0x18 = Tidal Volume 240
if (flexmode == FLEX_AVAPS) {
+ if (data[0xe] < 24 || data[0xe] > 65) UNEXPECTED_VALUE(data[0xe], "24-65");
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TIDAL_VOLUME, data[0xe] * 10.0));
}
- //CHECK_VALUES(data[0xf], 0x0C, 0x0A); // 0xC = Breath Rate 12, 0xA = Breath Rate 10
- //CHECK_VALUE(data[0x10], 0x0A, 0x14); // 0xA = Timed Inspiration 1, 0x14 = Time Inspiration 2
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, data[0xf])); // can be 0
- this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, data[0x10], 0.1));
+ int breath_rate = data[0xf];
+ int timed_inspiration = data[0x10];
+ switch (cpapmode) {
+ case PRS1_MODE_CPAP:
+ case PRS1_MODE_S:
+ CHECK_VALUE(breath_rate, 0);
+ CHECK_VALUE(timed_inspiration, 0);
+ break;
+ case PRS1_MODE_PC:
+ CHECK_VALUE(flexmode, FLEX_AVAPS);
+ CHECK_VALUE(breath_rate, 0);
+ CHECK_VALUE(timed_inspiration, 30);
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate)); // can be 0 on reports
+ this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
+ break;
+ case PRS1_MODE_ST:
+ if (flexmode == FLEX_AVAPS) {
+ CHECK_VALUES(breath_rate, 0, 10);
+ CHECK_VALUES(timed_inspiration, 10, 30);
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate)); // can be 0 on reports
+ this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
+ break;
+ }
+ // fall through
+ default:
+ CHECK_VALUES(breath_rate, 0x0C, 0x0A); // 0xC = Breath Rate 12, 0xA = Breath Rate 10
+ CHECK_VALUES(timed_inspiration, 10, 20); // 0xA = Timed Inspiration 1, 0x14 = Time Inspiration 2 // TODO: confirm other values
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate)); // can be 0; TODO: what does this mean in the general case?
+ this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
+ }
CHECK_VALUE(data[0x11], 0);
@@ -5211,8 +5255,10 @@ bool PRS1DataChunk::ParseSettingsF3V3(const unsigned char* data, int /*size*/)
// Menu options?
CHECK_VALUES(data[0x17], 0x10, 0x90); // 0x10 = resist 1; 0x90 = resist 1, resist lock
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_LOCK, (data[0x17] & 0x80) != 0));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, 1)); // only value seen so far, CHECK_VALUES above will flag any others
- //this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TUBING_LOCK, (data[0x18] & 0x80) != 0)); // TODO: add this internal setting
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TUBING_LOCK, (data[0x18] & 0x80) != 0));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_HOSE_DIAMETER, (data[0x18] & 0x7f)));
CHECK_VALUES(data[0x18] & 0x7f, 22, 15); // 0x16 = tubing 22; 0x0F = tubing 15, 0x96 = tubing 22 with lock
// Alarms?
@@ -5522,6 +5568,8 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
int fixed_ipap = 0;
int min_ipap = 0;
int max_ipap = 0;
+ int breath_rate;
+ int timed_inspiration;
// Parse the nested data structure which contains settings
int pos = 0;
@@ -5640,19 +5688,25 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
case 0: // Breath Rate Off
// TODO: Is this mode essentially bilevel? The pressure graphs are confusing.
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Off));
+ CHECK_VALUE(data[pos+1], 0);
+ CHECK_VALUE(data[pos+2], 0);
break;
//case 1: // Breath Rate Auto in F5V3 setting 0x14
case 2: // Breath Rate (fixed BPM)
+ breath_rate = data[pos+1];
+ timed_inspiration = data[pos+2];
+ CHECK_VALUES(breath_rate, 10, 12); // TODO
+ if (timed_inspiration < 10 || timed_inspiration > 20) UNEXPECTED_VALUE(timed_inspiration, "10-20");
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, data[pos+1]));
- this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, data[pos+2], 0.1));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate));
+ this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
break;
default:
- CHECK_VALUES(data[pos], 0, 2); // 0 = Breath Rate off (S), 2 = fixed BPM (1 = auto on F5V3 setting 0x14)
+ CHECK_VALUES(data[pos], 0, 2); // 0 = Breath Rate off (S), 2 = fixed BPM (1 = auto on F5V3 setting 0x14, haven't seen it on F3V6 yet)
break;
}
break;
- //0x2b: Ramp type sounds like it's linear unless AAM is enabled, so no setting may be needed.
+ //0x2b: Ramp type sounds like it's linear for F3V6 unless AAM is enabled, so no setting may be needed.
case 0x2c: // Ramp Time
CHECK_VALUE(len, 1);
if (data[pos] != 0) { // 0 == ramp off, and ramp pressure setting doesn't appear
@@ -5664,7 +5718,6 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_RAMP_PRESSURE, data[pos], GAIN));
break;
case 0x2e: // Bi-Flex level or Rise Time
- CHECK_VALUE(len, 1);
// On F5V3 the first byte could specify Bi-Flex or Rise Time, and second byte contained the value.
// On F3V6 there's only one byte, which seems to correspond to Rise Time on the reports with flex
// mode None or AVAPS and to Bi-Flex Setting (level) in Bi-Flex mode.
@@ -5677,7 +5730,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
if (data[pos] < 1 || data[pos] > 6) UNEXPECTED_VALUE(data[pos], "1-6"); // 1-6 have been seen
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RISE_TIME, data[pos]));
}
- // TODO: where's timed inspiration?
+ // Timed inspiration specified in the backup breath rate.
break;
case 0x2f: // Rise Time lock? (was flex lock on F0V6, 0x80 for locked)
CHECK_VALUE(len, 1);
@@ -5686,7 +5739,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LOCK, data[pos] != 0));
} else {
CHECK_VALUE(data[pos], 0); // Rise Time Lock? not yet observed on F3V6
- //this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LOCK, data[pos] != 0));
+ //this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RISE_TIME_LOCK, data[pos] != 0));
}
break;
case 0x35: // Humidifier setting
@@ -5701,12 +5754,14 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
case 0x38: // Mask Resistance
CHECK_VALUE(len, 1);
if (data[pos] != 0) { // 0 == mask resistance off
+ CHECK_VALUES(data[pos], 1, 2); // TODO
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[pos]));
}
break;
case 0x39: // Tubing Type Lock
CHECK_VALUE(len, 1);
CHECK_VALUES(data[pos], 0, 0x80);
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TUBING_LOCK, data[pos] != 0));
break;
case 0x3b: // Tubing Type
CHECK_VALUE(len, 1);
@@ -5721,6 +5776,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_SHOW_AHI, data[pos] != 0));
break;
default:
+ UNEXPECTED_VALUE(code, "known setting");
qDebug() << "Unknown setting:" << hex << code << "in" << this->sessionid << "at" << pos;
this->AddEvent(new PRS1UnknownDataEvent(QByteArray((const char*) data, size), pos, len));
break;
@@ -5757,10 +5813,35 @@ bool PRS1DataChunk::ParseSettingsF5V012(const unsigned char* data, int /*size*/)
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PS_MIN, imin_ps));
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PS_MAX, imax_ps));
- // TODO: add settings for backup breathing
//CHECK_VALUE(data[0x07], 1, 2); // 1 = backup breath rate "Auto"; 2 = fixed BPM, see below
//CHECK_VALUE(data[0x08], 0); // backup "Breath Rate" in mode 2
//CHECK_VALUE(data[0x09], 0); // backup "Timed Inspiration" (gain 0.1) in mode 2
+ int pos = 0x7;
+ int backup_mode = data[pos];
+ int breath_rate;
+ int timed_inspiration;
+ switch (backup_mode) {
+ case 0:
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Off));
+ break;
+ case 1:
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Auto));
+ CHECK_VALUE(data[pos+1], 0);
+ CHECK_VALUE(data[pos+2], 0);
+ break;
+ case 2:
+ breath_rate = data[pos+1];
+ timed_inspiration = data[pos+2];
+ if (breath_rate < 10 || breath_rate > 18) UNEXPECTED_VALUE(breath_rate, "10-18");
+ if (timed_inspiration < 10 || timed_inspiration > 18) UNEXPECTED_VALUE(timed_inspiration, "10-18");
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate));
+ this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
+ break;
+ default:
+ UNEXPECTED_VALUE(backup_mode, "0-2");
+ break;
+ }
int ramp_time = data[0x0a];
int ramp_pressure = data[0x0b];
@@ -5770,7 +5851,6 @@ bool PRS1DataChunk::ParseSettingsF5V012(const unsigned char* data, int /*size*/)
quint8 flex = data[0x0c];
this->ParseFlexSettingF5V012(flex, cpapmode);
- int pos;
if (this->familyVersion == 0) { // TODO: either split this into two functions or use size to differentiate like FV3 parsers do
// TODO: Is there another flag for F5V0? Reports say "Bypass System One Humidification" as an option?
this->ParseHumidifierSetting50Series(data[0x0d], true);
@@ -5800,8 +5880,10 @@ bool PRS1DataChunk::ParseSettingsF5V012(const unsigned char* data, int /*size*/)
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_LOCK, (data[pos] & 0x40) != 0));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, resist_level));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_HOSE_DIAMETER, (data[pos] & 0x01) ? 15 : 22));
- //this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TUBING_LOCK, (data[pos] & 0x02) != 0)); // TODO: add this internal setting
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TUBING_LOCK, (data[pos] & 0x02) != 0));
CHECK_VALUE(data[pos] & (0x80|0x04), 0);
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_AUTO_ON, (data[pos+1] & 0x40) != 0));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_SHOW_AHI, (data[pos+1] & 1) != 0));
CHECK_VALUE(data[pos+1] & ~(0x40|1), 0);
CHECK_VALUES(data[pos+2], 0, 1); // 1 = apnea alarm 10
@@ -6081,7 +6163,7 @@ void PRS1DataChunk::ParseFlexSettingF0V234(quint8 flex, int cpapmode)
if (flexmode != FLEX_None) {
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LEVEL, flexlevel));
}
- // TODO: add flex mode lock
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LOCK, lock));
}
@@ -6127,7 +6209,7 @@ void PRS1DataChunk::ParseFlexSettingF5V012(quint8 flex, int cpapmode)
} else if (this->familyVersion == 1) {
if (unknown == false) {
CHECK_VALUE(flex, 0x08);
- flexlevel = 2; // Why do reports say Rise Time 2 for this value?
+ flexlevel = 2; // TODO: Why do reports say Rise Time 2 for this value?
}
if (lock) CHECK_VALUE(risetime, true); // so far we've only seen rise time lock, but this could mean bi-flex lock as well
if (flexlevel == 0 && unknown) UNEXPECTED_VALUE(flexlevel, "1-3");
@@ -6135,7 +6217,7 @@ void PRS1DataChunk::ParseFlexSettingF5V012(quint8 flex, int cpapmode)
CHECK_VALUE(flex, 0x02); // only seen one example, unsure if it matches F5V01
}
- // We're ony confident of values where the high bit is set
+ // We're only confident of values where the high bit is set
if (unknown) {
if (risetime) {
flexmode = FLEX_RiseTime;
@@ -6580,6 +6662,10 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
// The time of change is most likely in the events file. See slice 6 for ending pressure.
//CHECK_VALUE(pressure, 0x5a);
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE, pressure));
+ // TODO: Once OSCAR can handle more modes, we can include these settings; right now including
+ // these settings makes it think this is AutoCPAP.
+ //this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE_MIN, min_pressure));
+ //this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE_MAX, max_pressure));
break;
case 0x0d: // AutoCPAP pressure setting
CHECK_VALUE(len, 2);
@@ -6691,6 +6777,14 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
break;
case 0xB0: // P-Flex
flexmode = FLEX_PFlex; // TOOD: There's a level present in the settings, does it have any effect?
+ switch (cpapmode) {
+ case PRS1_MODE_AUTOCPAP:
+ break;
+ default: // TODO
+ HEX(flexmode);
+ UNEXPECTED_VALUE(cpapmode, "untested mode");
+ break;
+ }
break;
default:
UNEXPECTED_VALUE(data[pos], "known flex mode");
@@ -6706,6 +6800,9 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
case 0x30: // Flex level
CHECK_VALUE(len, 1);
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LEVEL, data[pos]));
+ if (flexmode == FLEX_PFlex) {
+ CHECK_VALUE(data[pos], 4); // No number appears on reports.
+ }
break;
case 0x35: // Humidifier setting
CHECK_VALUE(len, 2);
@@ -6719,12 +6816,15 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
case 0x38: // Mask Resistance
CHECK_VALUE(len, 1);
if (data[pos] != 0) { // 0 == mask resistance off
+ CHECK_VALUES(data[pos], 1, 2); // TODO
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[pos]));
}
break;
- case 0x39:
+ case 0x39: // Tubing Type Lock
CHECK_VALUE(len, 1);
- CHECK_VALUES(data[pos], 0, 0x80); // 0x80 maybe auto-trial?
+ CHECK_VALUES(data[pos], 0, 0x80);
+ CHECK_VALUE(data[pos], 0); // TODO to confirm in more samples
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TUBING_LOCK, data[pos] != 0));
break;
case 0x3b: // Tubing Type
CHECK_VALUE(len, 1);
@@ -6735,9 +6835,7 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
break;
case 0x40: // new to 400G, also seen on 500X110, alternate tubing type? appears after 0x39 and before 0x3c
CHECK_VALUE(len, 1);
- if (data[pos] != 3 && data[pos] != 0) {
- CHECK_VALUES(data[pos], 1, 2); // 0 = 22mm, 1 = 15mm, 2 = 15HT, 3 = 12mm
- }
+ if (data[pos] < 0 || data[pos] > 3) UNEXPECTED_VALUE(data[pos], "0-3"); // 0 = 22mm, 1 = 15mm, 2 = 15HT, 3 = 12mm
this->ParseTubingTypeV3(data[pos]);
break;
case 0x3c: // View Optional Screens
@@ -6768,6 +6866,7 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
CHECK_VALUE(data[pos], 1);
break;
default:
+ UNEXPECTED_VALUE(code, "known setting");
qDebug() << "Unknown setting:" << hex << code << "in" << this->sessionid << "at" << pos;
this->AddEvent(new PRS1UnknownDataEvent(QByteArray((const char*) data, size), pos, len));
break;
@@ -7144,6 +7243,8 @@ bool PRS1DataChunk::ParseSettingsF5V3(const unsigned char* data, int size)
int max_ps = 0;
int min_epap = 0;
int max_epap = 0;
+ int breath_rate;
+ int timed_inspiration;
// Parse the nested data structure which contains settings
int pos = 0;
@@ -7214,9 +7315,13 @@ bool PRS1DataChunk::ParseSettingsF5V3(const unsigned char* data, int size)
CHECK_VALUE(data[pos+2], 0); // 0 for auto
break;
case 2: // Breath Rate (fixed BPM)
+ breath_rate = data[pos+1];
+ timed_inspiration = data[pos+2];
+ CHECK_VALUE(breath_rate, 10); // TODO
+ CHECK_VALUE(timed_inspiration, 24); // TODO
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, data[pos+1])); // BPM
- this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, data[pos+2], 0.1));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate)); // BPM
+ this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
break;
default:
CHECK_VALUES(data[pos], 1, 2); // 1 = auto, 2 = fixed BPM (0 = off in F3V6 setting 0x1e)
@@ -7288,9 +7393,7 @@ bool PRS1DataChunk::ParseSettingsF5V3(const unsigned char* data, int size)
break;
case 0x3b: // Tubing Type
CHECK_VALUE(len, 1);
- if (data[pos] != 0) {
- CHECK_VALUES(data[pos], 2, 1); // 15HT = 2, 15 = 1, 22 = 0, though report only says "15" for 15HT
- }
+ if (data[pos] < 0 || data[pos] > 2) UNEXPECTED_VALUE(data[pos], "0-2"); // 15HT = 2, 15 = 1, 22 = 0, though report only says "15" for 15HT
this->ParseTubingTypeV3(data[pos]);
break;
case 0x3c: // View Optional Screens
@@ -7304,6 +7407,7 @@ bool PRS1DataChunk::ParseSettingsF5V3(const unsigned char* data, int size)
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_AUTO_ON, data[pos] != 0));
break;
default:
+ UNEXPECTED_VALUE(code, "known setting");
qDebug() << "Unknown setting:" << hex << code << "in" << this->sessionid << "at" << pos;
this->AddEvent(new PRS1UnknownDataEvent(QByteArray((const char*) data, size), pos, len));
break;
@@ -7468,7 +7572,9 @@ bool PRS1Import::ImportSummary()
case PRS1_SETTING_AUTO_TRIAL:
case PRS1_SETTING_EZ_START:
case PRS1_SETTING_FLEX_LOCK:
+ case PRS1_SETTING_TUBING_LOCK:
case PRS1_SETTING_RISE_TIME:
+ case PRS1_SETTING_RISE_TIME_LOCK:
case PRS1_SETTING_RAMP_TYPE:
case PRS1_SETTING_APNEA_ALARM:
case PRS1_SETTING_DISCONNECT_ALARM:
From 5c9c140248e87d6863001fb2c68860011b7a537a Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Sun, 22 Mar 2020 23:19:35 -0400
Subject: [PATCH 02/15] Update PRS1 settings range checks based on all test
data.
---
oscar/SleepLib/loader_plugins/prs1_loader.cpp | 29 ++++++++++---------
1 file changed, 15 insertions(+), 14 deletions(-)
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index b3e00dc8..55f8ca00 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -5224,8 +5224,10 @@ bool PRS1DataChunk::ParseSettingsF3V3(const unsigned char* data, int /*size*/)
break;
case PRS1_MODE_ST:
if (flexmode == FLEX_AVAPS) {
- CHECK_VALUES(breath_rate, 0, 10);
- CHECK_VALUES(timed_inspiration, 10, 30);
+ if (breath_rate) {
+ CHECK_VALUES(breath_rate, 9, 10);
+ }
+ if (timed_inspiration < 10 || timed_inspiration > 30) UNEXPECTED_VALUE(timed_inspiration, "10-30");
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate)); // can be 0 on reports
this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
@@ -5234,7 +5236,7 @@ bool PRS1DataChunk::ParseSettingsF3V3(const unsigned char* data, int /*size*/)
// fall through
default:
CHECK_VALUES(breath_rate, 0x0C, 0x0A); // 0xC = Breath Rate 12, 0xA = Breath Rate 10
- CHECK_VALUES(timed_inspiration, 10, 20); // 0xA = Timed Inspiration 1, 0x14 = Time Inspiration 2 // TODO: confirm other values
+ CHECK_VALUES(timed_inspiration, 10, 20); // 0xA = Timed Inspiration 1, 0x14 = Time Inspiration 2
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate)); // can be 0; TODO: what does this mean in the general case?
this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
@@ -5695,7 +5697,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
case 2: // Breath Rate (fixed BPM)
breath_rate = data[pos+1];
timed_inspiration = data[pos+2];
- CHECK_VALUES(breath_rate, 10, 12); // TODO
+ if (breath_rate < 9 || breath_rate > 12) UNEXPECTED_VALUE(breath_rate, "9-12");
if (timed_inspiration < 10 || timed_inspiration > 20) UNEXPECTED_VALUE(timed_inspiration, "10-20");
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate));
@@ -5754,7 +5756,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
case 0x38: // Mask Resistance
CHECK_VALUE(len, 1);
if (data[pos] != 0) { // 0 == mask resistance off
- CHECK_VALUES(data[pos], 1, 2); // TODO
+ if (data[pos] < 1 || data[pos] > 5) UNEXPECTED_VALUE(data[pos], "1-5");
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[pos]));
}
break;
@@ -5832,8 +5834,8 @@ bool PRS1DataChunk::ParseSettingsF5V012(const unsigned char* data, int /*size*/)
case 2:
breath_rate = data[pos+1];
timed_inspiration = data[pos+2];
- if (breath_rate < 10 || breath_rate > 18) UNEXPECTED_VALUE(breath_rate, "10-18");
- if (timed_inspiration < 10 || timed_inspiration > 18) UNEXPECTED_VALUE(timed_inspiration, "10-18");
+ if (breath_rate < 4 || breath_rate > 29) UNEXPECTED_VALUE(breath_rate, "4-29");
+ if (timed_inspiration < 5 || timed_inspiration > 20) UNEXPECTED_VALUE(timed_inspiration, "5-20");
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate));
this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
@@ -6776,13 +6778,13 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
}
break;
case 0xB0: // P-Flex
- flexmode = FLEX_PFlex; // TOOD: There's a level present in the settings, does it have any effect?
+ flexmode = FLEX_PFlex;
switch (cpapmode) {
case PRS1_MODE_AUTOCPAP:
break;
- default: // TODO
+ default:
HEX(flexmode);
- UNEXPECTED_VALUE(cpapmode, "untested mode");
+ UNEXPECTED_VALUE(cpapmode, "apap");
break;
}
break;
@@ -6816,14 +6818,13 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
case 0x38: // Mask Resistance
CHECK_VALUE(len, 1);
if (data[pos] != 0) { // 0 == mask resistance off
- CHECK_VALUES(data[pos], 1, 2); // TODO
+ if (data[pos] < 1 || data[pos] > 3) UNEXPECTED_VALUE(data[pos], "1-3");
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[pos]));
}
break;
case 0x39: // Tubing Type Lock
CHECK_VALUE(len, 1);
CHECK_VALUES(data[pos], 0, 0x80);
- CHECK_VALUE(data[pos], 0); // TODO to confirm in more samples
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TUBING_LOCK, data[pos] != 0));
break;
case 0x3b: // Tubing Type
@@ -7317,8 +7318,8 @@ bool PRS1DataChunk::ParseSettingsF5V3(const unsigned char* data, int size)
case 2: // Breath Rate (fixed BPM)
breath_rate = data[pos+1];
timed_inspiration = data[pos+2];
- CHECK_VALUE(breath_rate, 10); // TODO
- CHECK_VALUE(timed_inspiration, 24); // TODO
+ CHECK_VALUE(breath_rate, 10);
+ CHECK_VALUE(timed_inspiration, 24);
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate)); // BPM
this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
From fb59597367420bf041ab7f057dd0dbb823f7d6aa Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Mon, 23 Mar 2020 11:45:10 -0400
Subject: [PATCH 03/15] Clean up F3V3 breath rate range tests. No functional
changes.
---
oscar/SleepLib/loader_plugins/prs1_loader.cpp | 23 +++++++++----------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index 55f8ca00..4a49d519 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -5216,30 +5216,29 @@ bool PRS1DataChunk::ParseSettingsF3V3(const unsigned char* data, int /*size*/)
break;
case PRS1_MODE_PC:
CHECK_VALUE(flexmode, FLEX_AVAPS);
- CHECK_VALUE(breath_rate, 0);
+ CHECK_VALUE(breath_rate, 0); // only ever seen 0 on reports so far
CHECK_VALUE(timed_inspiration, 30);
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate)); // can be 0 on reports
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate));
this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
break;
case PRS1_MODE_ST:
if (flexmode == FLEX_AVAPS) {
- if (breath_rate) {
+ if (breath_rate) { // can be 0 on reports
CHECK_VALUES(breath_rate, 9, 10);
}
if (timed_inspiration < 10 || timed_inspiration > 30) UNEXPECTED_VALUE(timed_inspiration, "10-30");
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate)); // can be 0 on reports
- this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
- break;
+ } else if (flexmode == FLEX_None){
+ CHECK_VALUES(breath_rate, 0x0C, 0x0A); // 0xC = Breath Rate 12, 0xA = Breath Rate 10, can this be 0?
+ CHECK_VALUES(timed_inspiration, 10, 20); // 0xA = Timed Inspiration 1, 0x14 = Time Inspiration 2
}
- // fall through
- default:
- CHECK_VALUES(breath_rate, 0x0C, 0x0A); // 0xC = Breath Rate 12, 0xA = Breath Rate 10
- CHECK_VALUES(timed_inspiration, 10, 20); // 0xA = Timed Inspiration 1, 0x14 = Time Inspiration 2
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate)); // can be 0; TODO: what does this mean in the general case?
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate));
this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
+ break;
+ default:
+ UNEXPECTED_VALUE(cpapmode, "CPAP, S, S/T, or PC");
+ break;
}
CHECK_VALUE(data[0x11], 0);
From 3eb2ad4a7b019a8ea2d7102fd6d91ba37d812e5f Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Mon, 23 Mar 2020 12:59:06 -0400
Subject: [PATCH 04/15] Import all remaining parsed PRS1 settings seen on
bricks: flex lock, tubing type lock, mask resist lock, show AHI, and ramp
type.
Also remove superfluous mask resist status.
---
oscar/SleepLib/loader_plugins/prs1_loader.cpp | 98 ++++++++++++-------
oscar/tests/sessiontests.cpp | 5 +-
2 files changed, 64 insertions(+), 39 deletions(-)
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index 4a49d519..c5468706 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -225,6 +225,7 @@ enum BackupBreathMode { PRS1Backup_Off, PRS1Backup_Auto, PRS1Backup_Fixed };
enum HumidMode { HUMID_Fixed, HUMID_Adaptive, HUMID_HeatedTube };
ChannelID PRS1_TimedBreath = 0, PRS1_HumidMode = 0, PRS1_TubeTemp = 0;
+ChannelID PRS1_FlexLock = 0, PRS1_TubeLock = 0, PRS1_RampType = 0;
struct PRS1TestedModel
{
@@ -1195,7 +1196,6 @@ enum PRS1ParsedSettingType
PRS1_SETTING_HUMID_LEVEL,
PRS1_SETTING_MASK_RESIST_LOCK,
PRS1_SETTING_MASK_RESIST_SETTING,
- PRS1_SETTING_MASK_RESIST_STATUS,
PRS1_SETTING_HOSE_DIAMETER,
PRS1_SETTING_TUBING_LOCK,
PRS1_SETTING_AUTO_ON,
@@ -1749,7 +1749,6 @@ static QString parsedSettingTypeName(PRS1ParsedSettingType t)
ENUMSTRING(PRS1_SETTING_HUMID_LEVEL);
ENUMSTRING(PRS1_SETTING_MASK_RESIST_LOCK);
ENUMSTRING(PRS1_SETTING_MASK_RESIST_SETTING);
- ENUMSTRING(PRS1_SETTING_MASK_RESIST_STATUS);
ENUMSTRING(PRS1_SETTING_HOSE_DIAMETER);
ENUMSTRING(PRS1_SETTING_TUBING_LOCK);
ENUMSTRING(PRS1_SETTING_AUTO_ON);
@@ -4198,12 +4197,18 @@ bool PRS1Import::ImportCompliance()
case PRS1_SETTING_FLEX_LEVEL:
session->settings[PRS1_FlexLevel] = e->m_value;
break;
+ case PRS1_SETTING_FLEX_LOCK:
+ session->settings[PRS1_FlexLock] = (bool) e->m_value;
+ break;
case PRS1_SETTING_RAMP_TIME:
session->settings[CPAP_RampTime] = e->m_value;
break;
case PRS1_SETTING_RAMP_PRESSURE:
session->settings[CPAP_RampPressure] = e->value();
break;
+ case PRS1_SETTING_RAMP_TYPE:
+ session->settings[PRS1_RampType] = e->m_value;
+ break;
case PRS1_SETTING_HUMID_STATUS:
session->settings[PRS1_HumidStatus] = (bool) e->m_value;
break;
@@ -4217,17 +4222,18 @@ bool PRS1Import::ImportCompliance()
session->settings[PRS1_HumidLevel] = e->m_value;
break;
case PRS1_SETTING_MASK_RESIST_LOCK:
- //TODO: channel.add if we ever want to import this
- //session->settings[PRS1_SysLock] = (bool) e->m_value;
+ session->settings[PRS1_SysLock] = (bool) e->m_value;
break;
case PRS1_SETTING_MASK_RESIST_SETTING:
- case PRS1_SETTING_MASK_RESIST_STATUS:
// Don't bother importing these for bricks, because they're always locked off.
CHECK_VALUE(e->m_value, 0);
break;
case PRS1_SETTING_HOSE_DIAMETER:
session->settings[PRS1_HoseDiam] = e->m_value;
break;
+ case PRS1_SETTING_TUBING_LOCK:
+ session->settings[PRS1_TubeLock] = (bool) e->m_value;
+ break;
case PRS1_SETTING_AUTO_ON:
session->settings[PRS1_AutoOn] = (bool) e->m_value;
break;
@@ -4238,13 +4244,7 @@ bool PRS1Import::ImportCompliance()
session->settings[PRS1_MaskAlert] = (bool) e->m_value;
break;
case PRS1_SETTING_SHOW_AHI:
- //TODO: channel.add if we ever want to import this
- //session->settings[PRS1_ShowAHI] = (bool) e->m_value;
- break;
- case PRS1_SETTING_FLEX_LOCK:
- case PRS1_SETTING_TUBING_LOCK:
- case PRS1_SETTING_RAMP_TYPE:
- //TODO: define and add new channels for any of these that we want to import
+ session->settings[PRS1_ShowAHI] = (bool) e->m_value;
break;
default:
qWarning() << "Unknown PRS1 setting type" << (int) s->m_setting;
@@ -4547,10 +4547,12 @@ bool PRS1DataChunk::ParseSettingsF0V23(const unsigned char* data, int /*size*/)
// Tubing lock has no setting byte
// Menu Options
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_LOCK, (data[0x0a] & 0x80) != 0)); // System One Resistance Lock Setting
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_STATUS, (data[0x0a] & 0x40) != 0)); // System One Resistance Status bit
+ bool mask_resist_on = ((data[0x0a] & 0x40) != 0); // System One Resistance Status bit
+ int mask_resist_setting = data[0x0a] & 7; // System One Resistance setting value
+ CHECK_VALUE(mask_resist_on, mask_resist_setting > 0); // Confirm that we can ignore the status bit.
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_LOCK, (data[0x0a] & 0x80) != 0)); // System One Resistance Lock Setting, only seen on bricks
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_HOSE_DIAMETER, (data[0x0a] & 0x08) ? 15 : 22)); // TODO: unconfirmed
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[0x0a] & 7)); // System One Resistance setting value
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, mask_resist_setting));
CHECK_VALUE(data[0x0a] & (0x20 | 0x10), 0);
CHECK_VALUE(data[0x0b], 1);
@@ -7521,12 +7523,18 @@ bool PRS1Import::ImportSummary()
case PRS1_SETTING_FLEX_LEVEL:
session->settings[PRS1_FlexLevel] = e->m_value;
break;
+ case PRS1_SETTING_FLEX_LOCK:
+ session->settings[PRS1_FlexLock] = (bool) e->m_value;
+ break;
case PRS1_SETTING_RAMP_TIME:
session->settings[CPAP_RampTime] = e->m_value;
break;
case PRS1_SETTING_RAMP_PRESSURE:
session->settings[CPAP_RampPressure] = e->value();
break;
+ case PRS1_SETTING_RAMP_TYPE:
+ session->settings[PRS1_RampType] = e->m_value;
+ break;
case PRS1_SETTING_HUMID_STATUS:
session->settings[PRS1_HumidStatus] = (bool) e->m_value;
break;
@@ -7540,18 +7548,17 @@ bool PRS1Import::ImportSummary()
session->settings[PRS1_HumidLevel] = e->m_value;
break;
case PRS1_SETTING_MASK_RESIST_LOCK:
- //TODO: channel.add if we ever want to import this
- //session->settings[PRS1_SysLock] = (bool) e->m_value;
+ session->settings[PRS1_SysLock] = (bool) e->m_value;
break;
case PRS1_SETTING_MASK_RESIST_SETTING:
session->settings[PRS1_SysOneResistSet] = e->m_value;
break;
- case PRS1_SETTING_MASK_RESIST_STATUS:
- session->settings[PRS1_SysOneResistStat] = (bool) e->m_value;
- break;
case PRS1_SETTING_HOSE_DIAMETER:
session->settings[PRS1_HoseDiam] = e->m_value;
break;
+ case PRS1_SETTING_TUBING_LOCK:
+ session->settings[PRS1_TubeLock] = (bool) e->m_value;
+ break;
case PRS1_SETTING_AUTO_ON:
session->settings[PRS1_AutoOn] = (bool) e->m_value;
break;
@@ -7562,8 +7569,7 @@ bool PRS1Import::ImportSummary()
session->settings[PRS1_MaskAlert] = (bool) e->m_value;
break;
case PRS1_SETTING_SHOW_AHI:
- //TODO: channel.add if we ever want to import this
- //session->settings[PRS1_ShowAHI] = (bool) e->m_value;
+ session->settings[PRS1_ShowAHI] = (bool) e->m_value;
break;
case PRS1_SETTING_BACKUP_BREATH_MODE:
case PRS1_SETTING_BACKUP_BREATH_RATE:
@@ -7571,11 +7577,8 @@ bool PRS1Import::ImportSummary()
case PRS1_SETTING_TIDAL_VOLUME:
case PRS1_SETTING_AUTO_TRIAL:
case PRS1_SETTING_EZ_START:
- case PRS1_SETTING_FLEX_LOCK:
- case PRS1_SETTING_TUBING_LOCK:
case PRS1_SETTING_RISE_TIME:
case PRS1_SETTING_RISE_TIME_LOCK:
- case PRS1_SETTING_RAMP_TYPE:
case PRS1_SETTING_APNEA_ALARM:
case PRS1_SETTING_DISCONNECT_ALARM:
case PRS1_SETTING_LOW_MV_ALARM:
@@ -8655,6 +8658,15 @@ void PRS1Loader::initChannels()
chan->addOption(4, QObject::tr("x4"));
chan->addOption(5, QObject::tr("x5"));
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_FlexLock = 0xe111, SETTING, MT_CPAP, SESSION,
+ "PRS1FlexLock",
+ QObject::tr("Flex Lock"),
+ QObject::tr("Whether Flex settings are available to you."),
+ QObject::tr("Flex Lock"),
+ "", LOOKUP, Qt::black));
+ chan->addOption(0, STR_TR_Off);
+ chan->addOption(1, STR_TR_On);
+
channel.add(GRP_CPAP, chan = new Channel(PRS1_HumidStatus = 0xe101, SETTING, MT_CPAP, SESSION,
"PRS1HumidStat",
QObject::tr("Humidifier Status"),
@@ -8700,15 +8712,6 @@ void PRS1Loader::initChannels()
chan->addOption(4, QObject::tr("4"));
chan->addOption(5, QObject::tr("5"));
- channel.add(GRP_CPAP, chan = new Channel(PRS1_SysOneResistStat = 0xe103, SETTING, MT_CPAP, SESSION,
- "SysOneResistStat",
- QObject::tr("System One Resistance Status"),
- QObject::tr("System One Resistance Status"),
- QObject::tr("Sys1 Resist. Status"),
- "", LOOKUP, Qt::green));
- chan->addOption(0, STR_TR_Off);
- chan->addOption(1, STR_TR_On);
-
channel.add(GRP_CPAP, chan = new Channel(PRS1_SysOneResistSet = 0xe104, SETTING, MT_CPAP, SESSION,
"SysOneResistSet",
QObject::tr("System One Resistance Setting"),
@@ -8732,12 +8735,21 @@ void PRS1Loader::initChannels()
chan->addOption(15, QObject::tr("15mm"));
chan->addOption(12, QObject::tr("12mm"));
- channel.add(GRP_CPAP, chan = new Channel(PRS1_SysOneResistStat = 0xe108, SETTING, MT_CPAP, SESSION,
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_TubeLock = 0xe112, SETTING, MT_CPAP, SESSION,
+ "PRS1TubeLock",
+ QObject::tr("Tubing Type Lock"),
+ QObject::tr("Whether tubing type settings are available to you."),
+ QObject::tr("Tube Lock"),
+ "", LOOKUP, Qt::black));
+ chan->addOption(0, STR_TR_Off);
+ chan->addOption(1, STR_TR_On);
+
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_SysLock = 0xe108, SETTING, MT_CPAP, SESSION,
"SysOneLock",
QObject::tr("System One Resistance Lock"),
QObject::tr("Whether System One resistance settings are available to you."),
QObject::tr("Sys1 Resist. Lock"),
- "", LOOKUP, Qt::green));
+ "", LOOKUP, Qt::black));
chan->addOption(0, STR_TR_Off);
chan->addOption(1, STR_TR_On);
@@ -8768,15 +8780,25 @@ void PRS1Loader::initChannels()
chan->addOption(0, STR_TR_Off);
chan->addOption(1, STR_TR_On);
- channel.add(GRP_CPAP, chan = new Channel(PRS1_MaskAlert = 0xe10c, SETTING, MT_CPAP, SESSION,
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_ShowAHI = 0xe10c, SETTING, MT_CPAP, SESSION,
"PRS1ShowAHI",
QObject::tr("Show AHI"),
- QObject::tr("Whether or not machine shows AHI via LCD panel."),
+ QObject::tr("Whether or not machine shows AHI via built-in display."),
QObject::tr("Show AHI"),
"", LOOKUP, Qt::green));
chan->addOption(0, STR_TR_Off);
chan->addOption(1, STR_TR_On);
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_RampType = 0xe113, SETTING, MT_CPAP, SESSION,
+ "PRS1RampType",
+ QObject::tr("Ramp Type"),
+ QObject::tr("Type of ramp curve to use."),
+ QObject::tr("Ramp Type"),
+ "", LOOKUP, Qt::black));
+ chan->addOption(0, QObject::tr("Linear"));
+ chan->addOption(1, QObject::tr("SmartRamp"));
+
+ // TODO: is the below useful?
//
//
//
diff --git a/oscar/tests/sessiontests.cpp b/oscar/tests/sessiontests.cpp
index fddc692c..408f124a 100644
--- a/oscar/tests/sessiontests.cpp
+++ b/oscar/tests/sessiontests.cpp
@@ -54,6 +54,7 @@ static QString eventListTypeName(EventListType t)
// below by hand.
#define CHANNELNAME(CH) if (i == CH) { s = #CH; break; }
extern ChannelID PRS1_TimedBreath, PRS1_HumidMode, PRS1_TubeTemp;
+extern ChannelID PRS1_FlexLock, PRS1_TubeLock, PRS1_RampType;
extern ChannelID RMS9_EPR, RMS9_EPRLevel, RMS9_Mode, RMS9_SmartStart, RMS9_HumidStatus, RMS9_HumidLevel,
RMS9_PtAccess, RMS9_Mask, RMS9_ABFilter, RMS9_ClimateControl, RMS9_TubeType,
@@ -88,13 +89,15 @@ static QString settingChannel(ChannelID i)
CHANNELNAME(PRS1_HumidLevel);
CHANNELNAME(PRS1_SysLock);
CHANNELNAME(PRS1_SysOneResistSet);
- CHANNELNAME(PRS1_SysOneResistStat);
CHANNELNAME(PRS1_TimedBreath);
CHANNELNAME(PRS1_HoseDiam);
CHANNELNAME(PRS1_AutoOn);
CHANNELNAME(PRS1_AutoOff);
CHANNELNAME(PRS1_MaskAlert);
CHANNELNAME(PRS1_ShowAHI);
+ CHANNELNAME(PRS1_FlexLock);
+ CHANNELNAME(PRS1_TubeLock);
+ CHANNELNAME(PRS1_RampType);
CHANNELNAME(CPAP_BrokenSummary);
CHANNELNAME(ZEO_Awakenings);
CHANNELNAME(ZEO_MorningFeel);
From 77a34518bc3e61d6c391b641e80000b4e6b137c5 Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Mon, 23 Mar 2020 13:07:08 -0400
Subject: [PATCH 05/15] Rename PRS1 mask resistance lock and setting channels
for clarity.
---
oscar/SleepLib/loader_plugins/prs1_loader.cpp | 26 +++++++++----------
oscar/SleepLib/machine_common.cpp | 5 ++--
oscar/SleepLib/machine_common.h | 5 ++--
oscar/tests/sessiontests.cpp | 4 +--
4 files changed, 19 insertions(+), 21 deletions(-)
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index c5468706..aa576c35 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -4222,7 +4222,7 @@ bool PRS1Import::ImportCompliance()
session->settings[PRS1_HumidLevel] = e->m_value;
break;
case PRS1_SETTING_MASK_RESIST_LOCK:
- session->settings[PRS1_SysLock] = (bool) e->m_value;
+ session->settings[PRS1_MaskResistLock] = (bool) e->m_value;
break;
case PRS1_SETTING_MASK_RESIST_SETTING:
// Don't bother importing these for bricks, because they're always locked off.
@@ -7548,10 +7548,10 @@ bool PRS1Import::ImportSummary()
session->settings[PRS1_HumidLevel] = e->m_value;
break;
case PRS1_SETTING_MASK_RESIST_LOCK:
- session->settings[PRS1_SysLock] = (bool) e->m_value;
+ session->settings[PRS1_MaskResistLock] = (bool) e->m_value;
break;
case PRS1_SETTING_MASK_RESIST_SETTING:
- session->settings[PRS1_SysOneResistSet] = e->m_value;
+ session->settings[PRS1_MaskResistSet] = e->m_value;
break;
case PRS1_SETTING_HOSE_DIAMETER:
session->settings[PRS1_HoseDiam] = e->m_value;
@@ -8712,11 +8712,11 @@ void PRS1Loader::initChannels()
chan->addOption(4, QObject::tr("4"));
chan->addOption(5, QObject::tr("5"));
- channel.add(GRP_CPAP, chan = new Channel(PRS1_SysOneResistSet = 0xe104, SETTING, MT_CPAP, SESSION,
- "SysOneResistSet",
- QObject::tr("System One Resistance Setting"),
- QObject::tr("System One Mask Resistance Setting"),
- QObject::tr("Sys1 Resist. Set"),
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_MaskResistSet = 0xe104, SETTING, MT_CPAP, SESSION,
+ "MaskResistSet",
+ QObject::tr("Mask Resistance Setting"),
+ QObject::tr("Mask Resistance Setting"),
+ QObject::tr("Mask Resist."),
"", LOOKUP, Qt::green));
chan->addOption(0, STR_TR_Off);
chan->addOption(1, QObject::tr("x1"));
@@ -8744,11 +8744,11 @@ void PRS1Loader::initChannels()
chan->addOption(0, STR_TR_Off);
chan->addOption(1, STR_TR_On);
- channel.add(GRP_CPAP, chan = new Channel(PRS1_SysLock = 0xe108, SETTING, MT_CPAP, SESSION,
- "SysOneLock",
- QObject::tr("System One Resistance Lock"),
- QObject::tr("Whether System One resistance settings are available to you."),
- QObject::tr("Sys1 Resist. Lock"),
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_MaskResistLock = 0xe108, SETTING, MT_CPAP, SESSION,
+ "MaskResistLock",
+ QObject::tr("Mask Resistance Lock"),
+ QObject::tr("Whether mask resistance settings are available to you."),
+ QObject::tr("Mask Resist. Lock"),
"", LOOKUP, Qt::black));
chan->addOption(0, STR_TR_Off);
chan->addOption(1, STR_TR_On);
diff --git a/oscar/SleepLib/machine_common.cpp b/oscar/SleepLib/machine_common.cpp
index b11e6dd5..dc677d74 100644
--- a/oscar/SleepLib/machine_common.cpp
+++ b/oscar/SleepLib/machine_common.cpp
@@ -31,9 +31,8 @@ ChannelID RMS9_E01, RMS9_E02, RMS9_SetPressure, RMS9_MaskOnTime;
ChannelID INTELLIPAP_Unknown1, INTELLIPAP_Unknown2;
ChannelID PRS1_0E, CPAP_LargeLeak,
- PRS1_BND, PRS1_FlexMode, PRS1_FlexLevel, PRS1_HumidStatus, PRS1_HumidLevel, PRS1_SysLock,
- PRS1_SysOneResistStat,
- PRS1_SysOneResistSet, PRS1_HoseDiam, PRS1_AutoOn, PRS1_AutoOff, PRS1_MaskAlert, PRS1_ShowAHI;
+ PRS1_BND, PRS1_FlexMode, PRS1_FlexLevel, PRS1_HumidStatus, PRS1_HumidLevel, PRS1_MaskResistLock,
+ PRS1_MaskResistSet, PRS1_HoseDiam, PRS1_AutoOn, PRS1_AutoOff, PRS1_MaskAlert, PRS1_ShowAHI;
ChannelID OXI_Pulse, OXI_SPO2, OXI_Perf, OXI_PulseChange, OXI_SPO2Drop, OXI_Plethy;
diff --git a/oscar/SleepLib/machine_common.h b/oscar/SleepLib/machine_common.h
index b532a425..7bd77371 100644
--- a/oscar/SleepLib/machine_common.h
+++ b/oscar/SleepLib/machine_common.h
@@ -158,9 +158,8 @@ extern ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_EPAPLo, CP
extern ChannelID RMS9_E01, RMS9_E02, RMS9_SetPressure, RMS9_MaskOnTime;
extern ChannelID PRS1_0E, CPAP_LargeLeak, PRS1_BND,
- PRS1_FlexMode, PRS1_FlexLevel, PRS1_HumidStatus, PRS1_HumidLevel, CPAP_HumidSetting, PRS1_SysLock,
- PRS1_SysOneResistStat,
- PRS1_SysOneResistSet, PRS1_HoseDiam, PRS1_AutoOn, PRS1_AutoOff, PRS1_MaskAlert, PRS1_ShowAHI;
+ PRS1_FlexMode, PRS1_FlexLevel, PRS1_HumidStatus, PRS1_HumidLevel, CPAP_HumidSetting, PRS1_MaskResistLock,
+ PRS1_MaskResistSet, PRS1_HoseDiam, PRS1_AutoOn, PRS1_AutoOff, PRS1_MaskAlert, PRS1_ShowAHI;
extern ChannelID INTELLIPAP_Unknown1, INTELLIPAP_Unknown2;
diff --git a/oscar/tests/sessiontests.cpp b/oscar/tests/sessiontests.cpp
index 408f124a..f05bd9d7 100644
--- a/oscar/tests/sessiontests.cpp
+++ b/oscar/tests/sessiontests.cpp
@@ -87,8 +87,8 @@ static QString settingChannel(ChannelID i)
CHANNELNAME(PRS1_HumidMode);
CHANNELNAME(PRS1_TubeTemp);
CHANNELNAME(PRS1_HumidLevel);
- CHANNELNAME(PRS1_SysLock);
- CHANNELNAME(PRS1_SysOneResistSet);
+ CHANNELNAME(PRS1_MaskResistLock);
+ CHANNELNAME(PRS1_MaskResistSet);
CHANNELNAME(PRS1_TimedBreath);
CHANNELNAME(PRS1_HoseDiam);
CHANNELNAME(PRS1_AutoOn);
From 389ddd1631ecee1051c995dba6b0575a76949b4d Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Mon, 23 Mar 2020 21:09:55 -0400
Subject: [PATCH 06/15] Import remaining parsed PRS1 settings: backup breath
settings, tidal volume, rise time, EZ-Start, and Auto-Trial.
DreamStation ramp time and mask resistance "off" settings are now also imported.
Ventilator alarms are not yet parsed reliably enough to import at this time.
---
oscar/SleepLib/loader_plugins/prs1_loader.cpp | 120 +++++++++++++++---
oscar/tests/sessiontests.cpp | 14 +-
2 files changed, 113 insertions(+), 21 deletions(-)
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index aa576c35..eb4902e8 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -226,6 +226,8 @@ enum HumidMode { HUMID_Fixed, HUMID_Adaptive, HUMID_HeatedTube };
ChannelID PRS1_TimedBreath = 0, PRS1_HumidMode = 0, PRS1_TubeTemp = 0;
ChannelID PRS1_FlexLock = 0, PRS1_TubeLock = 0, PRS1_RampType = 0;
+ChannelID PRS1_BackupBreathMode = 0, PRS1_BackupBreathRate = 0, PRS1_BackupBreathTi = 0;
+ChannelID PRS1_AutoTrial = 0, PRS1_EZStart = 0, PRS1_RiseTime = 0, PRS1_RiseTimeLock = 0;
struct PRS1TestedModel
{
@@ -5713,8 +5715,9 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
case 0x2c: // Ramp Time
CHECK_VALUE(len, 1);
if (data[pos] != 0) { // 0 == ramp off, and ramp pressure setting doesn't appear
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RAMP_TIME, data[pos]));
+ if (data[pos] < 5 || data[pos] > 45) UNEXPECTED_VALUE(data[pos], "5-45");
}
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RAMP_TIME, data[pos]));
break;
case 0x2d: // Ramp Pressure (with ASV/ventilator pressure encoding), only present when ramp is on
CHECK_VALUE(len, 1);
@@ -5758,8 +5761,8 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
CHECK_VALUE(len, 1);
if (data[pos] != 0) { // 0 == mask resistance off
if (data[pos] < 1 || data[pos] > 5) UNEXPECTED_VALUE(data[pos], "1-5");
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[pos]));
}
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[pos]));
break;
case 0x39: // Tubing Type Lock
CHECK_VALUE(len, 1);
@@ -5836,7 +5839,7 @@ bool PRS1DataChunk::ParseSettingsF5V012(const unsigned char* data, int /*size*/)
breath_rate = data[pos+1];
timed_inspiration = data[pos+2];
if (breath_rate < 4 || breath_rate > 29) UNEXPECTED_VALUE(breath_rate, "4-29");
- if (timed_inspiration < 5 || timed_inspiration > 20) UNEXPECTED_VALUE(timed_inspiration, "5-20");
+ if (timed_inspiration < 5 || timed_inspiration > 30) UNEXPECTED_VALUE(timed_inspiration, "5-30");
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate));
this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
@@ -6733,8 +6736,9 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
case 0x2c: // Ramp Time
CHECK_VALUE(len, 1);
if (data[pos] != 0) { // 0 == ramp off, and ramp pressure setting doesn't appear
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RAMP_TIME, data[pos]));
+ if (data[pos] < 5 || data[pos] > 45) UNEXPECTED_VALUE(data[pos], "5-45");
}
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RAMP_TIME, data[pos]));
break;
case 0x2d: // Ramp Pressure
CHECK_VALUE(len, 1);
@@ -6819,9 +6823,9 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
case 0x38: // Mask Resistance
CHECK_VALUE(len, 1);
if (data[pos] != 0) { // 0 == mask resistance off
- if (data[pos] < 1 || data[pos] > 3) UNEXPECTED_VALUE(data[pos], "1-3");
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[pos]));
+ if (data[pos] < 1 || data[pos] > 5) UNEXPECTED_VALUE(data[pos], "1-5");
}
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[pos]));
break;
case 0x39: // Tubing Type Lock
CHECK_VALUE(len, 1);
@@ -7343,8 +7347,9 @@ bool PRS1DataChunk::ParseSettingsF5V3(const unsigned char* data, int size)
case 0x2c: // Ramp Time
CHECK_VALUE(len, 1);
if (data[pos] != 0) { // 0 == ramp off, and ramp pressure setting doesn't appear
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RAMP_TIME, data[pos]));
+ if (data[pos] < 5 || data[pos] > 45) UNEXPECTED_VALUE(data[pos], "5-45");
}
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RAMP_TIME, data[pos]));
break;
case 0x2d: // Ramp Pressure (with ASV pressure encoding)
CHECK_VALUE(len, 1);
@@ -7386,8 +7391,9 @@ bool PRS1DataChunk::ParseSettingsF5V3(const unsigned char* data, int size)
case 0x38: // Mask Resistance
CHECK_VALUE(len, 1);
if (data[pos] != 0) { // 0 == mask resistance off
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[pos]));
+ if (data[pos] < 1 || data[pos] > 5) UNEXPECTED_VALUE(data[pos], "1-5");
}
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[pos]));
break;
case 0x39:
CHECK_VALUE(len, 1);
@@ -7482,7 +7488,8 @@ bool PRS1Import::ImportSummary()
if (cpapmode == MODE_CPAP) { // Auto-Trial is reported as CPAP but with a minimum and maximum pressure,
cpapmode = MODE_APAP; // so import it as APAP, since that's what it's really doing.
}
- // TODO: what about CPAPCHECK?
+ // CPAP-Check no longer reports min/max pressures, since it should be treated as CPAP.
+ // (It only adjusts the CPAP pressure by 1 cmH2O every 30 hours at most.)
break;
case PRS1_SETTING_PRESSURE_MAX:
session->settings[CPAP_PressureMax] = e->value();
@@ -7572,18 +7579,34 @@ bool PRS1Import::ImportSummary()
session->settings[PRS1_ShowAHI] = (bool) e->m_value;
break;
case PRS1_SETTING_BACKUP_BREATH_MODE:
+ session->settings[PRS1_BackupBreathMode] = e->m_value;
+ break;
case PRS1_SETTING_BACKUP_BREATH_RATE:
+ session->settings[PRS1_BackupBreathRate] = e->m_value;
+ break;
case PRS1_SETTING_BACKUP_TIMED_INSPIRATION:
+ session->settings[PRS1_BackupBreathTi] = e->m_value;
+ break;
case PRS1_SETTING_TIDAL_VOLUME:
+ session->settings[CPAP_TidalVolume] = e->m_value;
+ break;
case PRS1_SETTING_AUTO_TRIAL:
+ session->settings[PRS1_AutoTrial] = e->m_value;
+ break;
case PRS1_SETTING_EZ_START:
+ session->settings[PRS1_EZStart] = (bool) e->m_value;
+ break;
case PRS1_SETTING_RISE_TIME:
+ session->settings[PRS1_RiseTime] = e->m_value;
+ break;
case PRS1_SETTING_RISE_TIME_LOCK:
+ session->settings[PRS1_RiseTimeLock] = (bool) e->m_value;
+ break;
case PRS1_SETTING_APNEA_ALARM:
case PRS1_SETTING_DISCONNECT_ALARM:
case PRS1_SETTING_LOW_MV_ALARM:
case PRS1_SETTING_LOW_TV_ALARM:
- //TODO: define and add new channels for any of these that we want to import
+ // TODO: define and add new channels for alarms once we have more samples and can reliably parse them.
break;
default:
qWarning() << "Unknown PRS1 setting type" << (int) s->m_setting;
@@ -8652,11 +8675,11 @@ void PRS1Loader::initChannels()
"", LOOKUP, Qt::blue));
chan->addOption(0, STR_TR_Off);
- chan->addOption(1, QObject::tr("x1"));
- chan->addOption(2, QObject::tr("x2"));
- chan->addOption(3, QObject::tr("x3"));
- chan->addOption(4, QObject::tr("x4"));
- chan->addOption(5, QObject::tr("x5"));
+ chan->addOption(1, QObject::tr("1"));
+ chan->addOption(2, QObject::tr("2"));
+ chan->addOption(3, QObject::tr("3"));
+ chan->addOption(4, QObject::tr("4"));
+ chan->addOption(5, QObject::tr("5"));
channel.add(GRP_CPAP, chan = new Channel(PRS1_FlexLock = 0xe111, SETTING, MT_CPAP, SESSION,
"PRS1FlexLock",
@@ -8667,6 +8690,22 @@ void PRS1Loader::initChannels()
chan->addOption(0, STR_TR_Off);
chan->addOption(1, STR_TR_On);
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_RiseTime = 0xe119, SETTING, MT_CPAP, SESSION,
+ "PRS1RiseTime",
+ QObject::tr("Rise Time"),
+ QObject::tr("Amount of time it takes to transition from EPAP to IPAP, the higher the number the slower the transition"),
+ QObject::tr("Rise Time"),
+ "", LOOKUP, Qt::blue));
+
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_RiseTimeLock = 0xe11a, SETTING, MT_CPAP, SESSION,
+ "PRS1RiseTimeLock",
+ QObject::tr("Rise Time Lock"),
+ QObject::tr("Whether Rise Time settings are available to you."),
+ QObject::tr("Rise Lock"),
+ "", LOOKUP, Qt::black));
+ chan->addOption(0, STR_TR_Off);
+ chan->addOption(1, STR_TR_On);
+
channel.add(GRP_CPAP, chan = new Channel(PRS1_HumidStatus = 0xe101, SETTING, MT_CPAP, SESSION,
"PRS1HumidStat",
QObject::tr("Humidifier Status"),
@@ -8719,11 +8758,11 @@ void PRS1Loader::initChannels()
QObject::tr("Mask Resist."),
"", LOOKUP, Qt::green));
chan->addOption(0, STR_TR_Off);
- chan->addOption(1, QObject::tr("x1"));
- chan->addOption(2, QObject::tr("x2"));
- chan->addOption(3, QObject::tr("x3"));
- chan->addOption(4, QObject::tr("x4"));
- chan->addOption(5, QObject::tr("x5"));
+ chan->addOption(1, QObject::tr("1"));
+ chan->addOption(2, QObject::tr("2"));
+ chan->addOption(3, QObject::tr("3"));
+ chan->addOption(4, QObject::tr("4"));
+ chan->addOption(5, QObject::tr("5"));
channel.add(GRP_CPAP, chan = new Channel(PRS1_HoseDiam = 0xe107, SETTING, MT_CPAP, SESSION,
"PRS1HoseDiam",
@@ -8798,6 +8837,47 @@ void PRS1Loader::initChannels()
chan->addOption(0, QObject::tr("Linear"));
chan->addOption(1, QObject::tr("SmartRamp"));
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_BackupBreathMode = 0xe114, SETTING, MT_CPAP, SESSION,
+ "PRS1BackupBreathMode",
+ QObject::tr("Backup Breath Mode"),
+ QObject::tr("The kind of backup breath rate in use: none (off), automatic, or fixed"),
+ QObject::tr("Breath Rate"),
+ "", LOOKUP, Qt::black));
+ chan->addOption(0, QObject::tr("Off"));
+ chan->addOption(1, QObject::tr("Auto"));
+ chan->addOption(2, QObject::tr("Fixed"));
+
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_BackupBreathRate = 0xe115, SETTING, MT_CPAP, SESSION,
+ "PRS1BackupBreathRate",
+ QObject::tr("Fixed Backup Breath BPM"),
+ QObject::tr("Minimum breaths per minute (BPM) below which a timed breath will be initiated"),
+ QObject::tr("Breath BPM"),
+ "", LOOKUP, Qt::black));
+
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_BackupBreathTi = 0xe116, SETTING, MT_CPAP, SESSION,
+ "PRS1BackupBreathTi",
+ QObject::tr("Timed Inspiration"),
+ QObject::tr("The time (in seconds) that a timed breath will provide IPAP before transitioning to EPAP"),
+ QObject::tr("Timed Insp."),
+ "", LOOKUP, Qt::blue));
+
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_AutoTrial = 0xe117, SETTING, MT_CPAP, SESSION,
+ "PRS1AutoTrial",
+ QObject::tr("Auto-Trial"),
+ QObject::tr("The number of days left in the Auto-CPAP trial period, after which the machine will revert to CPAP only"),
+ QObject::tr("Auto-Trial"),
+ "", LOOKUP, Qt::black));
+ chan->addOption(0, STR_TR_Off);
+
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_EZStart = 0xe118, SETTING, MT_CPAP, SESSION,
+ "PRS1EZStart",
+ QObject::tr("EZ-Start"),
+ QObject::tr("Whether or not EZ-Start is enabled"),
+ QObject::tr("EZ-Start"),
+ "", LOOKUP, Qt::black));
+ chan->addOption(0, STR_TR_Off);
+ chan->addOption(1, STR_TR_On);
+
// TODO: is the below useful?
//
//
diff --git a/oscar/tests/sessiontests.cpp b/oscar/tests/sessiontests.cpp
index f05bd9d7..b4f38e84 100644
--- a/oscar/tests/sessiontests.cpp
+++ b/oscar/tests/sessiontests.cpp
@@ -55,6 +55,8 @@ static QString eventListTypeName(EventListType t)
#define CHANNELNAME(CH) if (i == CH) { s = #CH; break; }
extern ChannelID PRS1_TimedBreath, PRS1_HumidMode, PRS1_TubeTemp;
extern ChannelID PRS1_FlexLock, PRS1_TubeLock, PRS1_RampType;
+extern ChannelID PRS1_BackupBreathMode, PRS1_BackupBreathRate, PRS1_BackupBreathTi;
+extern ChannelID PRS1_AutoTrial, PRS1_EZStart, PRS1_RiseTime, PRS1_RiseTimeLock;
extern ChannelID RMS9_EPR, RMS9_EPRLevel, RMS9_Mode, RMS9_SmartStart, RMS9_HumidStatus, RMS9_HumidLevel,
RMS9_PtAccess, RMS9_Mask, RMS9_ABFilter, RMS9_ClimateControl, RMS9_TubeType,
@@ -80,7 +82,10 @@ static QString settingChannel(ChannelID i)
CHANNELNAME(CPAP_RampTime);
CHANNELNAME(CPAP_RampPressure);
CHANNELNAME(CPAP_RespRate);
+ CHANNELNAME(CPAP_TidalVolume);
CHANNELNAME(OXI_Pulse);
+ CHANNELNAME(CPAP_BrokenSummary); // TODO: possibly obsolete and unused
+ // PRS1-specific channels
CHANNELNAME(PRS1_FlexMode);
CHANNELNAME(PRS1_FlexLevel);
CHANNELNAME(PRS1_HumidStatus);
@@ -98,7 +103,14 @@ static QString settingChannel(ChannelID i)
CHANNELNAME(PRS1_FlexLock);
CHANNELNAME(PRS1_TubeLock);
CHANNELNAME(PRS1_RampType);
- CHANNELNAME(CPAP_BrokenSummary);
+ CHANNELNAME(PRS1_BackupBreathMode);
+ CHANNELNAME(PRS1_BackupBreathRate);
+ CHANNELNAME(PRS1_BackupBreathTi);
+ CHANNELNAME(PRS1_AutoTrial);
+ CHANNELNAME(PRS1_EZStart);
+ CHANNELNAME(PRS1_RiseTime);
+ CHANNELNAME(PRS1_RiseTimeLock);
+ // ZEO-specific channels
CHANNELNAME(ZEO_Awakenings);
CHANNELNAME(ZEO_MorningFeel);
CHANNELNAME(ZEO_TimeInWake);
From d77ee5025a0373c45fbc1276a28a2196ff130bb0 Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Tue, 24 Mar 2020 13:50:30 -0400
Subject: [PATCH 07/15] Comment out BrokenSummary and BrokenWaveform channels,
as they are no longer used.
No functional change.
---
oscar/SleepLib/machine_common.cpp | 2 +-
oscar/SleepLib/machine_common.h | 2 +-
oscar/SleepLib/schema.cpp | 2 ++
oscar/daily.cpp | 8 +++++++-
oscar/docs/channels.xml | 3 ---
oscar/tests/sessiontests.cpp | 1 -
6 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/oscar/SleepLib/machine_common.cpp b/oscar/SleepLib/machine_common.cpp
index dc677d74..ecc1b561 100644
--- a/oscar/SleepLib/machine_common.cpp
+++ b/oscar/SleepLib/machine_common.cpp
@@ -21,7 +21,7 @@ ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_EPAPLo, CPAP_EPAP
CPAP_MaskPressureHi,
CPAP_RespEvent, CPAP_Snore, CPAP_MinuteVent, CPAP_RespRate, CPAP_TidalVolume, CPAP_PTB, CPAP_Leak,
CPAP_LeakMedian, CPAP_LeakTotal, CPAP_MaxLeak, CPAP_FLG, CPAP_IE, CPAP_Te, CPAP_Ti, CPAP_TgMV,
- CPAP_UserFlag1, CPAP_UserFlag2, CPAP_UserFlag3, CPAP_BrokenSummary, CPAP_BrokenWaveform, CPAP_RDI,
+ CPAP_UserFlag1, CPAP_UserFlag2, CPAP_UserFlag3, /*CPAP_BrokenSummary, CPAP_BrokenWaveform,*/ CPAP_RDI,
CPAP_PresReliefMode, CPAP_PresReliefLevel, CPAP_PSMin, CPAP_PSMax, CPAP_Test1,
CPAP_Test2, CPAP_HumidSetting,
CPAP_PressureSet, CPAP_IPAPSet, CPAP_EPAPSet;
diff --git a/oscar/SleepLib/machine_common.h b/oscar/SleepLib/machine_common.h
index 7bd77371..c2c46883 100644
--- a/oscar/SleepLib/machine_common.h
+++ b/oscar/SleepLib/machine_common.h
@@ -152,7 +152,7 @@ extern ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_EPAPLo, CP
CPAP_MaskPressureHi,
CPAP_RespEvent, CPAP_Snore, CPAP_MinuteVent, CPAP_RespRate, CPAP_TidalVolume, CPAP_PTB, CPAP_Leak,
CPAP_LeakMedian, CPAP_LeakTotal, CPAP_MaxLeak, CPAP_FLG, CPAP_IE, CPAP_Te, CPAP_Ti, CPAP_TgMV,
- CPAP_UserFlag1, CPAP_UserFlag2, CPAP_UserFlag3, CPAP_BrokenSummary, CPAP_BrokenWaveform, CPAP_RDI,
+ CPAP_UserFlag1, CPAP_UserFlag2, CPAP_UserFlag3, /*CPAP_BrokenSummary, CPAP_BrokenWaveform,*/ CPAP_RDI,
CPAP_PresReliefMode, CPAP_PresReliefLevel, CPAP_Test1, CPAP_Test2,
CPAP_PressureSet, CPAP_IPAPSet, CPAP_EPAPSet;
diff --git a/oscar/SleepLib/schema.cpp b/oscar/SleepLib/schema.cpp
index 69f5bc1e..506262b1 100644
--- a/oscar/SleepLib/schema.cpp
+++ b/oscar/SleepLib/schema.cpp
@@ -332,8 +332,10 @@ void init()
schema::channel.add(GRP_SLEEP, ch = new Channel(ZEO_ZQ = 0x2009, DATA, MT_SLEEPSTAGE, SESSION, "ZeoZQ", QObject::tr("Zeo ZQ"), QObject::tr("Zeo sleep quality measurement"), QObject::tr("ZEO ZQ"), QString(), INTEGER, Qt::black));
NoChannel = 0;
+ /*
CPAP_BrokenSummary = schema::channel["BrokenSummary"].id();
CPAP_BrokenWaveform = schema::channel["BrokenWaveform"].id();
+ */
//
//
diff --git a/oscar/daily.cpp b/oscar/daily.cpp
index 99965ad4..b93d9112 100644
--- a/oscar/daily.cpp
+++ b/oscar/daily.cpp
@@ -983,7 +983,7 @@ QString Daily::getSessionInformation(Day * day)
// Machine * cpap = day->machine(MT_CPAP);
QDateTime fd,ld;
- bool corrupted_waveform=false;
+ //bool corrupted_waveform=false;
QString type;
@@ -1023,9 +1023,11 @@ QString Daily::getSessionInformation(Day * day)
QList sesslist = day->getSessions(mi.key(), true);
for (QList::iterator s=sesslist.begin(); s != sesslist.end(); ++s) {
+ /*
if (((*s)->type() == MT_CPAP) &&
((*s)->settings.find(CPAP_BrokenWaveform) != (*s)->settings.end()))
corrupted_waveform=true;
+ */
fd=QDateTime::fromTime_t((*s)->first()/1000L);
ld=QDateTime::fromTime_t((*s)->last()/1000L);
@@ -1066,9 +1068,11 @@ QString Daily::getSessionInformation(Day * day)
}
}
+ /*
if (corrupted_waveform) {
html+=QString("
%1
").arg(tr("One or more waveform record(s) for this session had faulty source data. Some waveform overlay points may not match up correctly."));
}
+ */
html+="";
return html;
}
@@ -1084,9 +1088,11 @@ QString Daily::getMachineSettings(Day * day) {
if (day->noSettings(cpap)) {
html+="
"+tr("Please Note: All settings shown below are based on assumptions that nothing has changed since previous days.")+"
\n";
+ /*
} else if ((day->settingExists(CPAP_BrokenSummary))) {
html+="
"+tr("Machine Settings Unavailable")+"
\n";
return html;
+ */
}
QMap other;
diff --git a/oscar/docs/channels.xml b/oscar/docs/channels.xml
index e6899dc0..cdf27778 100644
--- a/oscar/docs/channels.xml
+++ b/oscar/docs/channels.xml
@@ -14,9 +14,6 @@ Important: One id code per item, DO NOT CHANGE ID NUMBERS!!!
-
-
-
diff --git a/oscar/tests/sessiontests.cpp b/oscar/tests/sessiontests.cpp
index b4f38e84..e9abdc9f 100644
--- a/oscar/tests/sessiontests.cpp
+++ b/oscar/tests/sessiontests.cpp
@@ -84,7 +84,6 @@ static QString settingChannel(ChannelID i)
CHANNELNAME(CPAP_RespRate);
CHANNELNAME(CPAP_TidalVolume);
CHANNELNAME(OXI_Pulse);
- CHANNELNAME(CPAP_BrokenSummary); // TODO: possibly obsolete and unused
// PRS1-specific channels
CHANNELNAME(PRS1_FlexMode);
CHANNELNAME(PRS1_FlexLevel);
From 6a3c8c1a26498c0d173568863a52492288fe07e5 Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Tue, 24 Mar 2020 17:15:29 -0400
Subject: [PATCH 08/15] Add PRS1-specific mode channel, allowing correct
display of S, S/T, and PC modes.
Also fix pressure settings for F3V3, and display of rise time and backup breath settings.
---
oscar/SleepLib/day.cpp | 18 +++++
oscar/SleepLib/loader_plugins/prs1_loader.cpp | 65 ++++++++++++++-----
oscar/SleepLib/loader_plugins/prs1_loader.h | 12 ++--
oscar/tests/sessiontests.cpp | 2 +
4 files changed, 75 insertions(+), 22 deletions(-)
diff --git a/oscar/SleepLib/day.cpp b/oscar/SleepLib/day.cpp
index eb7419cb..659da7b4 100644
--- a/oscar/SleepLib/day.cpp
+++ b/oscar/SleepLib/day.cpp
@@ -805,6 +805,8 @@ qint64 Day::total_time(MachineType type)
}
ChannelID Day::getPressureChannelID() {
+ // TODO: This is an awful hack that depends on the enum ordering of the generic CPAP_Mode channel.
+ // See the comment in getCPAPModeStr().
// Determined the preferred pressure channel (CPAP_IPAP or CPAP_Pressure)
CPAPMode cpapmode = (CPAPMode)(int)settings_max(CPAP_Mode);
ChannelID preferredID = CPAP_Pressure;
@@ -1401,15 +1403,23 @@ void Day::removeMachine(Machine * mach)
int Day::getCPAPMode()
{
+ // NOTE: This needs to return the generic mode, unlike getCPAPModeStr().
+ // This function is used only to determine whether to use advanced graphs,
+ // which refer to the generic mode.
+ /*
Machine * mach = machine(MT_CPAP);
if (!mach) return 0;
CPAPLoader * loader = qobject_cast(mach->loader());
ChannelID modechan = loader->CPAPModeChannel();
+ */
+ ChannelID modechan = CPAP_Mode;
// schema::Channel & chan = schema::channel[modechan];
+ // TODO: This is an awful hack that depends on the enum ordering of the machine-specific CPAP mode.
+ // See the comment in getCPAPModeStr().
int mode = (CPAPMode)(int)qRound(settings_wavg(modechan));
return mode;
@@ -1426,6 +1436,10 @@ QString Day::getCPAPModeStr()
schema::Channel & chan = schema::channel[modechan];
+ // TODO: This is an awful hack that depends on the enum ordering of the machine-specific CPAP mode.
+ // Instead, we should calculate how long each mode was in operation and
+ // determine the one that was running the longest, along with the settings
+ // while that mode was in operation.
int mode = (CPAPMode)(int)qRound(settings_wavg(modechan));
return chan.option(mode);
@@ -1463,6 +1477,8 @@ QString Day::getPressureRelief()
ChannelID pr_mode_chan = loader->PresReliefMode();
if ((pr_mode_chan != NoChannel) && settingExists(pr_mode_chan)) {
+ // TODO: This is an awful hack that depends on the enum ordering of the pressure relief mode.
+ // See the comment in getCPAPModeStr().
int pr_mode = qRound(settings_wavg(pr_mode_chan));
pr_str = QObject::tr("%1%2").arg(loader->PresReliefLabel()).arg(schema::channel[pr_mode_chan].option(pr_mode));
@@ -1488,6 +1504,8 @@ QString Day::getPressureSettings()
return QString();
}
+ // TODO: This is an awful hack that depends on the enum ordering of the generic CPAP_Mode channel.
+ // See the comment in getCPAPModeStr().
CPAPMode mode = (CPAPMode)(int)settings_max(CPAP_Mode);
QString units = schema::channel[CPAP_Pressure].units();
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index eb4902e8..74654274 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -224,11 +224,20 @@ enum BackupBreathMode { PRS1Backup_Off, PRS1Backup_Auto, PRS1Backup_Fixed };
enum HumidMode { HUMID_Fixed, HUMID_Adaptive, HUMID_HeatedTube };
+ChannelID PRS1_Mode = 0;
ChannelID PRS1_TimedBreath = 0, PRS1_HumidMode = 0, PRS1_TubeTemp = 0;
ChannelID PRS1_FlexLock = 0, PRS1_TubeLock = 0, PRS1_RampType = 0;
ChannelID PRS1_BackupBreathMode = 0, PRS1_BackupBreathRate = 0, PRS1_BackupBreathTi = 0;
ChannelID PRS1_AutoTrial = 0, PRS1_EZStart = 0, PRS1_RiseTime = 0, PRS1_RiseTimeLock = 0;
+QString PRS1Loader::PresReliefLabel() { return QObject::tr(""); }
+ChannelID PRS1Loader::PresReliefMode() { return PRS1_FlexMode; }
+ChannelID PRS1Loader::PresReliefLevel() { return PRS1_FlexLevel; }
+ChannelID PRS1Loader::CPAPModeChannel() { return PRS1_Mode; }
+ChannelID PRS1Loader::HumidifierConnected() { return PRS1_HumidStatus; }
+ChannelID PRS1Loader::HumidifierLevel() { return PRS1_HumidLevel; }
+
+
struct PRS1TestedModel
{
QString model;
@@ -1558,16 +1567,16 @@ PRS1_ALARM_EVENT(PRS1LowMinuteVentilationAlarmEvent, EV_PRS1_LOW_MV_ALARM);
enum PRS1Mode {
PRS1_MODE_UNKNOWN = -1,
- PRS1_MODE_CPAP = 0, // "CPAP"
- PRS1_MODE_CPAPCHECK, // "CPAP-Check"
+ PRS1_MODE_CPAPCHECK = 0, // "CPAP-Check"
+ PRS1_MODE_CPAP, // "CPAP"
PRS1_MODE_AUTOCPAP, // "AutoCPAP"
+ PRS1_MODE_AUTOTRIAL, // "Auto-Trial"
PRS1_MODE_BILEVEL, // "Bi-Level"
PRS1_MODE_AUTOBILEVEL, // "AutoBiLevel"
PRS1_MODE_ASV, // "ASV"
PRS1_MODE_S, // "S"
PRS1_MODE_ST, // "S/T"
PRS1_MODE_PC, // "PC"
- PRS1_MODE_AUTOTRIAL, // "Auto-Trial"
};
// Returns the set of all channels ever reported/supported by the parser for the given chunk.
@@ -4149,19 +4158,22 @@ const QVector & GetSupportedEvents(const PRS1DataChunk* chu
CPAPMode PRS1Import::importMode(int prs1mode)
{
- CPAPMode mode;
+ CPAPMode mode = MODE_UNKNOWN;
switch (prs1mode) {
+ case PRS1_MODE_CPAPCHECK: mode = MODE_CPAP; break;
case PRS1_MODE_CPAP: mode = MODE_CPAP; break;
- case PRS1_MODE_CPAPCHECK: mode = MODE_CPAP; break; // "CPAP-Check" in report, but seems like CPAP
case PRS1_MODE_AUTOCPAP: mode = MODE_APAP; break;
+ case PRS1_MODE_AUTOTRIAL: mode = MODE_APAP; break;
case PRS1_MODE_BILEVEL: mode = MODE_BILEVEL_FIXED; break;
case PRS1_MODE_AUTOBILEVEL: mode = MODE_BILEVEL_AUTO_VARIABLE_PS; break;
case PRS1_MODE_ASV: mode = MODE_ASV_VARIABLE_EPAP; break;
case PRS1_MODE_S: mode = MODE_BILEVEL_FIXED; break; // TODO
case PRS1_MODE_ST: mode = MODE_BILEVEL_FIXED; break; // TODO, pressure seems variable
case PRS1_MODE_PC: mode = MODE_AVAPS; break; // TODO, maybe only PC - AVAPS mode
- default: mode = MODE_UNKNOWN; break;
+ default:
+ UNEXPECTED_VALUE(prs1mode, "known PRS1 mode");
+ break;
}
// TODO: fixed vs. variable PS seems to be independent from ventilator mode, for example
// S/T can be fixed (single IPAP pressure) or variable (IPAP min/max).
@@ -4188,6 +4200,7 @@ bool PRS1Import::ImportCompliance()
PRS1ParsedSettingEvent* s = (PRS1ParsedSettingEvent*) e;
switch (s->m_setting) {
case PRS1_SETTING_CPAP_MODE:
+ session->settings[PRS1_Mode] = (PRS1Mode) e->m_value;
session->settings[CPAP_Mode] = importMode(e->m_value);
break;
case PRS1_SETTING_PRESSURE:
@@ -5172,13 +5185,14 @@ bool PRS1DataChunk::ParseSettingsF3V3(const unsigned char* data, int /*size*/)
}
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_MODE, (int) flexmode));
- int epap = data[4] + (data[5] << 1); // 0x82 = EPAP 13 cmH2O; 0x78 = EPAP 12 cmH2O; 0x50 = EPAP 8 cmH2O
- int min_ipap = data[6] + (data[7] << 1); // 0xA0 = IPAP 16 cmH2O; 0xBE = 19 cmH2O min IPAP (in AVAPS); 0x78 = IPAP 12 cmH2O
- int max_ipap = data[8] + (data[9] << 1); // 0xAA = ???; 0x12C = 30 cmH2O max IPAP (in AVAPS); 0x78 = ???
+ // TODO: add based on mode
+ int epap = data[4] + (data[5] << 8); // 0x82 = EPAP 13 cmH2O; 0x78 = EPAP 12 cmH2O; 0x50 = EPAP 8 cmH2O
+ int min_ipap = data[6] + (data[7] << 8); // 0xA0 = IPAP 16 cmH2O; 0xBE = 19 cmH2O min IPAP (in AVAPS); 0x78 = IPAP 12 cmH2O
+ int max_ipap = data[8] + (data[9] << 8); // 0xAA = ???; 0x12C = 30 cmH2O max IPAP (in AVAPS); 0x78 = ???
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_EPAP, epap));
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MIN, min_ipap));
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MAX, max_ipap));
- // TODO: calculte PS or min/max PS? Create IPAP event when not AVAPS?
+ // TODO: calculate PS or min/max PS? Create IPAP event when not AVAPS?
if (flexmode == FLEX_None || flexmode == FLEX_AVAPS) {
int rise_time = data[0xa]; // 1 = Rise Time Setting 1, 2 = Rise Time Setting 2, 3 = Rise Time Setting 3
@@ -7478,6 +7492,7 @@ bool PRS1Import::ImportSummary()
PRS1ParsedSettingEvent* s = (PRS1ParsedSettingEvent*) e;
switch (s->m_setting) {
case PRS1_SETTING_CPAP_MODE:
+ session->settings[PRS1_Mode] = (PRS1Mode) e->m_value;
cpapmode = importMode(e->m_value);
break;
case PRS1_SETTING_PRESSURE:
@@ -7585,7 +7600,7 @@ bool PRS1Import::ImportSummary()
session->settings[PRS1_BackupBreathRate] = e->m_value;
break;
case PRS1_SETTING_BACKUP_TIMED_INSPIRATION:
- session->settings[PRS1_BackupBreathTi] = e->m_value;
+ session->settings[PRS1_BackupBreathTi] = e->value();
break;
case PRS1_SETTING_TIDAL_VOLUME:
session->settings[CPAP_TidalVolume] = e->m_value;
@@ -8653,6 +8668,22 @@ void PRS1Loader::initChannels()
QObject::tr("PP"),
STR_UNIT_EventsPerHour, DEFAULT, QColor("dark red")));
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_Mode = 0xe120, SETTING, MT_CPAP, SESSION,
+ "PRS1Mode", QObject::tr("Mode"),
+ QObject::tr("PAP Mode"),
+ QObject::tr("Mode"),
+ "", LOOKUP, Qt::green));
+ chan->addOption(0, QObject::tr("CPAP-Check"));
+ chan->addOption(1, QObject::tr("CPAP"));
+ chan->addOption(2, QObject::tr("AutoCPAP"));
+ chan->addOption(3, QObject::tr("Auto-Trial"));
+ chan->addOption(4, QObject::tr("Bi-Level"));
+ chan->addOption(5, QObject::tr("AutoBiLevel"));
+ chan->addOption(6, QObject::tr("ASV"));
+ chan->addOption(7, QObject::tr("S"));
+ chan->addOption(8, QObject::tr("S/T"));
+ chan->addOption(9, QObject::tr("PC"));
+
channel.add(GRP_CPAP, chan = new Channel(PRS1_FlexMode = 0xe105, SETTING, MT_CPAP, SESSION,
"PRS1FlexMode", QObject::tr("Flex Mode"),
QObject::tr("PRS1 pressure relief mode."),
@@ -8695,7 +8726,7 @@ void PRS1Loader::initChannels()
QObject::tr("Rise Time"),
QObject::tr("Amount of time it takes to transition from EPAP to IPAP, the higher the number the slower the transition"),
QObject::tr("Rise Time"),
- "", LOOKUP, Qt::blue));
+ "", DEFAULT, Qt::blue));
channel.add(GRP_CPAP, chan = new Channel(PRS1_RiseTimeLock = 0xe11a, SETTING, MT_CPAP, SESSION,
"PRS1RiseTimeLock",
@@ -8768,7 +8799,7 @@ void PRS1Loader::initChannels()
"PRS1HoseDiam",
QObject::tr("Hose Diameter"),
QObject::tr("Diameter of primary CPAP hose"),
- QObject::tr("Hose Diameter"),
+ QObject::tr("Hose Diam."),
"", LOOKUP, Qt::green));
chan->addOption(22, QObject::tr("22mm"));
chan->addOption(15, QObject::tr("15mm"));
@@ -8787,7 +8818,7 @@ void PRS1Loader::initChannels()
"MaskResistLock",
QObject::tr("Mask Resistance Lock"),
QObject::tr("Whether mask resistance settings are available to you."),
- QObject::tr("Mask Resist. Lock"),
+ QObject::tr("Mask Res. Lock"),
"", LOOKUP, Qt::black));
chan->addOption(0, STR_TR_Off);
chan->addOption(1, STR_TR_On);
@@ -8852,14 +8883,14 @@ void PRS1Loader::initChannels()
QObject::tr("Fixed Backup Breath BPM"),
QObject::tr("Minimum breaths per minute (BPM) below which a timed breath will be initiated"),
QObject::tr("Breath BPM"),
- "", LOOKUP, Qt::black));
+ STR_UNIT_BreathsPerMinute, DEFAULT, Qt::black));
channel.add(GRP_CPAP, chan = new Channel(PRS1_BackupBreathTi = 0xe116, SETTING, MT_CPAP, SESSION,
"PRS1BackupBreathTi",
QObject::tr("Timed Inspiration"),
- QObject::tr("The time (in seconds) that a timed breath will provide IPAP before transitioning to EPAP"),
+ QObject::tr("The time that a timed breath will provide IPAP before transitioning to EPAP"),
QObject::tr("Timed Insp."),
- "", LOOKUP, Qt::blue));
+ STR_UNIT_Seconds, DEFAULT, Qt::blue));
channel.add(GRP_CPAP, chan = new Channel(PRS1_AutoTrial = 0xe117, SETTING, MT_CPAP, SESSION,
"PRS1AutoTrial",
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.h b/oscar/SleepLib/loader_plugins/prs1_loader.h
index 39f0c885..cf5adc75 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.h
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.h
@@ -420,16 +420,18 @@ class PRS1Loader : public CPAPLoader
}
- virtual QString PresReliefLabel() { return QObject::tr(""); }
+ virtual QString PresReliefLabel();
//! \brief Returns the PRS1 specific code for Pressure Relief Mode
- virtual ChannelID PresReliefMode() { return PRS1_FlexMode; }
+ virtual ChannelID PresReliefMode();
//! \brief Returns the PRS1 specific code for Pressure Relief Setting
- virtual ChannelID PresReliefLevel() { return PRS1_FlexLevel; }
+ virtual ChannelID PresReliefLevel();
+ //! \brief Returns the PRS1 specific code for PAP mode
+ virtual ChannelID CPAPModeChannel();
//! \brief Returns the PRS1 specific code for Humidifier Connected
- virtual ChannelID HumidifierConnected() { return PRS1_HumidStatus; }
+ virtual ChannelID HumidifierConnected();
//! \brief Returns the PRS1 specific code for Humidifier Level
- virtual ChannelID HumidifierLevel() { return PRS1_HumidLevel; }
+ virtual ChannelID HumidifierLevel();
//! \brief Called at application init, to set up any custom PRS1 Channels
void initChannels();
diff --git a/oscar/tests/sessiontests.cpp b/oscar/tests/sessiontests.cpp
index e9abdc9f..beaa7887 100644
--- a/oscar/tests/sessiontests.cpp
+++ b/oscar/tests/sessiontests.cpp
@@ -53,6 +53,7 @@ static QString eventListTypeName(EventListType t)
// for names, make sure there aren't duplicate values, etc. For now we just fill the
// below by hand.
#define CHANNELNAME(CH) if (i == CH) { s = #CH; break; }
+extern ChannelID PRS1_Mode;
extern ChannelID PRS1_TimedBreath, PRS1_HumidMode, PRS1_TubeTemp;
extern ChannelID PRS1_FlexLock, PRS1_TubeLock, PRS1_RampType;
extern ChannelID PRS1_BackupBreathMode, PRS1_BackupBreathRate, PRS1_BackupBreathTi;
@@ -85,6 +86,7 @@ static QString settingChannel(ChannelID i)
CHANNELNAME(CPAP_TidalVolume);
CHANNELNAME(OXI_Pulse);
// PRS1-specific channels
+ CHANNELNAME(PRS1_Mode);
CHANNELNAME(PRS1_FlexMode);
CHANNELNAME(PRS1_FlexLevel);
CHANNELNAME(PRS1_HumidStatus);
From aae1ce28a69d61df86692da6d15589fb6d7c4182 Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Tue, 24 Mar 2020 21:04:21 -0400
Subject: [PATCH 09/15] Add specific AVAPS PRS1 modes and improve F3V3/F3V6
settings parsers.
In particular F3V3 won't always create IPAPmin/max settings when in
CPAP mode or with a single IPAP setting.
Also consider ventilators to have a flex mode of "Rise Time" when
they have a rise time configured.
---
oscar/SleepLib/loader_plugins/prs1_loader.cpp | 196 +++++++++++-------
1 file changed, 125 insertions(+), 71 deletions(-)
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index 74654274..118fcc30 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -218,7 +218,7 @@ void PRS1Loader::LogUnexpectedMessage(const QString & message)
}
-enum FlexMode { FLEX_None, FLEX_CFlex, FLEX_CFlexPlus, FLEX_AFlex, FLEX_RiseTime, FLEX_BiFlex, FLEX_AVAPS, FLEX_PFlex, FLEX_Unknown };
+enum FlexMode { FLEX_None, FLEX_CFlex, FLEX_CFlexPlus, FLEX_AFlex, FLEX_RiseTime, FLEX_BiFlex, FLEX_PFlex, FLEX_Unknown };
enum BackupBreathMode { PRS1Backup_Off, PRS1Backup_Auto, PRS1Backup_Fixed };
@@ -1577,6 +1577,8 @@ enum PRS1Mode {
PRS1_MODE_S, // "S"
PRS1_MODE_ST, // "S/T"
PRS1_MODE_PC, // "PC"
+ PRS1_MODE_ST_AVAPS, // "S/T - AVAPS"
+ PRS1_MODE_PC_AVAPS, // "PC - AVAPS"
};
// Returns the set of all channels ever reported/supported by the parser for the given chunk.
@@ -1793,6 +1795,8 @@ static QString parsedModeName(int m)
ENUMSTRING(PRS1_MODE_S);
ENUMSTRING(PRS1_MODE_ST);
ENUMSTRING(PRS1_MODE_PC);
+ ENUMSTRING(PRS1_MODE_ST_AVAPS);
+ ENUMSTRING(PRS1_MODE_PC_AVAPS);
default:
s = hex(m);
qDebug() << "Unknown PRS1Mode:" << qPrintable(s);
@@ -4171,6 +4175,8 @@ CPAPMode PRS1Import::importMode(int prs1mode)
case PRS1_MODE_S: mode = MODE_BILEVEL_FIXED; break; // TODO
case PRS1_MODE_ST: mode = MODE_BILEVEL_FIXED; break; // TODO, pressure seems variable
case PRS1_MODE_PC: mode = MODE_AVAPS; break; // TODO, maybe only PC - AVAPS mode
+ case PRS1_MODE_ST_AVAPS: mode = MODE_AVAPS; break; // TODO, maybe only PC - AVAPS mode
+ case PRS1_MODE_PC_AVAPS: mode = MODE_AVAPS; break; // TODO, maybe only PC - AVAPS mode
default:
UNEXPECTED_VALUE(prs1mode, "known PRS1 mode");
break;
@@ -5162,13 +5168,16 @@ bool PRS1DataChunk::ParseSettingsF3V3(const unsigned char* data, int /*size*/)
UNEXPECTED_VALUE(data[2], "known device mode");
break;
}
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_CPAP_MODE, (int) cpapmode));
switch (data[3]) {
case 0: // 0 = None
- flexmode = FLEX_None;
- if (cpapmode != PRS1_MODE_CPAP) {
- CHECK_VALUES(cpapmode, PRS1_MODE_S, PRS1_MODE_ST);
+ switch (cpapmode) {
+ case PRS1_MODE_CPAP: flexmode = FLEX_None; break;
+ case PRS1_MODE_S: flexmode = FLEX_RiseTime; break; // reports say "None" but then list a rise time setting
+ case PRS1_MODE_ST: flexmode = FLEX_RiseTime; break; // reports say "None" but then list a rise time setting
+ default:
+ UNEXPECTED_VALUE(cpapmode, "CPAP, S, or S/T");
+ break;
}
break;
case 1: // 1 = Bi-Flex, only seen with "S - Bi-Flex"
@@ -5176,88 +5185,117 @@ bool PRS1DataChunk::ParseSettingsF3V3(const unsigned char* data, int /*size*/)
CHECK_VALUE(cpapmode, PRS1_MODE_S);
break;
case 2: // 2 = AVAPS: usually "PC - AVAPS", sometimes "S/T - AVAPS"
- flexmode = FLEX_AVAPS;
- CHECK_VALUES(cpapmode, PRS1_MODE_ST, PRS1_MODE_PC);
+ switch (cpapmode) {
+ case PRS1_MODE_ST: cpapmode = PRS1_MODE_ST_AVAPS; break;
+ case PRS1_MODE_PC: cpapmode = PRS1_MODE_PC_AVAPS; break;
+ default:
+ UNEXPECTED_VALUE(cpapmode, "S/T or PC");
+ break;
+ }
+ flexmode = FLEX_RiseTime; // reports say "AVAPS" but then list a rise time setting
break;
default:
UNEXPECTED_VALUE(data[3], "known flex mode");
break;
}
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_CPAP_MODE, (int) cpapmode));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_MODE, (int) flexmode));
- // TODO: add based on mode
int epap = data[4] + (data[5] << 8); // 0x82 = EPAP 13 cmH2O; 0x78 = EPAP 12 cmH2O; 0x50 = EPAP 8 cmH2O
int min_ipap = data[6] + (data[7] << 8); // 0xA0 = IPAP 16 cmH2O; 0xBE = 19 cmH2O min IPAP (in AVAPS); 0x78 = IPAP 12 cmH2O
int max_ipap = data[8] + (data[9] << 8); // 0xAA = ???; 0x12C = 30 cmH2O max IPAP (in AVAPS); 0x78 = ???
- this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_EPAP, epap));
- this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MIN, min_ipap));
- this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MAX, max_ipap));
- // TODO: calculate PS or min/max PS? Create IPAP event when not AVAPS?
-
- if (flexmode == FLEX_None || flexmode == FLEX_AVAPS) {
- int rise_time = data[0xa]; // 1 = Rise Time Setting 1, 2 = Rise Time Setting 2, 3 = Rise Time Setting 3
- if (cpapmode == PRS1_MODE_CPAP) {
- CHECK_VALUE(rise_time, 0);
- } else {
- if (rise_time < 1 || rise_time > 6) UNEXPECTED_VALUE(rise_time, "1-6"); // TODO: what is 0?
- CHECK_VALUES(data[0xb], 0, 1); // 1 = Rise Time Lock (in "None" and AVAPS flex mode)
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RISE_TIME, rise_time));
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RISE_TIME_LOCK, data[0xb] == 1));
- }
- } else {
- CHECK_VALUES(data[0xa], 0, 3); // TODO: May also be Bi-Flex 3? But how is this different from [0xc] below?
- CHECK_VALUE(data[0xb], 0);
+ switch (cpapmode) {
+ case PRS1_MODE_CPAP:
+ this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE, epap));
+ CHECK_VALUE(min_ipap, 0);
+ CHECK_VALUE(max_ipap, 0);
+ break;
+ case PRS1_MODE_S:
+ case PRS1_MODE_ST:
+ this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_EPAP, epap));
+ this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP, min_ipap));
+ this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PS, min_ipap - epap));
+ //CHECK_VALUES(max_ipap, 170, 300);
+ break;
+ case PRS1_MODE_ST_AVAPS:
+ case PRS1_MODE_PC_AVAPS:
+ this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_EPAP, epap));
+ this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MIN, min_ipap));
+ this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MAX, max_ipap));
+ this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PS_MIN, min_ipap - epap));
+ this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PS_MAX, max_ipap - epap));
+ break;
+ default:
+ UNEXPECTED_VALUE(cpapmode, "expected mode");
+ break;
}
- if (flexmode == FLEX_BiFlex) {
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LEVEL, data[0xc])); // 3 = Bi-Flex 3 (in bi-flex mode)
+
+ if (cpapmode == PRS1_MODE_CPAP) {
+ CHECK_VALUE(flexmode, FLEX_None);
+ CHECK_VALUE(data[0xa], 0);
+ CHECK_VALUE(data[0xb], 0);
+ CHECK_VALUE(data[0xc], 0);
+ }
+ if (flexmode == FLEX_RiseTime) {
+ int rise_time = data[0xa]; // 1 = Rise Time Setting 1, 2 = Rise Time Setting 2, 3 = Rise Time Setting 3
+ if (rise_time < 1 || rise_time > 6) UNEXPECTED_VALUE(rise_time, "1-6"); // TODO: what is 0?
+ CHECK_VALUES(data[0xb], 0, 1); // 1 = Rise Time Lock (in "None" and AVAPS flex mode)
+ CHECK_VALUE(data[0xc], 0);
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RISE_TIME, rise_time));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RISE_TIME_LOCK, data[0xb] == 1));
+ } else if (flexmode == FLEX_BiFlex) {
+ CHECK_VALUE(data[0xa], 3); // TODO: May also be Bi-Flex 3? But how is this different from [0xc] below?
+ CHECK_VALUE(data[0xb], 0);
CHECK_VALUE(data[0xc], 3);
CHECK_VALUE(data[0x0a], data[0xc]);
- } else {
- CHECK_VALUE(data[0xc], 0);
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LEVEL, data[0xc])); // 3 = Bi-Flex 3 (in bi-flex mode)
}
CHECK_VALUE(data[0xd], 0);
- if (flexmode != FLEX_AVAPS && cpapmode != PRS1_MODE_CPAP) CHECK_VALUE(data[0xe], 0x14); // 0x14 = ??? in S and non-AVAPS S/T and PC
- if (cpapmode == PRS1_MODE_CPAP) CHECK_VALUE(data[0xe], 0);
- if (flexmode == FLEX_AVAPS) {
+ if (flexmode == FLEX_None) CHECK_VALUE(data[0xe], 0);
+ if (cpapmode == PRS1_MODE_ST_AVAPS || cpapmode == PRS1_MODE_PC_AVAPS) {
if (data[0xe] < 24 || data[0xe] > 65) UNEXPECTED_VALUE(data[0xe], "24-65");
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TIDAL_VOLUME, data[0xe] * 10.0));
+ } else if (flexmode == FLEX_BiFlex || flexmode == FLEX_RiseTime) {
+ CHECK_VALUE(data[0xe], 0x14); // 0x14 = ???
}
+
int breath_rate = data[0xf];
int timed_inspiration = data[0x10];
+ bool backup = false;
switch (cpapmode) {
case PRS1_MODE_CPAP:
case PRS1_MODE_S:
CHECK_VALUE(breath_rate, 0);
CHECK_VALUE(timed_inspiration, 0);
break;
- case PRS1_MODE_PC:
- CHECK_VALUE(flexmode, FLEX_AVAPS);
+ case PRS1_MODE_PC_AVAPS:
CHECK_VALUE(breath_rate, 0); // only ever seen 0 on reports so far
CHECK_VALUE(timed_inspiration, 30);
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate));
- this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
+ backup = true;
+ break;
+ case PRS1_MODE_ST_AVAPS:
+ if (breath_rate) { // can be 0 on reports
+ CHECK_VALUES(breath_rate, 9, 10);
+ }
+ if (timed_inspiration < 10 || timed_inspiration > 30) UNEXPECTED_VALUE(timed_inspiration, "10-30");
+ backup = true;
break;
case PRS1_MODE_ST:
- if (flexmode == FLEX_AVAPS) {
- if (breath_rate) { // can be 0 on reports
- CHECK_VALUES(breath_rate, 9, 10);
- }
- if (timed_inspiration < 10 || timed_inspiration > 30) UNEXPECTED_VALUE(timed_inspiration, "10-30");
- } else if (flexmode == FLEX_None){
- CHECK_VALUES(breath_rate, 0x0C, 0x0A); // 0xC = Breath Rate 12, 0xA = Breath Rate 10, can this be 0?
- CHECK_VALUES(timed_inspiration, 10, 20); // 0xA = Timed Inspiration 1, 0x14 = Time Inspiration 2
- }
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate));
- this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
+ CHECK_VALUES(breath_rate, 0x0C, 0x0A); // 0xC = Breath Rate 12, 0xA = Breath Rate 10, can this be 0?
+ CHECK_VALUES(timed_inspiration, 10, 20); // 0xA = Timed Inspiration 1, 0x14 = Time Inspiration 2
+ backup = true;
break;
default:
UNEXPECTED_VALUE(cpapmode, "CPAP, S, S/T, or PC");
break;
}
+ if (backup) {
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Fixed));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_RATE, breath_rate));
+ this->AddEvent(new PRS1ScaledSettingEvent(PRS1_SETTING_BACKUP_TIMED_INSPIRATION, timed_inspiration, 0.1));
+ }
CHECK_VALUE(data[0x11], 0);
@@ -5625,15 +5663,18 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
UNEXPECTED_VALUE(data[pos], "known device mode");
break;
}
- this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_CPAP_MODE, (int) cpapmode));
break;
case 1: // Flex Mode
CHECK_VALUE(len, 1);
switch (data[pos]) {
case 0: // 0 = None
- flexmode = FLEX_None;
- if (cpapmode != PRS1_MODE_CPAP) {
- CHECK_VALUES(cpapmode, PRS1_MODE_S, PRS1_MODE_ST);
+ switch (cpapmode) {
+ case PRS1_MODE_CPAP: flexmode = FLEX_None; break;
+ case PRS1_MODE_S: flexmode = FLEX_RiseTime; break; // reports say "None" but then list a rise time setting
+ case PRS1_MODE_ST: flexmode = FLEX_RiseTime; break; // reports say "None" but then list a rise time setting
+ default:
+ UNEXPECTED_VALUE(cpapmode, "CPAP, S, or S/T");
+ break;
}
break;
case 1: // 1 = Bi-Flex, only seen with "S - Bi-Flex"
@@ -5641,13 +5682,20 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
CHECK_VALUE(cpapmode, PRS1_MODE_S);
break;
case 2: // 2 = AVAPS: usually "PC - AVAPS", sometimes "S/T - AVAPS"
- flexmode = FLEX_AVAPS;
- CHECK_VALUES(cpapmode, PRS1_MODE_ST, PRS1_MODE_PC);
+ switch (cpapmode) {
+ case PRS1_MODE_ST: cpapmode = PRS1_MODE_ST_AVAPS; break;
+ case PRS1_MODE_PC: cpapmode = PRS1_MODE_PC_AVAPS; break;
+ default:
+ UNEXPECTED_VALUE(cpapmode, "S/T or PC");
+ break;
+ }
+ flexmode = FLEX_RiseTime; // reports say "AVAPS" but then list a rise time setting
break;
default:
UNEXPECTED_VALUE(data[pos], "known flex mode");
break;
}
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_CPAP_MODE, (int) cpapmode));
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_MODE, (int) flexmode));
break;
case 2: // ??? Maybe AAM?
@@ -5656,17 +5704,20 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
break;
case 3: // CPAP Pressure
CHECK_VALUE(len, 1);
+ CHECK_VALUE(cpapmode, PRS1_MODE_CPAP);
fixed_pressure = data[pos];
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE, fixed_pressure, GAIN));
break;
case 4: // EPAP Pressure
CHECK_VALUE(len, 1);
+ if (cpapmode == PRS1_MODE_CPAP) UNEXPECTED_VALUE(cpapmode, "!cpap");
// pressures seem variable on practice, maybe due to ramp or leaks?
fixed_epap = data[pos];
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_EPAP, fixed_epap, GAIN));
break;
case 7: // IPAP Pressure
CHECK_VALUE(len, 1);
+ CHECK_VALUES(cpapmode, PRS1_MODE_S, PRS1_MODE_ST);
// pressures seem variable on practice, maybe due to ramp or leaks?
fixed_ipap = data[pos];
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP, fixed_ipap, GAIN));
@@ -5677,6 +5728,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
case 8: // Min IPAP
CHECK_VALUE(len, 1);
CHECK_VALUE(fixed_ipap, 0);
+ CHECK_VALUES(cpapmode, PRS1_MODE_ST_AVAPS, PRS1_MODE_PC_AVAPS);
min_ipap = data[pos];
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MIN, min_ipap, GAIN));
// TODO: We need to revisit whether PS should be shown as a setting.
@@ -5686,6 +5738,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
case 9: // Max IPAP
CHECK_VALUE(len, 1);
CHECK_VALUE(fixed_ipap, 0);
+ CHECK_VALUES(cpapmode, PRS1_MODE_ST_AVAPS, PRS1_MODE_PC_AVAPS);
if (min_ipap == 0) UNEXPECTED_VALUE(min_ipap, ">0");
max_ipap = data[pos];
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MAX, max_ipap, GAIN));
@@ -5695,14 +5748,13 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
break;
case 0x19: // Tidal Volume (AVAPS)
CHECK_VALUE(len, 1);
- CHECK_VALUES(cpapmode, PRS1_MODE_ST, PRS1_MODE_PC);
- CHECK_VALUE(flexmode, FLEX_AVAPS);
+ CHECK_VALUES(cpapmode, PRS1_MODE_ST_AVAPS, PRS1_MODE_PC_AVAPS);
//CHECK_VALUE(data[pos], 47); // gain 10.0
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TIDAL_VOLUME, data[pos] * 10.0));
break;
case 0x1e: // (Backup) Breath Rate (S/T and PC)
CHECK_VALUE(len, 3);
- CHECK_VALUES(cpapmode, PRS1_MODE_ST, PRS1_MODE_PC);
+ if (cpapmode == PRS1_MODE_CPAP || cpapmode == PRS1_MODE_S) UNEXPECTED_VALUE(cpapmode, "S/T or PC");
switch (data[pos]) {
case 0: // Breath Rate Off
// TODO: Is this mode essentially bilevel? The pressure graphs are confusing.
@@ -5745,7 +5797,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
if (flexmode == FLEX_BiFlex) {
// Bi-Flex level
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LEVEL, data[pos]));
- } else if (flexmode == FLEX_None || flexmode == FLEX_AVAPS) {
+ } else if (flexmode == FLEX_RiseTime) {
// Rise time
if (data[pos] < 1 || data[pos] > 6) UNEXPECTED_VALUE(data[pos], "1-6"); // 1-6 have been seen
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RISE_TIME, data[pos]));
@@ -8673,16 +8725,18 @@ void PRS1Loader::initChannels()
QObject::tr("PAP Mode"),
QObject::tr("Mode"),
"", LOOKUP, Qt::green));
- chan->addOption(0, QObject::tr("CPAP-Check"));
- chan->addOption(1, QObject::tr("CPAP"));
- chan->addOption(2, QObject::tr("AutoCPAP"));
- chan->addOption(3, QObject::tr("Auto-Trial"));
- chan->addOption(4, QObject::tr("Bi-Level"));
- chan->addOption(5, QObject::tr("AutoBiLevel"));
- chan->addOption(6, QObject::tr("ASV"));
- chan->addOption(7, QObject::tr("S"));
- chan->addOption(8, QObject::tr("S/T"));
- chan->addOption(9, QObject::tr("PC"));
+ chan->addOption(PRS1_MODE_CPAPCHECK, QObject::tr("CPAP-Check"));
+ chan->addOption(PRS1_MODE_CPAP, QObject::tr("CPAP"));
+ chan->addOption(PRS1_MODE_AUTOCPAP, QObject::tr("AutoCPAP"));
+ chan->addOption(PRS1_MODE_AUTOTRIAL, QObject::tr("Auto-Trial"));
+ chan->addOption(PRS1_MODE_BILEVEL, QObject::tr("Bi-Level"));
+ chan->addOption(PRS1_MODE_AUTOBILEVEL, QObject::tr("AutoBiLevel"));
+ chan->addOption(PRS1_MODE_ASV, QObject::tr("ASV"));
+ chan->addOption(PRS1_MODE_S, QObject::tr("S"));
+ chan->addOption(PRS1_MODE_ST, QObject::tr("S/T"));
+ chan->addOption(PRS1_MODE_PC, QObject::tr("PC"));
+ chan->addOption(PRS1_MODE_ST_AVAPS, QObject::tr("S/T - AVAPS"));
+ chan->addOption(PRS1_MODE_PC_AVAPS, QObject::tr("PC - AVAPS"));
channel.add(GRP_CPAP, chan = new Channel(PRS1_FlexMode = 0xe105, SETTING, MT_CPAP, SESSION,
"PRS1FlexMode", QObject::tr("Flex Mode"),
@@ -8696,7 +8750,7 @@ void PRS1Loader::initChannels()
chan->addOption(FLEX_PFlex, QObject::tr("P-Flex"));
chan->addOption(FLEX_RiseTime, QObject::tr("Rise Time"));
chan->addOption(FLEX_BiFlex, QObject::tr("Bi-Flex"));
- chan->addOption(FLEX_AVAPS, QObject::tr("AVAPS"));
+ //chan->addOption(FLEX_AVAPS, QObject::tr("AVAPS")); // Converted into AVAPS PRS1_Mode with FLEX_RiseTime
channel.add(GRP_CPAP, chan = new Channel(PRS1_FlexLevel = 0xe106, SETTING, MT_CPAP, SESSION,
"PRS1FlexSet",
From 6b581bc3038e64fbff0b2c9a8240708335706d81 Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Tue, 24 Mar 2020 21:33:50 -0400
Subject: [PATCH 10/15] Fix AVAPS settings display.
---
oscar/SleepLib/day.cpp | 5 +++--
oscar/SleepLib/loader_plugins/prs1_loader.cpp | 12 +++++-------
oscar/welcome.cpp | 2 +-
3 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/oscar/SleepLib/day.cpp b/oscar/SleepLib/day.cpp
index 659da7b4..fcc90d9f 100644
--- a/oscar/SleepLib/day.cpp
+++ b/oscar/SleepLib/day.cpp
@@ -1534,9 +1534,10 @@ QString Day::getPressureSettings()
arg(validPressure(settings_min(CPAP_PSMax))).
arg(units);
} else if (mode == MODE_AVAPS) {
- return QObject::tr("EPAP %1 IPAP %2 (%3)").
+ return QObject::tr("EPAP %1 IPAP %2-%3 (%4)").
arg(validPressure(settings_min(CPAP_EPAP))).
- arg(validPressure(settings_max(CPAP_IPAP))).
+ arg(validPressure(settings_max(CPAP_IPAPLo))).
+ arg(validPressure(settings_max(CPAP_IPAPHi))).
arg(units);
}
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index 118fcc30..9dfcbc5b 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -4172,17 +4172,15 @@ CPAPMode PRS1Import::importMode(int prs1mode)
case PRS1_MODE_BILEVEL: mode = MODE_BILEVEL_FIXED; break;
case PRS1_MODE_AUTOBILEVEL: mode = MODE_BILEVEL_AUTO_VARIABLE_PS; break;
case PRS1_MODE_ASV: mode = MODE_ASV_VARIABLE_EPAP; break;
- case PRS1_MODE_S: mode = MODE_BILEVEL_FIXED; break; // TODO
- case PRS1_MODE_ST: mode = MODE_BILEVEL_FIXED; break; // TODO, pressure seems variable
- case PRS1_MODE_PC: mode = MODE_AVAPS; break; // TODO, maybe only PC - AVAPS mode
- case PRS1_MODE_ST_AVAPS: mode = MODE_AVAPS; break; // TODO, maybe only PC - AVAPS mode
- case PRS1_MODE_PC_AVAPS: mode = MODE_AVAPS; break; // TODO, maybe only PC - AVAPS mode
+ case PRS1_MODE_S: mode = MODE_BILEVEL_FIXED; break;
+ case PRS1_MODE_ST: mode = MODE_BILEVEL_FIXED; break;
+ case PRS1_MODE_PC: mode = MODE_BILEVEL_FIXED; break;
+ case PRS1_MODE_ST_AVAPS: mode = MODE_AVAPS; break;
+ case PRS1_MODE_PC_AVAPS: mode = MODE_AVAPS; break;
default:
UNEXPECTED_VALUE(prs1mode, "known PRS1 mode");
break;
}
- // TODO: fixed vs. variable PS seems to be independent from ventilator mode, for example
- // S/T can be fixed (single IPAP pressure) or variable (IPAP min/max).
return mode;
}
diff --git a/oscar/welcome.cpp b/oscar/welcome.cpp
index c263eb33..d1559132 100644
--- a/oscar/welcome.cpp
+++ b/oscar/welcome.cpp
@@ -244,7 +244,7 @@ QString Welcome::GenerateCPAPHTML()
EventDataType ipap = day->percentile(pressChanID, perc/100.0);
EventDataType epap = day->percentile(CPAP_EPAP, perc/100.0);
html += tr("Your machine was under %1-%2 %3 for %4% of the time.").arg(epap).arg(ipap).arg(schema::channel[pressChanID].units()).arg(perc);
- } else if (cpapmode == MODE_ASV){
+ } else if (cpapmode == MODE_ASV || cpapmode == MODE_AVAPS){
EventDataType ipap = day->percentile(pressChanID, perc/100.0);
EventDataType epap = qRound(day->settings_wavg(CPAP_EPAP));
From c271a64625f4961f7cd023a1d5132ff2df79aacf Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Wed, 25 Mar 2020 17:02:32 -0400
Subject: [PATCH 11/15] Improve import of F5V012 flex and rise time settings.
---
oscar/SleepLib/loader_plugins/prs1_loader.cpp | 38 ++++++++++---------
1 file changed, 20 insertions(+), 18 deletions(-)
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index 9dfcbc5b..1b91dac5 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -6264,7 +6264,7 @@ void PRS1DataChunk::ParseFlexSettingF0V234(quint8 flex, int cpapmode)
void PRS1DataChunk::ParseFlexSettingF5V012(quint8 flex, int cpapmode)
{
FlexMode flexmode = FLEX_Unknown;
- bool unknown = (flex & 0x80) != 0;
+ bool valid = (flex & 0x80) != 0;
bool lock = (flex & 0x40) != 0;
bool risetime = (flex & 0x08) != 0;
int flexlevel = flex & 0x03;
@@ -6272,34 +6272,36 @@ void PRS1DataChunk::ParseFlexSettingF5V012(quint8 flex, int cpapmode)
if (flex & (0x20 | 0x10 | 0x04)) UNEXPECTED_VALUE(flex, "known bits");
CHECK_VALUE(cpapmode, PRS1_MODE_ASV);
if (this->familyVersion == 0) {
- CHECK_VALUE(unknown, true);
+ CHECK_VALUE(valid, true);
CHECK_VALUE(lock, false);
CHECK_VALUE(risetime, false);
- if (flexlevel == 0) UNEXPECTED_VALUE(flexlevel, "1-3");
} else if (this->familyVersion == 1) {
- if (unknown == false) {
+ if (valid == false) {
CHECK_VALUE(flex, 0x08);
- flexlevel = 2; // TODO: Why do reports say Rise Time 2 for this value?
+ flexlevel = 2; // These get reported as Rise Time 2
+ valid = true;
}
- if (lock) CHECK_VALUE(risetime, true); // so far we've only seen rise time lock, but this could mean bi-flex lock as well
- if (flexlevel == 0 && unknown) UNEXPECTED_VALUE(flexlevel, "1-3");
} else {
- CHECK_VALUE(flex, 0x02); // only seen one example, unsure if it matches F5V01
+ CHECK_VALUE(flex, 0x02); // only seen one example, unsure if it matches F5V01; seems to encode Bi-Flex 2
+ valid = true; // add the flex mode and setting to the parsed settings
}
+ if (flexlevel == 0 || flexlevel >3) UNEXPECTED_VALUE(flexlevel, "1-3");
- // We're only confident of values where the high bit is set
- if (unknown) {
- if (risetime) {
- flexmode = FLEX_RiseTime;
- } else {
- flexmode = FLEX_BiFlex;
- }
+ CHECK_VALUE(valid, true);
+ if (risetime) {
+ flexmode = FLEX_RiseTime;
+ } else {
+ flexmode = FLEX_BiFlex;
}
-
- // TODO: rise or bi-flex lock once we're confident about it
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_MODE, (int) flexmode));
- if (flexmode != FLEX_Unknown) {
+
+ if (flexmode == FLEX_BiFlex) {
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LEVEL, flexlevel));
+ CHECK_VALUE(lock, 0); // Flag any sample data that will let us confirm flex lock
+ //this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LOCK, lock != 0));
+ } else {
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RISE_TIME, flexlevel));
+ this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RISE_TIME_LOCK, lock != 0));
}
}
From 3749a73fd17f19b6e9fa1c6990b4e25c93d47d49 Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Wed, 25 Mar 2020 19:17:39 -0400
Subject: [PATCH 12/15] Update channel LOOKUP data type to show unspecified
options as integers.
---
oscar/daily.cpp | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/oscar/daily.cpp b/oscar/daily.cpp
index b93d9112..e57b6444 100644
--- a/oscar/daily.cpp
+++ b/oscar/daily.cpp
@@ -1128,7 +1128,11 @@ QString Daily::getMachineSettings(Day * day) {
QString data;
if (chan.datatype() == schema::LOOKUP) {
- data = chan.option(it.value().toInt());
+ int value = it.value().toInt();
+ data = chan.option(value);
+ if (data.isEmpty()) {
+ data = QString().number(value) + " " + chan.units();;
+ }
} else if (chan.datatype() == schema::BOOL) {
data = (it.value().toBool() ? STR_TR_Yes : STR_TR_No);
} else if (chan.datatype() == schema::DOUBLE) {
From f49ba51cb70f79372f29009faedd9454388e74e5 Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Wed, 25 Mar 2020 21:14:25 -0400
Subject: [PATCH 13/15] Improve PRS1 CPAP-Check and Auto-Trial setting import.
Also clean up PRS1 channels given the LOOKUP behavior, and clean
up ImportSummary now that native PRS1 modes are supported.
---
oscar/SleepLib/loader_plugins/prs1_loader.cpp | 74 +++++++------------
1 file changed, 28 insertions(+), 46 deletions(-)
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index 1b91dac5..53171e7b 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -6734,10 +6734,8 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
// The time of change is most likely in the events file. See slice 6 for ending pressure.
//CHECK_VALUE(pressure, 0x5a);
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE, pressure));
- // TODO: Once OSCAR can handle more modes, we can include these settings; right now including
- // these settings makes it think this is AutoCPAP.
- //this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE_MIN, min_pressure));
- //this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE_MAX, max_pressure));
+ this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE_MIN, min_pressure));
+ this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE_MAX, max_pressure));
break;
case 0x0d: // AutoCPAP pressure setting
CHECK_VALUE(len, 2);
@@ -6770,12 +6768,16 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PS_MAX, imax_ps));
break;
case 0x10: // Auto-Trial mode
- // TODO: F0V4 considers this a separate mode from CPAP or CPAPCHECK, should F0V6 as well?
- // TODO: Check how auto-trial sessions are labeled in F0V6 reports.
+ // This is not encoded as a separate mode as in F0V4, but instead as an auto-trial
+ // duration on top of the CPAP or CPAP-Check mode. Reports show Auto-CPAP results,
+ // but curiously report the use of C-Flex+, even though Auto-CPAP uses A-Flex.
CHECK_VALUE(len, 3);
- CHECK_VALUES(cpapmode, PRS1_MODE_CPAP, PRS1_MODE_CPAPCHECK); // TODO: What's the difference between auto-trial and CPAP-Check?
+ CHECK_VALUES(cpapmode, PRS1_MODE_CPAP, PRS1_MODE_CPAPCHECK);
CHECK_VALUES(data[pos], 30, 5); // Auto-Trial Duration
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_AUTO_TRIAL, data[pos]));
+ // If we want C-Flex+ to be reported as A-Flex, we can set cpapmode = PRS1_MODE_AUTOTRIAL here.
+ // (Note that the setting event has already been added above, which is why ImportSummary needs
+ // to adjust it when it sees this setting.)
min_pressure = data[pos+1];
max_pressure = data[pos+2];
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PRESSURE_MIN, min_pressure));
@@ -7517,6 +7519,7 @@ bool PRS1Import::ImportSummary()
qint64 start = qint64(summary->timestamp) * 1000L;
session->set_first(start);
+ // TODO: The below max pressures aren't right for the 30 cmH2O models.
session->setPhysMax(CPAP_LeakTotal, 120);
session->setPhysMin(CPAP_LeakTotal, 0);
session->setPhysMax(CPAP_Pressure, 25);
@@ -7531,6 +7534,7 @@ bool PRS1Import::ImportSummary()
bool ok;
ok = summary->ParseSummary();
+ PRS1Mode nativemode = PRS1_MODE_UNKNOWN;
CPAPMode cpapmode = MODE_UNKNOWN;
for (int i=0; i < summary->m_parsedData.count(); i++) {
PRS1ParsedEvent* e = summary->m_parsedData.at(i);
@@ -7544,7 +7548,7 @@ bool PRS1Import::ImportSummary()
PRS1ParsedSettingEvent* s = (PRS1ParsedSettingEvent*) e;
switch (s->m_setting) {
case PRS1_SETTING_CPAP_MODE:
- session->settings[PRS1_Mode] = (PRS1Mode) e->m_value;
+ nativemode = (PRS1Mode) e->m_value;
cpapmode = importMode(e->m_value);
break;
case PRS1_SETTING_PRESSURE:
@@ -7552,11 +7556,6 @@ bool PRS1Import::ImportSummary()
break;
case PRS1_SETTING_PRESSURE_MIN:
session->settings[CPAP_PressureMin] = e->value();
- if (cpapmode == MODE_CPAP) { // Auto-Trial is reported as CPAP but with a minimum and maximum pressure,
- cpapmode = MODE_APAP; // so import it as APAP, since that's what it's really doing.
- }
- // CPAP-Check no longer reports min/max pressures, since it should be treated as CPAP.
- // (It only adjusts the CPAP pressure by 1 cmH2O every 30 hours at most.)
break;
case PRS1_SETTING_PRESSURE_MAX:
session->settings[CPAP_PressureMax] = e->value();
@@ -7578,9 +7577,6 @@ bool PRS1Import::ImportSummary()
break;
case PRS1_SETTING_IPAP_MIN:
session->settings[CPAP_IPAPLo] = e->value();
- if (cpapmode == MODE_BILEVEL_FIXED) {
- cpapmode = MODE_BILEVEL_AUTO_VARIABLE_PS; // TODO: this isn't quite right, on ventilators it's actually fixed EPAP with variable PS
- }
break;
case PRS1_SETTING_IPAP_MAX:
session->settings[CPAP_IPAPHi] = e->value();
@@ -7657,8 +7653,10 @@ bool PRS1Import::ImportSummary()
case PRS1_SETTING_TIDAL_VOLUME:
session->settings[CPAP_TidalVolume] = e->m_value;
break;
- case PRS1_SETTING_AUTO_TRIAL:
+ case PRS1_SETTING_AUTO_TRIAL: // new to F0V6
session->settings[PRS1_AutoTrial] = e->m_value;
+ nativemode = PRS1_MODE_AUTOTRIAL; // Note: F0V6 reports show the underlying CPAP mode rather than Auto-Trial.
+ cpapmode = importMode(nativemode);
break;
case PRS1_SETTING_EZ_START:
session->settings[PRS1_EZStart] = (bool) e->m_value;
@@ -7684,8 +7682,14 @@ bool PRS1Import::ImportSummary()
if (!ok) {
return false;
}
- session->settings[CPAP_Mode] = cpapmode;
+ if (summary->m_parsedData.count() > 0) {
+ if (nativemode == PRS1_MODE_UNKNOWN) UNEXPECTED_VALUE(nativemode, "known mode");
+ if (cpapmode == MODE_UNKNOWN) UNEXPECTED_VALUE(cpapmode, "known mode");
+ session->settings[PRS1_Mode] = nativemode;
+ session->settings[CPAP_Mode] = cpapmode;
+ }
+
if (summary->duration == 0) {
// This does occasionally happen and merely indicates a brief session with no useful data.
// This requires the use of really_set_last below, which otherwise rejects 0 length.
@@ -8758,13 +8762,7 @@ void PRS1Loader::initChannels()
QObject::tr("PRS1 pressure relief setting."),
QObject::tr("Flex Level"),
"", LOOKUP, Qt::blue));
-
chan->addOption(0, STR_TR_Off);
- chan->addOption(1, QObject::tr("1"));
- chan->addOption(2, QObject::tr("2"));
- chan->addOption(3, QObject::tr("3"));
- chan->addOption(4, QObject::tr("4"));
- chan->addOption(5, QObject::tr("5"));
channel.add(GRP_CPAP, chan = new Channel(PRS1_FlexLock = 0xe111, SETTING, MT_CPAP, SESSION,
"PRS1FlexLock",
@@ -8780,7 +8778,7 @@ void PRS1Loader::initChannels()
QObject::tr("Rise Time"),
QObject::tr("Amount of time it takes to transition from EPAP to IPAP, the higher the number the slower the transition"),
QObject::tr("Rise Time"),
- "", DEFAULT, Qt::blue));
+ "", LOOKUP, Qt::blue));
channel.add(GRP_CPAP, chan = new Channel(PRS1_RiseTimeLock = 0xe11a, SETTING, MT_CPAP, SESSION,
"PRS1RiseTimeLock",
@@ -8817,11 +8815,6 @@ void PRS1Loader::initChannels()
QObject::tr("Tube Temp."),
"", LOOKUP, Qt::red));
chan->addOption(0, STR_TR_Off);
- chan->addOption(1, QObject::tr("1"));
- chan->addOption(2, QObject::tr("2"));
- chan->addOption(3, QObject::tr("3"));
- chan->addOption(4, QObject::tr("4"));
- chan->addOption(5, QObject::tr("5"));
channel.add(GRP_CPAP, chan = new Channel(PRS1_HumidLevel = 0xe102, SETTING, MT_CPAP, SESSION,
"PRS1HumidLevel",
@@ -8830,11 +8823,6 @@ void PRS1Loader::initChannels()
QObject::tr("Humid. Lvl"),
"", LOOKUP, Qt::blue));
chan->addOption(0, STR_TR_Off);
- chan->addOption(1, QObject::tr("1"));
- chan->addOption(2, QObject::tr("2"));
- chan->addOption(3, QObject::tr("3"));
- chan->addOption(4, QObject::tr("4"));
- chan->addOption(5, QObject::tr("5"));
channel.add(GRP_CPAP, chan = new Channel(PRS1_MaskResistSet = 0xe104, SETTING, MT_CPAP, SESSION,
"MaskResistSet",
@@ -8843,11 +8831,6 @@ void PRS1Loader::initChannels()
QObject::tr("Mask Resist."),
"", LOOKUP, Qt::green));
chan->addOption(0, STR_TR_Off);
- chan->addOption(1, QObject::tr("1"));
- chan->addOption(2, QObject::tr("2"));
- chan->addOption(3, QObject::tr("3"));
- chan->addOption(4, QObject::tr("4"));
- chan->addOption(5, QObject::tr("5"));
channel.add(GRP_CPAP, chan = new Channel(PRS1_HoseDiam = 0xe107, SETTING, MT_CPAP, SESSION,
"PRS1HoseDiam",
@@ -8928,7 +8911,7 @@ void PRS1Loader::initChannels()
QObject::tr("The kind of backup breath rate in use: none (off), automatic, or fixed"),
QObject::tr("Breath Rate"),
"", LOOKUP, Qt::black));
- chan->addOption(0, QObject::tr("Off"));
+ chan->addOption(0, STR_TR_Off);
chan->addOption(1, QObject::tr("Auto"));
chan->addOption(2, QObject::tr("Fixed"));
@@ -8937,7 +8920,7 @@ void PRS1Loader::initChannels()
QObject::tr("Fixed Backup Breath BPM"),
QObject::tr("Minimum breaths per minute (BPM) below which a timed breath will be initiated"),
QObject::tr("Breath BPM"),
- STR_UNIT_BreathsPerMinute, DEFAULT, Qt::black));
+ STR_UNIT_BreathsPerMinute, LOOKUP, Qt::black));
channel.add(GRP_CPAP, chan = new Channel(PRS1_BackupBreathTi = 0xe116, SETTING, MT_CPAP, SESSION,
"PRS1BackupBreathTi",
@@ -8948,11 +8931,10 @@ void PRS1Loader::initChannels()
channel.add(GRP_CPAP, chan = new Channel(PRS1_AutoTrial = 0xe117, SETTING, MT_CPAP, SESSION,
"PRS1AutoTrial",
- QObject::tr("Auto-Trial"),
- QObject::tr("The number of days left in the Auto-CPAP trial period, after which the machine will revert to CPAP only"),
- QObject::tr("Auto-Trial"),
+ QObject::tr("Auto-Trial Duration"),
+ QObject::tr("The number of days in the Auto-CPAP trial period, after which the machine will revert to CPAP"),
+ QObject::tr("Auto-Trial Dur."),
"", LOOKUP, Qt::black));
- chan->addOption(0, STR_TR_Off);
channel.add(GRP_CPAP, chan = new Channel(PRS1_EZStart = 0xe118, SETTING, MT_CPAP, SESSION,
"PRS1EZStart",
From a96a6659876cc01f470b248ce835552c853f032a Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Thu, 26 Mar 2020 09:01:28 -0400
Subject: [PATCH 14/15] Rename PRS1_0E mystery channel to Variable Breathing
based on forum discussion, disable it by default.
Also make TB an on-demand channel.
---
oscar/SleepLib/loader_plugins/prs1_loader.cpp | 70 ++++++++-----------
oscar/SleepLib/machine_common.cpp | 2 +-
oscar/SleepLib/machine_common.h | 2 +-
oscar/tests/sessiontests.cpp | 3 +-
4 files changed, 35 insertions(+), 42 deletions(-)
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index 53171e7b..bedcda30 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -229,6 +229,7 @@ ChannelID PRS1_TimedBreath = 0, PRS1_HumidMode = 0, PRS1_TubeTemp = 0;
ChannelID PRS1_FlexLock = 0, PRS1_TubeLock = 0, PRS1_RampType = 0;
ChannelID PRS1_BackupBreathMode = 0, PRS1_BackupBreathRate = 0, PRS1_BackupBreathTi = 0;
ChannelID PRS1_AutoTrial = 0, PRS1_EZStart = 0, PRS1_RiseTime = 0, PRS1_RiseTimeLock = 0;
+ChannelID PRS1_VariableBreathing = 0; // TODO: UNCONFIRMED, but seems to match sample data
QString PRS1Loader::PresReliefLabel() { return QObject::tr(""); }
ChannelID PRS1Loader::PresReliefMode() { return PRS1_FlexMode; }
@@ -1127,7 +1128,7 @@ enum PRS1ParsedEventType
EV_PRS1_FL,
EV_PRS1_PB,
EV_PRS1_LL,
- EV_PRS1_UNK_DURATION, // unknown duration event, rename once we figure it out
+ EV_PRS1_VB, // UNCONFIRMED
EV_PRS1_HY,
EV_PRS1_OA_COUNT, // F3V3 only
EV_PRS1_CA_COUNT, // F3V3 only
@@ -1530,7 +1531,7 @@ PRS1_DURATION_EVENT(PRS1ClearAirwayEvent, EV_PRS1_CA);
PRS1_DURATION_EVENT(PRS1FlowLimitationEvent, EV_PRS1_FL);
PRS1_DURATION_EVENT(PRS1PeriodicBreathingEvent, EV_PRS1_PB);
PRS1_DURATION_EVENT(PRS1LargeLeakEvent, EV_PRS1_LL);
-PRS1_DURATION_EVENT(PRS1UnknownDurationEvent, EV_PRS1_UNK_DURATION);
+PRS1_DURATION_EVENT(PRS1VariableBreathingEvent, EV_PRS1_VB);
PRS1_DURATION_EVENT(PRS1HypopneaEvent, EV_PRS1_HY);
PRS1_VALUE_EVENT(PRS1TotalLeakEvent, EV_PRS1_TOTLEAK);
@@ -1589,7 +1590,7 @@ const QVector & GetSupportedEvents(const PRS1DataChunk* chu
// they're reported/supported by the parser.
static const QVector PRS1OnDemandChannels =
{
- //PRS1TimedBreathEvent::TYPE, // TODO: TB could be on-demand
+ PRS1TimedBreathEvent::TYPE,
PRS1PressurePulseEvent::TYPE,
// Pressure initialized on-demand for F0 due to the possibility of bilevel vs. single pressure.
@@ -1655,7 +1656,7 @@ static const QHash> PRS1ImportChannelMap
{ PRS1ApneaAlarmEvent::TYPE, { /* Not imported */ } },
{ PRS1SnoresAtPressureEvent::TYPE, { /* Not imported */ } },
{ PRS1AutoPressureSetEvent::TYPE, { /* Not imported */ } },
- { PRS1UnknownDurationEvent::TYPE, { &PRS1_0E } },
+ { PRS1VariableBreathingEvent::TYPE, { &PRS1_VariableBreathing } }, // UNCONFIRMED
{ PRS1HypopneaCount::TYPE, { &CPAP_Hypopnea } }, // F3V3 only, generates individual events on import
{ PRS1ObstructiveApneaCount::TYPE, { &CPAP_Obstructive } }, // F3V3 only, generates individual events on import
@@ -1682,7 +1683,7 @@ static QString parsedEventTypeName(PRS1ParsedEventType t)
ENUMSTRING(EV_PRS1_FL);
ENUMSTRING(EV_PRS1_PB);
ENUMSTRING(EV_PRS1_LL);
- ENUMSTRING(EV_PRS1_UNK_DURATION);
+ ENUMSTRING(EV_PRS1_VB);
ENUMSTRING(EV_PRS1_HY);
ENUMSTRING(EV_PRS1_OA_COUNT);
ENUMSTRING(EV_PRS1_CA_COUNT);
@@ -2897,15 +2898,15 @@ bool PRS1Import::ImportEventChunk(PRS1DataChunk* event)
continue;
}
- // Skip zero-length PB or LL (or unknown duration 0E) events
- if ((e->m_type == PRS1PeriodicBreathingEvent::TYPE || e->m_type == PRS1LargeLeakEvent::TYPE || e->m_type == PRS1UnknownDurationEvent::TYPE) &&
+ // Skip zero-length PB or LL or VB events
+ if ((e->m_type == PRS1PeriodicBreathingEvent::TYPE || e->m_type == PRS1LargeLeakEvent::TYPE || e->m_type == PRS1VariableBreathingEvent::TYPE) &&
(e->m_duration == 0)) {
// LL occasionally appear about a minute before a new mask-on slice
// begins, when the previous mask-on slice ended with a large leak.
// This probably indicates the end of LL and beginning
// of breath detection, but we don't get any real data until mask-on.
//
- // It has also happened once in a similar scenario for PB and 0E, even when
+ // It has also happened once in a similar scenario for PB and VB, even when
// the two mask-on slices are in different sessions!
continue;
}
@@ -2947,9 +2948,9 @@ bool PRS1Import::ImportEventChunk(PRS1DataChunk* event)
// Sanity check: warn if a (non-slice) event is earlier than the current mask-on slice
if (t < (*m_currentSlice).start && (*m_currentSlice).status == MaskOn) {
if (!PRS1NonSliceChannels.contains(e->m_type)) {
- // LL and PRS1_0E at the beginning of a mask-on session sometimes start 1 second early,
+ // LL and VB at the beginning of a mask-on session sometimes start 1 second early,
// so suppress that warning.
- if ((*m_currentSlice).start - t > 1000 || (e->m_type != PRS1LargeLeakEvent::TYPE && e->m_type != PRS1UnknownDurationEvent::TYPE)) {
+ if ((*m_currentSlice).start - t > 1000 || (e->m_type != PRS1LargeLeakEvent::TYPE && e->m_type != PRS1VariableBreathingEvent::TYPE)) {
qWarning() << sessionid << "Event" << e->m_type << "before mask-on slice:" << ts(t);
}
}
@@ -3097,7 +3098,7 @@ void PRS1Import::ImportEvent(qint64 t, PRS1ParsedEvent* e)
case PRS1PeriodicBreathingEvent::TYPE:
case PRS1LargeLeakEvent::TYPE:
- case PRS1UnknownDurationEvent::TYPE:
+ case PRS1VariableBreathingEvent::TYPE:
// TODO: The graphs silently treat the timestamp of a span as an end time rather than start (see gFlagsLine::paint).
// Decide whether to preserve that behavior or change it universally and update either this code or comment.
duration = e->m_duration * 1000L;
@@ -3497,7 +3498,7 @@ static const QVector ParsedEventsF0V23 = {
PRS1HypopneaEvent::TYPE,
PRS1FlowLimitationEvent::TYPE,
PRS1VibratorySnoreEvent::TYPE,
- PRS1UnknownDurationEvent::TYPE,
+ PRS1VariableBreathingEvent::TYPE,
PRS1PeriodicBreathingEvent::TYPE,
PRS1LargeLeakEvent::TYPE,
PRS1TotalLeakEvent::TYPE,
@@ -3621,13 +3622,12 @@ bool PRS1DataChunk::ParseEventsF0V23()
// no data bytes
this->AddEvent(new PRS1VibratorySnoreEvent(t, 0));
break;
- case 0x0e: // ???
- // 5 bytes like PB and LL, but what is it?
+ case 0x0e: // Variable Breathing?
// TODO: does duration double like F0V4?
- duration = (data[pos] | (data[pos+1] << 8)); // this looks like a 16-bit value, so may be duration like PB?
+ duration = (data[pos] | (data[pos+1] << 8));
elapsed = data[pos+2]; // this is always 60 seconds unless it's at the end, so it seems like elapsed
CHECK_VALUES(elapsed, 60, 0);
- this->AddEvent(new PRS1UnknownDurationEvent(t - elapsed - duration, duration));
+ this->AddEvent(new PRS1VariableBreathingEvent(t - elapsed - duration, duration));
break;
case 0x0f: // Periodic Breathing
// PB events are reported some time after they conclude, and they do have a reported duration.
@@ -3697,7 +3697,7 @@ static const QVector ParsedEventsF0V4 = {
PRS1HypopneaEvent::TYPE,
PRS1FlowLimitationEvent::TYPE,
PRS1VibratorySnoreEvent::TYPE,
- PRS1UnknownDurationEvent::TYPE,
+ PRS1VariableBreathingEvent::TYPE,
PRS1PeriodicBreathingEvent::TYPE,
PRS1LargeLeakEvent::TYPE,
PRS1TotalLeakEvent::TYPE,
@@ -3822,13 +3822,12 @@ bool PRS1DataChunk::ParseEventsF0V4()
// no data bytes
this->AddEvent(new PRS1VibratorySnoreEvent(t, 0));
break;
- case 0x0e: // ???
- // 5 bytes like PB and LL, but what is it?
+ case 0x0e: // Variable Breathing?
// TODO: does duration double like it does for PB/LL?
- duration = 2 * (data[pos] | (data[pos+1] << 8)); // this looks like a 16-bit value, so may be duration like PB?
+ duration = 2 * (data[pos] | (data[pos+1] << 8));
elapsed = data[pos+2]; // this is always 60 seconds unless it's at the end, so it seems like elapsed
CHECK_VALUES(elapsed, 60, 0);
- this->AddEvent(new PRS1UnknownDurationEvent(t - elapsed - duration, duration));
+ this->AddEvent(new PRS1VariableBreathingEvent(t - elapsed - duration, duration));
break;
case 0x0f: // Periodic Breathing
// PB events are reported some time after they conclude, and they do have a reported duration.
@@ -3907,7 +3906,7 @@ static const QVector ParsedEventsF0V6 = {
PRS1HypopneaEvent::TYPE,
PRS1FlowLimitationEvent::TYPE,
PRS1VibratorySnoreEvent::TYPE,
- PRS1UnknownDurationEvent::TYPE,
+ PRS1VariableBreathingEvent::TYPE,
PRS1PeriodicBreathingEvent::TYPE,
PRS1LargeLeakEvent::TYPE,
PRS1TotalLeakEvent::TYPE,
@@ -4046,12 +4045,11 @@ bool PRS1DataChunk::ParseEventsF0V6()
// no data bytes
this->AddEvent(new PRS1VibratorySnoreEvent(t, 0));
break;
- case 0x0e: // ???
- // 5 bytes like PB and LL, but what is it?
- duration = 2 * (data[pos] | (data[pos+1] << 8)); // this looks like a 16-bit value, so may be duration like PB?
+ case 0x0e: // Variable Breathing?
+ duration = 2 * (data[pos] | (data[pos+1] << 8));
elapsed = data[pos+2]; // this is always 60 seconds unless it's at the end, so it seems like elapsed
CHECK_VALUES(elapsed, 60, 0);
- this->AddEvent(new PRS1UnknownDurationEvent(t - elapsed - duration, duration));
+ this->AddEvent(new PRS1VariableBreathingEvent(t - elapsed - duration, duration));
break;
case 0x0f: // Periodic Breathing
// PB events are reported some time after they conclude, and they do have a reported duration.
@@ -8945,21 +8943,15 @@ void PRS1Loader::initChannels()
chan->addOption(0, STR_TR_Off);
chan->addOption(1, STR_TR_On);
- // TODO: is the below useful?
-//
-//
-//
-//
-//
-//
-
- channel.add(GRP_CPAP, new Channel(PRS1_0E = 0x1157, SPAN, MT_CPAP, SESSION,
- "PRS1_UNK",
- QObject::tr("PRS1 Unknown"),
- QObject::tr("Unknown PRS1 span 0x0E"),
- "??",
+ channel.add(GRP_CPAP, chan = new Channel(PRS1_VariableBreathing = 0x1156, SPAN, MT_CPAP, SESSION,
+ "PRS1_VariableBreathing",
+ QObject::tr("Variable Breathing"),
+ QObject::tr("UNCONFIRMED: Possibly variable breathing, which are periods of high deviation from the peak inspiratory flow trend"),
+ "VB",
STR_UNIT_Seconds,
DEFAULT, QColor("#ffe8f0")));
+ chan->setEnabled(false); // disable by default
+
channel.add(GRP_CPAP, new Channel(PRS1_BND = 0x1159, SPAN, MT_CPAP, SESSION,
"PRS1_BND",
QObject::tr("Breathing Not Detected"),
diff --git a/oscar/SleepLib/machine_common.cpp b/oscar/SleepLib/machine_common.cpp
index ecc1b561..65da71ff 100644
--- a/oscar/SleepLib/machine_common.cpp
+++ b/oscar/SleepLib/machine_common.cpp
@@ -30,7 +30,7 @@ ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_EPAPLo, CPAP_EPAP
ChannelID RMS9_E01, RMS9_E02, RMS9_SetPressure, RMS9_MaskOnTime;
ChannelID INTELLIPAP_Unknown1, INTELLIPAP_Unknown2;
-ChannelID PRS1_0E, CPAP_LargeLeak,
+ChannelID CPAP_LargeLeak,
PRS1_BND, PRS1_FlexMode, PRS1_FlexLevel, PRS1_HumidStatus, PRS1_HumidLevel, PRS1_MaskResistLock,
PRS1_MaskResistSet, PRS1_HoseDiam, PRS1_AutoOn, PRS1_AutoOff, PRS1_MaskAlert, PRS1_ShowAHI;
diff --git a/oscar/SleepLib/machine_common.h b/oscar/SleepLib/machine_common.h
index c2c46883..8176fb1a 100644
--- a/oscar/SleepLib/machine_common.h
+++ b/oscar/SleepLib/machine_common.h
@@ -157,7 +157,7 @@ extern ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_EPAPLo, CP
CPAP_PressureSet, CPAP_IPAPSet, CPAP_EPAPSet;
extern ChannelID RMS9_E01, RMS9_E02, RMS9_SetPressure, RMS9_MaskOnTime;
-extern ChannelID PRS1_0E, CPAP_LargeLeak, PRS1_BND,
+extern ChannelID CPAP_LargeLeak, PRS1_BND,
PRS1_FlexMode, PRS1_FlexLevel, PRS1_HumidStatus, PRS1_HumidLevel, CPAP_HumidSetting, PRS1_MaskResistLock,
PRS1_MaskResistSet, PRS1_HoseDiam, PRS1_AutoOn, PRS1_AutoOff, PRS1_MaskAlert, PRS1_ShowAHI;
diff --git a/oscar/tests/sessiontests.cpp b/oscar/tests/sessiontests.cpp
index beaa7887..0e07540c 100644
--- a/oscar/tests/sessiontests.cpp
+++ b/oscar/tests/sessiontests.cpp
@@ -58,6 +58,7 @@ extern ChannelID PRS1_TimedBreath, PRS1_HumidMode, PRS1_TubeTemp;
extern ChannelID PRS1_FlexLock, PRS1_TubeLock, PRS1_RampType;
extern ChannelID PRS1_BackupBreathMode, PRS1_BackupBreathRate, PRS1_BackupBreathTi;
extern ChannelID PRS1_AutoTrial, PRS1_EZStart, PRS1_RiseTime, PRS1_RiseTimeLock;
+extern ChannelID PRS1_VariableBreathing;
extern ChannelID RMS9_EPR, RMS9_EPRLevel, RMS9_Mode, RMS9_SmartStart, RMS9_HumidStatus, RMS9_HumidLevel,
RMS9_PtAccess, RMS9_Mask, RMS9_ABFilter, RMS9_ClimateControl, RMS9_TubeType,
@@ -176,7 +177,7 @@ static QString eventChannel(ChannelID i)
CHANNELNAME(CPAP_Test2);
CHANNELNAME(CPAP_PressurePulse);
CHANNELNAME(CPAP_Pressure);
- CHANNELNAME(PRS1_0E);
+ CHANNELNAME(PRS1_VariableBreathing);
CHANNELNAME(CPAP_PressureSet);
CHANNELNAME(CPAP_IPAPSet);
CHANNELNAME(CPAP_EPAPSet);
From 90434d5f11f1620d1c3d21acc77ecd98c81d4d34 Mon Sep 17 00:00:00 2001
From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com>
Date: Thu, 26 Mar 2020 09:54:55 -0400
Subject: [PATCH 15/15] Bump the PRS1 loader version to force a rebuild, update
release notes.
Also fix a typo in the rebuild dialog.
---
oscar/SleepLib/loader_plugins/prs1_loader.h | 2 +-
oscar/SleepLib/profiles.cpp | 2 +-
oscar/daily.cpp | 2 ++
oscar/docs/release_notes.html | 14 +++++++++++++-
4 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.h b/oscar/SleepLib/loader_plugins/prs1_loader.h
index cf5adc75..55e4278e 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.h
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.h
@@ -25,7 +25,7 @@
//********************************************************************************************
// Please INCREMENT the following value when making changes to this loaders implementation
// BEFORE making a release
-const int prs1_data_version = 17;
+const int prs1_data_version = 18;
//
//********************************************************************************************
#if 0 // Apparently unused
diff --git a/oscar/SleepLib/profiles.cpp b/oscar/SleepLib/profiles.cpp
index 4c079452..fd2e52c6 100644
--- a/oscar/SleepLib/profiles.cpp
+++ b/oscar/SleepLib/profiles.cpp
@@ -541,7 +541,7 @@ void Profile::DataFormatError(Machine *m)
msg = msg + QObject::tr("This means you will need to import this machine data again afterwards from your own backups or data card.") + "
";
}
- msg += ""+QObject::tr("Important:")+" "+QObject::tr("Once you upgrade, you can not use this profile with the previous version anymore.")+"
"+
+ msg += ""+QObject::tr("Important:")+" "+QObject::tr("Once you upgrade, you cannot use this profile with the previous version anymore.")+"
"+
QObject::tr("If you are concerned, click No to exit, and backup your profile manually, before starting OSCAR again.")+ "