From f0356ee99c2a435d132aefbdf3ed5625e59e9168 Mon Sep 17 00:00:00 2001 From: Mark Watkins Date: Fri, 16 May 2014 03:56:53 +1000 Subject: [PATCH] Optimize waveform/leak/pressure processing calcs, FPIcon cleanup --- sleepyhead/SleepLib/calcs.cpp | 263 +++++++++---- .../SleepLib/loader_plugins/icon_loader.cpp | 357 +++++++++--------- sleepyhead/daily.cpp | 2 + 3 files changed, 351 insertions(+), 271 deletions(-) diff --git a/sleepyhead/SleepLib/calcs.cpp b/sleepyhead/SleepLib/calcs.cpp index 5d678190..2702a711 100644 --- a/sleepyhead/SleepLib/calcs.cpp +++ b/sleepyhead/SleepLib/calcs.cpp @@ -223,7 +223,7 @@ void FlowParser::openFlow(Session *session, EventList *flow) EventStoreType *eptr = inraw + m_samples; // Convert from store type to floats.. - for (; inraw < eptr; inraw++) { + for (; inraw < eptr; ++inraw) { *buf++ = EventDataType(*inraw) * m_gain; } @@ -437,10 +437,19 @@ void FlowParser::calc(bool calcResp, bool calcTv, bool calcTi, bool calcTe, bool qint32 timeval = 0; // Time relative to start - for (idx = 0; idx < nm; idx++) { - bs = breaths[idx].start; - bm = breaths[idx].middle; - be = breaths[idx].end; + + + BreathPeak * bpstr = breaths.data(); + BreathPeak * bpend = bpstr + nm; + for (BreathPeak * bp = bpstr; bp != bpend; ++bp) { + bs = bp->start; + bm = bp->middle; + be = bp->end; + +// for (idx = 0; idx < nm; idx++) { +// bs = breaths[idx].start; +// bm = breaths[idx].middle; +// be = breaths[idx].end; // Calculate start, middle and end time of this breath st = start + bs * m_rate; @@ -524,9 +533,13 @@ void FlowParser::calc(bool calcResp, bool calcTv, bool calcTi, bool calcTe, bool //et2=et; // Step back through last minute and count breaths - for (int i = idx; i >= 0; i--) { - st2 = start + double(breaths[i].start) * m_rate; - et2 = start + double(breaths[i].end) * m_rate; + BreathPeak *bpstr1 = bpstr-1; + for (BreathPeak *p = bp; p != bpstr1; --p) { + st2 = start + double(p->start) * m_rate; + et2 = start + double(p->end) * m_rate; +// for (int i = idx; i >= 0; i--) { +// st2 = start + double(breaths[i].start) * m_rate; +// et2 = start + double(breaths[i].end) * m_rate; if (et2 < stmin) { break; @@ -631,13 +644,21 @@ void FlowParser::flagEvents() bool allowDuplicates = PROFILE.cpap->userEventDuplicates(); - for (int i = 0; i < numbreaths; i++) { - mx = breaths[i].max; - mn = breaths[i].min; - br.push_back(qAbs(mx)); - br.push_back(qAbs(mn)); + BreathPeak *bpstr = breaths.data(); + BreathPeak *bpend = bpstr + numbreaths; + + for (BreathPeak *p = bpstr; p != bpend; ++p) { + br.push_back(qAbs(p->max)); + br.push_back(qAbs(p->min)); } +// for (int i = 0; i < numbreaths; i++) { +// mx = breaths[i].max; +// mn = breaths[i].min; +// br.push_back(qAbs(mx)); +// br.push_back(qAbs(mn)); +// } + //EventList * uf2=m_session->AddEventList(CPAP_UserFlag2,EVL_Event); //EventList * uf3=m_session->AddEventList(CPAP_UserFlag3,EVL_Event); @@ -651,13 +672,19 @@ void FlowParser::flagEvents() int bs, bm, be, bs1, bm1, be1; - for (int i = 0; i < numbreaths; i++) { - bs = breaths[i].start; - bm = breaths[i].middle; - be = breaths[i].end; + for (BreathPeak *p = bpstr; p != bpend; ++p) { + bs = p->start; + bm = p->middle; + be = p->end; + mx = p->max; + mn = p->min; +// for (int i = 0; i < numbreaths; i++) { +// bs = breaths[i].start; +// bm = breaths[i].middle; +// be = breaths[i].end; - mx = breaths[i].max; - mn = breaths[i].min; +// mx = breaths[i].max; +// mn = breaths[i].min; val = mx - mn; // if (qAbs(mx) > cutoffval) { @@ -779,8 +806,8 @@ void calcRespRate(Session *session, FlowParser *flowparser) } QVector &list = session->eventlist[CPAP_RespRate]; - - for (int i = 0; i < list.size(); i++) { + int size = list.size(); + for (int i = 0; i < size; ++i) { delete list[i]; } @@ -788,7 +815,8 @@ void calcRespRate(Session *session, FlowParser *flowparser) QVector &list2 = session->eventlist[CPAP_TidalVolume]; - for (int i = 0; i < list2.size(); i++) { + size = list2.size(); + for (int i = 0; i < size; ++i) { delete list2[i]; } @@ -796,7 +824,8 @@ void calcRespRate(Session *session, FlowParser *flowparser) QVector &list3 = session->eventlist[CPAP_MinuteVent]; - for (int i = 0; i < list3.size(); i++) { + size = list3.size(); + for (int i = 0; i < size; ++i) { delete list3[i]; } @@ -813,8 +842,11 @@ void calcRespRate(Session *session, FlowParser *flowparser) //flowparser->addFilter(FilterXPass,0.5); EventList *flow; - for (int ws = 0; ws < session->eventlist[CPAP_FlowRate].size(); ws++) { - flow = session->eventlist[CPAP_FlowRate][ws]; + QVector &EVL = session->eventlist[CPAP_FlowRate]; + int size = EVL.size(); + + for (int ws = 0; ws < size; ++ws) { + flow = EVL[ws]; if (flow->count() > 20) { flowparser->openFlow(session, flow); @@ -1157,23 +1189,26 @@ void zMaskProfile::scanPressure(Session *session) prescnt = session->count(CPAP_Pressure); Pressure.reserve(prescnt); - for (int j = 0; j < session->eventlist[CPAP_Pressure].size(); j++) { - QVector &el = session->eventlist[CPAP_Pressure]; + // WTF IS THIS DOING??? WHY THE HECK DID I PUT AN INNER LOOP HERE?? + QVector &EVL=session->eventlist[CPAP_Pressure]; + int size = EVL.size(); + for (int j = 0; j < size; ++j) { + scanPressureList(EVL[j]); +// QVector &el = session->eventlist[CPAP_Pressure]; - for (int e = 0; e < el.size(); e++) { - scanPressureList(el[e]); - } +// for (int e = 0; e < el.size(); e++) { +// scanPressureList(el[e]); +// } } } else if (session->eventlist.contains(CPAP_IPAP)) { prescnt = session->count(CPAP_IPAP); Pressure.reserve(prescnt); - for (int j = 0; j < session->eventlist[CPAP_IPAP].size(); j++) { - QVector &el = session->eventlist[CPAP_IPAP]; + QVector &EVL=session->eventlist[CPAP_IPAP]; + int size = EVL.size(); - for (int e = 0; e < el.size(); e++) { - scanPressureList(el[e]); - } + for (int j = 0; j < size; ++j) { + scanPressureList(EVL[j]); } } @@ -1195,6 +1230,12 @@ void zMaskProfile::scanLeakList(EventList *el) qint64 ti; bool found; + int psize = Pressure.size(); + if (psize == 0) return; + TimeValue *tvstr = Pressure.data(); + TimeValue *tvend = tvstr + (psize - 1); + TimeValue *p1, *p2; + for (; dptr < eptr; dptr++) { leak = *dptr; ti = start + *tptr++; @@ -1202,20 +1243,34 @@ void zMaskProfile::scanLeakList(EventList *el) found = false; pressure = Pressure[0].value; - if (Pressure.size() > 1) { - for (int i = 0; i < Pressure.size() - 1; i++) { - const TimeValue &p1 = Pressure[i]; - const TimeValue &p2 = Pressure[i + 1]; + if (psize > 1) { + for (p1 = tvstr; p1 != tvend; ++p1) { + p2 = p1+1; - if ((p2.time > ti) && (p1.time <= ti)) { - pressure = p1.value; + if ((p2->time > ti) && (p1->time <= ti)) { + pressure = p1->value; found = true; break; - } else if (p2.time == ti) { - pressure = p2.value; + } else if (p2->time == ti) { + pressure = p2->value; found = true; break; } + + +// for (int i = 0; i < Pressure.size() - 1; i++) { +// const TimeValue &p1 = Pressure[i]; +// const TimeValue &p2 = Pressure[i + 1]; + +// if ((p2.time > ti) && (p1.time <= ti)) { +// pressure = p1.value; +// found = true; +// break; +// } else if (p2.time == ti) { +// pressure = p2.value; +// found = true; +// break; +// } } } else { found = true; @@ -1245,8 +1300,14 @@ void zMaskProfile::scanLeaks(Session *session) { QVector &elv = session->eventlist[CPAP_LeakTotal]; - for (int i = 0; i < elv.size(); i++) { - scanLeakList(elv[i]); + int size=elv.size(); + if (!size) + return; + + QVector::iterator end = elv.end(); + QVector::iterator it; + for (it = elv.begin(); it != end; ++it) { + scanLeakList(*it); } } void zMaskProfile::updatePressureMin() @@ -1266,14 +1327,19 @@ void zMaskProfile::updatePressureMin() int sum1, sum2, w1, w2, N, k; - for (it = pressureleaks.begin(); it != pressureleaks.end(); it++) { + + QMap >::iterator plend = pressureleaks.end(); + + QMap::iterator lmend; + for (it = pressureleaks.begin(); it != plend; it++) { pressure = it.key(); QMap &leakmap = it.value(); lks = leakmap.size(); SN = 0; // First sum total counts of all leaks - for (QMap::iterator lit = leakmap.begin(); lit != leakmap.end(); lit++) { + lmend = leakmap.end(); + for (QMap::iterator lit = leakmap.begin(); lit != lmend; ++lit) { SN += lit.value(); } @@ -1292,15 +1358,14 @@ void zMaskProfile::updatePressureMin() bool found = false; double total = 0; - for (QMap::iterator lit = leakmap.begin(); lit != leakmap.end(); - lit++, k++) { + // why do this effectively twice? and k = size + for (QMap::iterator lit = leakmap.begin(); lit != lmend; ++lit, ++k) { total += lit.value(); } pressuretotal[pressure] = total; - for (QMap::iterator lit = leakmap.begin(); lit != leakmap.end(); - lit++, k++) { + for (QMap::iterator lit = leakmap.begin(); lit != lmend; ++lit, ++k) { //for (k=0;k < N;k++) { v1 = lit.key(); w1 = lit.value(); @@ -1379,8 +1444,13 @@ void zMaskProfile::updateProfile(Session *session) EventDataType maxcnt, maxval, lastval, lastcnt; - for (QMap >::iterator it = pressureleaks.begin(); - it != pressureleaks.end(); it++) { + + QMap >::iterator plend = pressureleaks.end(); + QMap >::iterator it = pressureleaks.begin(); + + QMap::iterator lit; + QMap::iterator lvend; + for (; it != plend; ++it) { p = it.key(); l = pressuremin[p]; QMap &leakval = it.value(); @@ -1391,7 +1461,8 @@ void zMaskProfile::updateProfile(Session *session) maxcnt = 0, maxval = 0, lastval = 0, lastcnt = 0; - for (QMap::iterator lit = leakval.begin(); lit != leakval.end(); lit++) { + lvend = leakval.end(); + for (lit = leakval.begin(); lit != lvend; ++lit) { cnt += lit.value(); if (lit.value() > maxcnt) { @@ -1418,7 +1489,7 @@ void zMaskProfile::updateProfile(Session *session) pressuremax[p] = lastval; sum = 0; - for (QMap::iterator lit = leakval.begin(); lit != leakval.end(); lit++) { + for (lit = leakval.begin(); lit != lvend; lit++) { tmp = lit.key() - mean; sum += tmp * tmp; } @@ -1435,16 +1506,16 @@ void zMaskProfile::updateProfile(Session *session) QMap pressureval2; EventDataType max = 0, tmp2, tmp3; - for (QMap::iterator it = pressuretotal.begin(); - it != pressuretotal.end(); it++) { - if (max < it.value()) { max = it.value(); } + QMap::iterator ptit; + QMap::iterator ptend = pressuretotal.end(); + for (ptit = pressuretotal.begin(); ptit != ptend; ++ptit) { + if (max < ptit.value()) { max = ptit.value(); } } - for (QMap::iterator it = pressuretotal.begin(); - it != pressuretotal.end(); it++) { - p = it.key(); + for (ptit = pressuretotal.begin(); ptit != pressuretotal.end(); ptit++) { + p = ptit.key(); tmp = pressurecount[p]; - tmp2 = it.value(); + tmp2 = ptit.value(); tmp3 = (tmp / tmp2) * (tmp2 / max); @@ -1546,8 +1617,18 @@ int calcLeaks(Session *session) EventList *leak = session->AddEventList(CPAP_Leak, EVL_Event, 1); - for (int i = 0; i < session->eventlist[CPAP_LeakTotal].size(); i++) { - EventList &el = *session->eventlist[CPAP_LeakTotal][i]; + QVector & EVL = session->eventlist[CPAP_LeakTotal]; + int evlsize = EVL.size(); + + TimeValue *p2, *pstr, *pend; + + // can this go out of the loop? + int mppressize = maskProfile->Pressure.size(); + pstr = maskProfile->Pressure.data(); + pend = maskProfile->Pressure.data()+(mppressize-1); + + for (int i = 0; i < evlsize; ++i) { + EventList &el = *EVL[i]; EventDataType gain = el.gain(), tmp, val; int count = el.count(); EventStoreType *dptr = el.rawData(); @@ -1560,28 +1641,41 @@ int calcLeaks(Session *session) bool found; - for (; dptr < eptr; dptr++) { + for (; dptr < eptr; ++dptr) { tmp = EventDataType(*dptr) * gain; ti = start + *tptr++; found = false; - pressure = maskProfile->Pressure[0].value; + pressure = pstr->value; - for (int i = 0; i < maskProfile->Pressure.size() - 1; i++) { - const TimeValue &p1 = maskProfile->Pressure[i]; - const TimeValue &p2 = maskProfile->Pressure[i + 1]; - - if ((p2.time > ti) && (p1.time <= ti)) { - pressure = p1.value; + for (TimeValue *p1 = pstr; p1 != pend; ++p1) { + p2 = p1+1; + if ((p2->time > ti) && (p1->time <= ti)) { + pressure = p1->value; found = true; break; - } else if (p2.time == ti) { - pressure = p2.value; + } else if (p2->time == ti) { + pressure = p2->value; found = true; break; } } +// for (int i = 0; i < mppressize - 1; ++i) { +// const TimeValue &p1 = &maskProfile->Pressure[i]; +// const TimeValue &p2 = maskProfile->Pressure[i + 1]; + +// if ((p2.time > ti) && (p1.time <= ti)) { +// pressure = p1.value; +// found = true; +// break; +// } else if (p2.time == ti) { +// pressure = p2.value; +// found = true; +// break; +// } +// } + if (found) { val = tmp - maskProfile->calcLeak(pressure); @@ -1622,10 +1716,12 @@ int calcPulseChange(Session *session) int max; - for (int e = 0; e < it.value().size(); e++) { + int size = it.value().size(); + for (int e = 0; e < size; ++e) { EventList &el = *(it.value()[e]); - for (unsigned i = 0; i < el.count(); i++) { + int elcount=el.count(); + for (int i = 0; i < elcount; ++i) { val = el.data(i); time = el.time(i); @@ -1634,7 +1730,7 @@ int calcPulseChange(Session *session) lv = change; max = 0; - for (unsigned j = i + 1; j < el.count(); j++) { // scan ahead in the window + for (int j = i + 1; j < elcount; ++j) { // scan ahead in the window time2 = el.time(j); if (time2 > time + window) { break; } @@ -1709,10 +1805,12 @@ int calcSPO2Drop(Session *session) // Calculate median baseline QList med; - for (int e = 0; e < it.value().size(); e++) { + int evsize = it.value().size(); + for (int e = 0; e < evsize; ++e) { EventList &el = *(it.value()[e]); - for (unsigned i = 0; i < el.count(); i++) { + int elcount = el.count(); + for (int i = 0; i < elcount; i++) { val = el.data(i); time = el.time(i); @@ -1746,10 +1844,11 @@ int calcSPO2Drop(Session *session) EventDataType current; qDebug() << "Calculated baseline" << baseline; - for (int e = 0; e < it.value().size(); e++) { + for (int e = 0; e < evsize; ++e) { EventList &el = *(it.value()[e]); - for (unsigned i = 0; i < el.count(); i++) { + int elcount = el.count(); + for (int i = 0; i < elcount; ++i) { current = el.data(i); if (!current) { continue; } @@ -1785,7 +1884,7 @@ int calcSPO2Drop(Session *session) min = val; - for (unsigned j = i; j < el.count(); j++) { // scan ahead in the window + for (int j = i; j < elcount; ++j) { // scan ahead in the window time2 = el.time(j); //if (time2 > time+window) break; val2 = el.data(j); diff --git a/sleepyhead/SleepLib/loader_plugins/icon_loader.cpp b/sleepyhead/SleepLib/loader_plugins/icon_loader.cpp index d35ad00d..b7ed544c 100644 --- a/sleepyhead/SleepLib/loader_plugins/icon_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/icon_loader.cpp @@ -286,28 +286,97 @@ int FPIconLoader::OpenMachine(Machine *mach, QString &path, Profile *profile) return true; } -QDateTime FPIconLoader::readFPDateTime(quint8 *data) +// !\brief Convert F&P 32bit date format to 32bit UNIX Timestamp +quint32 convertDate(quint32 timestamp) { - quint32 ts = (data[3] << 24) | (data[2] << 16) | ((data[1] << 8) | data[0]); // ^ 0xc00000; - // 0x20a41b18 + quint8 day, month,hour, minute, second; + quint16 year; - quint8 day = ts & 0x1f; // 0X18 24 - ts >>= 5; // 10520D8 - quint8 month = ts & 0x0f; // 0X08 8 - ts >>= 4; // 10520D - quint8 year = ts & 0x3f; // 0X0D 13 - ts >>= 6; // 4148 - quint8 second = ts & 0x3f; // 0X08 8 - ts >>= 6; // 20A - quint8 minute = ts & 0x3f; // 0A 10 - ts >>= 6; // 10 - quint8 hour = ts & 0x1f; // 10 16 - QDate date = QDate(2000 + year, month, day); - QTime time = QTime(hour, minute, second); - QDateTime datetime = QDateTime(date, time, Qt::UTC); - return datetime; + + day = timestamp & 0x1f; + month = (timestamp >> 5) & 0x0f; + year = 2000 + ((timestamp >> 9) & 0x3f); + timestamp >>= 15; + timestamp |= (timestamp >> 15) & 1; + + // Okay, why did I swap the first and last bits of the time field? + // What am I forgetting?? This seems to work properly like this + // Was I looking at older data that worked like this? + + second = timestamp & 0x3f; + minute = (timestamp >> 6) & 0x3f; + hour = (timestamp >> 12) & 0x1f; + + // in >> a1; + // in >> a2; + // t1 = a2 << 8 | a1; + + // if (t1 == 0xfafe) { + // break; + // } + + // day = t1 & 0x1f; + // month = (t1 >> 5) & 0x0f; + // year = 2000 + ((t1 >> 9) & 0x3f); + + // in >> a1; + // in >> a2; + + // ts = ((a2 << 8) | a1) << 1; + // ts |= (t1 >> 15) & 1; + + // second = (ts & 0x3f); + // minute = (ts >> 6) & 0x3f; + // hour = (ts >> 12) & 0x1f; + QDateTime dt = QDateTime(QDate(year, month, day), QTime(hour, minute, second), Qt::UTC); + + return dt.toTime_t(); } +quint32 convertFLWDate(quint32 timestamp) +{ + quint8 day, month, hour, minute, second; + quint16 year; + + day = timestamp & 0x1f; + month = (timestamp >> 5) & 0x0f; + year = 2000 + ((timestamp >> 9) & 0x3f); + timestamp >>= 15; + + // Okay, why did I swap the first and last bits of the time field? + // What am I forgetting?? This seems to work properly like this + // Was I looking at older data that worked like this? + + second = timestamp & 0x3f; + minute = (timestamp >> 6) & 0x3f; + hour = (timestamp >> 12) & 0x1f; + QDateTime dt = QDateTime(QDate(year, month, day), QTime(hour, minute, second), Qt::UTC); + + return dt.toTime_t(); +} + +//QDateTime FPIconLoader::readFPDateTime(quint8 *data) +//{ +// quint32 ts = (data[3] << 24) | (data[2] << 16) | ((data[1] << 8) | data[0]); // ^ 0xc00000; +// // 0x20a41b18 + +// quint8 day = ts & 0x1f; // 0X18 24 +// ts >>= 5; // 10520D8 +// quint8 month = ts & 0x0f; // 0X08 8 +// ts >>= 4; // 10520D +// quint8 year = ts & 0x3f; // 0X0D 13 +// ts >>= 6; // 4148 +// quint8 second = ts & 0x3f; // 0X08 8 +// ts >>= 6; // 20A +// quint8 minute = ts & 0x3f; // 0A 10 +// ts >>= 6; // 10 +// quint8 hour = ts & 0x1f; // 10 16 +// QDate date = QDate(2000 + year, month, day); +// QTime time = QTime(hour, minute, second); +// QDateTime datetime = QDateTime(date, time, Qt::UTC); +// return datetime; +//} + /* *in >> a1; in >> a2; @@ -348,19 +417,13 @@ bool FPIconLoader::OpenFLW(Machine *mach, QString filename, Profile *profile) { Q_UNUSED(mach); Q_UNUSED(profile); - QByteArray data; - quint16 t1; + quint32 ts; double ti; - EventList *flow = nullptr, * pressure = nullptr, *leak = nullptr; - QDateTime datetime; - - unsigned char *buf, *endbuf; - + EventList *flow = nullptr, * pressure = nullptr; qDebug() << filename; - QByteArray header; QFile file(filename); if (!file.open(QFile::ReadOnly)) { @@ -368,19 +431,14 @@ bool FPIconLoader::OpenFLW(Machine *mach, QString filename, Profile *profile) return false; } - header = file.read(0x200); - + QByteArray header = file.read(0x200); if (header.size() != 0x200) { qDebug() << "Short file" << filename; return false; } - unsigned char hsum = 0xff; - - for (int i = 0; i < 0x1ff; i++) { - hsum += header[i]; - } - + unsigned char hsum = 0x0; + for (int i = 0; i < 0x1ff; i++) { hsum ^= header[i]; } if (hsum != header[0x1ff]) { qDebug() << "Header checksum mismatch" << filename; } @@ -399,41 +457,21 @@ bool FPIconLoader::OpenFLW(Machine *mach, QString filename, Profile *profile) } fname.chop(4); - QString num = fname.right(4); - int filenum = num.toInt(); + // QString num = fname.right(4); + // int filenum = num.toInt(); - data = file.readAll(); - buf = (unsigned char *)data.data(); - endbuf = buf + data.size(); + QByteArray buf = file.read(4); + unsigned char * data = (unsigned char *)buf.data(); - t1 = buf[1] << 8 | buf[0]; + ts = data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24; - if (t1 == 0xfafe) { // End of file marker.. - qDebug() << "FaFE observed in" << filename; + if (ts == 0xffffffff) return false; - } - - datetime = readFPDateTime(buf); - buf += 4; - - - QDate date; - QTime time; - - if (!datetime.isValid()) { - qDebug() << "DateTime invalid in OpenFLW:" << filename; - return false; - } else { - date = datetime.date(); - time = datetime.time(); - ts = datetime.toTime_t(); - } + ts = convertFLWDate(ts); ti = qint64(ts) * 1000L; - EventStoreType pbuf[256]; - QMap::iterator sit = Sessions.find(ts); Session *sess; @@ -442,8 +480,9 @@ bool FPIconLoader::OpenFLW(Machine *mach, QString filename, Profile *profile) if (sit != Sessions.end()) { sess = sit.value(); - qDebug() << filenum << ":" << date << sess->session() << ":" << sess->hours() * 60.0; +// qDebug() << filenum << ":" << date << sess->session() << ":" << sess->hours() * 60.0; } else { + // Create a session qint64 k = -1; Session *s1 = nullptr; sess = nullptr; @@ -470,7 +509,7 @@ bool FPIconLoader::OpenFLW(Machine *mach, QString filename, Profile *profile) sess->setFirst(CPAP_FlowRate, ti); sess->setFirst(CPAP_MaskPressure, ti); newsess = true; - qDebug() << filenum << ":" << date << "couldn't find matching session for" << ts; +// qDebug() << filenum << ":" << date << "couldn't find matching session for" << ts; } } @@ -479,72 +518,65 @@ bool FPIconLoader::OpenFLW(Machine *mach, QString filename, Profile *profile) // F&P Overwrites this file, not appends to it. flow = new EventList(EVL_Waveform, 1.0F, 0, 0, 0, rate); - //leak=new EventList(EVL_Event,1.0,0,0,0,rate*double(samples_per_block)); // 1 per second - pressure = new EventList(EVL_Event, 0.01F, 0, 0, 0, - rate * double(samples_per_block)); // 1 per second + pressure = new EventList(EVL_Event, 0.01F, 0, 0, 0, rate * double(samples_per_block)); flow->setFirst(ti); - //leak->setFirst(ti); pressure->setFirst(ti); - qint16 pr; - quint16 lkaj; + quint16 endMarker; + quint8 offset; // offset from center for this block + quint16 pres; // mask pressure + + qint16 tmp; + QByteArray block; + qint16 samples[samples_per_block]; EventDataType val; - qint16 tmp; + // Each block represents 1 second of data.. therefore Flow waveform is at 50hz, and Pressure is at 1hz do { - quint8 *p = buf; - - // Scan ahead looking for end of block, marked by ff ff - do { - p++; - - if (p >= endbuf) { - delete flow; - delete leak; - delete pressure; - return false; - } - } while (!((p[0] == 0xff) && (p[1] == 0xff))); - - // The Pressure and lkaj codes are before the end of block marker - p -= 3; - pr = p[1] << 8 | p[0]; - lkaj = p[2]; - int i = 0; - - pressure->AddEvent(ti, pr); - //leak->AddEvent(ti,lkaj); - - do { - tmp = buf[1] << 8 | buf[0]; - val = (EventDataType(tmp) / 100.0) - lkaj; - - if (val < -128) { val = -128; } - else if (val > 128) { val = 128; } - - buf += 2; - - pbuf[i++] = val; - } while (buf < p); - - flow->AddWaveform(ti, pbuf, i, rate); - ti += i * rate; - - buf = p + 5; - - if (buf >= endbuf) { + block = file.read(105); + if (block.size() != 105) { break; } - } while (!((buf[0] == 0xff) && (buf[1] == 0x7f))); + data = (unsigned char *)block.data(); + endMarker = data[1] << 8 | data[0]; + if (endMarker == 0x7fff) { + // Reached end of file + break; + } + pres = data[101] << 8 | data[100]; + offset = data[102]; + + pressure->AddEvent(ti, pres); + + for (int i=0; i < samples_per_block; i++) { + tmp = ((char *)data)[(i<<1) + 1] << 8 | data[(i << 1)]; + + // Assuming Litres per hour, converting to litres per minute and applying offset? + // As in should be 60.0? + val = (EventDataType(tmp) / 100.0) - offset; + +// if (val < -128) { val = -128; } +// else if (val > 128) { val = 128; } + + samples[i]=val; + } + flow->AddWaveform(ti, samples, samples_per_block, rate); + + endMarker = data[103] << 8 | data[104]; + ti += samples_per_block * rate; + } while (endMarker == 0xffff); + + if (endMarker != 0x7fff) { + qDebug() << fname << "waveform does not end with the corrent marker"; + } if (sess) { sess->setLast(CPAP_FlowRate, ti); sess->setLast(CPAP_MaskPressure, ti); sess->eventlist[CPAP_FlowRate].push_back(flow); - // sess->eventlist[CPAP_Leak].push_back(leak); sess->eventlist[CPAP_MaskPressure].push_back(pressure); } @@ -555,6 +587,7 @@ bool FPIconLoader::OpenFLW(Machine *mach, QString filename, Profile *profile) return true; } + //////////////////////////////////////////////////////////////////////////////////////////// // Open Summary file //////////////////////////////////////////////////////////////////////////////////////////// @@ -620,32 +653,11 @@ bool FPIconLoader::OpenSummary(Machine *mach, QString filename, Profile *profile QDate date; do { - in >> a1; - in >> a2; - t1 = a2 << 8 | a1; + in >> ts; + if (ts == 0xffffffff) break; + if ((ts & 0xfafe) == 0xfafe) break; - if (t1 == 0xfafe) { - break; - } - - day = t1 & 0x1f; - month = (t1 >> 5) & 0x0f; - year = 2000 + ((t1 >> 9) & 0x3f); - - in >> a1; - in >> a2; - - ts = ((a2 << 8) | a1) << 1; - ts |= (t1 >> 15) & 1; - - second = (ts & 0x3f); - minute = (ts >> 6) & 0x3f; - hour = (ts >> 12) & 0x1f; - - datetime = QDateTime(QDate(year, month, day), QTime(hour, minute, second), Qt::UTC); - - date = datetime.date(); - ts = datetime.toTime_t(); + ts = convertDate(ts); // the following two quite often match in value in >> a1; // 0x04 Run Time @@ -733,8 +745,8 @@ bool FPIconLoader::OpenDetail(Machine *mach, QString filename, Profile *profile) return false; } + // Calculate and test checksum unsigned char hsum = 0; - for (int i = 0; i < 0x1ff; i++) { hsum += header[i]; } @@ -743,64 +755,29 @@ bool FPIconLoader::OpenDetail(Machine *mach, QString filename, Profile *profile) qDebug() << "Header checksum mismatch" << filename; } - QByteArray index; - index = file.read(0x800); - //long size=index.size(),pos=0; + QByteArray index = file.read(0x800); QDataStream in(index); in.setVersion(QDataStream::Qt_4_6); in.setByteOrder(QDataStream::LittleEndian); quint32 ts; - QDateTime datetime; - QDate date; - QTime time; - //FPDetIdx *idx=(FPDetIdx *)index.data(); - QVector times; QVector start; QVector records; - quint16 t1; quint16 strt; - quint8 recs, z1, z2; - - int day, month, year, hour, minute, second; + quint8 recs; int totalrecs = 0; do { - in >> z1; - in >> z2; - t1 = z2 << 8 | z1; + in >> ts; + if (ts == 0xffffffff) break; + if ((ts & 0xfafe) == 0xfafe) break; - if (t1 == 0xfafe) { - break; - } + ts = convertDate(ts); - day = t1 & 0x1f; - month = (t1 >> 5) & 0x0f; - year = 2000 + ((t1 >> 9) & 0x3f); - - in >> z1; - in >> z2; - // - - ts = ((z2 << 8) | z1) << 1; - ts |= (t1 >> 15) & 1; - - // - second = (ts & 0x3f); - minute = (ts >> 6) & 0x3f; - hour = (ts >> 12) & 0x1f; - - datetime = QDateTime(QDate(year, month, day), QTime(hour, minute, second), Qt::UTC); - //datetime=datetime.toTimeSpec(Qt::UTC); - - ts = datetime.toTime_t(); - - date = datetime.date(); - time = datetime.time(); in >> strt; in >> recs; totalrecs += recs; @@ -816,13 +793,14 @@ bool FPIconLoader::OpenDetail(Machine *mach, QString filename, Profile *profile) in.setVersion(QDataStream::Qt_4_6); in.setByteOrder(QDataStream::BigEndian); + // 5 byte repeating patterns quint8 *data = (quint8 *)databytes.data(); qint64 ti; - quint8 pressure, leak, a1, a2, a3; - quint8 sa1, sa2; // The two sense awake bits per 2 minutes + quint8 pressure, leak, a1, a2, a3, a4; +// quint8 sa1, sa2; // The two sense awake bits per 2 minutes SessionID sessid; Session *sess; int idx; @@ -834,7 +812,6 @@ bool FPIconLoader::OpenDetail(Machine *mach, QString filename, Profile *profile) sess->really_set_first(ti); EventList *LK = sess->AddEventList(CPAP_LeakTotal, EVL_Event, 1); EventList *PR = sess->AddEventList(CPAP_Pressure, EVL_Event, 0.1F); -// EventList *FLG = sess->AddEventList(CPAP_FLG, EVL_Event); EventList *OA = sess->AddEventList(CPAP_Obstructive, EVL_Event); EventList *H = sess->AddEventList(CPAP_Hypopnea, EVL_Event); EventList *FL = sess->AddEventList(CPAP_FlowLimit, EVL_Event); @@ -846,8 +823,8 @@ bool FPIconLoader::OpenDetail(Machine *mach, QString filename, Profile *profile) idx = stidx * 15; quint8 bitmask; - for (int i = 0; i < rec; i++) { - for (int j = 0; j < 3; j++) { + for (int i = 0; i < rec; ++i) { + for (int j = 0; j < 3; ++j) { pressure = data[idx]; PR->AddEvent(ti, pressure); @@ -858,17 +835,19 @@ bool FPIconLoader::OpenDetail(Machine *mach, QString filename, Profile *profile) a2 = data[idx + 3]; // [0..5] Hypopnea, [6..7] Unknown a3 = data[idx + 4]; // [0..5] Flow Limitation, [6..7] SensAwake bitflags, 1 per minute - sa1 = (a3 >> 6) & 1; // Sense awake bit for first minutes - sa2 = (a3 >> 7) & 1; // Sense awake bit for second minute + // Sure there isn't 6 SenseAwake bits? + a4 = (a1 >> 6) << 4 | ((a2 >> 6) << 2) | (a3 >> 6); - if (sa1) { SA->AddEvent(ti, 1); } - if (sa2) { SA->AddEvent(ti + 60000L, 1); } + // this does the same thing as behaviour + //a4 = (a3 >> 7) << 3 | ((a3 >> 6)&1); bitmask = 1; for (int k = 0; k < 6; k++) { // There are 6 flag sets per 2 minutes if (a1 & bitmask) { OA->AddEvent(ti, 1); } if (a2 & bitmask) { H->AddEvent(ti, 1); } if (a3 & bitmask) { FL->AddEvent(ti, 1); } + if (a4 & bitmask) { SA->AddEvent(ti, 1); } + bitmask <<= 1; ti += 20000L; // Increment 20 seconds } diff --git a/sleepyhead/daily.cpp b/sleepyhead/daily.cpp index 8983a268..9b5b3fae 100644 --- a/sleepyhead/daily.cpp +++ b/sleepyhead/daily.cpp @@ -309,6 +309,8 @@ Daily::Daily(QWidget *parent,gGraphView * shared) graphlist[schema::channel[CPAP_Snore].label()]->AddLayer(AddCPAP(new gLineChart(CPAP_Snore, COLOR_Snore, true))); graphlist[schema::channel[CPAP_PTB].label()]->AddLayer(AddCPAP(new gLineChart(CPAP_PTB, COLOR_PTB, square))); + + graphlist[schema::channel[CPAP_MaskPressure].label()]->AddLayer(AddCPAP(new gLineChart(CPAP_MaskPressure, COLOR_MaskPressure, false))); graphlist[schema::channel[CPAP_RespRate].label()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_RespRate, COLOR_RespRate, square)));