/* EDF Parser Implementation * * Copyright (c) 2011-2018 Mark Watkins * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of the source code * for more details. */ #include #include #include #include #ifdef _MSC_VER #include #else #include #endif #include "edfparser.h" EDFParser::EDFParser(QString name) { buffer = nullptr; if (!name.isEmpty()) Open(name); } EDFParser::~EDFParser() { for (auto & s : edfsignals) { if (s.data) { delete [] s.data; } } } // Read a 16 bits integer qint16 EDFParser::Read16() { if ((pos + 2) > datasize) { eof = true; return 0; } #ifdef Q_LITTLE_ENDIAN // Intel, etc... qint16 res = *(qint16 *)&buffer[pos]; #else // ARM, PPC, etc.. qint16 res = quint8(buffer[pos]) | (qint8(buffer[pos+1]) << 8); #endif pos += 2; return res; } QString EDFParser::Read(unsigned n) { if ((pos + long(n)) > datasize) { eof = true; return QString(); } QByteArray buf(&buffer[pos], n); pos+=n; return buf.trimmed(); } bool EDFParser::Parse() { bool ok; if (header == nullptr) { qWarning() << "EDFParser::Parse() called without valid EDF data" << filename; return false; } eof = false; version = QString::fromLatin1(header->version, 8).toLong(&ok); if (!ok) { return false; } //patientident=QString::fromLatin1(header.patientident,80); recordingident = QString::fromLatin1(header->recordingident, 80); // Serial number is in here.. int snp = recordingident.indexOf("SRN="); serialnumber.clear(); for (int i = snp + 4; i < recordingident.length(); i++) { if (recordingident[i] == ' ') { break; } serialnumber += recordingident[i]; } startdate_orig = QDateTime::fromString(QString::fromLatin1(header->datetime, 16), "dd.MM.yyHH.mm.ss"); QDate d2 = startdate_orig.date(); if (d2.year() < 2000) { d2.setDate(d2.year() + 100, d2.month(), d2.day()); startdate_orig.setDate(d2); } if (!startdate_orig.isValid()) { qDebug() << "Invalid date time retreieved parsing EDF File " << filename; return false; } startdate = qint64(startdate_orig.toTime_t()) * 1000L; //startdate-=timezoneOffset(); //qDebug() << startDate.toString("yyyy-MM-dd HH:mm:ss"); num_header_bytes = QString::fromLatin1(header->num_header_bytes, 8).toLong(&ok); if (!ok) { return false; } //reserved44=QString::fromLatin1(header.reserved,44); num_data_records = QString::fromLatin1(header->num_data_records, 8).toLong(&ok); if (!ok) { return false; } dur_data_record = (QString::fromLatin1(header->dur_data_records, 8).toDouble(&ok) * 1000.0L); if (!ok) { return false; } num_signals = QString::fromLatin1(header->num_signals, 4).toLong(&ok); if (!ok) { return false; } enddate = startdate + dur_data_record * qint64(num_data_records); // if (dur_data_record==0) // return false; // this could be loaded quicker by transducer_type[signal] etc.. // Initialize fixed-size signal list. edfsignals.resize(num_signals); for (auto & sig : edfsignals) { sig.data = nullptr; sig.label = Read(16); signal_labels.push_back(sig.label); signalList[sig.label].push_back(&sig); if (eof) return false; } for (auto & sig : edfsignals) { sig.transducer_type = Read(80); } for (auto & sig : edfsignals) { sig.physical_dimension = Read(8); } for (auto & sig : edfsignals) { sig.physical_minimum = Read(8).toDouble(&ok); } for (auto & sig : edfsignals) { sig.physical_maximum = Read(8).toDouble(&ok); } for (auto & sig : edfsignals) { sig.digital_minimum = Read(8).toDouble(&ok); } for (auto & sig : edfsignals) { sig.digital_maximum = Read(8).toDouble(&ok); sig.gain = (sig.physical_maximum - sig.physical_minimum) / (sig.digital_maximum - sig.digital_minimum); sig.offset = 0; } for (auto & sig : edfsignals) { sig.prefiltering = Read(80); } for (auto & sig : edfsignals) { sig.nr = Read(8).toLong(&ok); } for (auto & sig : edfsignals) { sig.reserved = Read(32); } // could do it earlier, but it won't crash from > EOF Reads if (eof) return false; // Now check the file isn't truncated before allocating all the memory long allocsize = 0; for (auto & sig : edfsignals) { if (num_data_records > 0) { allocsize += sig.nr * num_data_records * 2; } } if (allocsize > (datasize - pos)) { // Space required more than the remainder left to read, // so abort and let the user clean up the corrupted file themselves qWarning() << "EDFParser::Parse():" << filename << " is truncated!"; return false; } // allocate the buffers for (auto & sig : edfsignals) { long recs = sig.nr * num_data_records; if (num_data_records <= 0) { sig.data = nullptr; continue; } sig.data = new qint16 [recs]; sig.pos = 0; } for (int x = 0; x < num_data_records; x++) { for (auto & sig : edfsignals) { #ifdef Q_LITTLE_ENDIAN // Intel x86, etc.. memcpy((char *)&sig.data[sig.pos], (char *)&buffer[pos], sig.nr * 2); sig.pos += sig.nr; pos += sig.nr * 2; #else // Big endian safe for (int j=0;j EDFHeaderSize) { header = (EDFHeader *)data.constData(); buffer = (char *)data.constData() + EDFHeaderSize; datasize = filesize - EDFHeaderSize; } else goto badfile; pos = 0; return true; badfile: filesize = 0; datasize = 0; buffer = nullptr; header = nullptr; data.clear(); qDebug() << "EDFParser::Open() Couldn't open file" << name; return false; } EDFSignal *EDFParser::lookupLabel(const QString & name, int index) { auto it = signalList.find(name); if (it == signalList.end()) return nullptr; if (index >= it.value().size()) return nullptr; return it.value()[index]; } //QMutex EDFParser::EDFMutex;