Migrate F5V012 event loop logic to match F0V4.

No changes were intended in the imported data, but the parsers
were fantastically broken, so touching anything in the code
results in lots of differences in the output.

As a result, the imported data for F5V012 is now unstable.

On the plus side, the parser no longer bails on event 8 for F5V1.

This will almost certainly need to be split into 3 separate
functions.
This commit is contained in:
sawinglogz 2019-10-04 19:51:34 -04:00
parent 6992a2f1a9
commit 07aec55d80

View File

@ -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) {