diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp index 638798a2..81eee025 100644 --- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp +++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp @@ -1689,7 +1689,7 @@ void PRS1DataChunk::AddEvent(PRS1ParsedEvent* const event) m_parsedData.push_back(event); } -bool PRS1Import::ParseEventsF5V3() +bool PRS1Import::ImportEventsF5V0123() { float GAIN = PRS1PressureEvent::GAIN; if (event->familyVersion == 2 || event->familyVersion == 3) { @@ -2038,196 +2038,6 @@ bool PRS1DataChunk::ParseEventsF5V3(void) } -bool PRS1Import::ParseF5Events() -{ - float GAIN = PRS1PressureEvent::GAIN; - if (event->familyVersion == 2 || event->familyVersion == 3) { - // F5V2 and F5V3 use a gain of 0.125 rather than 0.1 to allow for a maximum value of 30 cmH2O - GAIN = 0.125; // TODO: this should be parameterized somewhere more logical - } - - // Required channels - EventList *CA = session->AddEventList(CPAP_ClearAirway, EVL_Event); - EventList *OA = session->AddEventList(CPAP_Obstructive, EVL_Event); - EventList *HY = session->AddEventList(CPAP_Hypopnea, EVL_Event); - - EventList *FL = session->AddEventList(CPAP_FlowLimit, EVL_Event); - EventList *SNORE = session->AddEventList(CPAP_Snore, EVL_Event); // snore count statistic - EventList *VS = session->AddEventList(CPAP_VSnore, EVL_Event); - EventList *VS2 = session->AddEventList(CPAP_VSnore2, EVL_Event); // flags nonzero snore-count intervals on overview - // No RERA detection - - EventList *PB = session->AddEventList(CPAP_PB, EVL_Event); - EventList *LL = session->AddEventList(CPAP_LargeLeak, EVL_Event); - EventList *TOTLEAK = session->AddEventList(CPAP_LeakTotal, EVL_Event); - EventList *LEAK = session->AddEventList(CPAP_Leak, EVL_Event); // TODO: calculated for F5V0, reported by F5V1 and higher - - EventList *RR = session->AddEventList(CPAP_RespRate, EVL_Event); - EventList *TV = session->AddEventList(CPAP_TidalVolume, EVL_Event, 10.0F); - EventList *MV = session->AddEventList(CPAP_MinuteVent, EVL_Event); - EventList *PTB = session->AddEventList(CPAP_PTB, EVL_Event); - - // TB could be on-demand - EventList *TB = session->AddEventList(PRS1_TimedBreath, EVL_Event, 0.1F); // TODO: a gain of 0.1 should affect display, but it doesn't - - EventList *IPAP = session->AddEventList(CPAP_IPAP, EVL_Event, GAIN); - EventList *EPAP = session->AddEventList(CPAP_EPAP, EVL_Event, GAIN); - EventList *PS = session->AddEventList(CPAP_PS, EVL_Event, GAIN); - EventList *IPAPLo = session->AddEventList(CPAP_IPAPLo, EVL_Event, GAIN); - EventList *IPAPHi = session->AddEventList(CPAP_IPAPHi, EVL_Event, GAIN); - - // On-demand channels - EventList *PP = nullptr; - - EventDataType currentPressure=0; - - // Unintentional leak calculation, see zMaskProfile:calcLeak in calcs.cpp for explanation - // Only needed for F5V0: F5V1 and later report it directly - bool calcLeaks = (event->familyVersion == 0) && p_profile->cpap->calculateUnintentionalLeaks(); - EventDataType lpm4 = p_profile->cpap->custom4cmH2OLeaks(); - EventDataType lpm20 = p_profile->cpap->custom20cmH2OLeaks(); - EventDataType lpm = lpm20 - lpm4; - EventDataType ppm = lpm / 16.0; - - qint64 duration; - qint64 t = qint64(event->timestamp) * 1000L; - session->updateFirst(t); - - bool ok; - ok = event->ParseEvents(); - - for (int i=0; i < event->m_parsedData.count(); i++) { - PRS1ParsedEvent* e = event->m_parsedData.at(i); - t = qint64(event->timestamp + e->m_start) * 1000L; - - switch (e->m_type) { - case PRS1EPAPSetEvent::TYPE: - // TODO: The below belong in average channels and then this should go into the EPAP adjustment channel. - break; - case PRS1IPAPAverageEvent::TYPE: - IPAP->AddEvent(t, e->m_value); // TODO: This belongs in an average channel rather than setting channel. - currentPressure = e->m_value; - break; - case PRS1IPAPLowEvent::TYPE: - IPAPLo->AddEvent(t, e->m_value); - break; - case PRS1IPAPHighEvent::TYPE: - IPAPHi->AddEvent(t, e->m_value); - break; - case PRS1EPAPAverageEvent::TYPE: - EPAP->AddEvent(t, e->m_value); // TODO: This belongs in an average channel rather than setting channel. - PS->AddEvent(t, currentPressure - e->m_value); // Pressure Support - break; - case PRS1TimedBreathEvent::TYPE: - // The duration appears to correspond to the length of the timed breath in seconds when multiplied by 0.1 (100ms)! - // TODO: consider changing parsers to use milliseconds for time, since it turns out there's at least one way - // they can express durations less than 1 second. - // TODO: consider allowing OSCAR to record millisecond durations so that the display will say "2.1" instead of "21" or "2". - duration = e->m_duration * 100L; // for now do this here rather than in parser, since parser events don't use milliseconds - TB->AddEvent(t - duration, e->m_duration * 0.1F); // TODO: a gain of 0.1 should render this unnecessary, but gain doesn't seem to work currently - break; - case PRS1ObstructiveApneaEvent::TYPE: - OA->AddEvent(t, e->m_duration); - break; - case PRS1ClearAirwayEvent::TYPE: - CA->AddEvent(t, e->m_duration); - break; - case PRS1HypopneaEvent::TYPE: - HY->AddEvent(t, e->m_duration); - break; - case PRS1FlowLimitationEvent::TYPE: - FL->AddEvent(t, e->m_duration); - break; - case PRS1PeriodicBreathingEvent::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; - PB->AddEvent(t + duration, e->m_duration); - break; - case PRS1LargeLeakEvent::TYPE: - // TODO: see PB comment above. - duration = e->m_duration * 1000L; - LL->AddEvent(t + duration, e->m_duration); - break; - case PRS1TotalLeakEvent::TYPE: - TOTLEAK->AddEvent(t, e->m_value); - // TODO: decide whether to keep this here, shouldn't keep it here just because it's "quicker". - // TODO: compare this value for the reported value for F5V1 and higher? - // TODO: Fix this for 0.125 gain: it assumes 0.1 (dividing by 10.0)... - // Or omit, because F5V1 and above reports unintentional leak below. - if (calcLeaks) { // Much Quicker doing this here than the recalc method. - EventDataType leak = e->m_value; - leak -= (((currentPressure/10.0f) - 4.0) * ppm + lpm4); - if (leak < 0) leak = 0; - LEAK->AddEvent(t, leak); - } - break; - case PRS1LeakEvent::TYPE: - LEAK->AddEvent(t, e->m_value); - break; - case PRS1SnoreEvent::TYPE: // snore count that shows up in flags but not waveform - // TODO: The numeric snore graph is the right way to present this information, - // but it needs to be shifted left 2 minutes, since it's not a starting value - // but a past statistic. - SNORE->AddEvent(t, e->m_value); - if (e->m_value > 0) { - // TODO: currently these get drawn on our waveforms, but they probably shouldn't, - // since they don't have a precise timestamp. They should continue to be drawn - // on the flags overview. - VS2->AddEvent(t, 0); - } - break; - case PRS1VibratorySnoreEvent::TYPE: // real VS marker on waveform - // TODO: These don't need to be drawn separately on the flag overview, since - // they're presumably included in the overall snore count statistic. They should - // continue to be drawn on the waveform, due to their precise timestamp. - VS->AddEvent(t, 0); - break; - case PRS1RespiratoryRateEvent::TYPE: - RR->AddEvent(t, e->m_value); - break; - case PRS1PatientTriggeredBreathsEvent::TYPE: - PTB->AddEvent(t, e->m_value); - break; - case PRS1MinuteVentilationEvent::TYPE: - MV->AddEvent(t, e->m_value); - break; - case PRS1TidalVolumeEvent::TYPE: - TV->AddEvent(t, e->m_value); - break; - case PRS1PressurePulseEvent::TYPE: // Only seen in F5V3, not F5V2 or earlier - if (!PP) { - if (!(PP = session->AddEventList(CPAP_PressurePulse, EVL_Event))) { return false; } - } - PP->AddEvent(t, e->m_value); - break; - case PRS1UnknownDataEvent::TYPE: - // These will show up in chunk YAML and any user alerts will be driven - // by the parser. - break; - default: - qWarning() << "Unknown PRS1 event type" << (int) e->m_type; - break; - } - } - - if (!ok) { - return false; - } - - // If the last event has a non-zero duration, t will not reflect the full duration of the chunk, so update it. - t = qint64(event->timestamp + event->duration) * 1000L; - session->updateLast(t); - session->m_cnt.clear(); - session->m_cph.clear(); - - session->m_valuesummary[CPAP_Pressure].clear(); - session->m_valuesummary.erase(session->m_valuesummary.find(CPAP_Pressure)); - - return true; -} - - // 950P is F5V0 bool PRS1DataChunk::ParseEventsF5V0(void) { @@ -7148,11 +6958,7 @@ bool PRS1Import::ParseEvents() } break; case 5: - if (event->familyVersion == 3) { - res = ParseEventsF5V3(); - } else { - res = ParseF5Events(); - } + res = this->ImportEventsF5V0123(); break; default: qDebug() << "Unknown PRS1 familyVersion" << event->familyVersion; diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.h b/oscar/SleepLib/loader_plugins/prs1_loader.h index 0c711ec5..73a25d64 100644 --- a/oscar/SleepLib/loader_plugins/prs1_loader.h +++ b/oscar/SleepLib/loader_plugins/prs1_loader.h @@ -312,10 +312,8 @@ public: bool ParseEventsF3V3(); //! \brief Parse a single data chunk from a .002 file containing event data for a family 3 ventilator machine (family version 6) bool ParseEventsF3V6(); - //! \brief Parse a single data chunk from a .002 file containing event data for a family 5 ASV machine (which has a different format) - bool ParseF5Events(); - //! \brief Parse a single data chunk from a .002 file containing event data for a family 5 ASV family version 3 machine (which has a different format again) - bool ParseEventsF5V3(); + //! \brief Import a single data chunk from a .002 file containing event data for a family 5 ASV family machine (family versions 0-3) + bool ImportEventsF5V0123(); protected: