Fix header parsing for 1160P event files, fix misconceptions in ReadWaveformHeader.

Now that we check header checksums, it uncovered a problem parsing 1160P event
headers. It turns out that the 1160P uses a "waveform" header for its .002
events files. So we can't use the file extension to decide which header to
parse, but there's a flag in the standard header that seems to reliably indicate
a waveform header. The 1160P events are listed at fixed intervals, as are
waveforms, so the flag has been named "interval" rather than "waveform."

The 1160P event headers have more than 2 signals in the header and an interval
longer than 1sec. This clarified the meaning of multiple waveform header fields
that were previously being parsed incorrectly.
This commit is contained in:
sawinglogz 2019-05-18 19:17:55 -04:00
parent ccafa1f16e
commit 21adfb7987
2 changed files with 65 additions and 30 deletions

View File

@ -33,6 +33,9 @@
//const int PRS1_EVENT_FILE=2; //const int PRS1_EVENT_FILE=2;
//const int PRS1_WAVEFORM_FILE=5; //const int PRS1_WAVEFORM_FILE=5;
const int PRS1_HTYPE_NORMAL=0;
const int PRS1_HTYPE_INTERVAL=1;
//******************************************************************************************** //********************************************************************************************
/// IMPORTANT!!! /// IMPORTANT!!!
@ -1783,6 +1786,12 @@ bool PRS1Import::ParseF3Events()
int hy, oa, ca; int hy, oa, ca;
qint64 div = 0; qint64 div = 0;
// TODO: make sure the assumptions here agree with the header:
// size == number of intervals
// interval seconds = 120
// interleave for each channel = 1
// also warn on any remainder of data size % record size (but don't fail)
const qint64 block_duration = 120000; const qint64 block_duration = 120000;
for (int x=0; x < size; x++) { for (int x=0; x < size; x++) {
@ -3555,10 +3564,11 @@ PRS1DataChunk* PRS1DataChunk::ParseNext(QFile & f)
} }
// Log mismatched waveform session IDs // Log mismatched waveform session IDs
if (chunk->ext >= 5) { if (chunk->htype == PRS1_HTYPE_INTERVAL) {
QFileInfo fi(f); QFileInfo fi(f);
bool numeric; bool numeric;
int sessionid_base = (chunk->fileVersion == 2 ? 10 : 16); int sessionid_base = (chunk->fileVersion == 2 ? 10 : 16);
if (chunk->family == 3 && chunk->familyVersion >= 3) sessionid_base = 16;
QString session_s = fi.fileName().section(".", 0, -2); QString session_s = fi.fileName().section(".", 0, -2);
quint32 sid = session_s.toInt(&numeric, sessionid_base); quint32 sid = session_s.toInt(&numeric, sessionid_base);
if (!numeric || sid != chunk->sessionid) { if (!numeric || sid != chunk->sessionid) {
@ -3619,10 +3629,15 @@ bool PRS1DataChunk::ReadHeader(QFile & f)
qWarning() << this->m_path << "@" << hex << this->m_filepos << "Never seen PRS1 header version < 2 or > 3 before"; qWarning() << this->m_path << "@" << hex << this->m_filepos << "Never seen PRS1 header version < 2 or > 3 before";
break; break;
} }
if (this->htype != PRS1_HTYPE_NORMAL && this->htype != PRS1_HTYPE_INTERVAL) {
qWarning() << this->m_path << "unexpected htype:" << this->htype;
//break; // don't break to avoid changing behavior (for now)
}
// Read format-specific variable-length header data. // Read format-specific variable-length header data.
bool hdr_ok = false; bool hdr_ok = false;
if (this->ext < 5) { // Not a waveform chunk if (this->htype != PRS1_HTYPE_INTERVAL) { // Not just waveforms: the 1160P uses this for its .002 events file.
// Not a waveform/interval chunk
switch (this->fileVersion) { switch (this->fileVersion) {
case 2: case 2:
hdr_ok = ReadNormalHeaderV2(f); hdr_ok = ReadNormalHeaderV2(f);
@ -3634,7 +3649,8 @@ bool PRS1DataChunk::ReadHeader(QFile & f)
//hdr_ok remains false, warning is above //hdr_ok remains false, warning is above
break; break;
} }
} else { // Waveform Chunk } else {
// Waveform/interval chunk
hdr_ok = ReadWaveformHeader(f); hdr_ok = ReadWaveformHeader(f);
} }
if (!hdr_ok) { if (!hdr_ok) {
@ -3724,25 +3740,24 @@ bool PRS1DataChunk::ReadWaveformHeader(QFile & f)
bool ok = false; bool ok = false;
unsigned char * header; unsigned char * header;
do { do {
QByteArray extra = f.read(5); // Read the fixed-length waveform header.
if (extra.size() != 5) { QByteArray extra = f.read(4);
if (extra.size() != 4) {
qWarning() << this->m_path << "read error in waveform header"; qWarning() << this->m_path << "read error in waveform header";
break; break;
} }
this->m_header.append(extra); this->m_header.append(extra);
// Get the header address again to be safe
header = (unsigned char *)this->m_header.data(); header = (unsigned char *)this->m_header.data();
this->duration = header[0x0f] | header[0x10] << 8; // Parse the fixed-length portion.
int always_1 = header[0x11]; this->interval_count = header[0x0f] | header[0x10] << 8;
if (always_1 != 1) { this->interval_seconds = header[0x11]; // not always 1 after all
qWarning() << this->m_path << always_1 << "!= 1"; this->duration = this->interval_count * this->interval_seconds; // ??? the last entry doesn't always seem to be a full interval?
//break; // don't break to avoid changing behavior (for now) quint8 wvfm_signals = header[0x12];
}
quint16 wvfm_signals = header[0x12] | header[0x13] << 8;
// Read the variable-length data + trailing byte.
int ws_size = (this->fileVersion == 3) ? 4 : 3; int ws_size = (this->fileVersion == 3) ? 4 : 3;
int sbsize = wvfm_signals * ws_size; int sbsize = wvfm_signals * ws_size + 1;
extra = f.read(sbsize); extra = f.read(sbsize);
if (extra.size() != sbsize) { if (extra.size() != sbsize) {
@ -3752,20 +3767,34 @@ bool PRS1DataChunk::ReadWaveformHeader(QFile & f)
this->m_header.append(extra); this->m_header.append(extra);
header = (unsigned char *)this->m_header.data(); header = (unsigned char *)this->m_header.data();
// Read the waveform information in reverse. // TODO: Double-check this, always seems to be flow then pressure. // Parse the variable-length waveform information.
int pos = 0x14 + (wvfm_signals - 1) * ws_size; int pos = 0x13;
for (int i = 0; i < wvfm_signals; ++i) { for (int i = 0; i < wvfm_signals; ++i) {
quint16 interleave = header[pos] | header[pos + 1] << 8; // samples per block (Usually 05 00) quint8 kind = header[pos];
if (this->fileVersion == 2) { if (kind != i) { // always seems to range from 0...wvfm_signals-1, alert if not
quint8 sample_format = header[pos + 2]; // TODO: sample_format seems to be unused anywhere else in the loader. qWarning() << this->m_path << kind << "!=" << i << "waveform kind";
this->waveformInfo.push_back(PRS1Waveform(interleave, sample_format)); //break; // don't break to avoid changing behavior (for now)
pos -= 3;
} else if (this->fileVersion == 3) {
//quint16 sample_size = header[pos + 2] | header[pos + 3] << 8; // size in bits?? (08 00)
// Possibly this is size in bits, and sign bit for the other byte?
this->waveformInfo.push_back(PRS1Waveform(interleave, 0));
pos -= 4;
} }
quint16 interleave = header[pos + 1] | header[pos + 2] << 8; // samples per interval
if (this->fileVersion == 2) {
this->waveformInfo.push_back(PRS1Waveform(interleave, kind));
pos += 3;
} else if (this->fileVersion == 3) {
int always_8 = header[pos + 3]; // sample size in bits?
if (always_8 != 8) {
qWarning() << this->m_path << always_8 << "!= 8 in waveform header";
//break; // don't break to avoid changing behavior (for now)
}
this->waveformInfo.push_back(PRS1Waveform(interleave, kind));
pos += 4;
}
}
// And the trailing byte, whatever it is.
int always_0 = header[pos];
if (always_0 != 0) {
qWarning() << this->m_path << always_0 << "!= 0 in waveform header";
//break; // don't break to avoid changing behavior (for now)
} }
ok = true; ok = true;

View File

@ -64,12 +64,12 @@ public:
PRS1DataChunk() { PRS1DataChunk() {
fileVersion = 0; fileVersion = 0;
blockSize = 0; blockSize = 0;
ext = 255;
htype = 0; htype = 0;
family = 0; family = 0;
familyVersion = 0; familyVersion = 0;
timestamp = 0; ext = 255;
sessionid = 0; sessionid = 0;
timestamp = 0;
duration = 0; duration = 0;
@ -90,20 +90,26 @@ public:
int m_index; // nth chunk in file int m_index; // nth chunk in file
inline void SetIndex(int index) { m_index = index; } inline void SetIndex(int index) { m_index = index; }
// Common fields
quint8 fileVersion; quint8 fileVersion;
quint16 blockSize; quint16 blockSize;
quint8 ext;
quint8 htype; quint8 htype;
quint8 family; quint8 family;
quint8 familyVersion; quint8 familyVersion;
quint32 timestamp; quint8 ext;
SessionID sessionid; SessionID sessionid;
quint32 timestamp;
quint16 duration; // Waveform-specific fields
quint16 interval_count;
quint8 interval_seconds;
int duration;
QList<PRS1Waveform> waveformInfo; QList<PRS1Waveform> waveformInfo;
// V3 normal/non-waveform fields
QMap<unsigned char, short> hblock; QMap<unsigned char, short> hblock;
// Trailing common fields
quint8 storedChecksum; // header checksum stored in file, last byte of m_header quint8 storedChecksum; // header checksum stored in file, last byte of m_header
quint8 calcChecksum; // header checksum as calculated when parsing quint8 calcChecksum; // header checksum as calculated when parsing
quint32 storedCrc; // header + data CRC stored in file, last 2-4 bytes of chunk quint32 storedCrc; // header + data CRC stored in file, last 2-4 bytes of chunk