mirror of
https://gitlab.com/pholy/OSCAR-code.git
synced 2025-04-06 11:10:44 +00:00
Parse backup breathing settings for DreamStation 1030X-1130X machines.
No change to imported data.
This commit is contained in:
parent
50d60385bd
commit
2e04755b53
@ -199,6 +199,8 @@ static crc32_t CRC32wchar(const unsigned char *data, size_t data_len, crc32_t cr
|
|||||||
|
|
||||||
enum FlexMode { FLEX_None, FLEX_CFlex, FLEX_CFlexPlus, FLEX_AFlex, FLEX_RiseTime, FLEX_BiFlex, FLEX_AVAPS, FLEX_Unknown };
|
enum FlexMode { FLEX_None, FLEX_CFlex, FLEX_CFlexPlus, FLEX_AFlex, FLEX_RiseTime, FLEX_BiFlex, FLEX_AVAPS, FLEX_Unknown };
|
||||||
|
|
||||||
|
enum BackupBreathMode { PRS1Backup_Off, PRS1Backup_Auto, PRS1Backup_Fixed };
|
||||||
|
|
||||||
ChannelID PRS1_TimedBreath = 0, PRS1_HeatedTubing = 0;
|
ChannelID PRS1_TimedBreath = 0, PRS1_HeatedTubing = 0;
|
||||||
|
|
||||||
struct PRS1TestedModel
|
struct PRS1TestedModel
|
||||||
@ -1064,6 +1066,9 @@ enum PRS1ParsedSettingType
|
|||||||
PRS1_SETTING_PS,
|
PRS1_SETTING_PS,
|
||||||
PRS1_SETTING_PS_MIN,
|
PRS1_SETTING_PS_MIN,
|
||||||
PRS1_SETTING_PS_MAX,
|
PRS1_SETTING_PS_MAX,
|
||||||
|
PRS1_SETTING_BACKUP_BREATH_MODE,
|
||||||
|
PRS1_SETTING_BACKUP_BREATH_RATE,
|
||||||
|
PRS1_SETTING_BACKUP_TIMED_INSPIRATION,
|
||||||
PRS1_SETTING_TIDAL_VOLUME,
|
PRS1_SETTING_TIDAL_VOLUME,
|
||||||
PRS1_SETTING_EZ_START,
|
PRS1_SETTING_EZ_START,
|
||||||
PRS1_SETTING_FLEX_LOCK,
|
PRS1_SETTING_FLEX_LOCK,
|
||||||
@ -1261,16 +1266,25 @@ public:
|
|||||||
PRS1ParsedSettingEvent(PRS1ParsedSettingType setting, int value) : PRS1ParsedValueEvent(TYPE, 0, value), m_setting(setting) {}
|
PRS1ParsedSettingEvent(PRS1ParsedSettingType setting, int value) : PRS1ParsedValueEvent(TYPE, 0, value), m_setting(setting) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PRS1PressureSettingEvent : public PRS1ParsedSettingEvent
|
class PRS1ScaledSettingEvent : public PRS1ParsedSettingEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PRS1ScaledSettingEvent(PRS1ParsedSettingType setting, int value, float gain)
|
||||||
|
: PRS1ParsedSettingEvent(setting, value)
|
||||||
|
{
|
||||||
|
m_gain = gain;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PRS1PressureSettingEvent : public PRS1ScaledSettingEvent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr float GAIN = PRS1PressureEvent::GAIN;
|
static constexpr float GAIN = PRS1PressureEvent::GAIN;
|
||||||
static const PRS1ParsedEventUnit UNIT = PRS1PressureEvent::UNIT;
|
static const PRS1ParsedEventUnit UNIT = PRS1PressureEvent::UNIT;
|
||||||
|
|
||||||
PRS1PressureSettingEvent(PRS1ParsedSettingType setting, int value, float gain=GAIN)
|
PRS1PressureSettingEvent(PRS1ParsedSettingType setting, int value, float gain=GAIN)
|
||||||
: PRS1ParsedSettingEvent(setting, value)
|
: PRS1ScaledSettingEvent(setting, value, gain)
|
||||||
{
|
{
|
||||||
m_gain = gain;
|
|
||||||
m_unit = UNIT;
|
m_unit = UNIT;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1502,6 +1516,9 @@ static QString parsedSettingTypeName(PRS1ParsedSettingType t)
|
|||||||
ENUMSTRING(PRS1_SETTING_PS);
|
ENUMSTRING(PRS1_SETTING_PS);
|
||||||
ENUMSTRING(PRS1_SETTING_PS_MIN);
|
ENUMSTRING(PRS1_SETTING_PS_MIN);
|
||||||
ENUMSTRING(PRS1_SETTING_PS_MAX);
|
ENUMSTRING(PRS1_SETTING_PS_MAX);
|
||||||
|
ENUMSTRING(PRS1_SETTING_BACKUP_BREATH_MODE);
|
||||||
|
ENUMSTRING(PRS1_SETTING_BACKUP_BREATH_RATE);
|
||||||
|
ENUMSTRING(PRS1_SETTING_BACKUP_TIMED_INSPIRATION);
|
||||||
ENUMSTRING(PRS1_SETTING_TIDAL_VOLUME);
|
ENUMSTRING(PRS1_SETTING_TIDAL_VOLUME);
|
||||||
ENUMSTRING(PRS1_SETTING_EZ_START);
|
ENUMSTRING(PRS1_SETTING_EZ_START);
|
||||||
ENUMSTRING(PRS1_SETTING_FLEX_LOCK);
|
ENUMSTRING(PRS1_SETTING_FLEX_LOCK);
|
||||||
@ -4357,6 +4374,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
|
|||||||
switch (code) {
|
switch (code) {
|
||||||
case 0: // Device Mode
|
case 0: // Device Mode
|
||||||
CHECK_VALUE(pos, 2); // always first?
|
CHECK_VALUE(pos, 2); // always first?
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
switch (data[pos]) {
|
switch (data[pos]) {
|
||||||
case 1: cpapmode = PRS1_MODE_S; break; // "S" mode
|
case 1: cpapmode = PRS1_MODE_S; break; // "S" mode
|
||||||
case 2: cpapmode = PRS1_MODE_ST; break; // "S/T" mode; pressure seems variable?
|
case 2: cpapmode = PRS1_MODE_ST; break; // "S/T" mode; pressure seems variable?
|
||||||
@ -4368,6 +4386,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
|
|||||||
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_CPAP_MODE, (int) cpapmode));
|
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_CPAP_MODE, (int) cpapmode));
|
||||||
break;
|
break;
|
||||||
case 1: // Flex Mode
|
case 1: // Flex Mode
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
switch (data[pos]) {
|
switch (data[pos]) {
|
||||||
case 0: // 0 = None
|
case 0: // 0 = None
|
||||||
flexmode = FLEX_None;
|
flexmode = FLEX_None;
|
||||||
@ -4388,14 +4407,17 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
|
|||||||
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_MODE, (int) flexmode));
|
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_MODE, (int) flexmode));
|
||||||
break;
|
break;
|
||||||
case 2: // ??? Maybe AAM?
|
case 2: // ??? Maybe AAM?
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
CHECK_VALUE(data[pos], 0);
|
CHECK_VALUE(data[pos], 0);
|
||||||
break;
|
break;
|
||||||
case 4: // EPAP Pressure
|
case 4: // EPAP Pressure
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
// pressures seem variable on practice, maybe due to ramp or leaks?
|
// pressures seem variable on practice, maybe due to ramp or leaks?
|
||||||
fixed_epap = data[pos];
|
fixed_epap = data[pos];
|
||||||
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_EPAP, fixed_epap, GAIN));
|
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_EPAP, fixed_epap, GAIN));
|
||||||
break;
|
break;
|
||||||
case 7: // IPAP Pressure
|
case 7: // IPAP Pressure
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
// pressures seem variable on practice, maybe due to ramp or leaks?
|
// pressures seem variable on practice, maybe due to ramp or leaks?
|
||||||
fixed_ipap = data[pos];
|
fixed_ipap = data[pos];
|
||||||
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP, fixed_ipap, GAIN));
|
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP, fixed_ipap, GAIN));
|
||||||
@ -4404,6 +4426,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
|
|||||||
if (fixed_epap == 0) UNEXPECTED_VALUE(fixed_epap, ">0");
|
if (fixed_epap == 0) UNEXPECTED_VALUE(fixed_epap, ">0");
|
||||||
break;
|
break;
|
||||||
case 8: // Min IPAP
|
case 8: // Min IPAP
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
CHECK_VALUE(fixed_ipap, 0);
|
CHECK_VALUE(fixed_ipap, 0);
|
||||||
min_ipap = data[pos];
|
min_ipap = data[pos];
|
||||||
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MIN, min_ipap, GAIN));
|
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MIN, min_ipap, GAIN));
|
||||||
@ -4412,6 +4435,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
|
|||||||
if (fixed_epap == 0) UNEXPECTED_VALUE(fixed_epap, ">0");
|
if (fixed_epap == 0) UNEXPECTED_VALUE(fixed_epap, ">0");
|
||||||
break;
|
break;
|
||||||
case 9: // Max IPAP
|
case 9: // Max IPAP
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
CHECK_VALUE(fixed_ipap, 0);
|
CHECK_VALUE(fixed_ipap, 0);
|
||||||
if (min_ipap == 0) UNEXPECTED_VALUE(min_ipap, ">0");
|
if (min_ipap == 0) UNEXPECTED_VALUE(min_ipap, ">0");
|
||||||
max_ipap = data[pos];
|
max_ipap = data[pos];
|
||||||
@ -4421,28 +4445,44 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
|
|||||||
if (fixed_epap == 0) UNEXPECTED_VALUE(fixed_epap, ">0");
|
if (fixed_epap == 0) UNEXPECTED_VALUE(fixed_epap, ">0");
|
||||||
break;
|
break;
|
||||||
case 0x19: // Tidal Volume (AVAPS)
|
case 0x19: // Tidal Volume (AVAPS)
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
CHECK_VALUES(cpapmode, PRS1_MODE_ST, PRS1_MODE_PC);
|
CHECK_VALUES(cpapmode, PRS1_MODE_ST, PRS1_MODE_PC);
|
||||||
CHECK_VALUE(flexmode, FLEX_AVAPS);
|
CHECK_VALUE(flexmode, FLEX_AVAPS);
|
||||||
//CHECK_VALUE(data[pos], 47); // gain 10.0
|
//CHECK_VALUE(data[pos], 47); // gain 10.0
|
||||||
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TIDAL_VOLUME, data[pos] * 10.0));
|
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_TIDAL_VOLUME, data[pos] * 10.0));
|
||||||
break;
|
break;
|
||||||
case 0x1e: // Backup rate (S/T and PC)
|
case 0x1e: // (Backup) Breath Rate (S/T and PC)
|
||||||
|
CHECK_VALUE(len, 3);
|
||||||
CHECK_VALUES(cpapmode, PRS1_MODE_ST, PRS1_MODE_PC);
|
CHECK_VALUES(cpapmode, PRS1_MODE_ST, PRS1_MODE_PC);
|
||||||
// TODO: Does mode breath rate off mean this is essentially bilevel? The pressure graphs are confusing.
|
switch (data[pos]) {
|
||||||
CHECK_VALUES(data[pos], 0, 2); // 0 = Breath Rate off (S), 2 = fixed BPM (1 = auto on F5V3 setting 0x14)
|
case 0: // Breath Rate Off
|
||||||
//CHECK_VALUE(data[pos+1], 10); // BPM for mode 2
|
// TODO: Is this mode essentially bilevel? The pressure graphs are confusing.
|
||||||
//CHECK_VALUE(data[pos+2], 10); // timed inspiration for mode 2 (gain 0.1)
|
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_BACKUP_BREATH_MODE, PRS1Backup_Off));
|
||||||
|
break;
|
||||||
|
//case 1: // Breath Rate Auto in F5V3 setting 0x14
|
||||||
|
case 2: // Breath Rate (fixed BPM)
|
||||||
|
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));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
CHECK_VALUES(data[pos], 0, 2); // 0 = Breath Rate off (S), 2 = fixed BPM (1 = auto on F5V3 setting 0x14)
|
||||||
|
break;
|
||||||
|
}
|
||||||
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 unless AAM is enabled, so no setting may be needed.
|
||||||
case 0x2c: // Ramp Time
|
case 0x2c: // Ramp Time
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
if (data[pos] != 0) { // 0 == ramp off, and ramp pressure setting doesn't appear
|
if (data[pos] != 0) { // 0 == ramp off, and ramp pressure setting doesn't appear
|
||||||
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RAMP_TIME, data[pos]));
|
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_RAMP_TIME, data[pos]));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x2d: // Ramp Pressure (with ASV/ventilator pressure encoding), only present when ramp is on
|
case 0x2d: // Ramp Pressure (with ASV/ventilator pressure encoding), only present when ramp is on
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_RAMP_PRESSURE, data[pos], GAIN));
|
this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_RAMP_PRESSURE, data[pos], GAIN));
|
||||||
break;
|
break;
|
||||||
case 0x2e: // Bi-Flex level or Rise Time
|
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 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
|
// 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.
|
// mode None or AVAPS and to Bi-Flex Setting (level) in Bi-Flex mode.
|
||||||
@ -4458,6 +4498,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
|
|||||||
// TODO: where's timed inspiration?
|
// TODO: where's timed inspiration?
|
||||||
break;
|
break;
|
||||||
case 0x2f: // Rise Time lock? (was flex lock on F0V6, 0x80 for locked)
|
case 0x2f: // Rise Time lock? (was flex lock on F0V6, 0x80 for locked)
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
if (cpapmode == PRS1_MODE_S) {
|
if (cpapmode == PRS1_MODE_S) {
|
||||||
CHECK_VALUES(data[pos], 0, 0x80); // Bi-Flex Lock
|
CHECK_VALUES(data[pos], 0, 0x80); // Bi-Flex Lock
|
||||||
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LOCK, data[pos] != 0));
|
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_FLEX_LOCK, data[pos] != 0));
|
||||||
@ -4467,27 +4508,33 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x35: // Humidifier setting
|
case 0x35: // Humidifier setting
|
||||||
|
CHECK_VALUE(len, 2);
|
||||||
this->ParseHumidifierSettingV3(data[pos], data[pos+1], true);
|
this->ParseHumidifierSettingV3(data[pos], data[pos+1], true);
|
||||||
break;
|
break;
|
||||||
case 0x36: // Mask Resistance Lock
|
case 0x36: // Mask Resistance Lock
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
CHECK_VALUE(data[pos], 0); // 0x80 = locked on F5V3, not yet observed on F3V6
|
CHECK_VALUE(data[pos], 0); // 0x80 = locked on F5V3, not yet observed on F3V6
|
||||||
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_LOCK, data[pos] != 0));
|
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_LOCK, data[pos] != 0));
|
||||||
break;
|
break;
|
||||||
case 0x38: // Mask Resistance
|
case 0x38: // Mask Resistance
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
if (data[pos] != 0) { // 0 == mask resistance off
|
if (data[pos] != 0) { // 0 == mask resistance off
|
||||||
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[pos]));
|
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_MASK_RESIST_SETTING, data[pos]));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x39:
|
case 0x39:
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
CHECK_VALUE(data[pos], 0);
|
CHECK_VALUE(data[pos], 0);
|
||||||
break;
|
break;
|
||||||
case 0x3b: // Tubing Type
|
case 0x3b: // Tubing Type
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
if (data[pos] != 0) {
|
if (data[pos] != 0) {
|
||||||
CHECK_VALUES(data[pos], 2, 1); // 15HT = 2, 15 = 1, 22 = 0, though report only says "15" for 15HT
|
CHECK_VALUES(data[pos], 2, 1); // 15HT = 2, 15 = 1, 22 = 0, though report only says "15" for 15HT
|
||||||
}
|
}
|
||||||
this->ParseTubingTypeV3(data[pos]);
|
this->ParseTubingTypeV3(data[pos]);
|
||||||
break;
|
break;
|
||||||
case 0x3c: // View Optional Screens
|
case 0x3c: // View Optional Screens
|
||||||
|
CHECK_VALUE(len, 1);
|
||||||
CHECK_VALUES(data[pos], 0, 0x80);
|
CHECK_VALUES(data[pos], 0, 0x80);
|
||||||
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_SHOW_AHI, data[pos] != 0));
|
this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_SHOW_AHI, data[pos] != 0));
|
||||||
break;
|
break;
|
||||||
@ -5666,6 +5713,9 @@ bool PRS1Import::ImportSummary()
|
|||||||
//TODO: channel.add if we ever want to import this
|
//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;
|
break;
|
||||||
|
case PRS1_SETTING_BACKUP_BREATH_MODE:
|
||||||
|
case PRS1_SETTING_BACKUP_BREATH_RATE:
|
||||||
|
case PRS1_SETTING_BACKUP_TIMED_INSPIRATION:
|
||||||
case PRS1_SETTING_TIDAL_VOLUME:
|
case PRS1_SETTING_TIDAL_VOLUME:
|
||||||
case PRS1_SETTING_AUTO_TRIAL:
|
case PRS1_SETTING_AUTO_TRIAL:
|
||||||
case PRS1_SETTING_EZ_START:
|
case PRS1_SETTING_EZ_START:
|
||||||
|
Loading…
Reference in New Issue
Block a user