mirror of
https://gitlab.com/pholy/OSCAR-code.git
synced 2025-04-08 12:10:43 +00:00
Interim commit - resmed_loader still needs work
This commit is contained in:
parent
93b363465f
commit
531edc6ad2
@ -19,100 +19,104 @@
|
|||||||
|
|
||||||
#include "edfparser.h"
|
#include "edfparser.h"
|
||||||
|
|
||||||
EDFParser::EDFParser(QString name)
|
EDFInfo::EDFInfo()
|
||||||
{
|
{
|
||||||
filesize = 0;
|
filesize = 0;
|
||||||
datasize = 0;
|
datasize = 0;
|
||||||
signalPtr = nullptr;
|
signalPtr = nullptr;
|
||||||
hdrPtr = nullptr;
|
hdrPtr = nullptr;
|
||||||
fileData.clear();
|
fileData = nullptr;
|
||||||
if (!name.isEmpty())
|
|
||||||
Open(name);
|
|
||||||
}
|
}
|
||||||
EDFParser::~EDFParser()
|
|
||||||
|
EDFInfo::~EDFInfo()
|
||||||
{
|
{
|
||||||
for (auto & s : edfsignals) {
|
for (auto & s : edfsignals) {
|
||||||
if (s.value)
|
if (s.value)
|
||||||
delete [] s.value;
|
delete [] s.value;
|
||||||
}
|
}
|
||||||
|
// for (auto & a : annotations)
|
||||||
|
// delete a;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EDFParser::Open(const QString & name)
|
QByteArray * EDFInfo::Open(const QString & name)
|
||||||
{
|
{
|
||||||
if (hdrPtr != nullptr) {
|
if (hdrPtr != nullptr) {
|
||||||
qWarning() << "EDFParser::Open() called with file already open " << name;
|
qWarning() << "EDFInfo::Open() called with file already open " << name;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
QFile fi(name);
|
QFile fi(name);
|
||||||
if (!fi.open(QFile::ReadOnly)) {
|
if (!fi.open(QFile::ReadOnly)) {
|
||||||
qDebug() << "EDFParser::Open() Couldn't open file " << name;
|
qDebug() << "EDFInfo::Open() Couldn't open file " << name;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
fileData = new QByteArray();
|
||||||
if (name.endsWith(STR_ext_gz)) {
|
if (name.endsWith(STR_ext_gz)) {
|
||||||
fileData = gUncompress(fi.readAll()); // Open and decompress file
|
*fileData = gUncompress(fi.readAll()); // Open and decompress file
|
||||||
} else {
|
} else {
|
||||||
fileData = fi.readAll(); // Open and read uncompressed file
|
*fileData = fi.readAll(); // Open and read uncompressed file
|
||||||
}
|
}
|
||||||
fi.close();
|
fi.close();
|
||||||
if (fileData.size() <= EDFHeaderSize) {
|
if (fileData->size() <= EDFHeaderSize) {
|
||||||
qDebug() << "EDFParser::Open() File too short " << name;
|
delete fileData;
|
||||||
|
qDebug() << "EDFInfo::Open() File too short " << name;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
hdrPtr = (EDFHeaderRaw *)fileData.constData();
|
|
||||||
signalPtr = (char *)fileData.constData() + EDFHeaderSize;
|
|
||||||
filename = name;
|
filename = name;
|
||||||
filesize = fileData.size();
|
return fileData;
|
||||||
datasize = filesize - EDFHeaderSize;
|
|
||||||
pos = 0;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EDFParser::Parse()
|
bool EDFInfo::Parse(QByteArray * fileData )
|
||||||
{
|
{
|
||||||
bool ok;
|
bool ok;
|
||||||
|
|
||||||
if (hdrPtr == nullptr) {
|
if (fileData == nullptr) {
|
||||||
qWarning() << "EDFParser::Parse() called without valid EDF data " << filename;
|
qWarning() << "EDFInfo::Parse() called without valid EDF data " << filename;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hdrPtr = (EDFHeaderRaw *)(*fileData).constData();
|
||||||
|
signalPtr = (char *)(*fileData).constData() + EDFHeaderSize;
|
||||||
|
filesize = (*fileData).size();
|
||||||
|
datasize = filesize - EDFHeaderSize;
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
eof = false;
|
eof = false;
|
||||||
edfHdr.version = QString::fromLatin1(hdrPtr->version, 8).toLong(&ok);
|
edfHdr.version = QString::fromLatin1(hdrPtr->version, 8).toLong(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
qWarning() << "EDFParser::Parser() Bad Version " << filename;
|
qWarning() << "EDFInfo::Parse() Bad Version " << filename;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//patientident=QString::fromLatin1(header.patientident,80);
|
edfHdr.patientident=QString::fromLatin1(hdrPtr->patientident,80);
|
||||||
edfHdr.recordingident = QString::fromLatin1(hdrPtr->recordingident, 80); // Serial number is in here..
|
edfHdr.recordingident = QString::fromLatin1(hdrPtr->recordingident, 80); // Serial number is in here..
|
||||||
edfHdr.startdate_orig = QDateTime::fromString(QString::fromLatin1(hdrPtr->datetime, 16), "dd.MM.yyHH.mm.ss");
|
edfHdr.startdate_orig = QDateTime::fromString(QString::fromLatin1(hdrPtr->datetime, 16), "dd.MM.yyHH.mm.ss");
|
||||||
edfHdr.num_header_bytes = QString::fromLatin1(hdrPtr->num_header_bytes, 8).toLong(&ok);
|
edfHdr.num_header_bytes = QString::fromLatin1(hdrPtr->num_header_bytes, 8).toLong(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
qWarning() << "EDFParser::Parde() Bad header byte count " << filename;
|
qWarning() << "EDFInfo::Parse() Bad header byte count " << filename;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
edfHdr.reserved44=QString::fromLatin1(hdrPtr->reserved, 44);
|
edfHdr.reserved44=QString::fromLatin1(hdrPtr->reserved, 44);
|
||||||
edfHdr.num_data_records = QString::fromLatin1(hdrPtr->num_data_records, 8).toLong(&ok);
|
edfHdr.num_data_records = QString::fromLatin1(hdrPtr->num_data_records, 8).toLong(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
qWarning() << "EDFParser::Parse() Bad data record count " << filename;
|
qWarning() << "EDFInfo::Parse() Bad data record count " << filename;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
edfHdr.duration_Seconds = QString::fromLatin1(hdrPtr->dur_data_records, 8).toDouble(&ok);
|
edfHdr.duration_Seconds = QString::fromLatin1(hdrPtr->dur_data_records, 8).toDouble(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
qWarning() << "EDFParser::Parse() Bad duration " << filename;
|
qWarning() << "EDFInfo::Parse() Bad duration " << filename;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
edfHdr.num_signals = QString::fromLatin1(hdrPtr->num_signals, 4).toLong(&ok);
|
edfHdr.num_signals = QString::fromLatin1(hdrPtr->num_signals, 4).toLong(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
qWarning() << "EDFParser::Parse() Bad number of signals " << filename;
|
qWarning() << "EDFInfo::Parse() Bad number of signals " << filename;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -123,49 +127,49 @@ bool EDFParser::Parse()
|
|||||||
// Now copy all the Signal descriptives into edfsignals
|
// Now copy all the Signal descriptives into edfsignals
|
||||||
for (auto & sig : edfsignals) {
|
for (auto & sig : edfsignals) {
|
||||||
sig.value = nullptr;
|
sig.value = nullptr;
|
||||||
sig.label = Read(16);
|
sig.label = ReadBytes(16);
|
||||||
|
|
||||||
signal_labels.push_back(sig.label);
|
signal_labels.push_back(sig.label);
|
||||||
signalList[sig.label].push_back(&sig);
|
signalList[sig.label].push_back(&sig);
|
||||||
if (eof) {
|
if (eof) {
|
||||||
qWarning() << "EDFParser::Parse() Early end of file " << filename;
|
qWarning() << "EDFInfo::Parse() Early end of file " << filename;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto & sig : edfsignals) {
|
for (auto & sig : edfsignals) {
|
||||||
sig.transducer_type = Read(80);
|
sig.transducer_type = ReadBytes(80);
|
||||||
}
|
}
|
||||||
for (auto & sig : edfsignals) {
|
for (auto & sig : edfsignals) {
|
||||||
sig.physical_dimension = Read(8);
|
sig.physical_dimension = ReadBytes(8);
|
||||||
}
|
}
|
||||||
for (auto & sig : edfsignals) {
|
for (auto & sig : edfsignals) {
|
||||||
sig.physical_minimum = Read(8).toDouble(&ok);
|
sig.physical_minimum = ReadBytes(8).toDouble(&ok);
|
||||||
}
|
}
|
||||||
for (auto & sig : edfsignals) {
|
for (auto & sig : edfsignals) {
|
||||||
sig.physical_maximum = Read(8).toDouble(&ok);
|
sig.physical_maximum = ReadBytes(8).toDouble(&ok);
|
||||||
}
|
}
|
||||||
for (auto & sig : edfsignals) {
|
for (auto & sig : edfsignals) {
|
||||||
sig.digital_minimum = Read(8).toDouble(&ok);
|
sig.digital_minimum = ReadBytes(8).toDouble(&ok);
|
||||||
}
|
}
|
||||||
for (auto & sig : edfsignals) {
|
for (auto & sig : edfsignals) {
|
||||||
sig.digital_maximum = Read(8).toDouble(&ok);
|
sig.digital_maximum = ReadBytes(8).toDouble(&ok);
|
||||||
sig.gain = (sig.physical_maximum - sig.physical_minimum) / (sig.digital_maximum - sig.digital_minimum);
|
sig.gain = (sig.physical_maximum - sig.physical_minimum) / (sig.digital_maximum - sig.digital_minimum);
|
||||||
sig.offset = 0;
|
sig.offset = 0;
|
||||||
}
|
}
|
||||||
for (auto & sig : edfsignals) {
|
for (auto & sig : edfsignals) {
|
||||||
sig.prefiltering = Read(80);
|
sig.prefiltering = ReadBytes(80);
|
||||||
}
|
}
|
||||||
for (auto & sig : edfsignals) {
|
for (auto & sig : edfsignals) {
|
||||||
sig.nr = Read(8).toLong(&ok);
|
sig.nr = ReadBytes(8).toLong(&ok);
|
||||||
}
|
}
|
||||||
for (auto & sig : edfsignals) {
|
for (auto & sig : edfsignals) {
|
||||||
sig.reserved = Read(32);
|
sig.reserved = ReadBytes(32);
|
||||||
}
|
}
|
||||||
|
|
||||||
// could do it earlier, but it won't crash from > EOF Reads
|
// could do it earlier, but it won't crash from > EOF Reads
|
||||||
if (eof) {
|
if (eof) {
|
||||||
qWarning() << "EDFParser::Parse() Early end of file " << filename;
|
qWarning() << "EDFInfo::Parse() Early end of file " << filename;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -180,7 +184,7 @@ bool EDFParser::Parse()
|
|||||||
if (allocsize > (datasize - pos)) {
|
if (allocsize > (datasize - pos)) {
|
||||||
// Space required more than the remainder left to read,
|
// Space required more than the remainder left to read,
|
||||||
// so abort and let the user clean up the corrupted file themselves
|
// so abort and let the user clean up the corrupted file themselves
|
||||||
qWarning() << "EDFParser::Parse(): " << filename << " is too short!";
|
qWarning() << "EDFInfo::Parse(): " << filename << " is too short!";
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -197,6 +201,10 @@ bool EDFParser::Parse()
|
|||||||
}
|
}
|
||||||
for (int x = 0; x < edfHdr.num_data_records; x++) {
|
for (int x = 0; x < edfHdr.num_data_records; x++) {
|
||||||
for (auto & sig : edfsignals) {
|
for (auto & sig : edfsignals) {
|
||||||
|
if ( sig.label.contains("ANNOTATIONS") ) {
|
||||||
|
annotations.push_back(ReadAnnotations( (char *)&signalPtr[pos], sig.nr*2));
|
||||||
|
pos += sig.nr * 2;
|
||||||
|
} else { // it's got genuine 16-bit values
|
||||||
#ifdef Q_LITTLE_ENDIAN
|
#ifdef Q_LITTLE_ENDIAN
|
||||||
// Intel x86, etc..
|
// Intel x86, etc..
|
||||||
memcpy((char *)&sig.value[sig.pos], (char *)&signalPtr[pos], sig.nr * 2);
|
memcpy((char *)&sig.value[sig.pos], (char *)&signalPtr[pos], sig.nr * 2);
|
||||||
@ -211,47 +219,102 @@ bool EDFParser::Parse()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now massage some stuff into OSCAR's layout
|
|
||||||
int snp = edfHdr.recordingident.indexOf("SRN=");
|
|
||||||
serialnumber.clear();
|
|
||||||
|
|
||||||
for (int i = snp + 4; i < edfHdr.recordingident.length(); i++) {
|
|
||||||
if (edfHdr.recordingident[i] == ' ') {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
serialnumber += edfHdr.recordingident[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
QDate d2 = edfHdr.startdate_orig.date();
|
|
||||||
if (d2.year() < 2000) {
|
|
||||||
d2.setDate(d2.year() + 100, d2.month(), d2.day());
|
|
||||||
edfHdr.startdate_orig.setDate(d2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!edfHdr.startdate_orig.isValid()) {
|
|
||||||
qDebug() << "Invalid date time retreieved parsing EDF File " << filename;
|
|
||||||
sleep(1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
startdate = qint64(edfHdr.startdate_orig.toTime_t()) * 1000LL;
|
|
||||||
//startdate-=timezoneOffset();
|
|
||||||
if (startdate == 0) {
|
|
||||||
qDebug() << "Invalid startdate = 0 in EDF File " << filename;
|
|
||||||
sleep(1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dur_data_record = (edfHdr.duration_Seconds * 1000.0L);
|
|
||||||
|
|
||||||
enddate = startdate + dur_data_record * qint64(edfHdr.num_data_records);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse the EDF file to get the annotations out of it.
|
||||||
|
QVector<Annotation> * EDFInfo::ReadAnnotations(const char * data, int charLen)
|
||||||
|
{
|
||||||
|
QVector<Annotation> * annoVec = new QVector<Annotation>;
|
||||||
|
|
||||||
|
// Process event annotation record
|
||||||
|
|
||||||
|
long pos = 0;
|
||||||
|
double offset;
|
||||||
|
double duration;
|
||||||
|
|
||||||
|
while (pos < charLen) {
|
||||||
|
QString text;
|
||||||
|
bool sign, ok;
|
||||||
|
char c = data[pos];
|
||||||
|
|
||||||
|
if ((c != '+') && (c != '-')) // Annotaion must start with a +/- sign
|
||||||
|
break;
|
||||||
|
sign = (data[pos++] == '+');
|
||||||
|
|
||||||
|
text = "";
|
||||||
|
c = data[pos];
|
||||||
|
|
||||||
|
do { // collect the offset
|
||||||
|
text += c;
|
||||||
|
pos++;
|
||||||
|
c = data[pos];
|
||||||
|
} while ((c != AnnoSep) && (c != AnnoDurMark)); // a duration is optional
|
||||||
|
|
||||||
|
offset = text.toDouble(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
qDebug() << "Faulty offset in annotation record ";
|
||||||
|
// sleep(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sign)
|
||||||
|
offset = -offset;
|
||||||
|
|
||||||
|
duration = -1.0; // This indicates no duration supplied
|
||||||
|
// First entry
|
||||||
|
if (data[pos] == AnnoDurMark) { // get duration.(preceded by decimal 21 byte)
|
||||||
|
pos++;
|
||||||
|
text = "";
|
||||||
|
|
||||||
|
do { // collect the duration
|
||||||
|
text += data[pos];
|
||||||
|
pos++;
|
||||||
|
} while ((data[pos] != AnnoSep) && (pos < charLen)); // separator code
|
||||||
|
|
||||||
|
duration = text.toDouble(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
qDebug() << "Faulty duration in annotation record ";
|
||||||
|
// sleep(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((data[pos] == AnnoSep) && (pos < charLen)) {
|
||||||
|
int textLen = 0;
|
||||||
|
pos++;
|
||||||
|
const char * textStart = &data[pos];
|
||||||
|
if (data[pos] == AnnoEnd)
|
||||||
|
break;
|
||||||
|
if (data[pos] == AnnoSep) {
|
||||||
|
pos++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
do { // collect the annotation text
|
||||||
|
pos++; // officially UTF-8 is allowed here, so don't mangle it
|
||||||
|
textLen++;
|
||||||
|
} while ((data[pos] != AnnoSep) && (pos < charLen)); // separator code
|
||||||
|
text.fromUtf8(textStart, textLen);
|
||||||
|
annoVec->push_back( Annotation( offset, duration, text) );
|
||||||
|
if (pos >= charLen) {
|
||||||
|
qDebug() << "Short EDF Annotations record";
|
||||||
|
// sleep(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((pos < charLen) && (data[pos] == AnnoEnd))
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (pos >= charLen)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return annoVec;
|
||||||
|
}
|
||||||
|
|
||||||
// Read a 16 bits integer
|
// Read a 16 bits integer
|
||||||
qint16 EDFParser::Read16()
|
qint16 EDFInfo::Read16()
|
||||||
{
|
{
|
||||||
if ((pos + 2) > datasize) {
|
if ((pos + 2) > datasize) {
|
||||||
eof = true;
|
eof = true;
|
||||||
@ -266,7 +329,7 @@ qint16 EDFParser::Read16()
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EDFParser::Read(unsigned n)
|
QString EDFInfo::ReadBytes(unsigned n)
|
||||||
{
|
{
|
||||||
if ((pos + long(n)) > datasize) {
|
if ((pos + long(n)) > datasize) {
|
||||||
eof = true;
|
eof = true;
|
||||||
@ -277,7 +340,7 @@ QString EDFParser::Read(unsigned n)
|
|||||||
return buf.trimmed();
|
return buf.trimmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
EDFSignal *EDFParser::lookupLabel(const QString & name, int index)
|
EDFSignal *EDFInfo::lookupLabel(const QString & name, int index)
|
||||||
{
|
{
|
||||||
auto it = signalList.find(name);
|
auto it = signalList.find(name);
|
||||||
if (it == signalList.end())
|
if (it == signalList.end())
|
||||||
|
@ -57,8 +57,8 @@ struct EDFHeaderQT {
|
|||||||
long num_header_bytes;
|
long num_header_bytes;
|
||||||
QString reserved44;
|
QString reserved44;
|
||||||
long num_data_records;
|
long num_data_records;
|
||||||
long duration_Seconds;
|
double duration_Seconds;
|
||||||
long num_signals;
|
int num_signals;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! \struct EDFSignal
|
/*! \struct EDFSignal
|
||||||
@ -80,79 +80,87 @@ struct EDFSignal {
|
|||||||
long nr; //! \brief Number of records
|
long nr; //! \brief Number of records
|
||||||
QString reserved; //! \brief Reserved (usually blank)
|
QString reserved; //! \brief Reserved (usually blank)
|
||||||
qint16 *value; //! \brief Pointer to the signals sample data
|
qint16 *value; //! \brief Pointer to the signals sample data
|
||||||
|
|
||||||
int pos; //! \brief a non-EDF extra used internally to count the signal data
|
int pos; //! \brief a non-EDF extra used internally to count the signal data
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*! \class Annotation
|
||||||
|
\author Phil Olynyk
|
||||||
|
\brief Hold the annotation text from an EDF file
|
||||||
|
*/
|
||||||
|
class Annotation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Annotation() { duration = -1.0; };
|
||||||
|
Annotation( double off, double dur, QString tx ) {
|
||||||
|
offset = off;
|
||||||
|
duration = dur;
|
||||||
|
text = tx;
|
||||||
|
};
|
||||||
|
virtual ~Annotation() {};
|
||||||
|
|
||||||
/*! \class EDFParser
|
double offset;
|
||||||
|
double duration;
|
||||||
|
QString text;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! \class EDFInfo
|
||||||
|
\author Phil Olynyk
|
||||||
\author Mark Watkins <mark@jedimark.net>
|
\author Mark Watkins <mark@jedimark.net>
|
||||||
\brief Parse an EDF+ data file into a list of EDFSignal's
|
\brief Parse an EDF+ data file into a list of EDFSignal's
|
||||||
\note More information on the EDF+ file format can be obtained from http://edfplus.info
|
\note More information on the EDF+ file format can be obtained from http://edfplus.info
|
||||||
*/
|
*/
|
||||||
class EDFParser
|
class EDFInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//! \brief Constructs an EDFParser object, opening the filename if one supplied
|
//! \brief Constructs an EDFParser object, opening the filename if one supplied
|
||||||
EDFParser(QString filename = "");
|
EDFInfo();
|
||||||
|
|
||||||
~EDFParser();
|
virtual ~EDFInfo();
|
||||||
|
|
||||||
//! \brief Open the EDF+ file, and read it's header
|
virtual QByteArray * Open(const QString & name); //! \brief Open the EDF+ file, and read it's header
|
||||||
bool Open(const QString & name);
|
|
||||||
|
|
||||||
//! \brief Parse the EDF+ file into the list of EDFSignals.. Must be call Open(..) first.
|
virtual bool Parse(QByteArray * fileData); //! \brief Parse the EDF+ file into the EDFheaderQT. Must call Open(..) first.
|
||||||
bool Parse();
|
|
||||||
|
|
||||||
//! \brief Read n bytes of 8 bit data from the EDF+ data stream
|
virtual EDFSignal *lookupLabel(const QString & name, int index=0); //! \brief Return a ptr to the i'th signal with that name
|
||||||
QString Read(unsigned n);
|
|
||||||
|
|
||||||
//! \brief Read 16 bit word of data from the EDF+ data stream
|
virtual long GetNumSignals() { return edfHdr.num_signals; } //! \brief Returns the number of signals contained in this EDF file
|
||||||
qint16 Read16();
|
|
||||||
|
|
||||||
//! \brief Return a ptr to the i'th signal with the given name (if multiple signal with the same name(?!))
|
virtual long GetNumDataRecords() { return edfHdr.num_data_records; } //! \brief Returns the number of data records contained per signal.
|
||||||
EDFSignal *lookupLabel(const QString & name, int index=0);
|
|
||||||
|
|
||||||
//! \brief Returns the number of signals contained in this EDF file
|
virtual double GetDuration() { return edfHdr.duration_Seconds; } //! \brief Returns the duration represented by this EDF file
|
||||||
long GetNumSignals() { return edfHdr.num_signals; }
|
|
||||||
|
|
||||||
//! \brief Returns the number of data records contained per signal.
|
virtual QString GetPatient() { return edfHdr.patientident; } //! \brief Returns the patientid field from the EDF header
|
||||||
long GetNumDataRecords() { return edfHdr.num_data_records; }
|
|
||||||
|
|
||||||
//! \brief Returns the duration represented by this EDF file (in milliseconds)
|
|
||||||
qint64 GetDuration() { return dur_data_record; }
|
|
||||||
|
|
||||||
//! \brief Returns the patientid field from the EDF header
|
|
||||||
QString GetPatient() { return edfHdr.patientident; }
|
|
||||||
|
|
||||||
// The data members follow
|
// The data members follow
|
||||||
|
|
||||||
//! \brief The header in a QT friendly form
|
QString filename; //! \brief For debug and error messages
|
||||||
EDFHeaderQT edfHdr;
|
|
||||||
|
|
||||||
//! \brief Vector containing the list of EDFSignals contained in this edf file
|
EDFHeaderQT edfHdr; //! \brief The header in a QT friendly form
|
||||||
QVector<EDFSignal> edfsignals;
|
|
||||||
|
|
||||||
//! \brief An by-name indexed into the EDFSignal data
|
QVector<EDFSignal> edfsignals; //! \brief Holds the EDFSignals contained in this edf file
|
||||||
QStringList signal_labels;
|
|
||||||
|
|
||||||
//! \brief ResMed sometimes re-uses the SAME signal name
|
QVector< QVector<Annotation> * > annotations; //! \brief Holds the Annotaions for this EDF file
|
||||||
QHash<QString, QList<EDFSignal *> > signalList;
|
|
||||||
|
|
||||||
//! \brief The following are computed from the edfHdr data
|
QStringList signal_labels; //! \brief An by-name indexed into the EDFSignal data
|
||||||
QString serialnumber;
|
|
||||||
qint64 dur_data_record;
|
QHash<QString, QList<EDFSignal *> > signalList; //! \brief ResMed sometimes re-uses the SAME signal name
|
||||||
qint64 startdate;
|
|
||||||
qint64 enddate;
|
|
||||||
|
|
||||||
// the following could be private
|
// the following could be private
|
||||||
|
private:
|
||||||
|
QVector<Annotation> * ReadAnnotations( const char * data, int charLen ); //! \brief Create an Annotaion vector from the signal values
|
||||||
|
|
||||||
|
QString ReadBytes(unsigned n); //! \brief Read n bytes of 8 bit data from the EDF+ data stream
|
||||||
|
|
||||||
|
qint16 Read16(); //! \brief Read 16 bit word of data from the EDF+ data stream
|
||||||
|
|
||||||
//! \brief This is the array holding the EDF file data
|
//! \brief This is the array holding the EDF file data
|
||||||
QByteArray fileData;
|
QByteArray * fileData;
|
||||||
//! \brief The EDF+ files header structure, used as a place holder while processing the text data.
|
//! \brief The EDF+ files header structure, used as a place holder while processing the text data.
|
||||||
EDFHeaderRaw *hdrPtr;
|
EDFHeaderRaw *hdrPtr;
|
||||||
//! \brief This is the array of signal descriptors and values
|
//! \brief This is the array of signal descriptors and values
|
||||||
char *signalPtr;
|
char *signalPtr;
|
||||||
|
|
||||||
QString filename;
|
|
||||||
long filesize;
|
long filesize;
|
||||||
long datasize;
|
long datasize;
|
||||||
long pos;
|
long pos;
|
||||||
|
@ -59,11 +59,54 @@ const QString STR_ext_TGT = "tgt";
|
|||||||
const QString STR_ext_CRC = "crc";
|
const QString STR_ext_CRC = "crc";
|
||||||
|
|
||||||
|
|
||||||
ResMedEDFParser::ResMedEDFParser(QString filename) :EDFParser(filename) { }
|
ResMedEDFInfo::ResMedEDFInfo() :EDFInfo() { }
|
||||||
ResMedEDFParser::~ResMedEDFParser() { }
|
ResMedEDFInfo::~ResMedEDFInfo() { }
|
||||||
|
|
||||||
|
bool ResMedEDFInfo::Parse(QByteArray * fileData ) // overrides and calls the super's Parse
|
||||||
|
{
|
||||||
|
EDFInfo::Parse( fileData );
|
||||||
|
|
||||||
|
// Now massage some stuff into OSCAR's layout
|
||||||
|
int snp = edfHdr.recordingident.indexOf("SRN=");
|
||||||
|
serialnumber.clear();
|
||||||
|
|
||||||
|
for (int i = snp + 4; i < edfHdr.recordingident.length(); i++) {
|
||||||
|
if (edfHdr.recordingident[i] == ' ') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
serialnumber += edfHdr.recordingident[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
QDate d2 = edfHdr.startdate_orig.date();
|
||||||
|
if (d2.year() < 2000) {
|
||||||
|
d2.setDate(d2.year() + 100, d2.month(), d2.day());
|
||||||
|
edfHdr.startdate_orig.setDate(d2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!edfHdr.startdate_orig.isValid()) {
|
||||||
|
qDebug() << "Invalid date time retreieved parsing EDF File " << filename;
|
||||||
|
sleep(1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
startdate = qint64(edfHdr.startdate_orig.toTime_t()) * 1000LL;
|
||||||
|
//startdate-=timezoneOffset();
|
||||||
|
if (startdate == 0) {
|
||||||
|
qDebug() << "Invalid startdate = 0 in EDF File " << filename;
|
||||||
|
sleep(1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dur_data_record = (edfHdr.duration_Seconds * 1000.0L);
|
||||||
|
|
||||||
|
enddate = startdate + dur_data_record * qint64(edfHdr.num_data_records);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Looks up foreign language Signal names that match this channelID
|
// Looks up foreign language Signal names that match this channelID
|
||||||
EDFSignal *ResMedEDFParser::lookupSignal(ChannelID ch)
|
EDFSignal *ResMedEDFInfo::lookupSignal(ChannelID ch)
|
||||||
{
|
{
|
||||||
// Get list of all known foreign language names for this channel
|
// Get list of all known foreign language names for this channel
|
||||||
auto channames = resmed_codes.find(ch);
|
auto channames = resmed_codes.find(ch);
|
||||||
@ -113,7 +156,7 @@ void ResmedLoader::ParseSTR(Machine *mach, QMap<QDate, STRFile> & STRmap)
|
|||||||
int totalRecs = 0;
|
int totalRecs = 0;
|
||||||
for (auto it=STRmap.begin(), end=STRmap.end(); it != end; ++it) {
|
for (auto it=STRmap.begin(), end=STRmap.end(); it != end; ++it) {
|
||||||
STRFile & file = it.value();
|
STRFile & file = it.value();
|
||||||
ResMedEDFParser & str = *file.edf;
|
ResMedEDFInfo & str = *file.edf;
|
||||||
totalRecs += str.GetNumDataRecords();
|
totalRecs += str.GetNumDataRecords();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +169,7 @@ void ResmedLoader::ParseSTR(Machine *mach, QMap<QDate, STRFile> & STRmap)
|
|||||||
for (auto it=STRmap.begin(), end=STRmap.end(); it != end; ++it) {
|
for (auto it=STRmap.begin(), end=STRmap.end(); it != end; ++it) {
|
||||||
STRFile & file = it.value();
|
STRFile & file = it.value();
|
||||||
QString & strfile = file.filename;
|
QString & strfile = file.filename;
|
||||||
ResMedEDFParser & str = *file.edf;
|
ResMedEDFInfo & str = *file.edf;
|
||||||
|
|
||||||
QDate date = str.edfHdr.startdate_orig.date(); // each STR.edf record starts at 12 noon
|
QDate date = str.edfHdr.startdate_orig.date(); // each STR.edf record starts at 12 noon
|
||||||
|
|
||||||
@ -569,7 +612,7 @@ ResmedLoader::ResmedLoader() {
|
|||||||
m_type = MT_CPAP;
|
m_type = MT_CPAP;
|
||||||
|
|
||||||
timeInTimeDelta = timeInLoadBRP = timeInLoadPLD = timeInLoadEVE = 0;
|
timeInTimeDelta = timeInLoadBRP = timeInLoadPLD = timeInLoadEVE = 0;
|
||||||
timeInLoadCSL = timeInLoadSAD = timeInEDFParser = timeInEDFOpen = timeInAddWaveform = 0;
|
timeInLoadCSL = timeInLoadSAD = timeInEDFInfo = timeInEDFOpen = timeInAddWaveform = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,8 +718,9 @@ EDFType lookupEDFType(const QString & text)
|
|||||||
// Pretend to parse the EVE file to get the duration out of it.
|
// Pretend to parse the EVE file to get the duration out of it.
|
||||||
int PeekAnnotations(const QString & path, quint32 &start, quint32 &end)
|
int PeekAnnotations(const QString & path, quint32 &start, quint32 &end)
|
||||||
{
|
{
|
||||||
ResMedEDFParser edf(path);
|
ResMedEDFInfo edf;
|
||||||
if (!edf.Parse())
|
QByteArray * fileData = edf.Open(path);
|
||||||
|
if (!edf.Parse(fileData))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
QString t;
|
QString t;
|
||||||
@ -895,7 +939,7 @@ EDFduration getEDFDuration(const QString & filename)
|
|||||||
// Have to get the actual duration of the EVE file by parsing the annotations. :(
|
// Have to get the actual duration of the EVE file by parsing the annotations. :(
|
||||||
|
|
||||||
|
|
||||||
// Can we cache the stupid EDFParser file for later ???
|
// Can we cache the stupid EDFInfo file for later ???
|
||||||
|
|
||||||
int recs = PeekAnnotations(filename, st2, en2);
|
int recs = PeekAnnotations(filename, st2, en2);
|
||||||
if (recs > 0) {
|
if (recs > 0) {
|
||||||
@ -1764,8 +1808,9 @@ int ResmedLoader::Open(const QString & dirpath)
|
|||||||
|
|
||||||
// Now place any of these files in the Backup folder sorted by the file date
|
// Now place any of these files in the Backup folder sorted by the file date
|
||||||
for (auto & filename : strfiles) {
|
for (auto & filename : strfiles) {
|
||||||
ResMedEDFParser * stredf = new ResMedEDFParser(filename);
|
ResMedEDFInfo * stredf = new ResMedEDFInfo();
|
||||||
if ( ! stredf->Parse()) {
|
QByteArray * fileData = stredf->Open(filename);
|
||||||
|
if ( ! stredf->Parse(fileData)) {
|
||||||
qDebug() << "Faulty STR file" << filename;
|
qDebug() << "Faulty STR file" << filename;
|
||||||
delete stredf;
|
delete stredf;
|
||||||
continue;
|
continue;
|
||||||
@ -1838,8 +1883,9 @@ int ResmedLoader::Open(const QString & dirpath)
|
|||||||
if (STRmap.contains(date))
|
if (STRmap.contains(date))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ResMedEDFParser * stredf = new ResMedEDFParser(fi.canonicalFilePath());
|
ResMedEDFInfo * stredf = new ResMedEDFInfo();
|
||||||
if (!stredf->Parse()) {
|
QByteArray * fileData = stredf->Open(fi.canonicalFilePath() );
|
||||||
|
if (!stredf->Parse(fileData)) {
|
||||||
qDebug() << "Faulty STR file" << filename;
|
qDebug() << "Faulty STR file" << filename;
|
||||||
delete stredf;
|
delete stredf;
|
||||||
continue;
|
continue;
|
||||||
@ -2002,7 +2048,7 @@ int ResmedLoader::Open(const QString & dirpath)
|
|||||||
qDebug() << "Total toTimeDelta function usage:" << totalbytes << "in" << double(totalns) / 1000000000.0 << "seconds";
|
qDebug() << "Total toTimeDelta function usage:" << totalbytes << "in" << double(totalns) / 1000000000.0 << "seconds";
|
||||||
|
|
||||||
qDebug() << "Total CPU time in EDF Open" << timeInEDFOpen;
|
qDebug() << "Total CPU time in EDF Open" << timeInEDFOpen;
|
||||||
qDebug() << "Total CPU time in EDF Parser" << timeInEDFParser;
|
qDebug() << "Total CPU time in EDF Parser" << timeInEDFInfo;
|
||||||
qDebug() << "Total CPU time in LoadBRP" << timeInLoadBRP;
|
qDebug() << "Total CPU time in LoadBRP" << timeInLoadBRP;
|
||||||
qDebug() << "Total CPU time in LoadPLD" << timeInLoadPLD;
|
qDebug() << "Total CPU time in LoadPLD" << timeInLoadPLD;
|
||||||
qDebug() << "Total CPU time in LoadSAD" << timeInLoadSAD;
|
qDebug() << "Total CPU time in LoadSAD" << timeInLoadSAD;
|
||||||
@ -2013,10 +2059,10 @@ int ResmedLoader::Open(const QString & dirpath)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
sessfiles.clear();
|
// sessfiles.clear();
|
||||||
strsess.clear();
|
// strsess.clear();
|
||||||
|
// strdate.clear();
|
||||||
|
|
||||||
strdate.clear();
|
|
||||||
channel_efficiency.clear();
|
channel_efficiency.clear();
|
||||||
channel_time.clear();
|
channel_time.clear();
|
||||||
|
|
||||||
@ -2098,14 +2144,15 @@ bool ResmedLoader::LoadCSL(Session *sess, const QString & path)
|
|||||||
time.start();
|
time.start();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ResMedEDFParser edf(path);
|
ResMedEDFInfo edf;
|
||||||
|
QByteArray * fileData = edf.Open(path);
|
||||||
|
|
||||||
#ifdef DEBUG_EFFICIENCY
|
#ifdef DEBUG_EFFICIENCY
|
||||||
int edfopentime = time.elapsed();
|
int edfopentime = time.elapsed();
|
||||||
time.start();
|
time.start();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!edf.Parse())
|
if (!edf.Parse(fileData))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef DEBUG_EFFICIENCY
|
#ifdef DEBUG_EFFICIENCY
|
||||||
@ -2249,7 +2296,7 @@ bool ResmedLoader::LoadCSL(Session *sess, const QString & path)
|
|||||||
timeMutex.lock();
|
timeMutex.lock();
|
||||||
timeInLoadCSL += time.elapsed();
|
timeInLoadCSL += time.elapsed();
|
||||||
timeInEDFOpen += edfopentime;
|
timeInEDFOpen += edfopentime;
|
||||||
timeInEDFParser += edfparsetime;
|
timeInEDFInfo += edfparsetime;
|
||||||
timeMutex.unlock();
|
timeMutex.unlock();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -2262,12 +2309,13 @@ bool ResmedLoader::LoadEVE(Session *sess, const QString & path)
|
|||||||
QTime time;
|
QTime time;
|
||||||
time.start();
|
time.start();
|
||||||
#endif
|
#endif
|
||||||
ResMedEDFParser edf(path);
|
ResMedEDFInfo edf;
|
||||||
|
QByteArray * fileData = edf.Open(path);
|
||||||
#ifdef DEBUG_EFFICIENCY
|
#ifdef DEBUG_EFFICIENCY
|
||||||
int edfopentime = time.elapsed();
|
int edfopentime = time.elapsed();
|
||||||
time.start();
|
time.start();
|
||||||
#endif
|
#endif
|
||||||
if (!edf.Parse())
|
if (!edf.Parse(fileData))
|
||||||
return false;
|
return false;
|
||||||
#ifdef DEBUG_EFFICIENCY
|
#ifdef DEBUG_EFFICIENCY
|
||||||
int edfparsetime = time.elapsed();
|
int edfparsetime = time.elapsed();
|
||||||
@ -2422,7 +2470,7 @@ bool ResmedLoader::LoadEVE(Session *sess, const QString & path)
|
|||||||
timeMutex.lock();
|
timeMutex.lock();
|
||||||
timeInLoadEVE += time.elapsed();
|
timeInLoadEVE += time.elapsed();
|
||||||
timeInEDFOpen += edfopentime;
|
timeInEDFOpen += edfopentime;
|
||||||
timeInEDFParser += edfparsetime;
|
timeInEDFInfo += edfparsetime;
|
||||||
timeMutex.unlock();
|
timeMutex.unlock();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -2435,12 +2483,13 @@ bool ResmedLoader::LoadBRP(Session *sess, const QString & path)
|
|||||||
QTime time;
|
QTime time;
|
||||||
time.start();
|
time.start();
|
||||||
#endif
|
#endif
|
||||||
ResMedEDFParser edf(path);
|
ResMedEDFInfo edf;
|
||||||
|
QByteArray * fileData = edf.Open(path);
|
||||||
#ifdef DEBUG_EFFICIENCY
|
#ifdef DEBUG_EFFICIENCY
|
||||||
int edfopentime = time.elapsed();
|
int edfopentime = time.elapsed();
|
||||||
time.start();
|
time.start();
|
||||||
#endif
|
#endif
|
||||||
if (!edf.Parse())
|
if (!edf.Parse(fileData))
|
||||||
return false;
|
return false;
|
||||||
#ifdef DEBUG_EFFICIENCY
|
#ifdef DEBUG_EFFICIENCY
|
||||||
int edfparsetime = time.elapsed();
|
int edfparsetime = time.elapsed();
|
||||||
@ -2450,7 +2499,7 @@ bool ResmedLoader::LoadBRP(Session *sess, const QString & path)
|
|||||||
sess->updateFirst(edf.startdate);
|
sess->updateFirst(edf.startdate);
|
||||||
|
|
||||||
QTime time2;
|
QTime time2;
|
||||||
qint64 duration = edf.GetNumDataRecords() * edf.GetDuration();
|
qint64 duration = edf.GetNumDataRecords() * edf.GetDurationMillis();
|
||||||
sess->updateLast(edf.startdate + duration);
|
sess->updateLast(edf.startdate + duration);
|
||||||
|
|
||||||
for (auto & es : edf.edfsignals) {
|
for (auto & es : edf.edfsignals) {
|
||||||
@ -2510,7 +2559,7 @@ bool ResmedLoader::LoadBRP(Session *sess, const QString & path)
|
|||||||
timeMutex.lock();
|
timeMutex.lock();
|
||||||
timeInLoadBRP += time.elapsed();
|
timeInLoadBRP += time.elapsed();
|
||||||
timeInEDFOpen += edfopentime;
|
timeInEDFOpen += edfopentime;
|
||||||
timeInEDFParser += edfparsetime;
|
timeInEDFInfo += edfparsetime;
|
||||||
timeInAddWaveform += AddWavetime;
|
timeInAddWaveform += AddWavetime;
|
||||||
timeMutex.unlock();
|
timeMutex.unlock();
|
||||||
#endif
|
#endif
|
||||||
@ -2519,7 +2568,7 @@ bool ResmedLoader::LoadBRP(Session *sess, const QString & path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert EDFSignal data to OSCAR's Time-Delta Event format
|
// Convert EDFSignal data to OSCAR's Time-Delta Event format
|
||||||
void ResmedLoader::ToTimeDelta(Session *sess, ResMedEDFParser &edf, EDFSignal &es, ChannelID code,
|
void ResmedLoader::ToTimeDelta(Session *sess, ResMedEDFInfo &edf, EDFSignal &es, ChannelID code,
|
||||||
long recs, qint64 duration, EventDataType t_min, EventDataType t_max, bool square)
|
long recs, qint64 duration, EventDataType t_min, EventDataType t_max, bool square)
|
||||||
{
|
{
|
||||||
if (t_min == t_max) {
|
if (t_min == t_max) {
|
||||||
@ -2569,9 +2618,7 @@ void ResmedLoader::ToTimeDelta(Session *sess, ResMedEDFParser &edf, EDFSignal &e
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tt += rate;
|
tt += rate;
|
||||||
|
|
||||||
} while (sptr < eptr);
|
} while (sptr < eptr);
|
||||||
|
|
||||||
if (!el)
|
if (!el)
|
||||||
@ -2585,13 +2632,11 @@ void ResmedLoader::ToTimeDelta(Session *sess, ResMedEDFParser &edf, EDFSignal &e
|
|||||||
tmp = EventDataType(last) * es.gain;
|
tmp = EventDataType(last) * es.gain;
|
||||||
|
|
||||||
if ((tmp >= t_min) && (tmp <= t_max)) {
|
if ((tmp >= t_min) && (tmp <= t_max)) {
|
||||||
if (tmp < min) {
|
if (tmp < min)
|
||||||
min = tmp;
|
min = tmp;
|
||||||
}
|
|
||||||
|
|
||||||
if (tmp > max) {
|
if (tmp > max)
|
||||||
max = tmp;
|
max = tmp;
|
||||||
}
|
|
||||||
|
|
||||||
el->AddEvent(tt, last);
|
el->AddEvent(tt, last);
|
||||||
} else {
|
} else {
|
||||||
@ -2601,22 +2646,18 @@ void ResmedLoader::ToTimeDelta(Session *sess, ResMedEDFParser &edf, EDFSignal &e
|
|||||||
el->setDimension(es.physical_dimension);
|
el->setDimension(es.physical_dimension);
|
||||||
|
|
||||||
el = sess->AddEventList(code, EVL_Event, es.gain, es.offset, 0, 0);
|
el = sess->AddEventList(code, EVL_Event, es.gain, es.offset, 0, 0);
|
||||||
} else {
|
} else
|
||||||
el->clear(); // reuse the object
|
el->clear(); // reuse the object
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
tmp = EventDataType(c) * es.gain;
|
tmp = EventDataType(c) * es.gain;
|
||||||
|
|
||||||
if ((tmp >= t_min) && (tmp <= t_max)) {
|
if ((tmp >= t_min) && (tmp <= t_max)) {
|
||||||
if (tmp < min) {
|
if (tmp < min)
|
||||||
min = tmp;
|
min = tmp;
|
||||||
}
|
if (tmp > max)
|
||||||
|
|
||||||
if (tmp > max) {
|
|
||||||
max = tmp;
|
max = tmp;
|
||||||
}
|
|
||||||
|
|
||||||
el->AddEvent(tt, c);
|
el->AddEvent(tt, c);
|
||||||
} else {
|
} else {
|
||||||
@ -2625,7 +2666,8 @@ void ResmedLoader::ToTimeDelta(Session *sess, ResMedEDFParser &edf, EDFSignal &e
|
|||||||
|
|
||||||
// Create and attach new EventList
|
// Create and attach new EventList
|
||||||
el = sess->AddEventList(code, EVL_Event, es.gain, es.offset, 0, 0);
|
el = sess->AddEventList(code, EVL_Event, es.gain, es.offset, 0, 0);
|
||||||
} else { el->clear(); }
|
} else
|
||||||
|
el->clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2636,9 +2678,8 @@ void ResmedLoader::ToTimeDelta(Session *sess, ResMedEDFParser &edf, EDFSignal &e
|
|||||||
|
|
||||||
tmp = EventDataType(c) * es.gain;
|
tmp = EventDataType(c) * es.gain;
|
||||||
|
|
||||||
if ((tmp >= t_min) && (tmp <= t_max)) {
|
if ((tmp >= t_min) && (tmp <= t_max))
|
||||||
el->AddEvent(tt, c);
|
el->AddEvent(tt, c);
|
||||||
}
|
|
||||||
|
|
||||||
sess->setMin(code, min);
|
sess->setMin(code, min);
|
||||||
sess->setMax(code, max);
|
sess->setMax(code, max);
|
||||||
@ -2667,7 +2708,7 @@ void ResmedLoader::ToTimeDelta(Session *sess, ResMedEDFParser &edf, EDFSignal &e
|
|||||||
timeInTimeDelta += time.elapsed();
|
timeInTimeDelta += time.elapsed();
|
||||||
timeMutex.unlock();
|
timeMutex.unlock();
|
||||||
#endif
|
#endif
|
||||||
}
|
} // end ResMedLoader::ToTimeDelta
|
||||||
|
|
||||||
// Load SAD Oximetry Signals
|
// Load SAD Oximetry Signals
|
||||||
bool ResmedLoader::LoadSAD(Session *sess, const QString & path)
|
bool ResmedLoader::LoadSAD(Session *sess, const QString & path)
|
||||||
@ -2677,14 +2718,15 @@ bool ResmedLoader::LoadSAD(Session *sess, const QString & path)
|
|||||||
time.start();
|
time.start();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ResMedEDFParser edf(path);
|
ResMedEDFInfo edf;
|
||||||
|
QByteArray * fileData = edf.Open(path);
|
||||||
|
|
||||||
#ifdef DEBUG_EFFICIENCY
|
#ifdef DEBUG_EFFICIENCY
|
||||||
int edfopentime = time.elapsed();
|
int edfopentime = time.elapsed();
|
||||||
time.start();
|
time.start();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!edf.Parse())
|
if (!edf.Parse(fileData))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef DEBUG_EFFICIENCY
|
#ifdef DEBUG_EFFICIENCY
|
||||||
@ -2693,7 +2735,7 @@ bool ResmedLoader::LoadSAD(Session *sess, const QString & path)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
sess->updateFirst(edf.startdate);
|
sess->updateFirst(edf.startdate);
|
||||||
qint64 duration = edf.GetNumDataRecords() * edf.GetDuration();
|
qint64 duration = edf.GetNumDataRecords() * edf.GetDurationMillis();
|
||||||
sess->updateLast(edf.startdate + duration);
|
sess->updateLast(edf.startdate + duration);
|
||||||
|
|
||||||
for (auto & es : edf.edfsignals) {
|
for (auto & es : edf.edfsignals) {
|
||||||
@ -2732,7 +2774,7 @@ bool ResmedLoader::LoadSAD(Session *sess, const QString & path)
|
|||||||
timeMutex.lock();
|
timeMutex.lock();
|
||||||
timeInLoadSAD += time.elapsed();
|
timeInLoadSAD += time.elapsed();
|
||||||
timeInEDFOpen += edfopentime;
|
timeInEDFOpen += edfopentime;
|
||||||
timeInEDFParser += edfparsetime;
|
timeInEDFInfo += edfparsetime;
|
||||||
timeMutex.unlock();
|
timeMutex.unlock();
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
@ -2745,12 +2787,13 @@ bool ResmedLoader::LoadPLD(Session *sess, const QString & path)
|
|||||||
QTime time;
|
QTime time;
|
||||||
time.start();
|
time.start();
|
||||||
#endif
|
#endif
|
||||||
ResMedEDFParser edf(path);
|
ResMedEDFInfo edf;
|
||||||
|
QByteArray * fileData = edf.Open(path);
|
||||||
#ifdef DEBUG_EFFICIENCY
|
#ifdef DEBUG_EFFICIENCY
|
||||||
int edfopentime = time.elapsed();
|
int edfopentime = time.elapsed();
|
||||||
time.start();
|
time.start();
|
||||||
#endif
|
#endif
|
||||||
if (!edf.Parse())
|
if (!edf.Parse(fileData))
|
||||||
return false;
|
return false;
|
||||||
#ifdef DEBUG_EFFICIENCY
|
#ifdef DEBUG_EFFICIENCY
|
||||||
int edfparsetime = time.elapsed();
|
int edfparsetime = time.elapsed();
|
||||||
@ -2760,7 +2803,7 @@ bool ResmedLoader::LoadPLD(Session *sess, const QString & path)
|
|||||||
// Is it safe to assume the order does not change here?
|
// Is it safe to assume the order does not change here?
|
||||||
enum PLDType { MaskPres = 0, TherapyPres, ExpPress, Leak, RR, Vt, Mv, SnoreIndex, FFLIndex, U1, U2 };
|
enum PLDType { MaskPres = 0, TherapyPres, ExpPress, Leak, RR, Vt, Mv, SnoreIndex, FFLIndex, U1, U2 };
|
||||||
|
|
||||||
qint64 duration = edf.GetNumDataRecords() * edf.GetDuration();
|
qint64 duration = edf.GetNumDataRecords() * edf.GetDurationMillis();
|
||||||
sess->updateFirst(edf.startdate);
|
sess->updateFirst(edf.startdate);
|
||||||
sess->updateLast(edf.startdate + duration);
|
sess->updateLast(edf.startdate + duration);
|
||||||
QString t;
|
QString t;
|
||||||
@ -2896,7 +2939,7 @@ bool ResmedLoader::LoadPLD(Session *sess, const QString & path)
|
|||||||
timeMutex.lock();
|
timeMutex.lock();
|
||||||
timeInLoadPLD += time.elapsed();
|
timeInLoadPLD += time.elapsed();
|
||||||
timeInEDFOpen += edfopentime;
|
timeInEDFOpen += edfopentime;
|
||||||
timeInEDFParser += edfparsetime;
|
timeInEDFInfo += edfparsetime;
|
||||||
timeMutex.unlock();
|
timeMutex.unlock();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -30,18 +30,46 @@ EDFType lookupEDFType(const QString & text);
|
|||||||
|
|
||||||
const QString resmed_class_name = STR_MACH_ResMed;
|
const QString resmed_class_name = STR_MACH_ResMed;
|
||||||
|
|
||||||
class ResMedEDFParser:public EDFParser
|
class ResMedEDFInfo : public EDFInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ResMedEDFParser(QString filename = "");
|
ResMedEDFInfo();
|
||||||
~ResMedEDFParser();
|
~ResMedEDFInfo();
|
||||||
|
|
||||||
|
virtual bool Parse(QByteArray * fileData) override; // overrides and calls the super's Parse
|
||||||
|
|
||||||
|
virtual qint64 GetDurationMillis() { return dur_data_record; } // overrides the super
|
||||||
|
|
||||||
EDFSignal *lookupSignal(ChannelID ch);
|
EDFSignal *lookupSignal(ChannelID ch);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//! \brief The following are computed from the edfHdr data
|
||||||
|
QString serialnumber;
|
||||||
|
qint64 dur_data_record;
|
||||||
|
qint64 startdate;
|
||||||
|
qint64 enddate;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class EDFduration
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EDFduration() { start = end = 0; type = EDF_UNKNOWN; }
|
||||||
|
EDFduration(quint32 start, quint32 end, QString path) :
|
||||||
|
start(start), end(end), path(path) {}
|
||||||
|
|
||||||
|
quint32 start;
|
||||||
|
quint32 end;
|
||||||
|
QString path;
|
||||||
|
QString filename;
|
||||||
|
EDFType type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class STRRecord
|
||||||
struct STRRecord
|
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
STRRecord() {
|
STRRecord() {
|
||||||
maskon.clear();
|
maskon.clear();
|
||||||
maskoff.clear();
|
maskoff.clear();
|
||||||
@ -125,82 +153,8 @@ struct STRRecord
|
|||||||
|
|
||||||
date=QDate();
|
date=QDate();
|
||||||
}
|
}
|
||||||
STRRecord(const STRRecord & copy) {
|
|
||||||
maskon = copy.maskon;
|
|
||||||
maskoff = copy.maskoff;
|
|
||||||
maskdur = copy.maskdur;
|
|
||||||
maskevents = copy.maskevents;
|
|
||||||
mode = copy.mode;
|
|
||||||
rms9_mode = copy.rms9_mode;
|
|
||||||
set_pressure = copy.set_pressure;
|
|
||||||
epap = copy.epap;
|
|
||||||
max_pressure = copy.max_pressure;
|
|
||||||
min_pressure = copy.min_pressure;
|
|
||||||
max_ps = copy.max_ps;
|
|
||||||
min_ps = copy.min_ps;
|
|
||||||
ps = copy.ps;
|
|
||||||
max_epap = copy.max_epap;
|
|
||||||
min_epap = copy.min_epap;
|
|
||||||
ipap = copy.ipap;
|
|
||||||
max_ipap = copy.max_ipap;
|
|
||||||
min_ipap = copy.min_ipap;
|
|
||||||
epr = copy.epr;
|
|
||||||
epr_level = copy.epr_level;
|
|
||||||
sessionid = copy.sessionid;
|
|
||||||
ahi = copy.ahi;
|
|
||||||
ai = copy.ai;
|
|
||||||
oai = copy.oai;
|
|
||||||
hi = copy.hi;
|
|
||||||
uai = copy.uai;
|
|
||||||
cai = copy.cai;
|
|
||||||
csr = copy.csr;
|
|
||||||
|
|
||||||
date = copy.date;
|
// All the data members
|
||||||
leak50 = copy.leak50;
|
|
||||||
leak95 = copy.leak95;
|
|
||||||
leakmax = copy.leakmax;
|
|
||||||
rr50 = copy.rr50;
|
|
||||||
rr95 = copy.rr95;
|
|
||||||
rrmax = copy.rrmax;
|
|
||||||
mv50 = copy.mv50;
|
|
||||||
mv95 = copy.mv95;
|
|
||||||
mvmax = copy.mvmax;
|
|
||||||
ie50 = copy.ie50;
|
|
||||||
ie95 = copy.ie95;
|
|
||||||
iemax = copy.iemax;
|
|
||||||
tv50 = copy.tv50;
|
|
||||||
tv95 = copy.tv95;
|
|
||||||
tvmax = copy.tvmax;
|
|
||||||
mp50 = copy.mp50;
|
|
||||||
mp95 = copy.mp95;
|
|
||||||
mpmax = copy.mpmax;
|
|
||||||
|
|
||||||
|
|
||||||
tgtepap50 = copy.tgtepap50;
|
|
||||||
tgtepap95 = copy.tgtepap95;
|
|
||||||
tgtepapmax = copy.tgtepapmax;
|
|
||||||
tgtipap50 = copy.tgtipap50;
|
|
||||||
tgtipap95 = copy.tgtipap95;
|
|
||||||
tgtipapmax = copy.tgtipapmax;
|
|
||||||
|
|
||||||
s_EPREnable = copy.s_EPREnable;
|
|
||||||
s_EPR_ClinEnable = copy.s_EPREnable;
|
|
||||||
s_RampEnable = copy.s_RampEnable;
|
|
||||||
s_RampTime = copy.s_RampTime;
|
|
||||||
|
|
||||||
s_SmartStart = copy.s_SmartStart;
|
|
||||||
s_PtAccess = copy.s_PtAccess;
|
|
||||||
s_ABFilter = copy.s_ABFilter;
|
|
||||||
s_Mask = copy.s_Mask;
|
|
||||||
|
|
||||||
s_Tube = copy.s_Tube;
|
|
||||||
s_ClimateControl = copy.s_ClimateControl;
|
|
||||||
s_HumEnable = copy.s_HumEnable;
|
|
||||||
s_HumLevel = copy.s_HumLevel;
|
|
||||||
s_TempEnable = copy.s_TempEnable;
|
|
||||||
s_Temp = copy.s_Temp;
|
|
||||||
ramp_pressure = copy.ramp_pressure;
|
|
||||||
}
|
|
||||||
QVector<quint32> maskon;
|
QVector<quint32> maskon;
|
||||||
QVector<quint32> maskoff;
|
QVector<quint32> maskoff;
|
||||||
|
|
||||||
@ -279,47 +233,6 @@ struct STRRecord
|
|||||||
|
|
||||||
class ResmedLoader;
|
class ResmedLoader;
|
||||||
|
|
||||||
struct EDFGroup {
|
|
||||||
EDFGroup() { }
|
|
||||||
EDFGroup(QString &brp, QString &eve, QString &pld, QString &sad, QString &csl) {
|
|
||||||
BRP = brp;
|
|
||||||
EVE = eve;
|
|
||||||
CSL = csl;
|
|
||||||
PLD = pld;
|
|
||||||
SAD = sad;
|
|
||||||
}
|
|
||||||
EDFGroup(const EDFGroup & copy) {
|
|
||||||
BRP = copy.BRP;
|
|
||||||
EVE = copy.EVE;
|
|
||||||
CSL = copy.CSL;
|
|
||||||
PLD = copy.PLD;
|
|
||||||
SAD = copy.SAD;
|
|
||||||
}
|
|
||||||
QString BRP;
|
|
||||||
QString EVE;
|
|
||||||
QString CSL;
|
|
||||||
QString PLD;
|
|
||||||
QString SAD;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct EDFduration {
|
|
||||||
EDFduration() { start = end = 0; type = EDF_UNKNOWN; }
|
|
||||||
EDFduration(const EDFduration & copy) {
|
|
||||||
path = copy.path;
|
|
||||||
start = copy.start;
|
|
||||||
end = copy.end;
|
|
||||||
type = copy.type;
|
|
||||||
filename = copy.filename;
|
|
||||||
}
|
|
||||||
EDFduration(quint32 start, quint32 end, QString path) :
|
|
||||||
start(start), end(end), path(path) {}
|
|
||||||
quint32 start;
|
|
||||||
quint32 end;
|
|
||||||
QString path;
|
|
||||||
QString filename;
|
|
||||||
EDFType type;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ResMedDay {
|
struct ResMedDay {
|
||||||
QDate date;
|
QDate date;
|
||||||
STRRecord str;
|
STRRecord str;
|
||||||
@ -342,50 +255,19 @@ protected:
|
|||||||
ResMedDay * resday;
|
ResMedDay * resday;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct STRFile {
|
class STRFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
STRFile() :
|
STRFile() :
|
||||||
filename(QString()), edf(nullptr) {}
|
filename(QString()), edf(nullptr) {}
|
||||||
STRFile(QString name, ResMedEDFParser *str) :
|
STRFile(QString name, ResMedEDFInfo *str) :
|
||||||
filename(name), edf(str) {}
|
filename(name), edf(str) {}
|
||||||
STRFile(const STRFile & copy) {
|
virtual ~STRFile() {}
|
||||||
filename = copy.filename;
|
|
||||||
edf = copy.edf;
|
|
||||||
}
|
|
||||||
~STRFile() {
|
|
||||||
}
|
|
||||||
|
|
||||||
QString filename;
|
QString filename;
|
||||||
ResMedEDFParser * edf;
|
ResMedEDFInfo * edf;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*class ResmedImport:public ImportTask
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ResmedImport(ResmedLoader * l, SessionID s, QHash<EDFType, QStringList> grp, Machine * m): loader(l), sessionid(s), files(grp), mach(m) {}
|
|
||||||
virtual ~ResmedImport() {}
|
|
||||||
virtual void run();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ResmedLoader * loader;
|
|
||||||
SessionID sessionid;
|
|
||||||
QHash<EDFType, QStringList> files;
|
|
||||||
Machine * mach;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ResmedImportStage2:public ImportTask
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ResmedImportStage2(ResmedLoader * l, STRRecord r, Machine * m): loader(l), R(r), mach(m) {}
|
|
||||||
virtual ~ResmedImportStage2() {}
|
|
||||||
virtual void run();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ResmedLoader * loader;
|
|
||||||
STRRecord R;
|
|
||||||
Machine * mach;
|
|
||||||
}; */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*! \class ResmedLoader
|
/*! \class ResmedLoader
|
||||||
@ -416,7 +298,7 @@ class ResmedLoader : public CPAPLoader
|
|||||||
virtual const QString &loaderName() { return resmed_class_name; }
|
virtual const QString &loaderName() { return resmed_class_name; }
|
||||||
|
|
||||||
//! \brief Converts EDFSignal data to time delta packed EventList, and adds to Session
|
//! \brief Converts EDFSignal data to time delta packed EventList, and adds to Session
|
||||||
void ToTimeDelta(Session *sess, ResMedEDFParser &edf, EDFSignal &es, ChannelID code, long recs,
|
void ToTimeDelta(Session *sess, ResMedEDFInfo &edf, EDFSignal &es, ChannelID code, long recs,
|
||||||
qint64 duration, EventDataType min = 0, EventDataType max = 0, bool square = false);
|
qint64 duration, EventDataType min = 0, EventDataType max = 0, bool square = false);
|
||||||
|
|
||||||
//! \brief Register the ResmedLoader with the list of other machine loaders
|
//! \brief Register the ResmedLoader with the list of other machine loaders
|
||||||
@ -443,7 +325,8 @@ class ResmedLoader : public CPAPLoader
|
|||||||
bool LoadPLD(Session *sess, const QString & path);
|
bool LoadPLD(Session *sess, const QString & path);
|
||||||
|
|
||||||
virtual MachineInfo newInfo() {
|
virtual MachineInfo newInfo() {
|
||||||
return MachineInfo(MT_CPAP, 0, resmed_class_name, QObject::tr("ResMed"), QString(), QString(), QString(), QObject::tr("S9"), QDateTime::currentDateTime(), resmed_data_version);
|
return MachineInfo(MT_CPAP, 0, resmed_class_name, QObject::tr("ResMed"), QString(),
|
||||||
|
QString(), QString(), QObject::tr("S9"), QDateTime::currentDateTime(), resmed_data_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void initChannels();
|
virtual void initChannels();
|
||||||
@ -463,17 +346,20 @@ class ResmedLoader : public CPAPLoader
|
|||||||
volatile int sessionCount;
|
volatile int sessionCount;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
//! \brief The STR.edf file is a unique edf file with many signals
|
||||||
void ParseSTR(Machine *, QMap<QDate, STRFile> &);
|
void ParseSTR(Machine *, QMap<QDate, STRFile> &);
|
||||||
|
|
||||||
|
|
||||||
//! \brief Scan for new files to import, group into sessions and add to task que
|
//! \brief Scan for new files to import, group into sessions and add to task que
|
||||||
int scanFiles(Machine * mach, const QString & datalog_path);
|
int scanFiles(Machine * mach, const QString & datalog_path);
|
||||||
|
|
||||||
|
//! \brief Write a backup copy to the backup path
|
||||||
QString backup(const QString & file, const QString & backup_path);
|
QString backup(const QString & file, const QString & backup_path);
|
||||||
|
|
||||||
QMap<SessionID, QStringList> sessfiles;
|
// The data members
|
||||||
QMap<quint32, STRRecord> strsess;
|
// QMap<SessionID, QStringList> sessfiles;
|
||||||
QMap<QDate, QList<STRRecord *> > strdate;
|
// QMap<quint32, STRRecord> strsess;
|
||||||
|
// QMap<QDate, QList<STRRecord *> > strdate;
|
||||||
|
|
||||||
QMap<QDate, ResMedDay> resdayList;
|
QMap<QDate, ResMedDay> resdayList;
|
||||||
|
|
||||||
#ifdef DEBUG_EFFICIENCY
|
#ifdef DEBUG_EFFICIENCY
|
||||||
@ -485,7 +371,7 @@ protected:
|
|||||||
volatile qint64 timeInLoadCSL;
|
volatile qint64 timeInLoadCSL;
|
||||||
volatile qint64 timeInLoadSAD;
|
volatile qint64 timeInLoadSAD;
|
||||||
volatile qint64 timeInEDFOpen;
|
volatile qint64 timeInEDFOpen;
|
||||||
volatile qint64 timeInEDFParser;
|
volatile qint64 timeInEDFInfo;
|
||||||
volatile qint64 timeInAddWaveform;
|
volatile qint64 timeInAddWaveform;
|
||||||
volatile qint64 timeInTimeDelta;
|
volatile qint64 timeInTimeDelta;
|
||||||
QMutex timeMutex;
|
QMutex timeMutex;
|
||||||
|
Loading…
Reference in New Issue
Block a user