From 9a25647c252f5a581c951db284d202da068d826f Mon Sep 17 00:00:00 2001 From: sawinglogz <3787776-sawinglogz@users.noreply.gitlab.com> Date: Tue, 29 Oct 2019 11:14:57 -0400 Subject: [PATCH] Add initial support for PRS1 settings channel (as distinct from statistics). Right now this results in slightly confusing graphs, since sometimes the IPAP/EPAP data for a machine is primarily reported by its settings events, and for other machines by its statistics events. Also, the "average" pressure on F0 looks like it might be effectivley EPAP in single-pressure mode rather than the true average in bi-level mode. Once we decide on the best presentation, we can either update the importer or the display to show the most helpful channels for a given session. --- oscar/Graphs/gLineChart.cpp | 14 ++++--- oscar/SleepLib/loader_plugins/prs1_loader.cpp | 39 +++++++++---------- oscar/SleepLib/machine_common.cpp | 3 +- oscar/SleepLib/machine_common.h | 3 +- oscar/SleepLib/schema.cpp | 6 ++- oscar/daily.cpp | 3 ++ oscar/tests/sessiontests.cpp | 3 ++ 7 files changed, 42 insertions(+), 29 deletions(-) diff --git a/oscar/Graphs/gLineChart.cpp b/oscar/Graphs/gLineChart.cpp index c1efed05..19ee86f5 100644 --- a/oscar/Graphs/gLineChart.cpp +++ b/oscar/Graphs/gLineChart.cpp @@ -1,5 +1,6 @@ -/* gLineChart Implementation +/* gLineChart Implementation * + * Copyright (c) 2019 The OSCAR Team * Copyright (c) 2011-2018 Mark Watkins * * This file is subject to the terms and conditions of the GNU General Public @@ -348,11 +349,11 @@ QString gLineChart::getMetaString(qint64 time) val = m_day->lookupValue(code, time, m_square_plot); lasttext += " "+QString("%1: %2").arg(schema::channel[code].label()).arg(val,0,'f',2); //.arg(schema::channel[code].units()); - if (code == CPAP_IPAP) { + if (code == CPAP_IPAP || code == CPAP_IPAPSet) { ipap = val; addPS = true; } - if (code == CPAP_EPAP) { + if (code == CPAP_EPAP || code == CPAP_EPAPSet) { epap = val; } } @@ -526,7 +527,10 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) float lineThickness = AppSetting->lineThickness()+0.001F; - for (const auto & code : m_codes) { + for (int ic = 0; ic < m_codes.count(); ic++) { + const auto & code = m_codes[ic]; + square_plot = m_square[ic]; // set the mode per-channel + const schema::Channel &chan = schema::channel[code]; //////////////////////////////////////////////////////////////////////// @@ -613,7 +617,7 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) if (m_disable_accel) { accel = false; } - square_plot = m_square_plot; + //square_plot = m_square_plot; // now we set this per-channel above if (accel || num_points > 20000) { // Don't square plot if too many points or waveform square_plot = false; diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp index 3ed46ab1..5c5c52dd 100644 --- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp +++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp @@ -1563,12 +1563,12 @@ static const QHash> PRS1ImportChannelMap // F0 imports pressure-set events and ignores pressure average events. // F5 imports average events and ignores EPAP set events. // F3 imports average events and has no set events. - { PRS1PressureSetEvent::TYPE, { &CPAP_Pressure } }, - { PRS1IPAPSetEvent::TYPE, { &CPAP_IPAP } }, - { PRS1EPAPSetEvent::TYPE, { &CPAP_EPAP, &CPAP_PS } }, // PS is calculated from IPAP and EPAP - { PRS1PressureAverageEvent::TYPE, { /*&CPAP_Pressure*/ } }, // TODO: not currently imported, so don't create the channel + { PRS1PressureSetEvent::TYPE, { &CPAP_PressureSet } }, + { PRS1IPAPSetEvent::TYPE, { &CPAP_IPAPSet, &CPAP_PS } }, // PS is calculated from IPAPset and EPAPset when both are supported (F0) TODO: Should this be a separate channel since it's not a 2-minute average? + { PRS1EPAPSetEvent::TYPE, { &CPAP_EPAPSet } }, // EPAPset is supported on F5 without any corresponding IPAPset, so it shouldn't always create a PS channel + { PRS1PressureAverageEvent::TYPE, { &CPAP_Pressure } }, { PRS1IPAPAverageEvent::TYPE, { &CPAP_IPAP } }, - { PRS1EPAPAverageEvent::TYPE, { &CPAP_EPAP, &CPAP_PS } }, // PS is calculated from IPAP and EPAP + { PRS1EPAPAverageEvent::TYPE, { &CPAP_EPAP, &CPAP_PS } }, // PS is calculated from IPAP and EPAP averages (F3 and F5) { PRS1IPAPLowEvent::TYPE, { &CPAP_IPAPLo } }, { PRS1IPAPHighEvent::TYPE, { &CPAP_IPAPHi } }, @@ -2627,11 +2627,17 @@ bool PRS1Import::ImportEventChunk(PRS1DataChunk* event) { EventDataType currentPressure=0; + const QVector & supported = GetSupportedEvents(event); + + // Calculate PS from IPAP/EPAP set events only when both are supported. This includes F0, but excludes + // F5, which only reports EPAP set events, but both IPAP/EPAP average, from which PS will be calculated. + bool calcPSfromSet = supported.contains(PRS1IPAPSetEvent::TYPE) && supported.contains(PRS1EPAPSetEvent::TYPE); + // Unintentional leak calculation, see zMaskProfile:calcLeak in calcs.cpp for explanation bool calcLeaks = p_profile->cpap->calculateUnintentionalLeaks(); if (calcLeaks) { // Only needed for machines that don't support it directly. - calcLeaks = (GetSupportedEvents(event).contains(PRS1LeakEvent::TYPE) == false); + calcLeaks = (supported.contains(PRS1LeakEvent::TYPE) == false); } EventDataType lpm4 = p_profile->cpap->custom4cmH2OLeaks(); EventDataType lpm20 = p_profile->cpap->custom20cmH2OLeaks(); @@ -2661,30 +2667,21 @@ bool PRS1Import::ImportEventChunk(PRS1DataChunk* event) switch (e->m_type) { case PRS1UnknownDurationEvent::TYPE: // TODO: We should import and graph this as PRS1_0E break; // not imported or displayed - case PRS1PressureSetEvent::TYPE: - AddEvent(channel, t, e->m_value, e->m_gain); - currentPressure = e->m_value; - break; case PRS1IPAPSetEvent::TYPE: + case PRS1IPAPAverageEvent::TYPE: AddEvent(channel, t, e->m_value, e->m_gain); currentPressure = e->m_value; break; case PRS1EPAPSetEvent::TYPE: - if (event->family == 5) break; // TODO: Once there are separate average and setting channels, this special-case can be removed. - PS = *channels.at(1); AddEvent(channel, t, e->m_value, e->m_gain); - AddEvent(PS, t, currentPressure - e->m_value, e->m_gain); // Pressure Support - break; - case PRS1PressureAverageEvent::TYPE: - // TODO, we need OSCAR channels for average stats - break; - case PRS1IPAPAverageEvent::TYPE: - AddEvent(channel, t, e->m_value, e->m_gain); // TODO: This belongs in an average channel rather than setting channel. - currentPressure = e->m_value; + if (calcPSfromSet) { + PS = *(PRS1ImportChannelMap[PRS1IPAPSetEvent::TYPE].at(1)); + AddEvent(PS, t, currentPressure - e->m_value, e->m_gain); // Pressure Support + } break; case PRS1EPAPAverageEvent::TYPE: PS = *channels.at(1); - AddEvent(channel, t, e->m_value, e->m_gain); // TODO: This belongs in an average channel rather than setting channel. + AddEvent(channel, t, e->m_value, e->m_gain); AddEvent(PS, t, currentPressure - e->m_value, e->m_gain); // Pressure Support break; diff --git a/oscar/SleepLib/machine_common.cpp b/oscar/SleepLib/machine_common.cpp index 9be47d60..42ab5735 100644 --- a/oscar/SleepLib/machine_common.cpp +++ b/oscar/SleepLib/machine_common.cpp @@ -23,7 +23,8 @@ ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_EPAPLo, CPAP_EPAP 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_PresReliefMode, CPAP_PresReliefLevel, CPAP_PSMin, CPAP_PSMax, CPAP_Test1, - CPAP_Test2, CPAP_HumidSetting; + CPAP_Test2, CPAP_HumidSetting, + CPAP_PressureSet, CPAP_IPAPSet, CPAP_EPAPSet; ChannelID RMS9_E01, RMS9_E02, RMS9_SetPressure, RMS9_MaskOnTime; diff --git a/oscar/SleepLib/machine_common.h b/oscar/SleepLib/machine_common.h index 16992499..2731ac95 100644 --- a/oscar/SleepLib/machine_common.h +++ b/oscar/SleepLib/machine_common.h @@ -138,7 +138,8 @@ extern ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_EPAPLo, CP 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_PresReliefMode, CPAP_PresReliefLevel, CPAP_Test1, CPAP_Test2; + CPAP_PresReliefMode, CPAP_PresReliefLevel, CPAP_Test1, CPAP_Test2, + CPAP_PressureSet, CPAP_IPAPSet, CPAP_EPAPSet; extern ChannelID RMS9_E01, RMS9_E02, RMS9_SetPressure, RMS9_MaskOnTime; extern ChannelID PRS1_00, PRS1_01, PRS1_08, PRS1_0A, PRS1_0B, PRS1_0C, PRS1_0E, PRS1_0F, CPAP_LargeLeak, diff --git a/oscar/SleepLib/schema.cpp b/oscar/SleepLib/schema.cpp index f6b1633f..0dc60f06 100644 --- a/oscar/SleepLib/schema.cpp +++ b/oscar/SleepLib/schema.cpp @@ -1,5 +1,6 @@ -/* Channel / Schema Implementation +/* Channel / Schema Implementation * + * Copyright (c) 2019 The OSCAR Team * Copyright (c) 2011-2018 Mark Watkins * * This file is subject to the terms and conditions of the GNU General Public @@ -134,6 +135,9 @@ void init() schema::channel.add(GRP_CPAP, new Channel(CPAP_RampTime = 0x1022, SETTING, MT_CPAP, SESSION, "RampTime", QObject::tr("Ramp Time") , QObject::tr("Ramp Delay Period"), QObject::tr("Ramp Time"), STR_UNIT_Minutes, DEFAULT, QColor("black"))); schema::channel.add(GRP_CPAP, new Channel(CPAP_RampPressure = 0x1023, SETTING, MT_CPAP, SESSION, "RampPressure", QObject::tr("Ramp Pressure"), QObject::tr("Starting Ramp Pressure"), QObject::tr("Ramp Pressure"),STR_UNIT_CMH2O, DEFAULT, QColor("black"))); schema::channel.add(GRP_CPAP, new Channel(CPAP_Ramp = 0x1027, SPAN, MT_CPAP, SESSION, "Ramp", QObject::tr("Ramp Event") , QObject::tr("Ramp Event"), QObject::tr("Ramp"), STR_UNIT_EventsPerHour,DEFAULT, QColor("light blue"))); + schema::channel.add(GRP_CPAP, new Channel(CPAP_PressureSet = 0x11A1, WAVEFORM, MT_CPAP, SESSION, "PressureSet", QObject::tr("Pressure Set"), QObject::tr("Pressure Setting"), QObject::tr("Pressure Set"), STR_UNIT_CMH2O, DEFAULT, QColor("dark red"))); + schema::channel.add(GRP_CPAP, new Channel(CPAP_IPAPSet = 0x11A2, WAVEFORM, MT_CPAP, SESSION, "IPAPSet", QObject::tr("IPAP Set"), QObject::tr("IPAP Setting"), QObject::tr("IPAP Set"), STR_UNIT_CMH2O, DEFAULT, QColor("dark red"))); + schema::channel.add(GRP_CPAP, new Channel(CPAP_EPAPSet = 0x11A3, WAVEFORM, MT_CPAP, SESSION, "EPAPSet", QObject::tr("EPAP Set"), QObject::tr("EPAP Setting"), QObject::tr("EPAP Set"), STR_UNIT_CMH2O, DEFAULT, QColor("dark green"))); // Flags schema::channel.add(GRP_CPAP, new Channel(CPAP_CSR = 0x1000, SPAN, MT_CPAP, SESSION, "CSR", QObject::tr("Cheyne Stokes Respiration"), QObject::tr("An abnormal period of Cheyne Stokes Respiration"), QObject::tr("CSR"), STR_UNIT_Percentage,DEFAULT, COLOR_CSR)); diff --git a/oscar/daily.cpp b/oscar/daily.cpp index f0538202..dbda5e79 100644 --- a/oscar/daily.cpp +++ b/oscar/daily.cpp @@ -349,6 +349,9 @@ Daily::Daily(QWidget *parent,gGraphView * shared) pc->addPlot(CPAP_IPAPLo, square); pc->addPlot(CPAP_IPAP, square); pc->addPlot(CPAP_IPAPHi, square); + pc->addPlot(CPAP_PressureSet, false); + pc->addPlot(CPAP_EPAPSet, false); + pc->addPlot(CPAP_IPAPSet, false); // Create Timea at Pressure graph gGraph * TAP2; diff --git a/oscar/tests/sessiontests.cpp b/oscar/tests/sessiontests.cpp index e63b27df..06373810 100644 --- a/oscar/tests/sessiontests.cpp +++ b/oscar/tests/sessiontests.cpp @@ -139,6 +139,9 @@ static QString eventChannel(ChannelID i) CHANNELNAME(PRS1_0C); CHANNELNAME(PRS1_0E); CHANNELNAME(PRS1_15); + CHANNELNAME(CPAP_PressureSet); + CHANNELNAME(CPAP_IPAPSet); + CHANNELNAME(CPAP_EPAPSet); s = hex(i); qDebug() << "event channel" << qPrintable(s); } while(false);