diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp index e4dd1275..cf830225 100644 --- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp +++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp @@ -1615,6 +1615,24 @@ QMap<QString,QString> _PRS1ParsedEventContents(PRS1ParsedEvent* e) //******************************************************************************************** +static QString DumpEvent(int t, int code, const unsigned char* data, int size) +{ + int s = t; + int h = s / 3600; s -= h * 3600; + int m = s / 60; s -= m * 60; + QString dump = QString("%1:%2:%3") + .arg(h, 2, 10, QChar('0')) + .arg(m, 2, 10, QChar('0')) + .arg(s, 2, 10, QChar('0')); + dump = dump + " " + hex(code) + ":"; + for (int i = 0; i < size; i++) { + dump = dump + QString(" %1").arg(data[i]); + } + return dump; +} +#define DUMP_EVENT() qWarning() << this->sessionid << DumpEvent(t, code, data + pos, size - (pos - startpos)) << "@" << pos + + void PRS1DataChunk::AddEvent(PRS1ParsedEvent* const event) { m_parsedData.push_back(event); @@ -2104,67 +2122,62 @@ bool PRS1Import::ParseF5Events() // 950P is F5V0, 960P and 961P are F5V1, 960T is F5V2 bool PRS1DataChunk::ParseEventsF5V012(void) { - EventDataType data0, data1, data4, data5; + if (this->family != 5 || this->familyVersion > 2) { + qWarning() << "ParseEventsF5V012 called with family" << this->family << "familyVersion" << this->familyVersion; + return false; + } + const unsigned char * data = (unsigned char *)this->m_data.constData(); + int chunk_size = this->m_data.size(); + QMap<int,int> event_sizes; + // TODO: This probably needs to be split into 3 functions. + switch (this->familyVersion) { + case 0: event_sizes = { {0,3}, {1,2}, {3,4}, {9,3}, {0xa,2}, {0xb,5}, {0xc,5}, {0xd,0xc}, {0xf,5}, {0x10,5}, {0x11,2}, {0x12,6} }; break; + case 1: event_sizes = { {0,4}, {1,2}, {3,4}, {8,4}, {9,3}, {0xa,2}, {0xb,5}, {0xc,5}, {0xd,0xd}, {0xf,5}, {0x10,5}, {0x11,2}, {0x12,6} }; break; + case 2: event_sizes = { {0,4}, {1,2}, {3,4}, {9,4}, {0xa,2}, {0xb,5}, {0xc,5}, {0xd,5}, {0xe,0xd}, {0xf,5}, {0x10,5}, {0x11,2}, {0x12,6} }; break; + } + if (chunk_size < 1) { + // This does occasionally happen in F0V6. + qDebug() << this->sessionid << "Empty event data"; + return false; + } + + bool ok = true; + int pos = 0, startpos; + int code, size; int t = 0; - int pos = 0; - int cnt = 0; - short delta;//,duration; - bool badcode = false; - unsigned char lastcode3 = 0, lastcode2 = 0, lastcode = 0, code = 0; - int lastpos = 0, startpos = 0, lastpos2 = 0, lastpos3 = 0; + EventDataType data0, data1, data4, data5; + //int elapsed, duration, value; + do { + code = data[pos++]; - int size = this->m_data.size(); - unsigned char * buffer = (unsigned char *)this->m_data.data(); - - while (pos < size) { - lastcode3 = lastcode2; - lastcode2 = lastcode; - lastcode = code; - lastpos3 = lastpos2; - lastpos2 = lastpos; - lastpos = startpos; + size = 3; // default size = 2 bytes time delta + 1 byte data + if (event_sizes.contains(code)) { + size = event_sizes[code]; + } + if (pos + size > chunk_size) { + qWarning() << this->sessionid << "event" << code << "@" << pos << "longer than remaining chunk"; + ok = false; + break; + } startpos = pos; - code = buffer[pos++]; - - if (code >= 0x13) { - qDebug() << "Illegal PRS1 code " << hex << int(code) << " appeared at " << hex << startpos << "in" << this->sessionid;; - qDebug() << "1: (" << int(lastcode) << hex << lastpos << ")"; - qDebug() << "2: (" << int(lastcode2) << hex << lastpos2 << ")"; - qDebug() << "3: (" << int(lastcode3) << hex << lastpos3 << ")"; - this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos)); - return false; - } - - if (code == 0) { - // No delta - } else if (code != 0x12) { - delta = buffer[pos]; - //duration=buffer[pos+1]; - //delta=buffer[pos+1] << 8 | buffer[pos]; + if (code != 0 && code != 0x12) { // These two codes have no timestamp TODO: verify this applies to F5V012 + t += data[pos] /*| (data[pos+1] << 8)*/; // TODO: Is this really only 1 byte? pos += 2; - t += delta; } - //EventDataType PS; - cnt++; - int fc = 0; - switch (code) { case 0x00: // Unknown (ASV Pressure value) // offset? - data0 = buffer[pos++]; - fc++; + data0 = data[pos++]; - if (!buffer[pos - 1]) { // WTH??? - data1 = buffer[pos++]; - fc++; + if (!data[pos - 1]) { // WTH??? + data1 = data[pos++]; } - if (!buffer[pos - 1]) { - //data2 = buffer[pos++]; + if (!data[pos - 1]) { + //data2 = data[pos++]; pos++; - fc++; } break; @@ -2174,7 +2187,7 @@ bool PRS1DataChunk::ParseEventsF5V012(void) break; case 0x02: // Pressure ??? - data0 = buffer[pos++]; + data0 = data[pos++]; // if (!Code[2]) { // if (!(Code[2]=session->AddEventList(cpapcode,EVL_Event,0.1))) return false; // } @@ -2183,8 +2196,8 @@ bool PRS1DataChunk::ParseEventsF5V012(void) case 0x03: // BIPAP Pressure qDebug() << "0x03 Observed in ASV data!!????"; - data0 = buffer[pos++]; - data1 = buffer[pos++]; + data0 = data[pos++]; + data1 = data[pos++]; // data0/=10.0; // data1/=10.0; // session->AddEvent(new Event(t,CPAP_EAP, 0, data, 1)); @@ -2192,31 +2205,33 @@ bool PRS1DataChunk::ParseEventsF5V012(void) break; case 0x04: // Timed Breath - data0 = buffer[pos++]; + data0 = data[pos++]; this->AddEvent(new PRS1TimedBreathEvent(t, data0)); break; case 0x05: //code=CPAP_Obstructive; - data0 = buffer[pos++]; + data0 = data[pos++]; this->AddEvent(new PRS1ObstructiveApneaEvent(t - data0, data0)); break; case 0x06: //code=CPAP_ClearAirway; - data0 = buffer[pos++]; + data0 = data[pos++]; this->AddEvent(new PRS1ClearAirwayEvent(t - data0, data0)); break; case 0x07: //code=CPAP_Hypopnea; - data0 = buffer[pos++]; + data0 = data[pos++]; this->AddEvent(new PRS1HypopneaEvent(t - data0, data0)); break; case 0x08: // ??? - data0 = buffer[pos++]; + // This was breaking parsing for F5V1 prior to using fixed lengths. + // That means it's probably very wrong. + data0 = data[pos++]; //qDebug() << "Code 8 found at " << hex << pos - 1 << " " << tt; if (this->familyVersion>=2) { @@ -2224,7 +2239,7 @@ bool PRS1DataChunk::ParseEventsF5V012(void) } else { this->AddEvent(new PRS1UnknownValueEvent(code, t - data0, data0)); //???? - //data1=buffer[pos++]; // ??? + //data1=data[pos++]; // ??? //pos++; } break; @@ -2232,18 +2247,18 @@ bool PRS1DataChunk::ParseEventsF5V012(void) case 0x09: // ASV Codes if (this->familyVersion<2) { //code=CPAP_FlowLimit; - data0 = buffer[pos++]; + data0 = data[pos++]; this->AddEvent(new PRS1FlowLimitationEvent(t - data0, data0)); } else { - data0 = buffer[pos++]; - data1 = buffer[pos++]; + data0 = data[pos++]; + data1 = data[pos++]; } break; case 0x0a: - data0 = buffer[pos++]; + data0 = data[pos++]; if (this->familyVersion>=2) { this->AddEvent(new PRS1FlowLimitationEvent(t - data0, data0)); } else { @@ -2253,10 +2268,10 @@ bool PRS1DataChunk::ParseEventsF5V012(void) case 0x0b: // Cheyne Stokes - data0 = ((unsigned char *)buffer)[pos + 1] << 8 | ((unsigned char *)buffer)[pos]; + data0 = ((unsigned char *)data)[pos + 1] << 8 | ((unsigned char *)data)[pos]; //data0*=2; pos += 2; - data1 = ((unsigned char *)buffer)[pos]; //|buffer[pos+1] << 8 + data1 = ((unsigned char *)data)[pos]; //|data[pos+1] << 8 pos += 1; //tt-=delta; this->AddEvent(new PRS1PeriodicBreathingEvent(t - data1, data0)); @@ -2265,14 +2280,14 @@ bool PRS1DataChunk::ParseEventsF5V012(void) case 0x0c: if (this->familyVersion>=2) { - data0 = (buffer[pos + 1] << 8 | buffer[pos]); + data0 = (data[pos + 1] << 8 | data[pos]); data0 *= 2; pos += 2; - data1 = buffer[pos++]; + data1 = data[pos++]; this->AddEvent(new PRS1PeriodicBreathingEvent(t - data1, data0)); } else { - data0 = buffer[pos++]; + data0 = data[pos++]; qDebug() << "Code 12 found at " << hex << pos - 1 << " " << t - data0; this->AddEvent(new PRS1UnknownValueEvent(code, t - data0, data0)); @@ -2283,29 +2298,29 @@ bool PRS1DataChunk::ParseEventsF5V012(void) case 0x0d: // All the other ASV graph stuff. if (this->familyVersion>=2) { - data0 = (buffer[pos + 1] << 8 | buffer[pos]); + data0 = (data[pos + 1] << 8 | data[pos]); data0 *= 2; pos += 2; - data1 = buffer[pos++]; + data1 = data[pos++]; //tt = t - qint64(data1) * 1000L; } else { - this->AddEvent(new PRS1IPAPAverageEvent(t, buffer[pos++])); // 00=IAP - data4 = buffer[pos++]; + this->AddEvent(new PRS1IPAPAverageEvent(t, data[pos++])); // 00=IAP + data4 = data[pos++]; this->AddEvent(new PRS1IPAPLowEvent(t, data4)); // 01=IAP Low - data5 = buffer[pos++]; + data5 = data[pos++]; this->AddEvent(new PRS1IPAPHighEvent(t, data5)); // 02=IAP High - this->AddEvent(new PRS1TotalLeakEvent(t, buffer[pos++])); // 03=LEAK + this->AddEvent(new PRS1TotalLeakEvent(t, data[pos++])); // 03=LEAK - this->AddEvent(new PRS1RespiratoryRateEvent(t, buffer[pos++])); // 04=Breaths Per Minute - this->AddEvent(new PRS1PatientTriggeredBreathsEvent(t, buffer[pos++])); // 05=Patient Triggered Breaths - this->AddEvent(new PRS1MinuteVentilationEvent(t, buffer[pos++])); // 06=Minute Ventilation - //tmp=buffer[pos++] * 10.0; - this->AddEvent(new PRS1TidalVolumeEvent(t, buffer[pos++])); // 07=Tidal Volume - this->AddEvent(new PRS1SnoreEvent(t, buffer[pos++])); // 08=Snore - this->AddEvent(new PRS1EPAPAverageEvent(t, data1 = buffer[pos++])); // 09=EPAP + this->AddEvent(new PRS1RespiratoryRateEvent(t, data[pos++])); // 04=Breaths Per Minute + this->AddEvent(new PRS1PatientTriggeredBreathsEvent(t, data[pos++])); // 05=Patient Triggered Breaths + this->AddEvent(new PRS1MinuteVentilationEvent(t, data[pos++])); // 06=Minute Ventilation + //tmp=data[pos++] * 10.0; + this->AddEvent(new PRS1TidalVolumeEvent(t, data[pos++])); // 07=Tidal Volume + this->AddEvent(new PRS1SnoreEvent(t, data[pos++])); // 08=Snore + this->AddEvent(new PRS1EPAPAverageEvent(t, data1 = data[pos++])); // 09=EPAP if (this->familyVersion >= 1) { - data0 = buffer[pos++]; + data0 = data[pos++]; } } break; @@ -2313,20 +2328,20 @@ bool PRS1DataChunk::ParseEventsF5V012(void) case 0x0e: // Unknown // Family 5.2 has this code if (this->familyVersion>=2) { - this->AddEvent(new PRS1IPAPAverageEvent(t, data1=buffer[pos+0])); // 0 - this->AddEvent(new PRS1IPAPLowEvent(t, buffer[pos+1])); // 1 - this->AddEvent(new PRS1IPAPHighEvent(t, buffer[pos+2])); // 2 - this->AddEvent(new PRS1LeakEvent(t, buffer[pos+3])); // 3 // F5V2, is this really unintentional leak rather than total leak? - this->AddEvent(new PRS1TidalVolumeEvent(t, buffer[pos+7])); // 7 - this->AddEvent(new PRS1RespiratoryRateEvent(t, buffer[pos+4])); // 4 - this->AddEvent(new PRS1PatientTriggeredBreathsEvent(t, buffer[pos+5])); // 5 - this->AddEvent(new PRS1MinuteVentilationEvent(t, buffer[pos+6])); //6 - this->AddEvent(new PRS1SnoreEvent(t, buffer[pos+8])); //?? - this->AddEvent(new PRS1EPAPAverageEvent(t, buffer[pos+9])); // 9 + this->AddEvent(new PRS1IPAPAverageEvent(t, data1=data[pos+0])); // 0 + this->AddEvent(new PRS1IPAPLowEvent(t, data[pos+1])); // 1 + this->AddEvent(new PRS1IPAPHighEvent(t, data[pos+2])); // 2 + this->AddEvent(new PRS1LeakEvent(t, data[pos+3])); // 3 // F5V2, is this really unintentional leak rather than total leak? + this->AddEvent(new PRS1TidalVolumeEvent(t, data[pos+7])); // 7 + this->AddEvent(new PRS1RespiratoryRateEvent(t, data[pos+4])); // 4 + this->AddEvent(new PRS1PatientTriggeredBreathsEvent(t, data[pos+5])); // 5 + this->AddEvent(new PRS1MinuteVentilationEvent(t, data[pos+6])); //6 + this->AddEvent(new PRS1SnoreEvent(t, data[pos+8])); //?? + this->AddEvent(new PRS1EPAPAverageEvent(t, data[pos+9])); // 9 pos+=11; } else { qDebug() << "0x0E Observed in ASV data!!????"; - data0 = buffer[pos++]; // << 8) | buffer[pos]; + data0 = data[pos++]; // << 8) | data[pos]; } //session->AddEvent(new Event(t,cpapcode, 0, data, 1)); @@ -2334,25 +2349,25 @@ bool PRS1DataChunk::ParseEventsF5V012(void) case 0x0f: qDebug() << "0x0f Observed in ASV data!!????"; - data0 = buffer[pos + 1] << 8 | buffer[pos]; + data0 = data[pos + 1] << 8 | data[pos]; pos += 2; - data1 = buffer[pos]; //|buffer[pos+1] << 8 + data1 = data[pos]; //|data[pos+1] << 8 pos += 1; //tt -= qint64(data1) * 1000L; //session->AddEvent(new Event(tt,cpapcode, 0, data, 2)); break; case 0x10: // Unknown - data0 = buffer[pos + 1] << 8 | buffer[pos]; + data0 = data[pos + 1] << 8 | data[pos]; pos += 2; - data1 = buffer[pos++]; + data1 = data[pos++]; this->AddEvent(new PRS1LargeLeakEvent(t - data1, data0)); // qDebug() << "0x10 Observed in ASV data!!????"; -// data0 = buffer[pos++]; // << 8) | buffer[pos]; -// data1 = buffer[pos++]; -// data2 = buffer[pos++]; +// data0 = data[pos++]; // << 8) | data[pos]; +// data1 = data[pos++]; +// data2 = data[pos++]; //session->AddEvent(new Event(t,cpapcode, 0, data, 3)); break; case 0x11: // Not Leak Rate @@ -2360,34 +2375,36 @@ bool PRS1DataChunk::ParseEventsF5V012(void) //if (!Code[24]) { // Code[24]=new EventList(cpapcode,EVL_Event); //} - //Code[24]->AddEvent(t,buffer[pos++]); + //Code[24]->AddEvent(t,data[pos++]); break; case 0x12: // Summary qDebug() << "0x12 Observed in ASV data!!????"; - data0 = buffer[pos++]; - data1 = buffer[pos++]; - //data2 = buffer[pos + 1] << 8 | buffer[pos]; + data0 = data[pos++]; + data1 = data[pos++]; + //data2 = data[pos + 1] << 8 | data[pos]; pos += 2; //session->AddEvent(new Event(t,cpapcode, 0, data,3)); break; - default: // ERROR!!! - qWarning() << "Some new fandangled PRS1 code detected " << hex << int(code) << " at " << pos - 1; - this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos)); - badcode = true; - break; + default: + DUMP_EVENT(); + UNEXPECTED_VALUE(code, "known event code"); + this->AddEvent(new PRS1UnknownDataEvent(m_data, startpos)); + ok = false; // unlike F0V6, we don't know the size of unknown slices, so we can't recover + break; } + pos = startpos + size; + } while (ok && pos < chunk_size); - if (badcode) { - break; - } + if (ok && pos != chunk_size) { + qWarning() << this->sessionid << (this->size() - pos) << "trailing event bytes"; } - this->duration = t; // The last event might start before t, so record the last delta timestamp. - return true; + this->duration = t; + return ok; } @@ -3107,23 +3124,6 @@ bool PRS1Import::ParseF0Events() return true; } -static QString DumpEvent(int t, int code, const unsigned char* data, int size) -{ - int s = t; - int h = s / 3600; s -= h * 3600; - int m = s / 60; s -= m * 60; - QString dump = QString("%1:%2:%3") - .arg(h, 2, 10, QChar('0')) - .arg(m, 2, 10, QChar('0')) - .arg(s, 2, 10, QChar('0')); - dump = dump + " " + hex(code) + ":"; - for (int i = 0; i < size; i++) { - dump = dump + QString(" %1").arg(data[i]); - } - return dump; -} -#define DUMP_EVENT() qWarning() << this->sessionid << DumpEvent(t, code, data + pos, size - (pos - startpos)) << "@" << pos - bool PRS1DataChunk::ParseEventsF0V23(CPAPMode /*mode*/) { if (this->family != 0 || this->familyVersion < 2 || this->familyVersion > 3) {