diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp index 1d37a596..ca71b4ff 100644 --- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp +++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp @@ -19,6 +19,7 @@ #include "SleepLib/schema.h" #include "prs1_loader.h" +#include "prs1_parser.h" #include "SleepLib/session.h" #include "SleepLib/calcs.h" #include "rawdata.h" @@ -198,6 +199,12 @@ static QString ts(qint64 msecs) } +static QString hex(int i) +{ + return QString("0x") + QString::number(i, 16).toUpper(); +} + + // TODO: See the LogUnexpectedMessage TODO about generalizing this for other loaders. // Right now this macro assumes that it's called within a method that has a "loader" member // that points to the PRS1Loader* instance that's calling it. @@ -1380,479 +1387,6 @@ void PRS1Loader::ScanFiles(const QStringList & paths, int sessionid_base, Machin } -//******************************************************************************************** -// Internal PRS1 parsed data types -//******************************************************************************************** - -// For new events, add an enum here and then a class below with an PRS1_*_EVENT macro -enum PRS1ParsedEventType -{ - EV_PRS1_RAW = -1, // these only get logged - EV_PRS1_UNKNOWN = 0, // these have their value graphed - EV_PRS1_TB, - EV_PRS1_OA, - EV_PRS1_CA, - EV_PRS1_FL, - EV_PRS1_PB, - EV_PRS1_LL, - EV_PRS1_VB, // UNCONFIRMED - EV_PRS1_HY, - EV_PRS1_OA_COUNT, // F3V3 only - EV_PRS1_CA_COUNT, // F3V3 only - EV_PRS1_HY_COUNT, // F3V3 only - EV_PRS1_TOTLEAK, - EV_PRS1_LEAK, // unintentional leak - EV_PRS1_AUTO_PRESSURE_SET, - EV_PRS1_PRESSURE_SET, - EV_PRS1_IPAP_SET, - EV_PRS1_EPAP_SET, - EV_PRS1_PRESSURE_AVG, - EV_PRS1_FLEX_PRESSURE_AVG, - EV_PRS1_IPAP_AVG, - EV_PRS1_IPAPLOW, - EV_PRS1_IPAPHIGH, - EV_PRS1_EPAP_AVG, - EV_PRS1_RR, - EV_PRS1_PTB, - EV_PRS1_MV, - EV_PRS1_TV, - EV_PRS1_SNORE, - EV_PRS1_VS, - EV_PRS1_PP, - EV_PRS1_RERA, - EV_PRS1_FLOWRATE, - EV_PRS1_TEST1, - EV_PRS1_TEST2, - EV_PRS1_SETTING, - EV_PRS1_SLICE, - EV_PRS1_DISCONNECT_ALARM, - EV_PRS1_APNEA_ALARM, - EV_PRS1_LOW_MV_ALARM, - EV_PRS1_SNORES_AT_PRESSURE, - EV_PRS1_INTERVAL_BOUNDARY, // An artificial internal-only event used to separate stat intervals -}; - -enum PRS1ParsedEventUnit -{ - PRS1_UNIT_NONE, - PRS1_UNIT_CMH2O, - PRS1_UNIT_ML, - PRS1_UNIT_S, -}; - -enum PRS1ParsedSettingType -{ - PRS1_SETTING_CPAP_MODE, - PRS1_SETTING_AUTO_TRIAL, - PRS1_SETTING_PRESSURE, - PRS1_SETTING_PRESSURE_MIN, - PRS1_SETTING_PRESSURE_MAX, - PRS1_SETTING_EPAP, - PRS1_SETTING_EPAP_MIN, - PRS1_SETTING_EPAP_MAX, - PRS1_SETTING_IPAP, - PRS1_SETTING_IPAP_MIN, - PRS1_SETTING_IPAP_MAX, - PRS1_SETTING_PS, - PRS1_SETTING_PS_MIN, - 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_EZ_START, - PRS1_SETTING_FLEX_LOCK, - 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, - PRS1_SETTING_HUMID_STATUS, - PRS1_SETTING_HUMID_MODE, - PRS1_SETTING_HEATED_TUBE_TEMP, - PRS1_SETTING_HUMID_LEVEL, - PRS1_SETTING_MASK_RESIST_LOCK, - PRS1_SETTING_MASK_RESIST_SETTING, - PRS1_SETTING_HOSE_DIAMETER, - PRS1_SETTING_TUBING_LOCK, - PRS1_SETTING_AUTO_ON, - PRS1_SETTING_AUTO_OFF, - PRS1_SETTING_APNEA_ALARM, - PRS1_SETTING_DISCONNECT_ALARM, // Is this any different from mask alert? - PRS1_SETTING_LOW_MV_ALARM, - PRS1_SETTING_LOW_TV_ALARM, - PRS1_SETTING_MASK_ALERT, - PRS1_SETTING_SHOW_AHI, - PRS1_SETTING_HUMID_TARGET_TIME, -}; - - -static QString timeStr(int t); -static QString byteList(QByteArray data, int limit=-1); -static QString hex(int i); -static QString parsedSettingTypeName(PRS1ParsedSettingType t); -static QString parsedModeName(int m); - - -class PRS1ParsedEvent -{ -public: - PRS1ParsedEventType m_type; - int m_start; // seconds relative to chunk timestamp at which this event began - int m_duration; - int m_value; - float m_offset; - float m_gain; - PRS1ParsedEventUnit m_unit; - - inline float value(void) { return (m_value * m_gain) + m_offset; } - - static const PRS1ParsedEventType TYPE = EV_PRS1_UNKNOWN; - static constexpr float GAIN = 1.0; - static const PRS1ParsedEventUnit UNIT = PRS1_UNIT_NONE; - - virtual QMap contents(void) = 0; - -protected: - PRS1ParsedEvent(PRS1ParsedEventType type, int start) - : m_type(type), m_start(start), m_duration(0), m_value(0), m_offset(0.0), m_gain(GAIN), m_unit(UNIT) - { - } -public: - virtual ~PRS1ParsedEvent() - { - } -}; - - -class PRS1IntervalBoundaryEvent : public PRS1ParsedEvent -{ -public: - virtual QMap contents(void) - { - QMap out; - out["start"] = timeStr(m_start); - return out; - } - - static const PRS1ParsedEventType TYPE = EV_PRS1_INTERVAL_BOUNDARY; - - PRS1IntervalBoundaryEvent(int start) : PRS1ParsedEvent(TYPE, start) {} -}; - - -class PRS1ParsedDurationEvent : public PRS1ParsedEvent -{ -public: - virtual QMap contents(void) - { - QMap out; - out["start"] = timeStr(m_start); - out["duration"] = timeStr(m_duration); - return out; - } - - static const PRS1ParsedEventUnit UNIT = PRS1_UNIT_S; - - PRS1ParsedDurationEvent(PRS1ParsedEventType type, int start, int duration) : PRS1ParsedEvent(type, start) { m_duration = duration; } -}; -const PRS1ParsedEventUnit PRS1ParsedDurationEvent::UNIT; - - -class PRS1ParsedValueEvent : public PRS1ParsedEvent -{ -public: - virtual QMap contents(void) - { - QMap out; - out["start"] = timeStr(m_start); - out["value"] = QString::number(value()); - return out; - } - -protected: - PRS1ParsedValueEvent(PRS1ParsedEventType type, int start, int value) : PRS1ParsedEvent(type, start) { m_value = value; } -}; - -/* -class PRS1UnknownValueEvent : public PRS1ParsedValueEvent -{ -public: - virtual QMap contents(void) - { - QMap out; - out["start"] = timeStr(m_start); - out["code"] = hex(m_code); - out["value"] = QString::number(value()); - return out; - } - - int m_code; - PRS1UnknownValueEvent(int code, int start, int value, float gain=1.0) : PRS1ParsedValueEvent(TYPE, start, value), m_code(code) { m_gain = gain; } -}; -*/ - -class PRS1UnknownDataEvent : public PRS1ParsedEvent -{ -public: - virtual QMap contents(void) - { - QMap out; - out["pos"] = QString::number(m_pos); - out["data"] = byteList(m_data); - return out; - } - - static const PRS1ParsedEventType TYPE = EV_PRS1_RAW; - - int m_pos; - unsigned char m_code; - QByteArray m_data; - - PRS1UnknownDataEvent(const QByteArray & data, int pos, int len=18) - : PRS1ParsedEvent(TYPE, 0) - { - m_pos = pos; - m_data = data.mid(pos, len); - Q_ASSERT(m_data.size() >= 1); - m_code = m_data.at(0); - } -}; - -class PRS1PressureEvent : public PRS1ParsedValueEvent -{ -public: - static constexpr float GAIN = 0.1; - static const PRS1ParsedEventUnit UNIT = PRS1_UNIT_CMH2O; - - PRS1PressureEvent(PRS1ParsedEventType type, int start, int value, float gain=GAIN) - : PRS1ParsedValueEvent(type, start, value) - { - m_gain = gain; - m_unit = UNIT; - } -}; - -class PRS1TidalVolumeEvent : public PRS1ParsedValueEvent -{ -public: - static const PRS1ParsedEventType TYPE = EV_PRS1_TV; - - static constexpr float GAIN = 10.0; - static const PRS1ParsedEventUnit UNIT = PRS1_UNIT_ML; - - PRS1TidalVolumeEvent(int start, int value) - : PRS1ParsedValueEvent(TYPE, start, value) - { - m_gain = GAIN; - m_unit = UNIT; - } -}; -const PRS1ParsedEventType PRS1TidalVolumeEvent::TYPE; - -class PRS1ParsedSettingEvent : public PRS1ParsedValueEvent -{ -public: - virtual QMap contents(void) - { - QMap out; - QString v; - if (m_setting == PRS1_SETTING_CPAP_MODE) { - v = parsedModeName(value()); - } else { - v = QString::number(value()); - } - out[parsedSettingTypeName(m_setting)] = v; - return out; - } - - static const PRS1ParsedEventType TYPE = EV_PRS1_SETTING; - PRS1ParsedSettingType m_setting; - - PRS1ParsedSettingEvent(PRS1ParsedSettingType setting, int value) : PRS1ParsedValueEvent(TYPE, 0, value), m_setting(setting) {} -}; - -class PRS1ScaledSettingEvent : public PRS1ParsedSettingEvent -{ -public: - PRS1ScaledSettingEvent(PRS1ParsedSettingType setting, int value, float gain) - : PRS1ParsedSettingEvent(setting, value) - { - m_gain = gain; - } -}; - -class PRS1PressureSettingEvent : public PRS1ScaledSettingEvent -{ -public: - static constexpr float GAIN = PRS1PressureEvent::GAIN; - static const PRS1ParsedEventUnit UNIT = PRS1PressureEvent::UNIT; - - PRS1PressureSettingEvent(PRS1ParsedSettingType setting, int value, float gain=GAIN) - : PRS1ScaledSettingEvent(setting, value, gain) - { - m_unit = UNIT; - } -}; - -class PRS1ParsedSliceEvent : public PRS1ParsedValueEvent -{ -public: - virtual QMap contents(void) - { - QMap out; - out["start"] = timeStr(m_start); - QString s; - switch ((SliceStatus) m_value) { - case MaskOn: s = "MaskOn"; break; - case MaskOff: s = "MaskOff"; break; - case EquipmentOff: s = "EquipmentOff"; break; - case UnknownStatus: s = "Unknown"; break; - } - out["status"] = s; - return out; - } - - static const PRS1ParsedEventType TYPE = EV_PRS1_SLICE; - - PRS1ParsedSliceEvent(int start, SliceStatus status) : PRS1ParsedValueEvent(TYPE, start, (int) status) {} -}; - - -class PRS1ParsedAlarmEvent : public PRS1ParsedEvent -{ -public: - virtual QMap contents(void) - { - QMap out; - out["start"] = timeStr(m_start); - return out; - } - -protected: - PRS1ParsedAlarmEvent(PRS1ParsedEventType type, int start, int /*unused*/) : PRS1ParsedEvent(type, start) {} -}; - - -class PRS1SnoresAtPressureEvent : public PRS1PressureEvent -{ -public: - static const PRS1ParsedEventType TYPE = EV_PRS1_SNORES_AT_PRESSURE; - - PRS1SnoresAtPressureEvent(int start, int kind, int pressure, int count, float gain=GAIN) - : PRS1PressureEvent(TYPE, start, pressure, gain) - { - m_kind = kind; - m_count = count; - } - - virtual QMap contents(void) - { - QString label; - switch (m_kind) { - case 0: label = "pressure"; break; - case 1: label = "epap"; break; - case 2: label = "ipap"; break; - default: label = "unknown_pressure"; break; - } - - QMap out; - out["start"] = timeStr(m_start); - out[label] = QString::number(value()); - out["count"] = QString::number(m_count); - return out; - } - -protected: - int m_kind; - // m_value is pressure - int m_count; -}; -const PRS1ParsedEventType PRS1SnoresAtPressureEvent::TYPE; - - -#define _PRS1_EVENT(T, E, P, ARG) \ -class T : public P \ -{ \ -public: \ - static const PRS1ParsedEventType TYPE = E; \ - T(int start, int ARG) : P(TYPE, start, ARG) {} \ -}; \ -const PRS1ParsedEventType T::TYPE -#define PRS1_DURATION_EVENT(T, E) _PRS1_EVENT(T, E, PRS1ParsedDurationEvent, duration) -#define PRS1_VALUE_EVENT(T, E) _PRS1_EVENT(T, E, PRS1ParsedValueEvent, value) -#define PRS1_ALARM_EVENT(T, E) _PRS1_EVENT(T, E, PRS1ParsedAlarmEvent, value) -#define PRS1_PRESSURE_EVENT(T, E) \ -class T : public PRS1PressureEvent \ -{ \ -public: \ - static const PRS1ParsedEventType TYPE = E; \ - T(int start, int value, float gain=PRS1PressureEvent::GAIN) : PRS1PressureEvent(TYPE, start, value, gain) {} \ -}; \ -const PRS1ParsedEventType T::TYPE - -PRS1_DURATION_EVENT(PRS1TimedBreathEvent, EV_PRS1_TB); -PRS1_DURATION_EVENT(PRS1ObstructiveApneaEvent, EV_PRS1_OA); -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(PRS1VariableBreathingEvent, EV_PRS1_VB); -PRS1_DURATION_EVENT(PRS1HypopneaEvent, EV_PRS1_HY); - -PRS1_VALUE_EVENT(PRS1TotalLeakEvent, EV_PRS1_TOTLEAK); -PRS1_VALUE_EVENT(PRS1LeakEvent, EV_PRS1_LEAK); - -PRS1_PRESSURE_EVENT(PRS1AutoPressureSetEvent, EV_PRS1_AUTO_PRESSURE_SET); -PRS1_PRESSURE_EVENT(PRS1PressureSetEvent, EV_PRS1_PRESSURE_SET); -PRS1_PRESSURE_EVENT(PRS1IPAPSetEvent, EV_PRS1_IPAP_SET); -PRS1_PRESSURE_EVENT(PRS1EPAPSetEvent, EV_PRS1_EPAP_SET); -PRS1_PRESSURE_EVENT(PRS1PressureAverageEvent, EV_PRS1_PRESSURE_AVG); -PRS1_PRESSURE_EVENT(PRS1FlexPressureAverageEvent, EV_PRS1_FLEX_PRESSURE_AVG); -PRS1_PRESSURE_EVENT(PRS1IPAPAverageEvent, EV_PRS1_IPAP_AVG); -PRS1_PRESSURE_EVENT(PRS1IPAPHighEvent, EV_PRS1_IPAPHIGH); -PRS1_PRESSURE_EVENT(PRS1IPAPLowEvent, EV_PRS1_IPAPLOW); -PRS1_PRESSURE_EVENT(PRS1EPAPAverageEvent, EV_PRS1_EPAP_AVG); - -PRS1_VALUE_EVENT(PRS1RespiratoryRateEvent, EV_PRS1_RR); -PRS1_VALUE_EVENT(PRS1PatientTriggeredBreathsEvent, EV_PRS1_PTB); -PRS1_VALUE_EVENT(PRS1MinuteVentilationEvent, EV_PRS1_MV); -PRS1_VALUE_EVENT(PRS1SnoreEvent, EV_PRS1_SNORE); -PRS1_VALUE_EVENT(PRS1VibratorySnoreEvent, EV_PRS1_VS); -PRS1_VALUE_EVENT(PRS1PressurePulseEvent, EV_PRS1_PP); -PRS1_VALUE_EVENT(PRS1RERAEvent, EV_PRS1_RERA); // TODO: should this really be a duration event? -PRS1_VALUE_EVENT(PRS1FlowRateEvent, EV_PRS1_FLOWRATE); // TODO: is this a single event or an index/hour? -PRS1_VALUE_EVENT(PRS1Test1Event, EV_PRS1_TEST1); -PRS1_VALUE_EVENT(PRS1Test2Event, EV_PRS1_TEST2); -PRS1_VALUE_EVENT(PRS1HypopneaCount, EV_PRS1_HY_COUNT); // F3V3 only -PRS1_VALUE_EVENT(PRS1ClearAirwayCount, EV_PRS1_CA_COUNT); // F3V3 only -PRS1_VALUE_EVENT(PRS1ObstructiveApneaCount, EV_PRS1_OA_COUNT); // F3V3 only - -PRS1_ALARM_EVENT(PRS1DisconnectAlarmEvent, EV_PRS1_DISCONNECT_ALARM); -PRS1_ALARM_EVENT(PRS1ApneaAlarmEvent, EV_PRS1_APNEA_ALARM); -PRS1_ALARM_EVENT(PRS1LowMinuteVentilationAlarmEvent, EV_PRS1_LOW_MV_ALARM); - -enum PRS1Mode { - PRS1_MODE_UNKNOWN = -1, - 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_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. -const QVector & GetSupportedEvents(const PRS1DataChunk* chunk); - // The set of PRS1 "on-demand" channels that only get created on import if the session // contains events of that type. Any channels not on this list always get created if // they're reported/supported by the parser. @@ -1933,187 +1467,6 @@ static const QHash> PRS1ImportChannelMap //******************************************************************************************** -static QString hex(int i) -{ - return QString("0x") + QString::number(i, 16).toUpper(); -} - -#define ENUMSTRING(ENUM) case ENUM: s = QStringLiteral(#ENUM); break -static QString parsedEventTypeName(PRS1ParsedEventType t) -{ - QString s; - switch (t) { - ENUMSTRING(EV_PRS1_RAW); - ENUMSTRING(EV_PRS1_UNKNOWN); - ENUMSTRING(EV_PRS1_TB); - ENUMSTRING(EV_PRS1_OA); - ENUMSTRING(EV_PRS1_CA); - ENUMSTRING(EV_PRS1_FL); - ENUMSTRING(EV_PRS1_PB); - ENUMSTRING(EV_PRS1_LL); - ENUMSTRING(EV_PRS1_VB); - ENUMSTRING(EV_PRS1_HY); - ENUMSTRING(EV_PRS1_OA_COUNT); - ENUMSTRING(EV_PRS1_CA_COUNT); - ENUMSTRING(EV_PRS1_HY_COUNT); - ENUMSTRING(EV_PRS1_TOTLEAK); - ENUMSTRING(EV_PRS1_LEAK); - ENUMSTRING(EV_PRS1_AUTO_PRESSURE_SET); - ENUMSTRING(EV_PRS1_PRESSURE_SET); - ENUMSTRING(EV_PRS1_IPAP_SET); - ENUMSTRING(EV_PRS1_EPAP_SET); - ENUMSTRING(EV_PRS1_PRESSURE_AVG); - ENUMSTRING(EV_PRS1_FLEX_PRESSURE_AVG); - ENUMSTRING(EV_PRS1_IPAP_AVG); - ENUMSTRING(EV_PRS1_IPAPLOW); - ENUMSTRING(EV_PRS1_IPAPHIGH); - ENUMSTRING(EV_PRS1_EPAP_AVG); - ENUMSTRING(EV_PRS1_RR); - ENUMSTRING(EV_PRS1_PTB); - ENUMSTRING(EV_PRS1_MV); - ENUMSTRING(EV_PRS1_TV); - ENUMSTRING(EV_PRS1_SNORE); - ENUMSTRING(EV_PRS1_VS); - ENUMSTRING(EV_PRS1_PP); - ENUMSTRING(EV_PRS1_RERA); - ENUMSTRING(EV_PRS1_FLOWRATE); - ENUMSTRING(EV_PRS1_TEST1); - ENUMSTRING(EV_PRS1_TEST2); - ENUMSTRING(EV_PRS1_SETTING); - ENUMSTRING(EV_PRS1_SLICE); - ENUMSTRING(EV_PRS1_DISCONNECT_ALARM); - ENUMSTRING(EV_PRS1_APNEA_ALARM); - ENUMSTRING(EV_PRS1_LOW_MV_ALARM); - ENUMSTRING(EV_PRS1_SNORES_AT_PRESSURE); - ENUMSTRING(EV_PRS1_INTERVAL_BOUNDARY); - default: - s = hex(t); - qDebug() << "Unknown PRS1ParsedEventType type:" << qPrintable(s); - return s; - } - return s.mid(8).toLower(); // lop off initial EV_PRS1_ -} - -static QString parsedSettingTypeName(PRS1ParsedSettingType t) -{ - QString s; - switch (t) { - ENUMSTRING(PRS1_SETTING_CPAP_MODE); - ENUMSTRING(PRS1_SETTING_AUTO_TRIAL); - ENUMSTRING(PRS1_SETTING_PRESSURE); - ENUMSTRING(PRS1_SETTING_PRESSURE_MIN); - ENUMSTRING(PRS1_SETTING_PRESSURE_MAX); - ENUMSTRING(PRS1_SETTING_EPAP); - ENUMSTRING(PRS1_SETTING_EPAP_MIN); - ENUMSTRING(PRS1_SETTING_EPAP_MAX); - ENUMSTRING(PRS1_SETTING_IPAP); - ENUMSTRING(PRS1_SETTING_IPAP_MIN); - ENUMSTRING(PRS1_SETTING_IPAP_MAX); - ENUMSTRING(PRS1_SETTING_PS); - ENUMSTRING(PRS1_SETTING_PS_MIN); - 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_EZ_START); - ENUMSTRING(PRS1_SETTING_FLEX_LOCK); - 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); - ENUMSTRING(PRS1_SETTING_HUMID_STATUS); - ENUMSTRING(PRS1_SETTING_HUMID_MODE); - ENUMSTRING(PRS1_SETTING_HEATED_TUBE_TEMP); - ENUMSTRING(PRS1_SETTING_HUMID_LEVEL); - ENUMSTRING(PRS1_SETTING_HUMID_TARGET_TIME); - ENUMSTRING(PRS1_SETTING_MASK_RESIST_LOCK); - ENUMSTRING(PRS1_SETTING_MASK_RESIST_SETTING); - 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); - ENUMSTRING(PRS1_SETTING_DISCONNECT_ALARM); - ENUMSTRING(PRS1_SETTING_LOW_MV_ALARM); - ENUMSTRING(PRS1_SETTING_LOW_TV_ALARM); - ENUMSTRING(PRS1_SETTING_MASK_ALERT); - ENUMSTRING(PRS1_SETTING_SHOW_AHI); - default: - s = hex(t); - qDebug() << "Unknown PRS1ParsedSettingType type:" << qPrintable(s); - return s; - } - return s.mid(13).toLower(); // lop off initial PRS1_SETTING_ -} - -static QString parsedModeName(int m) -{ - QString s; - switch ((PRS1Mode) m) { - ENUMSTRING(PRS1_MODE_UNKNOWN); // TODO: Remove this when all the parsers are complete. - ENUMSTRING(PRS1_MODE_CPAP); - ENUMSTRING(PRS1_MODE_CPAPCHECK); - ENUMSTRING(PRS1_MODE_AUTOTRIAL); - ENUMSTRING(PRS1_MODE_AUTOCPAP); - ENUMSTRING(PRS1_MODE_BILEVEL); - ENUMSTRING(PRS1_MODE_AUTOBILEVEL); - ENUMSTRING(PRS1_MODE_ASV); - 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); - return s; - } - return s.mid(10).toLower(); // lop off initial PRS1_MODE_ -} - -static QString timeStr(int t) -{ - int h = t / 3600; - int m = (t - (h * 3600)) / 60; - int s = t % 60; -#if 1 - // Optimized after profiling regression tests. - return QString::asprintf("%02d:%02d:%02d", h, m, s); -#else - // Unoptimized original, slows down regression tests. - return QString("%1:%2:%3").arg(h, 2, 10, QChar('0')).arg(m, 2, 10, QChar('0')).arg(s, 2, 10, QChar('0')); -#endif -} - -static QString byteList(QByteArray data, int limit) -{ - int count = data.size(); - if (limit == -1 || limit > count) limit = count; - QStringList l; - for (int i = 0; i < limit; i++) { - l.push_back(QString( "%1" ).arg((int) data[i] & 0xFF, 2, 16, QChar('0') ).toUpper()); - } - if (limit < count) l.push_back("..."); - QString s = l.join(" "); - return s; -} - -QString _PRS1ParsedEventName(PRS1ParsedEvent* e) -{ - return parsedEventTypeName(e->m_type); -} - -QMap _PRS1ParsedEventContents(PRS1ParsedEvent* e) -{ - return e->contents(); -} - -//******************************************************************************************** - static QString DumpEvent(int t, int code, const unsigned char* data, int size) { diff --git a/oscar/SleepLib/loader_plugins/prs1_parser.cpp b/oscar/SleepLib/loader_plugins/prs1_parser.cpp new file mode 100644 index 00000000..5411922b --- /dev/null +++ b/oscar/SleepLib/loader_plugins/prs1_parser.cpp @@ -0,0 +1,326 @@ +/* SleepLib PRS1 Loader Parser Implementation + * + * Copyright (c) 2019-2021 The OSCAR Team + * Portions copyright (c) 2011-2018 Mark Watkins + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of the source code + * for more details. */ + + +#include "prs1_parser.h" +#include + + +const PRS1ParsedEventType PRS1TidalVolumeEvent::TYPE; +const PRS1ParsedEventType PRS1SnoresAtPressureEvent::TYPE; + +const PRS1ParsedEventType PRS1TimedBreathEvent::TYPE; +const PRS1ParsedEventType PRS1ObstructiveApneaEvent::TYPE; +const PRS1ParsedEventType PRS1ClearAirwayEvent::TYPE; +const PRS1ParsedEventType PRS1FlowLimitationEvent::TYPE; +const PRS1ParsedEventType PRS1PeriodicBreathingEvent::TYPE; +const PRS1ParsedEventType PRS1LargeLeakEvent::TYPE; +const PRS1ParsedEventType PRS1VariableBreathingEvent::TYPE; +const PRS1ParsedEventType PRS1HypopneaEvent::TYPE; +const PRS1ParsedEventType PRS1TotalLeakEvent::TYPE; +const PRS1ParsedEventType PRS1LeakEvent::TYPE; +const PRS1ParsedEventType PRS1AutoPressureSetEvent::TYPE; +const PRS1ParsedEventType PRS1PressureSetEvent::TYPE; +const PRS1ParsedEventType PRS1IPAPSetEvent::TYPE; +const PRS1ParsedEventType PRS1EPAPSetEvent::TYPE; +const PRS1ParsedEventType PRS1PressureAverageEvent::TYPE; +const PRS1ParsedEventType PRS1FlexPressureAverageEvent::TYPE; +const PRS1ParsedEventType PRS1IPAPAverageEvent::TYPE; +const PRS1ParsedEventType PRS1IPAPHighEvent::TYPE; +const PRS1ParsedEventType PRS1IPAPLowEvent::TYPE; +const PRS1ParsedEventType PRS1EPAPAverageEvent::TYPE; +const PRS1ParsedEventType PRS1RespiratoryRateEvent::TYPE; +const PRS1ParsedEventType PRS1PatientTriggeredBreathsEvent::TYPE; +const PRS1ParsedEventType PRS1MinuteVentilationEvent::TYPE; +const PRS1ParsedEventType PRS1SnoreEvent::TYPE; +const PRS1ParsedEventType PRS1VibratorySnoreEvent::TYPE; +const PRS1ParsedEventType PRS1PressurePulseEvent::TYPE; +const PRS1ParsedEventType PRS1RERAEvent::TYPE; +const PRS1ParsedEventType PRS1FlowRateEvent::TYPE; +const PRS1ParsedEventType PRS1Test1Event::TYPE; +const PRS1ParsedEventType PRS1Test2Event::TYPE; +const PRS1ParsedEventType PRS1HypopneaCount::TYPE; +const PRS1ParsedEventType PRS1ClearAirwayCount::TYPE; +const PRS1ParsedEventType PRS1ObstructiveApneaCount::TYPE; +//const PRS1ParsedEventType PRS1DisconnectAlarmEvent::TYPE; +const PRS1ParsedEventType PRS1ApneaAlarmEvent::TYPE; +//const PRS1ParsedEventType PRS1LowMinuteVentilationAlarmEvent::TYPE; + + +// MARK: Render parsed events as text + +static QString hex(int i) +{ + return QString("0x") + QString::number(i, 16).toUpper(); +} + +#define ENUMSTRING(ENUM) case ENUM: s = QStringLiteral(#ENUM); break +static QString parsedEventTypeName(PRS1ParsedEventType t) +{ + QString s; + switch (t) { + ENUMSTRING(EV_PRS1_RAW); + ENUMSTRING(EV_PRS1_UNKNOWN); + ENUMSTRING(EV_PRS1_TB); + ENUMSTRING(EV_PRS1_OA); + ENUMSTRING(EV_PRS1_CA); + ENUMSTRING(EV_PRS1_FL); + ENUMSTRING(EV_PRS1_PB); + ENUMSTRING(EV_PRS1_LL); + ENUMSTRING(EV_PRS1_VB); + ENUMSTRING(EV_PRS1_HY); + ENUMSTRING(EV_PRS1_OA_COUNT); + ENUMSTRING(EV_PRS1_CA_COUNT); + ENUMSTRING(EV_PRS1_HY_COUNT); + ENUMSTRING(EV_PRS1_TOTLEAK); + ENUMSTRING(EV_PRS1_LEAK); + ENUMSTRING(EV_PRS1_AUTO_PRESSURE_SET); + ENUMSTRING(EV_PRS1_PRESSURE_SET); + ENUMSTRING(EV_PRS1_IPAP_SET); + ENUMSTRING(EV_PRS1_EPAP_SET); + ENUMSTRING(EV_PRS1_PRESSURE_AVG); + ENUMSTRING(EV_PRS1_FLEX_PRESSURE_AVG); + ENUMSTRING(EV_PRS1_IPAP_AVG); + ENUMSTRING(EV_PRS1_IPAPLOW); + ENUMSTRING(EV_PRS1_IPAPHIGH); + ENUMSTRING(EV_PRS1_EPAP_AVG); + ENUMSTRING(EV_PRS1_RR); + ENUMSTRING(EV_PRS1_PTB); + ENUMSTRING(EV_PRS1_MV); + ENUMSTRING(EV_PRS1_TV); + ENUMSTRING(EV_PRS1_SNORE); + ENUMSTRING(EV_PRS1_VS); + ENUMSTRING(EV_PRS1_PP); + ENUMSTRING(EV_PRS1_RERA); + ENUMSTRING(EV_PRS1_FLOWRATE); + ENUMSTRING(EV_PRS1_TEST1); + ENUMSTRING(EV_PRS1_TEST2); + ENUMSTRING(EV_PRS1_SETTING); + ENUMSTRING(EV_PRS1_SLICE); + ENUMSTRING(EV_PRS1_DISCONNECT_ALARM); + ENUMSTRING(EV_PRS1_APNEA_ALARM); + ENUMSTRING(EV_PRS1_LOW_MV_ALARM); + ENUMSTRING(EV_PRS1_SNORES_AT_PRESSURE); + ENUMSTRING(EV_PRS1_INTERVAL_BOUNDARY); + default: + s = hex(t); + qDebug() << "Unknown PRS1ParsedEventType type:" << qPrintable(s); + return s; + } + return s.mid(8).toLower(); // lop off initial EV_PRS1_ +} + +static QString parsedSettingTypeName(PRS1ParsedSettingType t) +{ + QString s; + switch (t) { + ENUMSTRING(PRS1_SETTING_CPAP_MODE); + ENUMSTRING(PRS1_SETTING_AUTO_TRIAL); + ENUMSTRING(PRS1_SETTING_PRESSURE); + ENUMSTRING(PRS1_SETTING_PRESSURE_MIN); + ENUMSTRING(PRS1_SETTING_PRESSURE_MAX); + ENUMSTRING(PRS1_SETTING_EPAP); + ENUMSTRING(PRS1_SETTING_EPAP_MIN); + ENUMSTRING(PRS1_SETTING_EPAP_MAX); + ENUMSTRING(PRS1_SETTING_IPAP); + ENUMSTRING(PRS1_SETTING_IPAP_MIN); + ENUMSTRING(PRS1_SETTING_IPAP_MAX); + ENUMSTRING(PRS1_SETTING_PS); + ENUMSTRING(PRS1_SETTING_PS_MIN); + 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_EZ_START); + ENUMSTRING(PRS1_SETTING_FLEX_LOCK); + 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); + ENUMSTRING(PRS1_SETTING_HUMID_STATUS); + ENUMSTRING(PRS1_SETTING_HUMID_MODE); + ENUMSTRING(PRS1_SETTING_HEATED_TUBE_TEMP); + ENUMSTRING(PRS1_SETTING_HUMID_LEVEL); + ENUMSTRING(PRS1_SETTING_HUMID_TARGET_TIME); + ENUMSTRING(PRS1_SETTING_MASK_RESIST_LOCK); + ENUMSTRING(PRS1_SETTING_MASK_RESIST_SETTING); + 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); + ENUMSTRING(PRS1_SETTING_DISCONNECT_ALARM); + ENUMSTRING(PRS1_SETTING_LOW_MV_ALARM); + ENUMSTRING(PRS1_SETTING_LOW_TV_ALARM); + ENUMSTRING(PRS1_SETTING_MASK_ALERT); + ENUMSTRING(PRS1_SETTING_SHOW_AHI); + default: + s = hex(t); + qDebug() << "Unknown PRS1ParsedSettingType type:" << qPrintable(s); + return s; + } + return s.mid(13).toLower(); // lop off initial PRS1_SETTING_ +} + +static QString parsedModeName(int m) +{ + QString s; + switch ((PRS1Mode) m) { + ENUMSTRING(PRS1_MODE_UNKNOWN); // TODO: Remove this when all the parsers are complete. + ENUMSTRING(PRS1_MODE_CPAP); + ENUMSTRING(PRS1_MODE_CPAPCHECK); + ENUMSTRING(PRS1_MODE_AUTOTRIAL); + ENUMSTRING(PRS1_MODE_AUTOCPAP); + ENUMSTRING(PRS1_MODE_BILEVEL); + ENUMSTRING(PRS1_MODE_AUTOBILEVEL); + ENUMSTRING(PRS1_MODE_ASV); + 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); + return s; + } + return s.mid(10).toLower(); // lop off initial PRS1_MODE_ +} + +static QString timeStr(int t) +{ + int h = t / 3600; + int m = (t - (h * 3600)) / 60; + int s = t % 60; +#if 1 + // Optimized after profiling regression tests. + return QString::asprintf("%02d:%02d:%02d", h, m, s); +#else + // Unoptimized original, slows down regression tests. + return QString("%1:%2:%3").arg(h, 2, 10, QChar('0')).arg(m, 2, 10, QChar('0')).arg(s, 2, 10, QChar('0')); +#endif +} + +static QString byteList(QByteArray data, int limit=-1) +{ + int count = data.size(); + if (limit == -1 || limit > count) limit = count; + QStringList l; + for (int i = 0; i < limit; i++) { + l.push_back(QString( "%1" ).arg((int) data[i] & 0xFF, 2, 16, QChar('0') ).toUpper()); + } + if (limit < count) l.push_back("..."); + QString s = l.join(" "); + return s; +} + +QString _PRS1ParsedEventName(PRS1ParsedEvent* e) +{ + return parsedEventTypeName(e->m_type); +} + +QMap _PRS1ParsedEventContents(PRS1ParsedEvent* e) +{ + return e->contents(); +} + + +QMap PRS1IntervalBoundaryEvent::contents(void) +{ + QMap out; + out["start"] = timeStr(m_start); + return out; +} + + +QMap PRS1ParsedDurationEvent::contents(void) +{ + QMap out; + out["start"] = timeStr(m_start); + out["duration"] = timeStr(m_duration); + return out; +} + + +QMap PRS1ParsedValueEvent::contents(void) +{ + QMap out; + out["start"] = timeStr(m_start); + out["value"] = QString::number(value()); + return out; +} + + +QMap PRS1UnknownDataEvent::contents(void) +{ + QMap out; + out["pos"] = QString::number(m_pos); + out["data"] = byteList(m_data); + return out; +} + + +QMap PRS1ParsedSettingEvent::contents(void) +{ + QMap out; + QString v; + if (m_setting == PRS1_SETTING_CPAP_MODE) { + v = parsedModeName(value()); + } else { + v = QString::number(value()); + } + out[parsedSettingTypeName(m_setting)] = v; + return out; +} + + +QMap PRS1ParsedSliceEvent::contents(void) +{ + QMap out; + out["start"] = timeStr(m_start); + QString s; + switch ((SliceStatus) m_value) { + case MaskOn: s = "MaskOn"; break; + case MaskOff: s = "MaskOff"; break; + case EquipmentOff: s = "EquipmentOff"; break; + case UnknownStatus: s = "Unknown"; break; + } + out["status"] = s; + return out; +} + + +QMap PRS1ParsedAlarmEvent::contents(void) +{ + QMap out; + out["start"] = timeStr(m_start); + return out; +} + + +QMap PRS1SnoresAtPressureEvent::contents(void) +{ + QString label; + switch (m_kind) { + case 0: label = "pressure"; break; + case 1: label = "epap"; break; + case 2: label = "ipap"; break; + default: label = "unknown_pressure"; break; + } + + QMap out; + out["start"] = timeStr(m_start); + out[label] = QString::number(value()); + out["count"] = QString::number(m_count); + return out; +} diff --git a/oscar/SleepLib/loader_plugins/prs1_parser.h b/oscar/SleepLib/loader_plugins/prs1_parser.h new file mode 100644 index 00000000..d05de017 --- /dev/null +++ b/oscar/SleepLib/loader_plugins/prs1_parser.h @@ -0,0 +1,412 @@ +/* SleepLib PRS1 Loader Parser Header +* +* Copyright (c) 2019-2021 The OSCAR Team +* Portions copyright (c) 2011-2018 Mark Watkins +* +* This file is subject to the terms and conditions of the GNU General Public +* License. See the file COPYING in the main directory of the source code +* for more details. */ + +#ifndef PRS1PARSER_H +#define PRS1PARSER_H + +#include +#include +#include "SleepLib/session.h" + +//******************************************************************************************** +// Internal PRS1 parsed data types +//******************************************************************************************** + +// For new events, add an enum here and then a class below with an PRS1_*_EVENT macro +enum PRS1ParsedEventType +{ + EV_PRS1_RAW = -1, // these only get logged + EV_PRS1_UNKNOWN = 0, // these have their value graphed + EV_PRS1_TB, + EV_PRS1_OA, + EV_PRS1_CA, + EV_PRS1_FL, + EV_PRS1_PB, + EV_PRS1_LL, + EV_PRS1_VB, // UNCONFIRMED + EV_PRS1_HY, + EV_PRS1_OA_COUNT, // F3V3 only + EV_PRS1_CA_COUNT, // F3V3 only + EV_PRS1_HY_COUNT, // F3V3 only + EV_PRS1_TOTLEAK, + EV_PRS1_LEAK, // unintentional leak + EV_PRS1_AUTO_PRESSURE_SET, + EV_PRS1_PRESSURE_SET, + EV_PRS1_IPAP_SET, + EV_PRS1_EPAP_SET, + EV_PRS1_PRESSURE_AVG, + EV_PRS1_FLEX_PRESSURE_AVG, + EV_PRS1_IPAP_AVG, + EV_PRS1_IPAPLOW, + EV_PRS1_IPAPHIGH, + EV_PRS1_EPAP_AVG, + EV_PRS1_RR, + EV_PRS1_PTB, + EV_PRS1_MV, + EV_PRS1_TV, + EV_PRS1_SNORE, + EV_PRS1_VS, + EV_PRS1_PP, + EV_PRS1_RERA, + EV_PRS1_FLOWRATE, + EV_PRS1_TEST1, + EV_PRS1_TEST2, + EV_PRS1_SETTING, + EV_PRS1_SLICE, + EV_PRS1_DISCONNECT_ALARM, + EV_PRS1_APNEA_ALARM, + EV_PRS1_LOW_MV_ALARM, + EV_PRS1_SNORES_AT_PRESSURE, + EV_PRS1_INTERVAL_BOUNDARY, // An artificial internal-only event used to separate stat intervals +}; + +enum PRS1ParsedEventUnit +{ + PRS1_UNIT_NONE, + PRS1_UNIT_CMH2O, + PRS1_UNIT_ML, + PRS1_UNIT_S, +}; + +enum PRS1ParsedSettingType +{ + PRS1_SETTING_CPAP_MODE, + PRS1_SETTING_AUTO_TRIAL, + PRS1_SETTING_PRESSURE, + PRS1_SETTING_PRESSURE_MIN, + PRS1_SETTING_PRESSURE_MAX, + PRS1_SETTING_EPAP, + PRS1_SETTING_EPAP_MIN, + PRS1_SETTING_EPAP_MAX, + PRS1_SETTING_IPAP, + PRS1_SETTING_IPAP_MIN, + PRS1_SETTING_IPAP_MAX, + PRS1_SETTING_PS, + PRS1_SETTING_PS_MIN, + 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_EZ_START, + PRS1_SETTING_FLEX_LOCK, + 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, + PRS1_SETTING_HUMID_STATUS, + PRS1_SETTING_HUMID_MODE, + PRS1_SETTING_HEATED_TUBE_TEMP, + PRS1_SETTING_HUMID_LEVEL, + PRS1_SETTING_MASK_RESIST_LOCK, + PRS1_SETTING_MASK_RESIST_SETTING, + PRS1_SETTING_HOSE_DIAMETER, + PRS1_SETTING_TUBING_LOCK, + PRS1_SETTING_AUTO_ON, + PRS1_SETTING_AUTO_OFF, + PRS1_SETTING_APNEA_ALARM, + PRS1_SETTING_DISCONNECT_ALARM, // Is this any different from mask alert? + PRS1_SETTING_LOW_MV_ALARM, + PRS1_SETTING_LOW_TV_ALARM, + PRS1_SETTING_MASK_ALERT, + PRS1_SETTING_SHOW_AHI, + PRS1_SETTING_HUMID_TARGET_TIME, +}; + + +class PRS1ParsedEvent +{ +public: + PRS1ParsedEventType m_type; + int m_start; // seconds relative to chunk timestamp at which this event began + int m_duration; + int m_value; + float m_offset; + float m_gain; + PRS1ParsedEventUnit m_unit; + + inline float value(void) { return (m_value * m_gain) + m_offset; } + + static const PRS1ParsedEventType TYPE = EV_PRS1_UNKNOWN; + static constexpr float GAIN = 1.0; + static const PRS1ParsedEventUnit UNIT = PRS1_UNIT_NONE; + + virtual QMap contents(void) = 0; + +protected: + PRS1ParsedEvent(PRS1ParsedEventType type, int start) + : m_type(type), m_start(start), m_duration(0), m_value(0), m_offset(0.0), m_gain(GAIN), m_unit(UNIT) + { + } +public: + virtual ~PRS1ParsedEvent() + { + } +}; + + +class PRS1IntervalBoundaryEvent : public PRS1ParsedEvent +{ +public: + virtual QMap contents(void); + + static const PRS1ParsedEventType TYPE = EV_PRS1_INTERVAL_BOUNDARY; + + PRS1IntervalBoundaryEvent(int start) : PRS1ParsedEvent(TYPE, start) {} +}; + + +class PRS1ParsedDurationEvent : public PRS1ParsedEvent +{ +public: + virtual QMap contents(void); + + static const PRS1ParsedEventUnit UNIT = PRS1_UNIT_S; + + PRS1ParsedDurationEvent(PRS1ParsedEventType type, int start, int duration) : PRS1ParsedEvent(type, start) { m_duration = duration; } +}; + + +class PRS1ParsedValueEvent : public PRS1ParsedEvent +{ +public: + virtual QMap contents(void); + +protected: + PRS1ParsedValueEvent(PRS1ParsedEventType type, int start, int value) : PRS1ParsedEvent(type, start) { m_value = value; } +}; + +/* +class PRS1UnknownValueEvent : public PRS1ParsedValueEvent +{ +public: + virtual QMap contents(void) + { + QMap out; + out["start"] = timeStr(m_start); + out["code"] = hex(m_code); + out["value"] = QString::number(value()); + return out; + } + + int m_code; + PRS1UnknownValueEvent(int code, int start, int value, float gain=1.0) : PRS1ParsedValueEvent(TYPE, start, value), m_code(code) { m_gain = gain; } +}; +*/ + +class PRS1UnknownDataEvent : public PRS1ParsedEvent +{ +public: + virtual QMap contents(void); + + static const PRS1ParsedEventType TYPE = EV_PRS1_RAW; + + int m_pos; + unsigned char m_code; + QByteArray m_data; + + PRS1UnknownDataEvent(const QByteArray & data, int pos, int len=18) + : PRS1ParsedEvent(TYPE, 0) + { + m_pos = pos; + m_data = data.mid(pos, len); + Q_ASSERT(m_data.size() >= 1); + m_code = m_data.at(0); + } +}; + +class PRS1PressureEvent : public PRS1ParsedValueEvent +{ +public: + static constexpr float GAIN = 0.1; + static const PRS1ParsedEventUnit UNIT = PRS1_UNIT_CMH2O; + + PRS1PressureEvent(PRS1ParsedEventType type, int start, int value, float gain=GAIN) + : PRS1ParsedValueEvent(type, start, value) + { + m_gain = gain; + m_unit = UNIT; + } +}; + +class PRS1TidalVolumeEvent : public PRS1ParsedValueEvent +{ +public: + static const PRS1ParsedEventType TYPE = EV_PRS1_TV; + + static constexpr float GAIN = 10.0; + static const PRS1ParsedEventUnit UNIT = PRS1_UNIT_ML; + + PRS1TidalVolumeEvent(int start, int value) + : PRS1ParsedValueEvent(TYPE, start, value) + { + m_gain = GAIN; + m_unit = UNIT; + } +}; + +class PRS1ParsedSettingEvent : public PRS1ParsedValueEvent +{ +public: + virtual QMap contents(void); + + static const PRS1ParsedEventType TYPE = EV_PRS1_SETTING; + PRS1ParsedSettingType m_setting; + + PRS1ParsedSettingEvent(PRS1ParsedSettingType setting, int value) : PRS1ParsedValueEvent(TYPE, 0, value), m_setting(setting) {} +}; + +class PRS1ScaledSettingEvent : public PRS1ParsedSettingEvent +{ +public: + PRS1ScaledSettingEvent(PRS1ParsedSettingType setting, int value, float gain) + : PRS1ParsedSettingEvent(setting, value) + { + m_gain = gain; + } +}; + +class PRS1PressureSettingEvent : public PRS1ScaledSettingEvent +{ +public: + static constexpr float GAIN = PRS1PressureEvent::GAIN; + static const PRS1ParsedEventUnit UNIT = PRS1PressureEvent::UNIT; + + PRS1PressureSettingEvent(PRS1ParsedSettingType setting, int value, float gain=GAIN) + : PRS1ScaledSettingEvent(setting, value, gain) + { + m_unit = UNIT; + } +}; + +class PRS1ParsedSliceEvent : public PRS1ParsedValueEvent +{ +public: + virtual QMap contents(void); + + static const PRS1ParsedEventType TYPE = EV_PRS1_SLICE; + + PRS1ParsedSliceEvent(int start, SliceStatus status) : PRS1ParsedValueEvent(TYPE, start, (int) status) {} +}; + + +class PRS1ParsedAlarmEvent : public PRS1ParsedEvent +{ +public: + virtual QMap contents(void); + +protected: + PRS1ParsedAlarmEvent(PRS1ParsedEventType type, int start, int /*unused*/) : PRS1ParsedEvent(type, start) {} +}; + + +class PRS1SnoresAtPressureEvent : public PRS1PressureEvent +{ +public: + static const PRS1ParsedEventType TYPE = EV_PRS1_SNORES_AT_PRESSURE; + + PRS1SnoresAtPressureEvent(int start, int kind, int pressure, int count, float gain=GAIN) + : PRS1PressureEvent(TYPE, start, pressure, gain) + { + m_kind = kind; + m_count = count; + } + + virtual QMap contents(void); + +protected: + int m_kind; + // m_value is pressure + int m_count; +}; + + +#define _PRS1_EVENT(T, E, P, ARG) \ +class T : public P \ +{ \ +public: \ + static const PRS1ParsedEventType TYPE = E; \ + T(int start, int ARG) : P(TYPE, start, ARG) {} \ +}; +#define PRS1_DURATION_EVENT(T, E) _PRS1_EVENT(T, E, PRS1ParsedDurationEvent, duration) +#define PRS1_VALUE_EVENT(T, E) _PRS1_EVENT(T, E, PRS1ParsedValueEvent, value) +#define PRS1_ALARM_EVENT(T, E) _PRS1_EVENT(T, E, PRS1ParsedAlarmEvent, value) +#define PRS1_PRESSURE_EVENT(T, E) \ +class T : public PRS1PressureEvent \ +{ \ +public: \ + static const PRS1ParsedEventType TYPE = E; \ + T(int start, int value, float gain=PRS1PressureEvent::GAIN) : PRS1PressureEvent(TYPE, start, value, gain) {} \ +}; + +PRS1_DURATION_EVENT(PRS1TimedBreathEvent, EV_PRS1_TB); +PRS1_DURATION_EVENT(PRS1ObstructiveApneaEvent, EV_PRS1_OA); +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(PRS1VariableBreathingEvent, EV_PRS1_VB); +PRS1_DURATION_EVENT(PRS1HypopneaEvent, EV_PRS1_HY); + +PRS1_VALUE_EVENT(PRS1TotalLeakEvent, EV_PRS1_TOTLEAK); +PRS1_VALUE_EVENT(PRS1LeakEvent, EV_PRS1_LEAK); + +PRS1_PRESSURE_EVENT(PRS1AutoPressureSetEvent, EV_PRS1_AUTO_PRESSURE_SET); +PRS1_PRESSURE_EVENT(PRS1PressureSetEvent, EV_PRS1_PRESSURE_SET); +PRS1_PRESSURE_EVENT(PRS1IPAPSetEvent, EV_PRS1_IPAP_SET); +PRS1_PRESSURE_EVENT(PRS1EPAPSetEvent, EV_PRS1_EPAP_SET); +PRS1_PRESSURE_EVENT(PRS1PressureAverageEvent, EV_PRS1_PRESSURE_AVG); +PRS1_PRESSURE_EVENT(PRS1FlexPressureAverageEvent, EV_PRS1_FLEX_PRESSURE_AVG); +PRS1_PRESSURE_EVENT(PRS1IPAPAverageEvent, EV_PRS1_IPAP_AVG); +PRS1_PRESSURE_EVENT(PRS1IPAPHighEvent, EV_PRS1_IPAPHIGH); +PRS1_PRESSURE_EVENT(PRS1IPAPLowEvent, EV_PRS1_IPAPLOW); +PRS1_PRESSURE_EVENT(PRS1EPAPAverageEvent, EV_PRS1_EPAP_AVG); + +PRS1_VALUE_EVENT(PRS1RespiratoryRateEvent, EV_PRS1_RR); +PRS1_VALUE_EVENT(PRS1PatientTriggeredBreathsEvent, EV_PRS1_PTB); +PRS1_VALUE_EVENT(PRS1MinuteVentilationEvent, EV_PRS1_MV); +PRS1_VALUE_EVENT(PRS1SnoreEvent, EV_PRS1_SNORE); +PRS1_VALUE_EVENT(PRS1VibratorySnoreEvent, EV_PRS1_VS); +PRS1_VALUE_EVENT(PRS1PressurePulseEvent, EV_PRS1_PP); +PRS1_VALUE_EVENT(PRS1RERAEvent, EV_PRS1_RERA); // TODO: should this really be a duration event? +PRS1_VALUE_EVENT(PRS1FlowRateEvent, EV_PRS1_FLOWRATE); // TODO: is this a single event or an index/hour? +PRS1_VALUE_EVENT(PRS1Test1Event, EV_PRS1_TEST1); +PRS1_VALUE_EVENT(PRS1Test2Event, EV_PRS1_TEST2); +PRS1_VALUE_EVENT(PRS1HypopneaCount, EV_PRS1_HY_COUNT); // F3V3 only +PRS1_VALUE_EVENT(PRS1ClearAirwayCount, EV_PRS1_CA_COUNT); // F3V3 only +PRS1_VALUE_EVENT(PRS1ObstructiveApneaCount, EV_PRS1_OA_COUNT); // F3V3 only + +PRS1_ALARM_EVENT(PRS1DisconnectAlarmEvent, EV_PRS1_DISCONNECT_ALARM); +PRS1_ALARM_EVENT(PRS1ApneaAlarmEvent, EV_PRS1_APNEA_ALARM); +PRS1_ALARM_EVENT(PRS1LowMinuteVentilationAlarmEvent, EV_PRS1_LOW_MV_ALARM); + +enum PRS1Mode { + PRS1_MODE_UNKNOWN = -1, + 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_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. +const QVector & GetSupportedEvents(const class PRS1DataChunk* chunk); + + +#endif // PRS1PARSER_H diff --git a/oscar/oscar.pro b/oscar/oscar.pro index 82c87a49..8a9b15b3 100644 --- a/oscar/oscar.pro +++ b/oscar/oscar.pro @@ -298,6 +298,7 @@ SOURCES += \ SleepLib/loader_plugins/intellipap_loader.cpp \ SleepLib/loader_plugins/mseries_loader.cpp \ SleepLib/loader_plugins/prs1_loader.cpp \ + SleepLib/loader_plugins/prs1_parser.cpp \ SleepLib/loader_plugins/resmed_loader.cpp \ SleepLib/loader_plugins/resmed_EDFinfo.cpp \ SleepLib/loader_plugins/somnopose_loader.cpp \ @@ -379,6 +380,7 @@ HEADERS += \ SleepLib/loader_plugins/intellipap_loader.h \ SleepLib/loader_plugins/mseries_loader.h \ SleepLib/loader_plugins/prs1_loader.h \ + SleepLib/loader_plugins/prs1_parser.h \ SleepLib/loader_plugins/resmed_loader.h \ SleepLib/loader_plugins/resmed_EDFinfo.h \ SleepLib/loader_plugins/somnopose_loader.h \