From 0c816c7d4c11a6f475553d45476b1f869a949c10 Mon Sep 17 00:00:00 2001 From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com> Date: Tue, 20 Aug 2019 11:38:55 -0400 Subject: [PATCH 1/3] Avoid pos++ in PRS1 DreamStation parsers to that events can be issued independently of parsing order. --- oscar/SleepLib/loader_plugins/prs1_loader.cpp | 123 +++++++++--------- 1 file changed, 58 insertions(+), 65 deletions(-) diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp index acb86d36..ef2a97a2 100644 --- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp +++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp @@ -1671,53 +1671,53 @@ bool PRS1DataChunk::ParseEventsF5V3(void) // TB events have a duration in 0.1s, based on the review of pressure waveforms. // TODO: Ideally the starting time here would be adjusted here, but PRS1ParsedEvents // currently assume integer seconds rather than ms, so that's done at import. - duration = data[pos++]; + duration = data[pos]; this->AddEvent(new PRS1TimedBreathEvent(t, duration)); break; case 3: // Statistics // These appear every 2 minutes, so presumably summarize the preceding period. - this->AddEvent(new PRS1IPAPEvent(t, data[pos++], GAIN)); // 00=IPAP (average?) - this->AddEvent(new PRS1IPAPLowEvent(t, data[pos++], GAIN)); // 01=IAP Low - this->AddEvent(new PRS1IPAPHighEvent(t, data[pos++], GAIN)); // 02=IAP High - this->AddEvent(new PRS1TotalLeakEvent(t, data[pos++])); // 03=Total leak (average?) - this->AddEvent(new PRS1RespiratoryRateEvent(t, data[pos++])); // 04=Breaths Per Minute (average?) - this->AddEvent(new PRS1PatientTriggeredBreathsEvent(t, data[pos++])); // 05=Patient Triggered Breaths (average?) - this->AddEvent(new PRS1MinuteVentilationEvent(t, data[pos++])); // 06=Minute Ventilation (average?) - this->AddEvent(new PRS1TidalVolumeEvent(t, data[pos++])); // 07=Tidal Volume (average?) - this->AddEvent(new PRS1SnoreEvent(t, data[pos++])); // 08=Snore count // TODO: not a VS on official waveform, but appears in flags and contributes to overall VS index - this->AddEvent(new PRS1EPAPEvent(t, data[pos++], GAIN)); // 09=EPAP (average? see event 1 above) - this->AddEvent(new PRS1LeakEvent(t, data[pos++])); // 0A=Leak (average?) + this->AddEvent(new PRS1IPAPEvent(t, data[pos+0], GAIN)); // 00=IPAP (average?) + this->AddEvent(new PRS1IPAPLowEvent(t, data[pos+1], GAIN)); // 01=IAP Low + this->AddEvent(new PRS1IPAPHighEvent(t, data[pos+2], GAIN)); // 02=IAP High + this->AddEvent(new PRS1TotalLeakEvent(t, data[pos+3])); // 03=Total leak (average?) + this->AddEvent(new PRS1RespiratoryRateEvent(t, data[pos+4])); // 04=Breaths Per Minute (average?) + this->AddEvent(new PRS1PatientTriggeredBreathsEvent(t, data[pos+5])); // 05=Patient Triggered Breaths (average?) + this->AddEvent(new PRS1MinuteVentilationEvent(t, data[pos+6])); // 06=Minute Ventilation (average?) + this->AddEvent(new PRS1TidalVolumeEvent(t, data[pos+7])); // 07=Tidal Volume (average?) + this->AddEvent(new PRS1SnoreEvent(t, data[pos+8])); // 08=Snore count // TODO: not a VS on official waveform, but appears in flags and contributes to overall VS index + this->AddEvent(new PRS1EPAPEvent(t, data[pos+9], GAIN)); // 09=EPAP (average? see event 1 above) + this->AddEvent(new PRS1LeakEvent(t, data[pos+0xa])); // 0A=Leak (average?) break; case 0x04: // Pressure Pulse - duration = data[pos++]; // TODO: is this a duration? + duration = data[pos]; // TODO: is this a duration? this->AddEvent(new PRS1PressurePulseEvent(t, duration)); break; case 0x05: // Obstructive Apnea // OA events are instantaneous flags with no duration: reviewing waveforms // shows that the time elapsed between the flag and reporting often includes // non-apnea breathing. - elapsed = data[pos++]; + elapsed = data[pos]; this->AddEvent(new PRS1ObstructiveApneaEvent(t - elapsed, 0)); break; case 0x06: // Clear Airway Apnea // CA events are instantaneous flags with no duration: reviewing waveforms // shows that the time elapsed between the flag and reporting often includes // non-apnea breathing. - elapsed = data[pos++]; + elapsed = data[pos]; this->AddEvent(new PRS1ClearAirwayEvent(t - elapsed, 0)); break; case 0x07: // Hypopnea // TODO: How is this hypopnea different from events 0xd and 0xe? // TODO: What is the first byte? - pos++; // unknown first byte? - elapsed = data[pos++]; // based on sample waveform, the hypopnea is over after this + //data[pos+0]; // unknown first byte? + elapsed = data[pos+1]; // based on sample waveform, the hypopnea is over after this this->AddEvent(new PRS1HypopneaEvent(t - elapsed, 0)); break; case 0x08: // Flow Limitation // TODO: We should revisit whether this is elapsed or duration once (if) // we start calculating flow limitations ourselves. Flow limitations aren't // as obvious as OA/CA when looking at a waveform. - elapsed = data[pos++]; + elapsed = data[pos]; this->AddEvent(new PRS1FlowLimitationEvent(t - elapsed, 0)); break; case 0x09: // Vibratory Snore @@ -1731,15 +1731,13 @@ bool PRS1DataChunk::ParseEventsF5V3(void) case 0x0a: // Periodic Breathing // PB events are reported some time after they conclude, and they do have a reported duration. duration = 2 * (data[pos] | (data[pos+1] << 8)); - pos += 2; - elapsed = data[pos++]; + elapsed = data[pos+2]; this->AddEvent(new PRS1PeriodicBreathingEvent(t - elapsed - duration, duration)); break; case 0x0b: // Large Leak // LL events are reported some time after they conclude, and they do have a reported duration. duration = 2 * (data[pos] | (data[pos+1] << 8)); - pos += 2; - elapsed = data[pos++]; + elapsed = data[pos+2]; this->AddEvent(new PRS1LargeLeakEvent(t - elapsed - duration, duration)); break; case 0x0d: // Hypopnea @@ -1749,7 +1747,7 @@ bool PRS1DataChunk::ParseEventsF5V3(void) // TODO: We should revisit whether this is elapsed or duration once (if) // we start calculating hypopneas ourselves. Their official definition // is 40% reduction in flow lasting at least 10s. - duration = data[pos++]; + duration = data[pos]; this->AddEvent(new PRS1HypopneaEvent(t - duration, 0)); break; case 0x0f: @@ -2441,66 +2439,64 @@ bool PRS1DataChunk::ParseEventsF3V6(void) // TB events have a duration in 0.1s, based on the review of pressure waveforms. // TODO: Ideally the starting time here would be adjusted here, but PRS1ParsedEvents // currently assume integer seconds rather than ms, so that's done at import. - duration = data[pos++]; + duration = data[pos]; // TODO: make sure F3 import logic matches F5 in adjusting TB start time this->AddEvent(new PRS1TimedBreathEvent(t, duration)); break; case 2: // Statistics // These appear every 2 minutes, so presumably summarize the preceding period. - pos++; // TODO: 0 = ??? - this->AddEvent(new PRS1EPAPEvent(t, data[pos++], GAIN)); // 01=EPAP (average?) - this->AddEvent(new PRS1IPAPEvent(t, data[pos++], GAIN)); // 02=IPAP (average?) - this->AddEvent(new PRS1TotalLeakEvent(t, data[pos++])); // 03=Total leak (average?) - this->AddEvent(new PRS1RespiratoryRateEvent(t, data[pos++])); // 04=Breaths Per Minute (average?) - this->AddEvent(new PRS1PatientTriggeredBreathsEvent(t, data[pos++])); // 05=Patient Triggered Breaths (average?) - this->AddEvent(new PRS1MinuteVentilationEvent(t, data[pos++])); // 06=Minute Ventilation (average?) - this->AddEvent(new PRS1TidalVolumeEvent(t, data[pos++])); // 07=Tidal Volume (average?) - this->AddEvent(new PRS1Test2Event(t, data[pos++])); // 08=Flow??? - this->AddEvent(new PRS1Test1Event(t, data[pos++])); // 09=TMV??? - this->AddEvent(new PRS1SnoreEvent(t, data[pos++])); // 0A=Snore count // TODO: not a VS on official waveform, but appears in flags and contributes to overall VS index - this->AddEvent(new PRS1LeakEvent(t, data[pos++])); // 0B=Leak (average?) + //data[pos+0]; // TODO: 0 = ??? + this->AddEvent(new PRS1EPAPEvent(t, data[pos+1], GAIN)); // 01=EPAP (average?) + this->AddEvent(new PRS1IPAPEvent(t, data[pos+2], GAIN)); // 02=IPAP (average?) + this->AddEvent(new PRS1TotalLeakEvent(t, data[pos+3])); // 03=Total leak (average?) + this->AddEvent(new PRS1RespiratoryRateEvent(t, data[pos+4])); // 04=Breaths Per Minute (average?) + this->AddEvent(new PRS1PatientTriggeredBreathsEvent(t, data[pos+5])); // 05=Patient Triggered Breaths (average?) + this->AddEvent(new PRS1MinuteVentilationEvent(t, data[pos+6])); // 06=Minute Ventilation (average?) + this->AddEvent(new PRS1TidalVolumeEvent(t, data[pos+7])); // 07=Tidal Volume (average?) + this->AddEvent(new PRS1Test2Event(t, data[pos+8])); // 08=Flow??? + this->AddEvent(new PRS1Test1Event(t, data[pos+9])); // 09=TMV??? + this->AddEvent(new PRS1SnoreEvent(t, data[pos+0xa])); // 0A=Snore count // TODO: not a VS on official waveform, but appears in flags and contributes to overall VS index + this->AddEvent(new PRS1LeakEvent(t, data[pos+0xb])); // 0B=Leak (average?) break; case 0x03: // Pressure Pulse - duration = data[pos++]; // TODO: is this a duration? + duration = data[pos]; // TODO: is this a duration? this->AddEvent(new PRS1PressurePulseEvent(t, duration)); break; case 0x04: // Obstructive Apnea // OA events are instantaneous flags with no duration: reviewing waveforms // shows that the time elapsed between the flag and reporting often includes // non-apnea breathing. - elapsed = data[pos++]; + elapsed = data[pos]; this->AddEvent(new PRS1ObstructiveApneaEvent(t - elapsed, 0)); break; case 0x05: // Clear Airway Apnea // CA events are instantaneous flags with no duration: reviewing waveforms // shows that the time elapsed between the flag and reporting often includes // non-apnea breathing. - elapsed = data[pos++]; + elapsed = data[pos]; this->AddEvent(new PRS1ClearAirwayEvent(t - elapsed, 0)); break; case 0x06: // Hypopnea // TODO: How is this hypopnea different from events 0xd and 0xe? // TODO: What is the first byte? - pos++; // unknown first byte? - elapsed = data[pos++]; // based on sample waveform, the hypopnea is over after this + //data[pos+0]; // unknown first byte? + elapsed = data[pos+1]; // based on sample waveform, the hypopnea is over after this this->AddEvent(new PRS1HypopneaEvent(t - elapsed, 0)); break; case 0x07: // Periodic Breathing // PB events are reported some time after they conclude, and they do have a reported duration. duration = 2 * (data[pos] | (data[pos+1] << 8)); - pos += 2; - elapsed = data[pos++]; + elapsed = data[pos+2]; this->AddEvent(new PRS1PeriodicBreathingEvent(t - elapsed - duration, duration)); break; case 0x08: // RERA - elapsed = data[pos++]; // based on sample waveform, the RERA is over after this + elapsed = data[pos]; // based on sample waveform, the RERA is over after this this->AddEvent(new PRS1RERAEvent(t - elapsed, 0)); break; case 0x09: // Large Leak // LL events are reported some time after they conclude, and they do have a reported duration. duration = 2 * (data[pos] | (data[pos+1] << 8)); - pos += 2; - elapsed = data[pos++]; + elapsed = data[pos+2]; this->AddEvent(new PRS1LargeLeakEvent(t - elapsed - duration, duration)); break; case 0x0a: // Hypopnea @@ -2510,7 +2506,7 @@ bool PRS1DataChunk::ParseEventsF3V6(void) // TODO: We should revisit whether this is elapsed or duration once (if) // we start calculating hypopneas ourselves. Their official definition // is 40% reduction in flow lasting at least 10s. - duration = data[pos++]; + duration = data[pos]; this->AddEvent(new PRS1HypopneaEvent(t - duration, 0)); break; // case 0x0c? @@ -3398,7 +3394,7 @@ bool PRS1DataChunk::ParseEventsF0V6(CPAPMode /*mode*/) // adjustments. E.g. 6 at 28:11 and 7.3 at 29:05 results in the following dots: // 6 at 28:11, 6.5 around 28:30, 7.0 around 28:50, 7(.3) at 29:05. That holds until // subsequent "adjustment" of 7.3 at 30:09 followed by 8.0 at 30:19. - this->AddEvent(new PRS1CPAPEvent(t, data[pos++])); + this->AddEvent(new PRS1CPAPEvent(t, data[pos])); break; case 0x02: // Pressure adjustment (bi-level) // TODO: Have OSCAR treat pressure adjustment events differently than (average?) stats below. @@ -3417,25 +3413,25 @@ bool PRS1DataChunk::ParseEventsF0V6(CPAPMode /*mode*/) //CHECK_VALUE(data[pos], 40); break; case 0x04: // Pressure Pulse - duration = data[pos++]; // TODO: is this a duration? + duration = data[pos]; // TODO: is this a duration? this->AddEvent(new PRS1PressurePulseEvent(t, duration)); break; case 0x05: // RERA - elapsed = data[pos++]; // based on sample waveform, the RERA is over after this + elapsed = data[pos]; // based on sample waveform, the RERA is over after this this->AddEvent(new PRS1RERAEvent(t - elapsed, 0)); break; case 0x06: // Obstructive Apnea // OA events are instantaneous flags with no duration: reviewing waveforms // shows that the time elapsed between the flag and reporting often includes // non-apnea breathing. - elapsed = data[pos++]; + elapsed = data[pos]; this->AddEvent(new PRS1ObstructiveApneaEvent(t - elapsed, 0)); break; case 0x07: // Clear Airway Apnea // CA events are instantaneous flags with no duration: reviewing waveforms // shows that the time elapsed between the flag and reporting often includes // non-apnea breathing. - elapsed = data[pos++]; + elapsed = data[pos]; this->AddEvent(new PRS1ClearAirwayEvent(t - elapsed, 0)); break; //case 0x08: // never seen @@ -3444,15 +3440,15 @@ bool PRS1DataChunk::ParseEventsF0V6(CPAPMode /*mode*/) case 0x0b: // Hypopnea // TODO: How is this hypopnea different from events 0xa, 0x14 and 0x15? // TODO: What is the first byte? - pos++; // unknown first byte? - elapsed = data[pos++]; // based on sample waveform, the hypopnea is over after this + //data[pos+0]; // unknown first byte? + elapsed = data[pos+1]; // based on sample waveform, the hypopnea is over after this this->AddEvent(new PRS1HypopneaEvent(t - elapsed, 0)); break; case 0x0c: // Flow Limitation // TODO: We should revisit whether this is elapsed or duration once (if) // we start calculating flow limitations ourselves. Flow limitations aren't // as obvious as OA/CA when looking at a waveform. - elapsed = data[pos++]; + elapsed = data[pos]; this->AddEvent(new PRS1FlowLimitationEvent(t - elapsed, 0)); break; case 0x0d: // Vibratory Snore @@ -3466,33 +3462,30 @@ bool PRS1DataChunk::ParseEventsF0V6(CPAPMode /*mode*/) 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? - pos += 2; - elapsed = data[pos++]; // this is always 60 seconds unless it's at the end, so it seems like elapsed + 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 PRS1PeriodicBreathingEvent(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. duration = 2 * (data[pos] | (data[pos+1] << 8)); - pos += 2; - elapsed = data[pos++]; + elapsed = data[pos+2]; this->AddEvent(new PRS1PeriodicBreathingEvent(t - elapsed - duration, duration)); break; case 0x10: // Large Leak // LL events are reported some time after they conclude, and they do have a reported duration. duration = 2 * (data[pos] | (data[pos+1] << 8)); - pos += 2; - elapsed = data[pos++]; + elapsed = data[pos+2]; this->AddEvent(new PRS1LargeLeakEvent(t - elapsed - duration, duration)); break; case 0x11: // Statistics - this->AddEvent(new PRS1TotalLeakEvent(t, data[pos++])); - this->AddEvent(new PRS1SnoreEvent(t, data[pos++])); + this->AddEvent(new PRS1TotalLeakEvent(t, data[pos])); + this->AddEvent(new PRS1SnoreEvent(t, data[pos+1])); // Average pressure: this reads lower than the current CPAP set point when // a flex mode is on, and exactly the current CPAP set point when off. For // bi-level it's presumably an average of the actual pressures. // TODO: What to do with this average pressure? Actual pressure adjustments are handled above. - //this->AddEvent(new PRS1EPAPEvent(t, data[pos++])); + //this->AddEvent(new PRS1EPAPEvent(t, data[pos+2])); break; case 0x12: // Snore count per pressure // Some sessions (with lots of ramps) have multiple of these, each with a @@ -3516,7 +3509,7 @@ bool PRS1DataChunk::ParseEventsF0V6(CPAPMode /*mode*/) // TODO: We should revisit whether this is elapsed or duration once (if) // we start calculating hypopneas ourselves. Their official definition // is 40% reduction in flow lasting at least 10s. - duration = data[pos++]; + duration = data[pos]; this->AddEvent(new PRS1HypopneaEvent(t - duration, 0)); break; default: From ecb71e5706196123cc915afbf2cac444f8edbbf1 Mon Sep 17 00:00:00 2001 From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com> Date: Tue, 20 Aug 2019 12:03:51 -0400 Subject: [PATCH 2/3] Calculate PRS1 F3V6 pressure support settings, since we currently display them. --- oscar/SleepLib/loader_plugins/prs1_loader.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp index ef2a97a2..f1bae2a8 100644 --- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp +++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp @@ -2446,7 +2446,7 @@ bool PRS1DataChunk::ParseEventsF3V6(void) case 2: // Statistics // These appear every 2 minutes, so presumably summarize the preceding period. //data[pos+0]; // TODO: 0 = ??? - this->AddEvent(new PRS1EPAPEvent(t, data[pos+1], GAIN)); // 01=EPAP (average?) + this->AddEvent(new PRS1EPAPEvent(t, data[pos+1], GAIN)); // 01=EPAP (average?) // TODO: needs to be added second if we decide to calculate PS this->AddEvent(new PRS1IPAPEvent(t, data[pos+2], GAIN)); // 02=IPAP (average?) this->AddEvent(new PRS1TotalLeakEvent(t, data[pos+3])); // 03=Total leak (average?) this->AddEvent(new PRS1RespiratoryRateEvent(t, data[pos+4])); // 04=Breaths Per Minute (average?) @@ -4167,14 +4167,26 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size) // 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)); + // TODO: We need to revisit whether PS should be shown as a setting. + this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PS, fixed_ipap - fixed_epap, GAIN)); + if (fixed_epap == 0) UNEXPECTED_VALUE(fixed_epap, ">0"); break; case 8: // Min IPAP + CHECK_VALUE(fixed_ipap, 0); 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. + this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PS_MIN, min_ipap - fixed_epap, GAIN)); + if (fixed_epap == 0) UNEXPECTED_VALUE(fixed_epap, ">0"); break; case 9: // Max IPAP + CHECK_VALUE(fixed_ipap, 0); + if (min_ipap == 0) UNEXPECTED_VALUE(min_ipap, ">0"); max_ipap = data[pos]; this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_IPAP_MAX, max_ipap, GAIN)); + // TODO: We need to revisit whether PS should be shown as a setting. + this->AddEvent(new PRS1PressureSettingEvent(PRS1_SETTING_PS_MAX, max_ipap - fixed_epap, GAIN)); + if (fixed_epap == 0) UNEXPECTED_VALUE(fixed_epap, ">0"); break; case 0x19: // Tidal Volume (AVAPS) //CHECK_VALUE(data[pos], 47); // gain 10.0 From 42f07456669904eca0d5a3a6f356bf4c15ed5252 Mon Sep 17 00:00:00 2001 From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com> Date: Tue, 20 Aug 2019 12:48:15 -0400 Subject: [PATCH 3/3] Improve settings interpretation for F3V6, though still not exactly right. The next step will be to split parsing from mode interpretation, so that we can at least accurately identify all of PRS1's modes. Then we can work on mapping that to OSCAR's notion of modes, which probably then needs to be augmented. --- oscar/SleepLib/loader_plugins/prs1_loader.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp index f1bae2a8..4e39d9a5 100644 --- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp +++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp @@ -4141,13 +4141,14 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size) // TODO: We probably need additional enums for these modes, the below are just a rough guess mapping for now. switch (data[pos]) { case 1: cpapmode = MODE_BILEVEL_FIXED; break; // "S" mode - case 2: cpapmode = MODE_ASV; break; // "S/T" mode; pressure seems variable? + case 2: cpapmode = MODE_BILEVEL_FIXED; break; // "S/T" mode; pressure seems variable? case 4: cpapmode = MODE_AVAPS; break; // "PC" mode? Usually "PC - AVAPS", see setting 1 below + // 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). default: UNEXPECTED_VALUE(data[pos], "known device mode"); break; } - this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_CPAP_MODE, (int) cpapmode)); break; case 1: // ??? // How do these interact with the mode above? @@ -4173,6 +4174,9 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size) break; case 8: // Min IPAP CHECK_VALUE(fixed_ipap, 0); + if (cpapmode == MODE_BILEVEL_FIXED) { + cpapmode = MODE_BILEVEL_AUTO_VARIABLE_PS; // TODO: this isn't quite right, it's actually fixed EPAP with variable PS + } 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. @@ -4193,7 +4197,7 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size) // TODO: add a setting for this break; case 0x1e: // Backup rate (S/T and AVAPS) - CHECK_VALUES(cpapmode, MODE_ASV, MODE_AVAPS); + //CHECK_VALUES(cpapmode, MODE_BILEVEL_FIXED, MODE_AVAPS); // TODO: this should be testing for S/T rather than bilevel // TODO: Does mode breath rate off mean this is essentially bilevel? The pressure graphs are confusing. CHECK_VALUES(data[pos], 0, 2); // 0 = Breath Rate off (S), 2 = fixed BPM (1 = auto on F5V3 setting 0x14) //CHECK_VALUE(data[pos+1], 10); // BPM for mode 2 @@ -4245,6 +4249,8 @@ bool PRS1DataChunk::ParseSettingsF3V6(const unsigned char* data, int size) pos += len; } while (ok && pos + 2 <= size); + + this->AddEvent(new PRS1ParsedSettingEvent(PRS1_SETTING_CPAP_MODE, (int) cpapmode)); return ok; }