From 8761a810c620175f8c6fda15858390268529f236 Mon Sep 17 00:00:00 2001 From: Mark Watkins Date: Sat, 10 Dec 2011 22:14:48 +1000 Subject: [PATCH] PRS1 Loader Restructure to fix ASV bugs, and allow for chunked summary/event files --- Graphs/gFlagsLine.cpp | 10 +- Graphs/gFlagsLine.h | 2 + Graphs/gGraphView.cpp | 17 +- Graphs/gGraphView.h | 1 + Graphs/gLineChart.cpp | 3 + Graphs/gSummaryChart.cpp | 15 +- SleepLib/calcs.cpp | 71 +- SleepLib/day.cpp | 63 +- SleepLib/day.h | 2 + SleepLib/loader_plugins/prs1_loader.cpp | 1238 ++++++++++++++--------- SleepLib/loader_plugins/prs1_loader.h | 28 +- SleepLib/machine.h | 2 + SleepLib/machine_common.h | 2 + SleepLib/session.cpp | 13 +- SleepLib/session.h | 1 - daily.cpp | 196 ++-- daily.ui | 12 +- docs/channels.xml | 2 +- mainwindow.cpp | 14 + newprofile.ui | 10 +- 20 files changed, 1104 insertions(+), 598 deletions(-) diff --git a/Graphs/gFlagsLine.cpp b/Graphs/gFlagsLine.cpp index f72926e9..b489d605 100644 --- a/Graphs/gFlagsLine.cpp +++ b/Graphs/gFlagsLine.cpp @@ -19,6 +19,7 @@ gFlagsGroup::gFlagsGroup() quads->setAntiAlias(true); lines->setAntiAlias(false); m_barh=0; + m_empty=true; } gFlagsGroup::~gFlagsGroup() { @@ -41,14 +42,21 @@ void gFlagsGroup::SetDay(Day * d) { LayerGroup::SetDay(d); lvisible.clear(); + int cnt=0; for (int i=0;i(layers[i]); if (!f) continue; - if (!f->isEmpty() || f->isAlwaysVisible()) { + bool e=f->isEmpty(); + if (!e || f->isAlwaysVisible()) { lvisible.push_back(f); + if (!e) cnt++; } } + if (cnt==0) + m_empty=true; + else m_empty=false; + //if (lvisible.size()==0) m_empty=false; m_barh=0; } diff --git a/Graphs/gFlagsLine.h b/Graphs/gFlagsLine.h index 1f0162bf..4511adba 100644 --- a/Graphs/gFlagsLine.h +++ b/Graphs/gFlagsLine.h @@ -45,6 +45,7 @@ public: virtual qint64 Minx(); virtual qint64 Maxx(); virtual void SetDay(Day *); + virtual bool isEmpty() { return m_empty; } int count() { return lvisible.size(); } int barHeight() { return m_barh; } QVector & visibleLayers() { return lvisible; } @@ -53,6 +54,7 @@ protected: GLShortBuffer *quads, *lines; QVector lvisible; float m_barh; + bool m_empty; }; #endif // GFLAGSLINE_H diff --git a/Graphs/gGraphView.cpp b/Graphs/gGraphView.cpp index 813f07d2..3da107cd 100644 --- a/Graphs/gGraphView.cpp +++ b/Graphs/gGraphView.cpp @@ -103,7 +103,7 @@ void GetTextExtent(QString text, int & width, int & height, QFont *font) #ifdef Q_WS_WIN32 height=fm.ascent(); #else - height=fm.xHeight()+2; //fm.ascent(); + height=fm.xHeight()+2; // doesn't work properly on windows.. #endif #ifdef ENABLE_THREADED_DRAWING mut.unlock(); @@ -733,7 +733,8 @@ void Layer::SetDay(Day * d) bool Layer::isEmpty() { - if (m_day && (m_day->count(m_code)>0)) + //if (m_day && (m_day->count(m_code)>0)) + if (m_day && (m_day->channelExists(m_code))) return false; return true; } @@ -2621,6 +2622,18 @@ void gGraphView::setDay(Day * day) } ResetBounds(); } +bool gGraphView::isEmpty() +{ + bool res=true; + for (int i=0;iisEmpty()) { + res=false; + break; + } + } + return res; +} + void gGraphView::refreshTimeout() { updateGL(); diff --git a/Graphs/gGraphView.h b/Graphs/gGraphView.h index a06e7f62..99e9fcbb 100644 --- a/Graphs/gGraphView.h +++ b/Graphs/gGraphView.h @@ -428,6 +428,7 @@ public: inline float printScaleY() { return print_scaleY; } inline void setPrintScaleX(float x) { print_scaleX=x; } inline void setPrintScaleY(float y) { print_scaleY=y; } + bool isEmpty(); void deselect(); QPoint pointClicked() { return m_point_clicked; } diff --git a/Graphs/gLineChart.cpp b/Graphs/gLineChart.cpp index 4a828418..e88011ee 100644 --- a/Graphs/gLineChart.cpp +++ b/Graphs/gLineChart.cpp @@ -131,6 +131,9 @@ void gLineChart::paint(gGraph & w,int left, int top, int width, int height) qWarning() << "gLineChart::Plot() NULL Session Record.. This should not happen"; continue; } + if (m_code==CPAP_FlowRate){ + int i=5; + } schema::Channel ch=schema::channel[m_code]; bool fndbetter=false; for (QList::iterator l=ch.m_links.begin();l!=ch.m_links.end();l++) { diff --git a/Graphs/gSummaryChart.cpp b/Graphs/gSummaryChart.cpp index 4007e93d..91935487 100644 --- a/Graphs/gSummaryChart.cpp +++ b/Graphs/gSummaryChart.cpp @@ -128,8 +128,16 @@ void SummaryChart::SetDay(Day * nullday) if (day->machine_type()!=m_machinetype) continue; //m_values[dn][j+1]=0; - bool hascode=day->channelHasData(code) || day->settingExists(code); - if (type==ST_HOURS || type==ST_SESSIONS || hascode) { // too many lookups happening here.. stop the crap.. + bool hascode=//day->channelHasData(code) || + type==ST_HOURS || + type==ST_SESSIONS || + day->settingExists(code) || + day->hasData(code,type); + + if (code==CPAP_RespRate) { + int i=5; + } + if (hascode) { m_days[dn]=day; switch(m_type[j]) { case ST_AVG: tmp=day->avg(code); break; @@ -150,6 +158,9 @@ void SummaryChart::SetDay(Day * nullday) case ST_SETSUM: tmp=day->settings_sum(code); break; default: break; } + if (tmp>10000) { + int i=5; + } if (suboffset>0) { tmp-=suboffset; if (tmp<0) tmp=0; diff --git a/SleepLib/calcs.cpp b/SleepLib/calcs.cpp index 4d252e04..581ce30b 100644 --- a/SleepLib/calcs.cpp +++ b/SleepLib/calcs.cpp @@ -49,9 +49,9 @@ int filterFlow(EventList *in, EventList *out, EventList *tv, EventList *mv, doub //stage1[i]=in->data(i); // Anti-Alias the flow waveform to get rid of jagged edges. - stage2[0]=stage1[0]; - stage2[1]=stage1[1]; - stage2[2]=stage1[2]; + stage2[0]=in->data(0); + stage2[1]=in->data(1); + stage2[2]=in->data(2); i=3; for (;idata(0); - stage1[0]=stage2[0]; - for (int i=1;idata(i); stage1[i]=weight*stage2[i]+(1.0-weight)*stage1[i-1]; - } + } */ qint64 time=in->first(); qint64 u1=0,u2=0,len,l1=0,l2=0; @@ -91,7 +91,7 @@ int filterFlow(EventList *in, EventList *out, EventList *tv, EventList *mv, doub //int lasti=0; for (i=0;ithresh) { if (lastc<=thresh) { u2=u1; @@ -102,7 +102,7 @@ int filterFlow(EventList *in, EventList *out, EventList *tv, EventList *mv, doub if (tv) { // && z1>0) { // Tidal Volume Calculations EventDataType t=0; for (int g=z1;gfirst()-window/2; @@ -218,7 +222,7 @@ int filterFlow(EventList *in, EventList *out, EventList *tv, EventList *mv, doub } qSort(med); br=med[2]; - out->AddEvent(t,br); + if (out) out->AddEvent(t,br); //t=TV2_start[i]; med.clear(); @@ -227,9 +231,10 @@ int filterFlow(EventList *in, EventList *out, EventList *tv, EventList *mv, doub } qSort(med); tmp=med[3]; - tv->AddEvent(t,tmp); - mv->AddEvent(t,(tmp*br)/1000.0); + if (tv) tv->AddEvent(t,tmp); + + if (mv) mv->AddEvent(t,(tmp*br)/1000.0); } delete [] stage2; @@ -243,27 +248,43 @@ int calcRespRate(Session *session) { if (session->machine()->GetType()!=MT_CPAP) return 0; if (session->machine()->GetClass()!="PRS1") return 0; - if (session->eventlist.contains(CPAP_RespRate)) return 0; // already exists? + if (!session->eventlist.contains(CPAP_FlowRate)) + return 0; //need flow waveform - if (!session->eventlist.contains(CPAP_FlowRate)) return 0; //need flow waveform + if (session->eventlist.contains(CPAP_RespRate)) + return 0; // already exists? + + EventList *flow, *rr=NULL, *tv=NULL, *mv=NULL; - EventList *flow, *rr, *tv=NULL, *mv=NULL; if (!session->eventlist.contains(CPAP_TidalVolume)) { tv=new EventList(EVL_Event); - session->eventlist[CPAP_TidalVolume].push_back(tv); - } + } else tv=NULL; if (!session->eventlist.contains(CPAP_MinuteVent)) { mv=new EventList(EVL_Event); - session->eventlist[CPAP_MinuteVent].push_back(mv); - } + } else mv=NULL; + if (!session->eventlist.contains(CPAP_RespRate)) { + rr=new EventList(EVL_Event); + } else rr=NULL; + + if (!rr & !tv & !mv) return 0; + if (rr) session->eventlist[CPAP_RespRate].push_back(rr); + if (tv) session->eventlist[CPAP_TidalVolume].push_back(tv); + if (mv) session->eventlist[CPAP_MinuteVent].push_back(mv); + int cnt=0; for (int ws=0; ws < session->eventlist[CPAP_FlowRate].size(); ws++) { flow=session->eventlist[CPAP_FlowRate][ws]; if (flow->count() > 5) { - rr=new EventList(EVL_Event); - session->eventlist[CPAP_RespRate].push_back(rr); + + if (flow->count()==103200) { + int i=5; + } cnt+=filterFlow(flow,rr,tv,mv,flow->rate()); + + if (tv->count()==0) { + int i=5; + } } } return cnt; @@ -299,6 +320,11 @@ int calcAHIGraph(Session *session) if (session->machine()->GetType()!=MT_CPAP) return 0; if (session->eventlist.contains(CPAP_AHI)) return 0; // abort if already there + if (!session->channelExists(CPAP_Obstructive) && + !session->channelExists(CPAP_Hypopnea) && + !session->channelExists(CPAP_Apnea) && + !session->channelExists(CPAP_ClearAirway)) return 0; + const qint64 winsize=30000; // 30 second windows qint64 first=session->first(), @@ -370,6 +396,9 @@ int calcLeaks(Session *session) if (idx>=med.size()) idx--; median=tmp-med[idx]; if (median<0) median=0; + if (median>9999) { + int i=5; + } leak->AddEvent(ti,median); rpos=rpos % rbsize; diff --git a/SleepLib/day.cpp b/SleepLib/day.cpp index f11d0fa8..8de1e560 100644 --- a/SleepLib/day.cpp +++ b/SleepLib/day.cpp @@ -188,8 +188,8 @@ EventDataType Day::wavg(ChannelID code) for (QVector::iterator s=sessions.begin();s!=sessions.end();s++) { Session & sess=*(*s); if (sess.m_wavg.contains(code)) { - d=sess.last(code)-sess.first(code); - s0=double(d)/1000.0; + d=sess.length();//.last(code)-sess.first(code); + s0=double(d)/3600000.0; if (s0>0) { s1+=sess.wavg(code)*s0; s2+=s0; @@ -280,6 +280,54 @@ EventDataType Day::min(ChannelID code) return min; } +bool Day::hasData(ChannelID code, SummaryType type) +{ + bool has=false; + for (QVector::iterator s=sessions.begin();s!=sessions.end();s++) { + Session *sess=*s; + switch(type) { + case ST_90P: + has=sess->m_90p.contains(code); + break; + case ST_MIN: + has=sess->m_min.contains(code); + break; + case ST_MAX: + has=sess->m_max.contains(code); + break; + case ST_CNT: + has=sess->m_cnt.contains(code); + break; + case ST_AVG: + has=sess->m_avg.contains(code); + break; + case ST_WAVG: + has=sess->m_wavg.contains(code); + break; + case ST_CPH: + has=sess->m_cph.contains(code); + break; + case ST_SPH: + has=sess->m_sph.contains(code); + break; + case ST_FIRST: + has=sess->m_firstchan.contains(code); + break; + case ST_LAST: + has=sess->m_lastchan.contains(code); + break; + case ST_SUM: + has=sess->m_sum.contains(code); + break; + default: + break; + } + + if (has) break; + } + return has; +} + EventDataType Day::max(ChannelID code) { EventDataType max=0; @@ -345,7 +393,15 @@ bool Day::settingExists(ChannelID id) } bool Day::channelExists(ChannelID id) { - return channelHasData(id); + bool r=false; + for (int i=0;ieventlist.contains(id)) { + r=true; + break; + } + } + return r; +// return channelHasData(id); //if (machine->hasChannel(id)) return true; //return false; } @@ -355,6 +411,7 @@ bool Day::channelHasData(ChannelID id) for (int i=0;ichannelExists(id)) { r=true; + break; } } return r; diff --git a/SleepLib/day.h b/SleepLib/day.h index 5da4ec54..d51fdfb6 100644 --- a/SleepLib/day.h +++ b/SleepLib/day.h @@ -37,6 +37,8 @@ public: EventDataType sum(ChannelID code); EventDataType wavg(ChannelID code); + bool hasData(ChannelID code, SummaryType type); + EventDataType percentile(ChannelID mc,double percent); // Note, the following convert to doubles without considering the consequences fully. diff --git a/SleepLib/loader_plugins/prs1_loader.cpp b/SleepLib/loader_plugins/prs1_loader.cpp index cc6ed8a7..6e63ca24 100644 --- a/SleepLib/loader_plugins/prs1_loader.cpp +++ b/SleepLib/loader_plugins/prs1_loader.cpp @@ -18,6 +18,7 @@ License: GPL #include "SleepLib/schema.h" #include "prs1_loader.h" #include "SleepLib/session.h" +#include "SleepLib/calcs.h" const int PRS1_MAGIC_NUMBER=2; @@ -38,6 +39,8 @@ extern QProgressBar *qprogress; QHash ModelMap; +#define PRS1_CRC_CHECK +#ifdef PRS1_CRC_CHECK typedef quint16 crc_t; static const crc_t crc_table[256] = { @@ -88,6 +91,7 @@ crc_t CRC16(const unsigned char *data, size_t data_len) } return crc & 0xffff; } +#endif PRS1::PRS1(Profile *p,MachineID id):CPAP(p,id) { @@ -283,13 +287,14 @@ int PRS1Loader::OpenMachine(Machine *m,QString path,Profile *profile) //if (qprogress) qprogress->Pulse(); } - SessionID session; + SessionID sid; long ext; - typedef QVector StringList; - QMap sessfiles; + QHash sessfiles; int size=paths.size(); int cnt=0; bool ok; + + new_sessions.clear(); for (QList::iterator p=paths.begin(); p!=paths.end(); p++) { dir.setPath(*p); if (!dir.exists() || !dir.isReadable()) continue; @@ -302,67 +307,64 @@ int PRS1Loader::OpenMachine(Machine *m,QString path,Profile *profile) ext=ext_s.toLong(&ok); if (!ok) continue; - session=session_s.toLong(&ok); + sid=session_s.toLong(&ok); if (!ok) continue; - if (sessfiles[session].capacity()==0) sessfiles[session].resize(3); + if (m->SessionExists(sid)) continue; // could skip this and error check data by reloading summary. - if (ext==1) { - sessfiles[session][0]=fi.canonicalFilePath(); - } else if (ext==2) { - sessfiles[session][1]=fi.canonicalFilePath(); - } else if (ext==5) { - sessfiles[session][2]=fi.canonicalFilePath(); + //if (sessfiles[session].capacity()==0) sessfiles[session].resize(3); + + if ((ext==1) || (ext==0)) { + OpenFile(m,fi.canonicalFilePath()); // Open just the summary files first round + } else { + sessfiles[sid].push_back(fi.canonicalFilePath()); // and keep the rest of the names } - cnt++; + //cnt++; - if (qprogress) qprogress->setValue((float(cnt)/float(size)*33.0)); - - QApplication::processEvents(); + //if (qprogress) qprogress->setValue((float(cnt)/float(size)*33.0)); + //QApplication::processEvents(); } } - size=sessfiles.size(); - if (size==0) - return 0; - cnt=0; - for (QMap::iterator s=sessfiles.begin(); s!=sessfiles.end(); s++) { - session=s.key(); + size=new_sessions.size(); + for (QHash::iterator it=new_sessions.begin();it!=new_sessions.end();it++) { + sid=it.key(); + + if (sessfiles.contains(sid)) { + for (int j=0;jsetValue(33.0+(float(cnt)/float(size)*33.0)); + if (qprogress) qprogress->setValue(0.0+(float(cnt)/float(size)*100.0)); QApplication::processEvents(); + } + // strictly can do this in the above loop, but this is cautionary + cnt=0; + //QVector KillList; + for (QHash::iterator it=new_sessions.begin();it!=new_sessions.end();it++) { + Session *sess=it.value(); - if (m->SessionExists(session)) continue; - if (s.value()[0].isEmpty()) continue; - - Session *sess=new Session(m,session); - if (!OpenSummary(sess,s.value()[0])) { - //qWarning() << "PRS1Loader: Dodgy summary file " << s.value()[0]; - - delete sess; - continue; - } - //sess->SetSessionID(sess->start().GetTicks()); - if (!s.value()[1].isEmpty()) { - if (!OpenEvents(sess,s.value()[1])) { - qWarning() << "PRS1Loader: Couldn't open event file " << s.value()[1]; - } - } - if (!s.value()[2].isEmpty()) { - if (!OpenWaveforms(sess,s.value()[2])) { - qWarning() << "PRS1Loader: Couldn't open event file " << s.value()[2]; - } - } - if ((sess->last() - sess->first()) == 0) { + if ((sess->length()) == 0) { //const double ignore_thresh=300.0/3600.0;// Ignore useless sessions under 5 minute //if (sess->hours()<=ignore_thresh) { - qDebug() << "Ignoring empty session" << session;// << "which is only" << (sess->hours()*60.0) << "minute(s) long"; + qDebug() << "Ignoring empty session" << sess->session(); + SessionID id=sess->session(); delete sess; + new_sessions[id]=NULL; + //KillList.push_back(id); continue; } - if (sess->count(CPAP_IPAP)>0) { //sess->summaryCPAP_Mode]!=MODE_ASV) sess->settings[CPAP_Mode]=MODE_BIPAP; @@ -399,205 +401,429 @@ int PRS1Loader::OpenMachine(Machine *m,QString path,Profile *profile) } if (sess->settings[CPAP_Mode]==MODE_CPAP) { - sess->settings[CPAP_PressureMax]=sess->settings[CPAP_PressureMin]; + //sess->settings[CPAP_PressureMax]=sess->settings[CPAP_PressureMin]; } - //Printf(sess->start().Format()+wxT(" avgsummary=%.3f avgmine=%.3f\n"),sess->summary[CPAP_PressureAverage].GetDouble(),sess->weighted_avg_event_field(CPAP_Pressure,0)); sess->SetChanged(true); m->AddSession(sess,profile); + cnt++; + if (qprogress) qprogress->setValue(33.0+(float(cnt)/float(size)*33.0)); + QApplication::processEvents(); } - QString s; - s.sprintf("%i",prs1_data_version); - m->properties["DataVersion"]=s; - m->Save(); // Save any new sessions to disk in our format */ + + m->properties["DataVersion"]=QString().sprintf("%i",prs1_data_version); + m->properties["LastImported"]=QDateTime::currentDateTime().toString(Qt::ISODate); + m->Save(); // Save any new sessions to disk in our format if (qprogress) qprogress->setValue(100); - //qDebug() << "OpenMachine Done"; - return true; + return cnt; } -//bool PRS1Loader::OpenMachine() -//{ -//} -bool PRS1Loader::OpenSummary(Session *session,QString filename) // Read .001 file + +struct PRS1SummaryR5 { + quint8 unknown1; + quint8 unknown2; + quint8 unknown3; + quint8 pressure_min; + quint8 pressure_max; + quint8 unknown; + + quint32 flags; + + +} __attribute__((packed)); + +bool PRS1Loader::ParseSummary(Machine *mach, qint32 sequence, quint32 timestamp, unsigned char *data, quint16 size, char version) { - int size,seconds,br,htype,version,sequence; - qint64 timestamp; - unsigned char header[24]; - unsigned char ext,sum; - - //qDebug() << "Opening PRS1 Summary " << filename; - QFile f(filename); - - if (!f.open(QIODevice::ReadOnly)) - return false; - - if (!f.exists()) + if (mach->SessionExists(sequence)) return false; - int hl=16; + Session *session=new Session(mach,sequence); + session->really_set_first(qint64(timestamp)*1000L); + EventDataType max,min; - br=f.read((char *)header,hl); - - if (header[0]!=PRS1_MAGIC_NUMBER) - return false; - - sequence=size=timestamp=seconds=ext=0; - sequence=(header[10] << 24) | (header[9] << 16) | (header[8] << 8) | header[7]; - timestamp=(header[14] << 24) | (header[13] << 16) | (header[12] << 8) | header[11]; - size=(header[2] << 8) | header[1]; - ext=header[6]; - htype=header[3]; // 00 = normal // 01=waveform // could be a bool? - version=header[4]; - sequence=sequence; - version=version; // don't need it here? - - htype=htype; // shut the warning up.. this is useless. - - if (ext!=PRS1_SUMMARY_FILE) - return false; - - size-=(hl+2); - - // Calculate header checksum and compare to verify header - sum=0; - for (int i=0; iset_first(date); - - double max; - session->settings[CPAP_PressureMin]=(EventDataType)buffer[0x03]/10.0; - session->settings[CPAP_PressureMax]=max=(EventDataType)buffer[0x04]/10.0; + min=(data[0x03])/10.0; + session->settings[CPAP_PressureMin]=min; + max=(data[0x04])/10.0; + session->settings[CPAP_PressureMax]=max; int offset=0; - if (buffer[0x05]!=0) { // This is a time value for ASV stuff - // non zero adds extra fields.. - offset=4; + if (version==5) { //data[0x05]!=0) { // This is a time value for ASV stuff + offset=4; // non zero adds 4 extra fields.. } - session->settings[CPAP_RampTime]=(int)buffer[offset+0x06]; // Minutes. Convert to seconds/hours here? - session->settings[CPAP_RampPressure]=(EventDataType)buffer[offset+0x07]/10.0; + session->settings[CPAP_RampTime]=(int)data[offset+0x06]; // Minutes. Convert to seconds/hours here? + session->settings[CPAP_RampPressure]=(EventDataType)data[offset+0x07]/10.0; if (max>0) { // Ignoring bipap until I see some more data. session->settings[CPAP_Mode]=(int)MODE_APAP; } else session->settings[CPAP_Mode]=(int)MODE_CPAP; // This is incorrect.. - if (buffer[offset+0x08] & 0x80) { // Flex Setting - if (buffer[offset+0x08] & 0x08) { + if (data[offset+0x08] & 0x80) { // Flex Setting + if (data[offset+0x08] & 0x08) { if (max>0) session->settings[PRS1_FlexMode]=(int)PR_AFLEX; else session->settings[PRS1_FlexMode]=(int)PR_CFLEXPLUS; } else session->settings[PRS1_FlexMode]=(int)PR_CFLEX; } else session->settings[PRS1_FlexMode]=(int)PR_NONE; - session->settings["FlexSet"]=(int)buffer[offset+0x08] & 3; - session->settings["HumidSet"]=(int)buffer[offset+0x09]&0x0f; - session->settings["HumidStat"]=(buffer[offset+0x09]&0x80)==0x80; - session->settings["SysLock"]=(buffer[offset+0x0a]&0x80)==0x80; - session->settings["SysOneResistStat"]=(buffer[offset+0x0a]&0x40)==0x40; - session->settings["SysOneResistSet"]=(int)buffer[offset+0x0a]&7; - session->settings["HoseDiam"]=((buffer[offset+0x0a]&0x08)?"15mm":"22mm"); - session->settings["AutoOff"]=(buffer[offset+0x0c]&0x10)==0x10; - session->settings["MaskAlert"]=(buffer[offset+0x0c]&0x08)==0x08; - session->settings["ShowAHI"]=(buffer[offset+0x0c]&0x04)==0x04; + session->settings["FlexSet"]=(int)data[offset+0x08] & 3; + session->settings["HumidSet"]=(int)data[offset+0x09]&0x0f; + session->settings["HumidStat"]=(data[offset+0x09]&0x80)==0x80; + session->settings["SysLock"]=(data[offset+0x0a]&0x80)==0x80; + session->settings["SysOneResistStat"]=(data[offset+0x0a]&0x40)==0x40; + session->settings["SysOneResistSet"]=(int)data[offset+0x0a]&7; + session->settings["HoseDiam"]=((data[offset+0x0a]&0x08)?"15mm":"22mm"); + session->settings["AutoOff"]=(data[offset+0x0c]&0x10)==0x10; + session->settings["MaskAlert"]=(data[offset+0x0c]&0x08)==0x08; + session->settings["ShowAHI"]=(data[offset+0x0c]&0x04)==0x04; - unsigned duration=buffer[offset+0x14] | (buffer[0x15] << 8); - //session->settings[CPAP_Duration]=(int)duration; - //qDebug() << "ID: " << session->session() << " " << duration; - //float hours=float(duration)/3600.0; - //session->set_hours(hours); - if (!duration) - return false; - session->set_last(date+qint64(duration)*1000L); - //session->settings[PRS1_PressureMinAchieved]=buffer[offset+0x16]/10.0; - //session->settings[PRS1_PressureMaxAchieved]=buffer[offset+0x17]/10.0; - //session->settings[PRS1_PressureAvg]=buffer[offset+0x18]/10.0; - //session->settings[PRS1_Pressure90]=buffer[offset+0x19]/10.0; + unsigned duration; - if (max==0) { - // session->settings[PRS1_PressureAvg]=session->settings[PRS1_PressureMin]; + // up to this point appears to becorrect for 0x01 & 0x00 + if (size<59) { + duration=data[offset+0x12] | (data[0x13] << 8); + duration*=2; + session->really_set_last(qint64(timestamp+duration)*1000L); + } else { + // 0X28 & 0X29 is length on r5 + + duration=data[offset+0x14] | (data[0x15] << 8); + if (!duration) { + delete session; + return false; + } + + session->really_set_last(qint64(timestamp+duration)*1000L); + float hours=float(duration)/3600.0; + + // Not using these because sometimes this summary is broken. + EventDataType minp,maxp,avgp,p90p; + + minp=data[offset+0x16]/10.0; + maxp=data[offset+0x17]/10.0; + p90p=data[offset+0x18]/10.0; + avgp=data[offset+0x19]/10.0; + + if (minp>0) session->setMin(CPAP_Pressure,minp); else session->setMin(CPAP_Pressure,min); + if (maxp>0) session->setMax(CPAP_Pressure,maxp); else session->setMax(CPAP_Pressure,min); + if (avgp>0) session->setWavg(CPAP_Pressure,avgp); else session->setWavg(CPAP_Pressure,min); + if (p90p>0) session->set90p(CPAP_Pressure,p90p); else session->set90p(CPAP_Pressure,min); + + + + int oc, cc, hc, rc, fc; + session->setCount(CPAP_Obstructive,oc=(int)data[offset+0x1C] | (data[offset+0x1D] << 8)); + session->setCount(CPAP_ClearAirway,cc=(int)data[offset+0x20] | (data[offset+0x21] << 8)); + session->setCount(CPAP_Hypopnea,hc=(int)data[offset+0x2A] | (data[offset+0x2B] << 8)); + session->setCount(CPAP_RERA,rc=(int)data[offset+0x2E] | (data[offset+0x2F] << 8)); + session->setCount(CPAP_FlowLimit,fc=(int)data[offset+0x30] | (data[offset+0x31] << 8)); + + session->setCph(CPAP_Obstructive,float(oc/hours)); + session->setCph(CPAP_ClearAirway,float(cc/hours)); + session->setCph(CPAP_Hypopnea,float(hc/hours)); + session->setCph(CPAP_RERA,float(rc/hours)); + session->setCph(CPAP_FlowLimit,float(fc/hours)); } - - // Not using these because sometimes this summary is broken. - /*/if (size==0x4d) { - - session->settings[CPAP_Obstructive]=(int)buffer[offset+0x1C] | (buffer[offset+0x1D] << 8); - session->settings[CPAP_ClearAirway]=(int)buffer[offset+0x20] | (buffer[offset+0x21] << 8); - session->settings[CPAP_Hypopnea]=(int)buffer[offset+0x2A] | (buffer[offset+0x2B] << 8); - session->settings[CPAP_RERA]=(int)buffer[offset+0x2E] | (buffer[offset+0x2F] << 8); - session->settings[CPAP_FlowLimit]=(int)buffer[offset+0x30] | (buffer[offset+0x31] << 8); - }*/ + new_sessions[sequence]=session; return true; } - -// v2 event parser. -bool PRS1Loader::Parse002(Session *session,unsigned char *buffer,int size,qint64 timestamp) +bool PRS1Loader::Parse002v5(Machine *mach, qint32 sequence, quint32 timestamp, unsigned char *buffer, quint16 size) { - /*ChannelID Codes[]={ - PRS1_Unknown00, PRS1_Unknown01, CPAP_Pressure, CPAP_EPAP, CPAP_PressurePulse, CPAP_RERA, CPAP_Obstructive, CPAP_ClearAirway, - PRS1_Unknown08, PRS1_Unknown09, CPAP_Hypopnea, PRS1_Unknown0B, CPAP_FlowLimit, CPAP_VSnore, PRS1_Unknown0E, CPAP_CSR, PRS1_Unknown10, - CPAP_LeakTotal, PRS1_Unknown12 - }; - int ncodes=sizeof(Codes)/sizeof(ChannelID); */ + if (!new_sessions.contains(sequence)) + return false; + Session *session=new_sessions[sequence]; - //QHash Code; + QString Codes[]={ + PRS1_00, PRS1_01, CPAP_Pressure, CPAP_EPAP, CPAP_PressurePulse, CPAP_Obstructive, + CPAP_ClearAirway, CPAP_Hypopnea, PRS1_08, CPAP_FlowLimit, PRS1_0A, CPAP_CSR, + PRS1_0C, CPAP_VSnore, PRS1_0E, PRS1_0F, PRS1_10, + CPAP_LeakTotal, PRS1_12 + }; + + int ncodes=sizeof(Codes)/sizeof(QString); EventList * Code[0x20]={NULL}; - EventDataType data[10]; + EventDataType data[10],tmp; + - session->updateFirst(timestamp); //qint64 start=timestamp; - qint64 t=timestamp; + qint64 t=qint64(timestamp)*1000L; + session->updateFirst(t); qint64 tt; int pos=0; int cnt=0; - short delta; - int tdata; + short delta;//,duration; + QDateTime d; + bool badcode=false; + while (pos=70) { + int i=5; + } unsigned char code=buffer[pos++]; - if (code>0x12) { - qDebug() << "Illegal PRS1 code " << hex << int(code) << " appeared at " << hex << pos+16; + if (code>=ncodes) { + qDebug() << "Illegal PRS1 code " << hex << int(code) << " appeared at " << hex << pos; + return false; + } + if (code==0) { + } else + if (code!=0x12) { + delta=buffer[pos]; + //duration=buffer[pos+1]; + //delta=buffer[pos+1] << 8 | buffer[pos]; + pos+=2; + t+=qint64(delta)*1000L; + } + QString cpapcode=Codes[(int)code]; + //EventDataType PS; + tt=t; + cnt++; + int fc=0; + + switch (code) { + case 0x00: // Unknown (ASV Pressure value) + // offset? + data[0]=buffer[pos++]; + fc++; + if (!buffer[pos-1]) { // WTH??? + data[1]=buffer[pos++]; + fc++; + } + if (!buffer[pos-1]) { + data[2]=buffer[pos++]; + fc++; + } + break; + case 0x01: // Unknown + if (!Code[1]) { + if (!(Code[1]=session->AddEventList(cpapcode,EVL_Event,0.1))) return false; + } + Code[1]->AddEvent(t,0); + break; + + case 0x02: // Pressure + data[0]=buffer[pos++]; + if (!Code[2]) { + if (!(Code[2]=session->AddEventList(cpapcode,EVL_Event,0.1))) return false; + } + Code[2]->AddEvent(t,data[0]); + break; + case 0x04: // Pressure Pulse + data[0]=buffer[pos++]; + if (!Code[3]) { + if (!(Code[3]=session->AddEventList(cpapcode,EVL_Event))) return false; + } + Code[3]->AddEvent(t,data[0]); + break; + + case 0x05: + data[0]=buffer[pos++]; + tt-=qint64(data[0])*1000L; // Subtract Time Offset + if (!Code[4]) { + if (!(Code[4]=session->AddEventList(cpapcode,EVL_Event))) return false; + } + Code[4]->AddEvent(tt,data[0]); + break; + + case 0x06: + data[0]=buffer[pos++]; + tt-=qint64(data[0])*1000L; // Subtract Time Offset + if (!Code[5]) { + if (!(Code[5]=session->AddEventList(cpapcode,EVL_Event))) return false; + } + Code[5]->AddEvent(tt,data[0]); + break; + case 0x07: + data[0]=buffer[pos++]; + tt-=qint64(data[0])*1000L; // Subtract Time Offset + if (!Code[6]) { + if (!(Code[6]=session->AddEventList(cpapcode,EVL_Event))) return false; + } + Code[6]->AddEvent(tt,data[0]); + break; + case 0x08: // ASV Codes + data[0]=buffer[pos++]; + tt-=qint64(data[0])*1000L; // Subtract Time Offset + if (!Code[10]) { + if (!(Code[10]=session->AddEventList(cpapcode,EVL_Event))) return false; + } + Code[10]->AddEvent(tt,data[0]); + break; + case 0x09: // ASV Codes + data[0]=buffer[pos++]; + tt-=qint64(data[0])*1000L; // Subtract Time Offset + if (!Code[11]) { + if (!(Code[11]=session->AddEventList(cpapcode,EVL_Event))) return false; + } + Code[11]->AddEvent(tt,data[0]); + + break; + + case 0x0a: + data[0]=buffer[pos++]; + tt-=qint64(data[0])*1000L; // Subtract Time Offset + if (!Code[7]) { + if (!(Code[7]=session->AddEventList(cpapcode,EVL_Event))) return false; + } + Code[7]->AddEvent(tt,data[0]); + break; + + case 0x0c: + data[0]=buffer[pos++]; + tt-=qint64(data[0])*1000L; // Subtract Time Offset + if (!Code[8]) { + if (!(Code[8]=session->AddEventList(cpapcode,EVL_Event))) return false; + } + Code[8]->AddEvent(tt,data[0]); + break; + + + case 0x0b: // Cheyne Stokes + data[0]=((unsigned char *)buffer)[pos+1]<<8 | ((unsigned char *)buffer)[pos]; + //data[0]*=2; + pos+=2; + data[1]=((unsigned char *)buffer)[pos]; //|buffer[pos+1] << 8 + pos+=1; + //tt-=delta; + tt-=qint64(data[1])*1000L; + if (!Code[9]) { + if (!(Code[9]=session->AddEventList(cpapcode,EVL_Event))) + return false; + } + Code[9]->AddEvent(tt,data[0]); + //session->AddEvent(new Event(tt,cpapcode, data[0], data, 2)); + break; + case 0x0d: // All the other ASV graph stuff. + if (!Code[12]) { + if (!(Code[12]=session->AddEventList(CPAP_IPAP,EVL_Event,0.1))) return false; + if (!(Code[13]=session->AddEventList(CPAP_IPAPLo,EVL_Event,0.1))) return false; + if (!(Code[14]=session->AddEventList(CPAP_IPAPHi,EVL_Event,0.1))) return false; + if (!(Code[15]=session->AddEventList(CPAP_LeakTotal,EVL_Event))) return false; + if (!(Code[16]=session->AddEventList(CPAP_RespRate,EVL_Event))) return false; + if (!(Code[17]=session->AddEventList(CPAP_PTB,EVL_Event))) return false; + + if (!(Code[18]=session->AddEventList(CPAP_MinuteVent,EVL_Event))) return false; + if (!(Code[19]=session->AddEventList(CPAP_TidalVolume,EVL_Event))) return false; + if (!(Code[20]=session->AddEventList(CPAP_Snore,EVL_Event))) return false; + if (!(Code[22]=session->AddEventList(CPAP_EPAP,EVL_Event,0.1))) return false; + if (!(Code[23]=session->AddEventList(CPAP_PS,EVL_Event))) return false; + } + Code[12]->AddEvent(t,data[0]=buffer[pos++]); // IAP + Code[13]->AddEvent(t,buffer[pos++]); // IAP Low + Code[14]->AddEvent(t,buffer[pos++]); // IAP High + Code[15]->AddEvent(t,buffer[pos++]); // LEAK + Code[16]->AddEvent(t,buffer[pos++]); // Breaths Per Minute + Code[17]->AddEvent(t,buffer[pos++]); // Patient Triggered Breaths + Code[18]->AddEvent(t,buffer[pos++]); // Minute Ventilation + tmp=buffer[pos++]*10.0; + Code[19]->AddEvent(t,tmp); // Tidal Volume + Code[20]->AddEvent(t,data[2]=buffer[pos++]); // Snore + if (data[2]>0) { + if (!Code[21]) { + if (!(Code[21]=session->AddEventList("VSnore",EVL_Event))) + return false; + } + Code[21]->AddEvent(t,0); //data[2]); // VSnore + } + Code[22]->AddEvent(t,data[1]=buffer[pos++]); // EPAP + Code[23]->AddEvent(t,data[0]-data[1]); // Pressure Support + break; + case 0x03: // BIPAP Pressure + qDebug() << "0x03 Observed in ASV data!!????"; + + data[0]=buffer[pos++]; + data[1]=buffer[pos++]; +// data[0]/=10.0; +// data[1]/=10.0; +// session->AddEvent(new Event(t,CPAP_EAP, 0, data, 1)); +// session->AddEvent(new Event(t,CPAP_IAP, 0, &data[1], 1)); + break; + case 0x11: // Not Leak Rate + qDebug() << "0x11 Observed in ASV data!!????"; + //if (!Code[24]) { + // Code[24]=new EventList(cpapcode,EVL_Event); + //} + //Code[24]->AddEvent(t,buffer[pos++]); + break; + case 0x0e: // Unknown + qDebug() << "0x0E Observed in ASV data!!????"; + data[0]=buffer[pos++]; // << 8) | buffer[pos]; + //session->AddEvent(new Event(t,cpapcode, 0, data, 1)); + break; + + case 0x10: // Unknown + qDebug() << "0x10 Observed in ASV data!!????"; + data[0]=buffer[pos++]; // << 8) | buffer[pos]; + data[1]=buffer[pos++]; + data[2]=buffer[pos++]; + //session->AddEvent(new Event(t,cpapcode, 0, data, 3)); + break; + case 0x0f: + qDebug() << "0x0f Observed in ASV data!!????"; + + data[0]=buffer[pos+1]<<8 | buffer[pos]; + pos+=2; + data[1]=buffer[pos]; //|buffer[pos+1] << 8 + pos+=1; + tt-=qint64(data[1])*1000L; + //session->AddEvent(new Event(tt,cpapcode, 0, data, 2)); + break; + case 0x12: // Summary + qDebug() << "0x12 Observed in ASV data!!????"; + data[0]=buffer[pos++]; + data[1]=buffer[pos++]; + data[2]=buffer[pos+1]<<8 | buffer[pos]; + pos+=2; + //session->AddEvent(new Event(t,cpapcode, 0, data,3)); + break; + default: // ERROR!!! + qWarning() << "Some new fandangled PRS1 code detected " << hex << int(code) << " at " << pos-1; + badcode=true; + break; + } + if (badcode) { + int i=5; + break; + } + } + session->updateLast(t); + session->m_cnt.clear(); + session->m_cph.clear(); + return true; + +} + +bool PRS1Loader::Parse002(Machine *mach, qint32 sequence, quint32 timestamp, unsigned char *buffer, quint16 size) +{ + if (!new_sessions.contains(sequence)) + return false; + + unsigned char code; + EventList * Code[0x20]={0}; + EventDataType data[10],tmp; + int cnt=0; + short delta; + int tdata; + quint64 pos; + qint64 t=qint64(timestamp)*1000L,tt; + + Session *session=new_sessions[sequence]; + session->updateFirst(t); + + for (pos=0;pos0x12) { + qDebug() << "Illegal PRS1 code in" << sequence << hex << int(code) << " appeared at " << hex << pos; return false; } - delta=0; if (code!=0x12) { delta=buffer[pos+1] << 8 | buffer[pos]; pos+=2; t+=qint64(delta)*1000L; tt=t; } - cnt++; switch (code) { @@ -758,274 +984,213 @@ bool PRS1Loader::Parse002(Session *session,unsigned char *buffer,int size,qint64 break; default: // ERROR!!! - qWarning() << "Some new fandangled PRS1 code detected " << hex << int(code) << " at " << pos+15; + qWarning() << "Some new fandangled PRS1 code detected in" << sequence << hex << int(code) << " at " << pos-1; return false; } } session->updateLast(t); - + session->m_cnt.clear(); + session->m_cph.clear(); + session->m_lastchan.clear(); + session->m_firstchan.clear(); return true; } -bool PRS1Loader::Parse002ASV(Session *session,unsigned char *buffer,int size,qint64 timestamp) + +bool PRS1Loader::ParseWaveform(Machine *mach, qint32 sequence, quint32 timestamp, unsigned char *data, quint16 size, quint16 duration, quint16 num_signals, quint16 interleave, quint8 sample_format) { - QString Codes[]={ - PRS1_00, PRS1_01, CPAP_Pressure, CPAP_EPAP, CPAP_PressurePulse, CPAP_Obstructive, - CPAP_ClearAirway, CPAP_Hypopnea, PRS1_08, CPAP_FlowLimit, PRS1_0A, CPAP_CSR, - PRS1_0C, CPAP_VSnore, PRS1_0E, PRS1_0F, PRS1_10, - CPAP_LeakTotal, PRS1_12 - }; + if (!new_sessions.contains(sequence)) + return false; - int ncodes=sizeof(Codes)/sizeof(QString); + qint64 t=qint64(timestamp)*1000L; + Session *session=new_sessions[sequence]; + + if (num_signals==1) { + session->updateFirst(t); + double d=duration*1000; + EventDataType rate=d / EventDataType(size); + EventList *ev=session->AddEventList(CPAP_FlowRate,EVL_Waveform,1.0,0.00,0,0,rate); + ev->AddWaveform(t,(char *)data,size,qint64(duration)*1000L); + session->updateLast(t+qint64(duration)*1000L); + + } + +} + +bool PRS1Loader::OpenFile(Machine *mach, QString filename) +{ + int sequence,version; + quint32 timestamp; + qint64 pos; + unsigned char ext,htype,sum; + unsigned char *header,*data; + int chunk,hl,lasthl; + quint16 size,datasize,c16,crc; + + // waveform stuff + vector whl; + quint16 interleave,duration,num_signals; + quint8 sample_format; + + QFile f(filename); + if (!f.exists()) + return false; + + if (!f.open(QIODevice::ReadOnly)) + return false; + + qint64 filesize=f.size(); + if (f.read((char *)m_buffer,filesize)2) { + qWarning() << "More than 2 Waveforms in " << filename; + return false; + } + long pos2=0x14+(num_signals-1)*3; + // add the in reverse... + for (int i=0;i0) && (pos+size Code; EventList * Code[0x20]={NULL}; - //for (int i=0;i<0x20;i++) Code[i]=NULL; + + EventDataType data[10]; session->updateFirst(timestamp); - EventDataType data[10],tmp; - //qint64 start=timestamp; qint64 t=timestamp; qint64 tt; int pos=0; - int cnt=0; - short delta;//,duration; - QDateTime d; while (pos=ncodes) { - qDebug() << "Illegal PRS1 code " << hex << int(code) << " appeared at " << hex << pos+16; - return false; + if (code>0x12) { } - if (code==0) { - } else + delta=0; if (code!=0x12) { - delta=buffer[pos]; - //duration=buffer[pos+1]; - //delta=buffer[pos+1] << 8 | buffer[pos]; + delta=buffer[pos+1] << 8 | buffer[pos]; pos+=2; t+=qint64(delta)*1000L; + tt=t; } - QString cpapcode=Codes[(int)code]; - //EventDataType PS; - tt=t; - cnt++; - int fc=0; - switch (code) { - case 0x00: // Unknown (ASV Pressure value) - // offset? - data[0]=buffer[pos++]; - fc++; - if (!buffer[pos-1]) { // WTH??? - data[1]=buffer[pos++]; - fc++; - } - if (!buffer[pos-1]) { - data[2]=buffer[pos++]; - fc++; - } - break; - case 0x01: // Unknown - if (!Code[1]) { - if (!(Code[1]=session->AddEventList(cpapcode,EVL_Event,0.1))) return false; - } - Code[1]->AddEvent(t,0); - break; - - case 0x02: // Pressure - data[0]=buffer[pos++]; - if (!Code[2]) { - if (!(Code[2]=session->AddEventList(cpapcode,EVL_Event,0.1))) return false; - } - Code[2]->AddEvent(t,data[0]); - break; - case 0x04: // Pressure Pulse - data[0]=buffer[pos++]; - if (!Code[3]) { - if (!(Code[3]=session->AddEventList(cpapcode,EVL_Event))) return false; - } - Code[3]->AddEvent(t,data[0]); - break; - - case 0x05: - data[0]=buffer[pos++]; - tt-=qint64(data[0])*1000L; // Subtract Time Offset - if (!Code[4]) { - if (!(Code[4]=session->AddEventList(cpapcode,EVL_Event))) return false; - } - Code[4]->AddEvent(tt,data[0]); - break; - - case 0x06: - data[0]=buffer[pos++]; - tt-=qint64(data[0])*1000L; // Subtract Time Offset - if (!Code[5]) { - if (!(Code[5]=session->AddEventList(cpapcode,EVL_Event))) return false; - } - Code[5]->AddEvent(tt,data[0]); - break; - case 0x07: - data[0]=buffer[pos++]; - tt-=qint64(data[0])*1000L; // Subtract Time Offset - if (!Code[6]) { - if (!(Code[6]=session->AddEventList(cpapcode,EVL_Event))) return false; - } - Code[6]->AddEvent(tt,data[0]); - break; - case 0x08: // ASV Codes - data[0]=buffer[pos++]; - tt-=qint64(data[0])*1000L; // Subtract Time Offset - if (!Code[10]) { - if (!(Code[10]=session->AddEventList(cpapcode,EVL_Event))) return false; - } - Code[10]->AddEvent(tt,data[0]); - break; - case 0x09: // ASV Codes - data[0]=buffer[pos++]; - tt-=qint64(data[0])*1000L; // Subtract Time Offset - if (!Code[11]) { - if (!(Code[11]=session->AddEventList(cpapcode,EVL_Event))) return false; - } - Code[11]->AddEvent(tt,data[0]); - - break; - - case 0x0a: - data[0]=buffer[pos++]; - tt-=qint64(data[0])*1000L; // Subtract Time Offset - if (!Code[7]) { - if (!(Code[7]=session->AddEventList(cpapcode,EVL_Event))) return false; - } - Code[7]->AddEvent(tt,data[0]); - break; - - case 0x0c: - data[0]=buffer[pos++]; - tt-=qint64(data[0])*1000L; // Subtract Time Offset - if (!Code[8]) { - if (!(Code[8]=session->AddEventList(cpapcode,EVL_Event))) return false; - } - Code[8]->AddEvent(tt,data[0]); - break; - - - case 0x0b: // Cheyne Stokes - data[0]=((unsigned char *)buffer)[pos+1]<<8 | ((unsigned char *)buffer)[pos]; - //data[0]*=2; - pos+=2; - data[1]=((unsigned char *)buffer)[pos]; //|buffer[pos+1] << 8 - pos+=1; - //tt-=delta; - tt-=qint64(data[1])*1000L; - if (!Code[9]) { - if (!(Code[9]=session->AddEventList(cpapcode,EVL_Event))) return false; - } - Code[9]->AddEvent(tt,data[0]); - //session->AddEvent(new Event(tt,cpapcode, data[0], data, 2)); - break; - case 0x0d: // All the other ASV graph stuff. - if (!Code[12]) { - if (!(Code[12]=session->AddEventList(CPAP_IPAP,EVL_Event,0.1))) return false; - if (!(Code[13]=session->AddEventList(CPAP_IPAPLo,EVL_Event,0.1))) return false; - if (!(Code[14]=session->AddEventList(CPAP_IPAPHi,EVL_Event,0.1))) return false; - if (!(Code[15]=session->AddEventList(CPAP_LeakTotal,EVL_Event))) return false; - if (!(Code[16]=session->AddEventList(CPAP_RespRate,EVL_Event))) return false; - if (!(Code[17]=session->AddEventList(CPAP_PTB,EVL_Event))) return false; - - if (!(Code[18]=session->AddEventList(CPAP_MinuteVent,EVL_Event))) return false; - if (!(Code[19]=session->AddEventList(CPAP_TidalVolume,EVL_Event))) return false; - if (!(Code[20]=session->AddEventList(CPAP_Snore,EVL_Event))) return false; - if (!(Code[22]=session->AddEventList(CPAP_EPAP,EVL_Event,0.1))) return false; - if (!(Code[23]=session->AddEventList(CPAP_PS,EVL_Event))) return false; - } - Code[12]->AddEvent(t,data[0]=buffer[pos++]); // IAP - Code[13]->AddEvent(t,buffer[pos++]); // IAP Low - Code[14]->AddEvent(t,buffer[pos++]); // IAP High - Code[15]->AddEvent(t,buffer[pos++]); // LEAK - Code[16]->AddEvent(t,buffer[pos++]); // Breaths Per Minute - Code[17]->AddEvent(t,buffer[pos++]); // Patient Triggered Breaths - Code[18]->AddEvent(t,buffer[pos++]); // Minute Ventilation - tmp=buffer[pos++]*10.0; - Code[19]->AddEvent(t,tmp); // Tidal Volume - Code[20]->AddEvent(t,data[2]=buffer[pos++]); // Snore - if (data[2]>0) { - if (!Code[21]) { - if (!(Code[21]=session->AddEventList("VSnore",EVL_Event))) return false; - } - Code[21]->AddEvent(t,0); //data[2]); // VSnore - } - Code[22]->AddEvent(t,data[1]=buffer[pos++]); // EPAP - Code[23]->AddEvent(t,data[0]-data[1]); // Pressure Support - break; - case 0x03: // BIPAP Pressure - qDebug() << "0x03 Observed in ASV data!!????"; - - data[0]=buffer[pos++]; - data[1]=buffer[pos++]; - /*data[0]/=10.0; - data[1]/=10.0; - session->AddEvent(new Event(t,CPAP_EAP, 0, data, 1)); - session->AddEvent(new Event(t,CPAP_IAP, 0, &data[1], 1)); */ - break; - case 0x11: // Not Leak Rate - qDebug() << "0x11 Observed in ASV data!!????"; - //if (!Code[24]) { - // Code[24]=new EventList(cpapcode,EVL_Event); - //} - //Code[24]->AddEvent(t,buffer[pos++]); - break; - case 0x0e: // Unknown - qDebug() << "0x0E Observed in ASV data!!????"; - data[0]=buffer[pos++]; // << 8) | buffer[pos]; - //session->AddEvent(new Event(t,cpapcode, 0, data, 1)); - break; - - case 0x10: // Unknown - qDebug() << "0x10 Observed in ASV data!!????"; - data[0]=buffer[pos++]; // << 8) | buffer[pos]; - data[1]=buffer[pos++]; - data[2]=buffer[pos++]; - //session->AddEvent(new Event(t,cpapcode, 0, data, 3)); - break; - case 0x0f: - qDebug() << "0x0f Observed in ASV data!!????"; - - data[0]=buffer[pos+1]<<8 | buffer[pos]; - pos+=2; - data[1]=buffer[pos]; //|buffer[pos+1] << 8 - pos+=1; - tt-=qint64(data[1])*1000L; - //session->AddEvent(new Event(tt,cpapcode, 0, data, 2)); - break; - case 0x12: // Summary - qDebug() << "0x12 Observed in ASV data!!????"; - data[0]=buffer[pos++]; - data[1]=buffer[pos++]; - data[2]=buffer[pos+1]<<8 | buffer[pos]; - pos+=2; - //session->AddEvent(new Event(t,cpapcode, 0, data,3)); - break; - default: - // ERROR!!! - qWarning() << "Some new fandangled PRS1 code detected " << hex << int(code) << " at " << pos+15; - return false; - } - } session->updateLast(t); + return true; } - -bool PRS1Loader::OpenEvents(Session *session,QString filename) +bool PRS1Loader::Parse002ASV(Session *session,unsigned char *buffer,int size,qint64 timestamp,long fpos) { - int size,sequence,seconds,br,version; - qint64 timestamp; - unsigned char header[24]; // use m_buffer? - unsigned char ext,htype; +} +bool PRS1Loader::OpenSummary(Session *session,QString filename) // Read .001/.000 file +{ + int size,seconds,br,htype,version,sequence; + qint64 timestamp; + unsigned char header[24]; + unsigned char ext,sum; + + //qDebug() << "Opening PRS1 Summary " << filename; QFile f(filename); + if (!f.open(QIODevice::ReadOnly)) + return false; + + if (!f.exists()) return false; int hl=16; @@ -1039,28 +1204,41 @@ bool PRS1Loader::OpenEvents(Session *session,QString filename) sequence=(header[10] << 24) | (header[9] << 16) | (header[8] << 8) | header[7]; timestamp=(header[14] << 24) | (header[13] << 16) | (header[12] << 8) | header[11]; size=(header[2] << 8) | header[1]; - ext=header[6]; + ext=header[6]; // 0 = compliance only, 1 = has data + htype=header[3]; // 00 = normal // 01=waveform // could be a bool? - version=header[4];// | header[4]; - - htype=htype; + version=header[4]; sequence=sequence; - if (ext!=PRS1_EVENT_FILE) // 2 == Event file - return false; + version=version; // don't need it here? - //size|=(header[3]<<16) | (header[4]<<24); // the jury is still out on the 32bitness of one. doesn't matter here anyway. + htype=htype; // shut the warning up.. this is useless. + + if (ext!=PRS1_SUMMARY_FILE) + return false; size-=(hl+2); - unsigned char sum=0; + // Calculate header checksum and compare to verify header + sum=0; for (int i=0; iset_first(date); + + double max; + session->settings[CPAP_PressureMin]=(EventDataType)buffer[0x03]/10.0; + session->settings[CPAP_PressureMax]=max=(EventDataType)buffer[0x04]/10.0; + int offset=0; + if (buffer[0x05]!=0) { // This is a time value for ASV stuff + // non zero adds extra fields.. + offset=4; + } + + session->settings[CPAP_RampTime]=(int)buffer[offset+0x06]; // Minutes. Convert to seconds/hours here? + session->settings[CPAP_RampPressure]=(EventDataType)buffer[offset+0x07]/10.0; + + if (max>0) { // Ignoring bipap until I see some more data. + session->settings[CPAP_Mode]=(int)MODE_APAP; + } else session->settings[CPAP_Mode]=(int)MODE_CPAP; + + // This is incorrect.. + if (buffer[offset+0x08] & 0x80) { // Flex Setting + if (buffer[offset+0x08] & 0x08) { + if (max>0) session->settings[PRS1_FlexMode]=(int)PR_AFLEX; + else session->settings[PRS1_FlexMode]=(int)PR_CFLEXPLUS; + } else session->settings[PRS1_FlexMode]=(int)PR_CFLEX; + } else session->settings[PRS1_FlexMode]=(int)PR_NONE; + + session->settings["FlexSet"]=(int)buffer[offset+0x08] & 3; + session->settings["HumidSet"]=(int)buffer[offset+0x09]&0x0f; + session->settings["HumidStat"]=(buffer[offset+0x09]&0x80)==0x80; + session->settings["SysLock"]=(buffer[offset+0x0a]&0x80)==0x80; + session->settings["SysOneResistStat"]=(buffer[offset+0x0a]&0x40)==0x40; + session->settings["SysOneResistSet"]=(int)buffer[offset+0x0a]&7; + session->settings["HoseDiam"]=((buffer[offset+0x0a]&0x08)?"15mm":"22mm"); + session->settings["AutoOff"]=(buffer[offset+0x0c]&0x10)==0x10; + session->settings["MaskAlert"]=(buffer[offset+0x0c]&0x08)==0x08; + session->settings["ShowAHI"]=(buffer[offset+0x0c]&0x04)==0x04; + + unsigned duration=buffer[offset+0x14] | (buffer[0x15] << 8); + //session->settings[CPAP_Duration]=(int)duration; + //qDebug() << "ID: " << session->session() << " " << duration; + //float hours=float(duration)/3600.0; + //session->set_hours(hours); + if (!duration) + return false; + + session->set_last(date+qint64(duration)*1000L); + //session->settings[PRS1_PressureMinAchieved]=buffer[offset+0x16]/10.0; + //session->settings[PRS1_PressureMaxAchieved]=buffer[offset+0x17]/10.0; + //session->settings[PRS1_PressureAvg]=buffer[offset+0x18]/10.0; + //session->settings[PRS1_Pressure90]=buffer[offset+0x19]/10.0; + + if (max==0) { + // session->settings[PRS1_PressureAvg]=session->settings[PRS1_PressureMin]; } - if (version==0) { - if (!Parse002(session,buffer,size,timestamp*1000L)) { - qDebug() << "Couldn't Parse PRS1 Event File " << filename; - return false; - } - } else if (version==5) { - if (!Parse002ASV(session,buffer,size,timestamp*1000L)) { - qDebug() << "Couldn't Parse PRS1 (ASV) Event File " << filename; - return false; - } - } + +// if (size==0x4d) { + +// session->settings[CPAP_Obstructive]=(int)buffer[offset+0x1C] | (buffer[offset+0x1D] << 8); +// session->settings[CPAP_ClearAirway]=(int)buffer[offset+0x20] | (buffer[offset+0x21] << 8); +// session->settings[CPAP_Hypopnea]=(int)buffer[offset+0x2A] | (buffer[offset+0x2B] << 8); +// session->settings[CPAP_RERA]=(int)buffer[offset+0x2E] | (buffer[offset+0x2F] << 8); +// session->settings[CPAP_FlowLimit]=(int)buffer[offset+0x30] | (buffer[offset+0x31] << 8); +// } return true; } -struct WaveHeaderList { - quint16 interleave; - quint8 sample_format; - WaveHeaderList(quint16 i,quint8 f){ interleave=i; sample_format=f; } -}; - -bool PRS1Loader::OpenWaveforms(Session *session,QString filename) +bool PRS1Loader::OpenEvents(Session *session,QString filename) { + int size,sequence,seconds,br,version; + qint64 timestamp; + unsigned char header[24]; // use m_buffer? + unsigned char ext,htype; + + QFile f(filename); + if (!f.open(QIODevice::ReadOnly)) + return false; + + int hl=16; + int chunk=0; + + // Who'd of thought this chunked.. it does on certain ASV machines.. + + long pos=0; + do { + br=f.read((char *)header,hl); + + if (header[0]!=PRS1_MAGIC_NUMBER) { + if (chunk==0) + return false; + break; + } + sequence=size=timestamp=seconds=ext=0; + sequence=(header[10] << 24) | (header[9] << 16) | (header[8] << 8) | header[7]; + timestamp=(header[14] << 24) | (header[13] << 16) | (header[12] << 8) | header[11]; + size=(header[2] << 8) | header[1]; + ext=header[6]; + htype=header[3]; // 00 = normal // 01=waveform // could be a bool? + version=header[4];// == 5 + + htype=htype; + sequence=sequence; + if (ext!=PRS1_EVENT_FILE) { // 2 == Event file + if (chunk==0) + return false; + break; // just end and take what we got.. + } + + //size|=(header[3]<<16) | (header[4]<<24); // the jury is still out on the 32bitness of one. doesn't matter here anyway. + + size-=(hl+2); + + unsigned char sum=0; + for (int i=0; isetMax(120); a->setMin(-120); } else if (wc[i]==CPAP_MaskPressure) { - /* int v=ceil(a->max()/5); - a->setMax(v*5); - v=floor(a->min()/5); - a->setMin(v*5); */ +// int v=ceil(a->max()/5); +// a->setMax(v*5); +// v=floor(a->min()/5); +// a->setMin(v*5); } session->updateLast(start+(qint64(wdur[i])*1000L)); @@ -1289,10 +1605,10 @@ void InitModelMap() ModelMap[0x35]="RemStar Auto with A-Flex"; ModelMap[0x36]="RemStar BiPAP Pro with Bi-Flex"; ModelMap[0x37]="RemStar BiPAP Auto with Bi-Flex"; + ModelMap[0x38]="RemStar Plus :("; ModelMap[0x41]="BiPAP autoSV Advanced"; } - bool initialized=false; void PRS1Loader::Register() { diff --git a/SleepLib/loader_plugins/prs1_loader.h b/SleepLib/loader_plugins/prs1_loader.h index 7918ebe0..4e5f06fd 100644 --- a/SleepLib/loader_plugins/prs1_loader.h +++ b/SleepLib/loader_plugins/prs1_loader.h @@ -21,7 +21,7 @@ License: GPL //******************************************************************************************** // Please INCREMENT the following value when making changes to this loaders implementation. // -const int prs1_data_version=7; +const int prs1_data_version=8; // //******************************************************************************************** @@ -54,15 +54,27 @@ protected: QHash PRS1List; int OpenMachine(Machine *m,QString path,Profile *profile); bool ParseProperties(Machine *m,QString filename); - bool OpenSummary(Session *session,QString filename); - bool OpenEvents(Session *session,QString filename); - bool OpenWaveforms(Session *session,QString filename); - bool Parse002(Session *session,unsigned char *buffer,int size,qint64 timestamp); - bool Parse002ASV(Session *session,unsigned char *buffer,int size,qint64 timestamp); - void CalcRespiratoryRate(Session *); - void filterFlow(EventList *in, EventList *out); + //bool OpenSummary(Session *session,QString filename); + //bool OpenEvents(Session *session,QString filename); + bool OpenWaveforms(SessionID sid, QString filename); + bool ParseWaveform(Machine *mach, qint32 sequence, quint32 timestamp, unsigned char *data, quint16 size, quint16 duration, quint16 num_signals, quint16 interleave, quint8 sample_format); + bool ParseSummary(Machine *mach, qint32 sequence, quint32 timestamp, unsigned char *data, quint16 size, char version); + bool Parse002(Machine *mach, qint32 sequence, quint32 timestamp, unsigned char *data, quint16 size); + bool Parse002v5(Machine *mach, qint32 sequence, quint32 timestamp, unsigned char *data, quint16 size); + + bool OpenFile(Machine *mach, QString filename); + //bool Parse002(Session *session,unsigned char *buffer,int size,qint64 timestamp,long fpos); + //bool Parse002ASV(Session *session,unsigned char *buffer,int size,qint64 timestamp,long fpos); unsigned char * m_buffer; QHash extra_session; + QHash new_sessions; + qint32 summary_duration; +}; + +struct WaveHeaderList { + quint16 interleave; + quint8 sample_format; + WaveHeaderList(quint16 i,quint8 f){ interleave=i; sample_format=f; } }; #endif // PRS1LOADER_H diff --git a/SleepLib/machine.h b/SleepLib/machine.h index d75c19ac..c02d5b53 100644 --- a/SleepLib/machine.h +++ b/SleepLib/machine.h @@ -7,6 +7,7 @@ #ifndef MACHINE_H #define MACHINE_H + #include #include #include @@ -26,6 +27,7 @@ #include "SleepLib/day.h" + class Day; class Session; class Profile; diff --git a/SleepLib/machine_common.h b/SleepLib/machine_common.h index d76cf83d..51e0b1ba 100644 --- a/SleepLib/machine_common.h +++ b/SleepLib/machine_common.h @@ -29,6 +29,8 @@ const quint32 magic=0xC73216AB; // Magic number for Sleepyhead Data Files.. Don' //const int max_number_event_fields=10; +enum SummaryType { ST_CNT, ST_SUM, ST_AVG, ST_WAVG, ST_90P, ST_MIN, ST_MAX, ST_CPH, ST_SPH, ST_FIRST, ST_LAST, ST_HOURS, ST_SESSIONS, ST_SETMIN, ST_SETAVG, ST_SETMAX, ST_SETWAVG, ST_SETSUM }; + enum MachineType { MT_UNKNOWN=0,MT_CPAP,MT_OXIMETER,MT_SLEEPSTAGE,MT_JOURNAL }; //void InitMapsWithoutAwesomeInitializerLists(); diff --git a/SleepLib/session.cpp b/SleepLib/session.cpp index c79f00cd..65f1d2ba 100644 --- a/SleepLib/session.cpp +++ b/SleepLib/session.cpp @@ -500,8 +500,10 @@ EventDataType Session::min(ChannelID id) QVector & evec=j.value(); bool first=true; - EventDataType min,t1; + EventDataType min=0,t1; for (int i=0;icount()==0) + continue; t1=evec[i]->min(); if (t1==0 && t1==evec[i]->max()) continue; if (first) { @@ -511,6 +513,9 @@ EventDataType Session::min(ChannelID id) if (min>t1) min=t1; } } + if (min>10000) { + int i=5; + } m_min[id]=min; return min; } @@ -528,8 +533,9 @@ EventDataType Session::max(ChannelID id) QVector & evec=j.value(); bool first=true; - EventDataType max,t1; + EventDataType max=0,t1; for (int i=0;icount()==0) continue; t1=evec[i]->max(); if (t1==0 && t1==evec[i]->min()) continue; if (first) { @@ -897,6 +903,9 @@ EventDataType Session::wavg(ChannelID id) s1+=i.key()*s0; s2+=s0; } + if (s2==0) { + return m_wavg[id]=0; + } double j=double(s1)/double(s2); EventDataType v=j*gain; if (v>32768*gain) { diff --git a/SleepLib/session.h b/SleepLib/session.h index cc950eb2..2a21ea25 100644 --- a/SleepLib/session.h +++ b/SleepLib/session.h @@ -18,7 +18,6 @@ //class EventList; class Machine; -enum SummaryType { ST_CNT, ST_SUM, ST_AVG, ST_WAVG, ST_90P, ST_MIN, ST_MAX, ST_CPH, ST_SPH, ST_FIRST, ST_LAST, ST_HOURS, ST_SESSIONS, ST_SETMIN, ST_SETAVG, ST_SETMAX, ST_SETWAVG, ST_SETSUM }; class Session { diff --git a/daily.cpp b/daily.cpp index e296c6c1..9fb1cdd4 100644 --- a/daily.cpp +++ b/daily.cpp @@ -366,7 +366,8 @@ void Daily::ReloadGraphs() if (previous_date.isValid()) { d=previous_date; Unload(d); - } else d=PROFILE.LastDay(); + } //else + d=PROFILE.LastDay(); if (!d.isValid()) { d=ui->calendar->selectedDate(); } @@ -646,7 +647,14 @@ void Daily::Load(QDate date) CPAPMode mode=MODE_UNKNOWN; PRTypes pr; QString a; + bool isBrick=false; if (cpap) { + if (GraphView->isEmpty()) { + GraphView->setEmptyText("Brick Machine :("); + isBrick=true; + } else { + GraphView->setEmptyText("No Data"); + } mode=(CPAPMode)cpap->settings_max(CPAP_Mode); pr=(PRTypes)cpap->settings_max(PRS1_FlexMode); if (pr==PR_NONE) @@ -698,107 +706,114 @@ void Daily::Load(QDate date) .arg(QString().sprintf("%02i:%02i:%02i",h,m,s)); QString cs; - if (cpap->machine->GetClass()=="ResMed") { - cs="4 width='100%' align=center>"; - } else cs="2 width='50%'>"; - html+="" - ""+tr("AHI")+""+QString().sprintf("%.2f",ahi)+"\n" - " "+tr("Hypopnea")+""+QString().sprintf("%.2f",hi)+"\n"; - if (cpap->machine->GetClass()=="ResMed") { - html+=" "+tr("Unspecified Apnea")+""+QString().sprintf("%.2f",uai)+"\n"; - } - html+=" "+tr("Obstructive")+""+QString().sprintf("%.2f",oai)+"\n" - " "+tr("Clear Airway")+""+QString().sprintf("%.2f",cai)+"\n" - ""; - - if (cpap->machine->GetClass()=="PRS1") { - html+="" - "\n" - "\n" - "\n" - "\n" - "
 "+tr("RERA")+""+QString().sprintf("%.2f",rei)+"
 "+tr("Flow Limit")+""+a.sprintf("%.2f",fli)+"
 "+tr("Vsnore")+""+QString().sprintf("%.2f",vsi)+"
 "+tr("PB/CSR")+""+QString().sprintf("%.2f",csr)+"%
"; - } else if (cpap->machine->GetClass()=="Intellipap") { - html+="" - "\n" - "\n" - "\n" - "\n" + if (!isBrick) { + if (cpap->machine->GetClass()=="ResMed") { + cs="4 width='100%' align=center>"; + } else cs="2 width='50%'>"; + html+="\n" + "\n"; + if (cpap->machine->GetClass()=="ResMed") { + html+="\n"; + } + html+="\n" + "\n" "
 "+tr("NRI")+""+QString().sprintf("%.2f",nri)+"
 "+tr("Leak Idx")+""+a.sprintf("%.2f",lki)+"
 "+tr("V.Snore")+""+QString().sprintf("%.2f",vsi)+"
 "+tr("Exh. Puff")+""+QString().sprintf("%.2f",exp)+"
" + "
"+tr("AHI")+""+QString().sprintf("%.2f",ahi)+"
 "+tr("Hypopnea")+""+QString().sprintf("%.2f",hi)+"
 "+tr("Unspecified Apnea")+""+QString().sprintf("%.2f",uai)+"
 "+tr("Obstructive")+""+QString().sprintf("%.2f",oai)+"
 "+tr("Clear Airway")+""+QString().sprintf("%.2f",cai)+"
"; - } + if (cpap->machine->GetClass()=="PRS1") { + html+="" + "\n" + "\n" + "\n" + "\n" + "
 "+tr("RERA")+""+QString().sprintf("%.2f",rei)+"
 "+tr("Flow Limit")+""+a.sprintf("%.2f",fli)+"
 "+tr("Vsnore")+""+QString().sprintf("%.2f",vsi)+"
 "+tr("PB/CSR")+""+QString().sprintf("%.2f",csr)+"%
"; + } else if (cpap->machine->GetClass()=="Intellipap") { + html+="" + "\n" + "\n" + "\n" + "\n" + "
 "+tr("NRI")+""+QString().sprintf("%.2f",nri)+"
 "+tr("Leak Idx")+""+a.sprintf("%.2f",lki)+"
 "+tr("V.Snore")+""+QString().sprintf("%.2f",vsi)+"
 "+tr("Exh. Puff")+""+QString().sprintf("%.2f",exp)+"
"; - - // Note, this may not be a problem since Qt bug 13622 was discovered - // as it only relates to text drawing, which the Pie chart does not do - // ^^ Scratch that.. pie now includes text.. - - if (PROFILE["EnableGraphSnapshots"].toBool()) { // AHI Pie Chart - if (ahi+rei+fli>0) { - html+="\n"; //"+tr("Event Breakdown")+"\n"; - //G_AHI->setFixedSize(gwwidth,120); - //mainwin->snapshotGraph()->setPrintScaleX(1); - //mainwin->snapshotGraph()->setPrintScaleY(1); - QPixmap pixmap=snapGV->renderPixmap(172,172); - QByteArray byteArray; - QBuffer buffer(&byteArray); // use buffer to store pixmap into byteArray - buffer.open(QIODevice::WriteOnly); - pixmap.save(&buffer, "PNG"); - html += "\n"; - } else { - html += "\n"; } - } - } - html+=""; - html+="\n"; - if (cpap || oxi) { - html+="\n"; - //html+=("\n"); - html+=(""); - ChannelID chans[]={ - CPAP_Pressure,CPAP_EPAP,CPAP_IPAP,CPAP_PS,CPAP_PTB, - CPAP_MinuteVent,CPAP_AHI, CPAP_RespRate, CPAP_RespEvent,CPAP_FLG, - CPAP_Leak, CPAP_LeakTotal, CPAP_Snore,CPAP_IE,CPAP_Ti,CPAP_Te, CPAP_TgMV, - CPAP_TidalVolume, OXI_Pulse, OXI_SPO2 - }; - int numchans=sizeof(chans)/sizeof(ChannelID); - int suboffset=0; - for (int i=0;ichannelHasData(code)) { - //if (code==CPAP_LeakTotal) suboffset=PROFILE["IntentionalLeak"].toDouble(); else suboffset=0; - QString tooltip=schema::channel[code].description(); - if (!schema::channel[code].units().isEmpty()) tooltip+=" ("+schema::channel[code].units()+")"; - html+=""; + if (PROFILE["EnableGraphSnapshots"].toBool()) { // AHI Pie Chart + if (ahi+rei+fli>0) { + html+="\n"; //\n"; + //G_AHI->setFixedSize(gwwidth,120); + //mainwin->snapshotGraph()->setPrintScaleX(1); + //mainwin->snapshotGraph()->setPrintScaleY(1); + QPixmap pixmap=snapGV->renderPixmap(172,172); + QByteArray byteArray; + QBuffer buffer(&byteArray); // use buffer to store pixmap into byteArray + buffer.open(QIODevice::WriteOnly); + pixmap.save(&buffer, "PNG"); + html += "\n"; + } else { + html += "\n"; + } } - if (oxi && oxi->channelHasData(code)) { - QString tooltip=schema::channel[code].description(); - if (!schema::channel[code].units().isEmpty()) tooltip+=" ("+schema::channel[code].units()+")"; - html+=""; - } - } + html+="

 
MinAvg90%Max
"+schema::channel[code].label()+""; - html+=""+a.sprintf("%.2f",cpap->min(code)-suboffset); - html+=""+a.sprintf("%.2f",cpap->wavg(code)-suboffset); - html+=""+a.sprintf("%.2f",cpap->p90(code)-suboffset); - html+=""+a.sprintf("%.2f",cpap->max(code)-suboffset); - html+="
"+tr("Event Breakdown")+"
"+schema::channel[code].label()+""; - html+=""+a.sprintf("%.2f",oxi->min(code)); - html+=""+a.sprintf("%.2f",oxi->wavg(code)); - html+=""+a.sprintf("%.2f",oxi->p90(code)); - html+=""+a.sprintf("%.2f",oxi->max(code)); - html+="
"; + html+="\n"; + if (cpap || oxi) { + html+="\n"; + + //html+=("\n"); + + html+=(""); + ChannelID chans[]={ + CPAP_Pressure,CPAP_EPAP,CPAP_IPAP,CPAP_PS,CPAP_PTB, + CPAP_MinuteVent,CPAP_AHI, CPAP_RespRate, CPAP_RespEvent,CPAP_FLG, + CPAP_Leak, CPAP_LeakTotal, CPAP_Snore,CPAP_IE,CPAP_Ti,CPAP_Te, CPAP_TgMV, + CPAP_TidalVolume, OXI_Pulse, OXI_SPO2 + }; + int numchans=sizeof(chans)/sizeof(ChannelID); + int suboffset=0; + for (int i=0;ichannelHasData(code)) { + //if (code==CPAP_LeakTotal) suboffset=PROFILE["IntentionalLeak"].toDouble(); else suboffset=0; + QString tooltip=schema::channel[code].description(); + if (!schema::channel[code].units().isEmpty()) tooltip+=" ("+schema::channel[code].units()+")"; + html+=""; + } + if (oxi && oxi->channelHasData(code)) { + QString tooltip=schema::channel[code].description(); + if (!schema::channel[code].units().isEmpty()) tooltip+=" ("+schema::channel[code].units()+")"; + html+=""; + } + } + } + } else { + html+=""; + html+="\n"; + html+="\n"; + html+="\n"; + } } else { html+=""; html+="\n"; } html+="

 
MinAvg90%Max
"+schema::channel[code].label()+""; + html+=""+a.sprintf("%.2f",cpap->min(code)-suboffset); + html+=""+a.sprintf("%.2f",cpap->wavg(code)-suboffset); + html+=""+a.sprintf("%.2f",cpap->p90(code)-suboffset); + html+=""+a.sprintf("%.2f",cpap->max(code)-suboffset); + html+="
"+schema::channel[code].label()+""; + html+=""+a.sprintf("%.2f",oxi->min(code)); + html+=""+a.sprintf("%.2f",oxi->wavg(code)); + html+=""+a.sprintf("%.2f",oxi->p90(code)); + html+=""+a.sprintf("%.2f",oxi->max(code)); + html+="

"+tr("BRICK :(")+"

Sorry, your machine does not record data.
Complain to your Equipment Provider!
 
"+tr("No data available")+"
 
"; - html+="\n"; + //html+="
\n"; if (cpap) { // if ((*profile)["EnableGraphSnapshots"].toBool()) { @@ -833,9 +848,10 @@ void Daily::Load(QDate date) pixmap.save(&buffer, "PNG"); html+="\n"; } */ + html+="

"; //} - html+="
"; + html+="
"; QDateTime fd,ld; bool corrupted_waveform=false; QString tooltip; @@ -893,7 +909,7 @@ void Daily::Load(QDate date) ui->bookmarkTable->setHorizontalHeaderLabels(sl); ui->weightSpinBox->setValue(0); ui->ouncesSpinBox->setValue(0); - ui->ZombieMeter->setValue(50); + ui->ZombieMeter->setValue(5); ui->BMI->display(0); ui->BMI->setVisible(false); ui->BMIlabel->setVisible(false); @@ -1024,7 +1040,7 @@ void Daily::Unload(QDate date) journal->settings["BMI"]=bmi; journal->SetChanged(true); } - if ((!journal->settings.contains("ZombieMeter") && (ui->ZombieMeter->value()!=50)) || (journal->settings["ZombieMeter"].toDouble(&ok)!=ui->ZombieMeter->value())) { + if ((!journal->settings.contains("ZombieMeter") && (ui->ZombieMeter->value()!=5)) || (journal->settings["ZombieMeter"].toDouble(&ok)!=ui->ZombieMeter->value())) { journal->settings["ZombieMeter"]=ui->ZombieMeter->value(); journal->SetChanged(true); } diff --git a/daily.ui b/daily.ui index d70e25af..b1341831 100644 --- a/daily.ui +++ b/daily.ui @@ -248,7 +248,7 @@ - 3 + 2 true @@ -572,8 +572,14 @@ + + 10 + + + 5 + - 50 + 5 Qt::Horizontal @@ -582,7 +588,7 @@ QSlider::TicksAbove - 5 + 1 diff --git a/docs/channels.xml b/docs/channels.xml index b7300eb4..e742fa4f 100644 --- a/docs/channels.xml +++ b/docs/channels.xml @@ -91,7 +91,7 @@ One id code per item - + diff --git a/mainwindow.cpp b/mainwindow.cpp index f459c0ca..38248962 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -557,7 +557,21 @@ void MainWindow::on_action_Screenshot_triggered() } void MainWindow::DelayedScreenshot() { +//#ifdef Q_WS_MAC +// CGImageRef windowImage = CGWindowListCreateImage(imageBounds, singleWindowListOptions, windowID, imageOptions); +// originalPixmap = new QPixmap(QPixmap::fromMacCGImageRef(windowImage)); + + +// CGImageRef img = CGDisplayCreateImageForRect(displayID, *rect); +// CFMutableDataRef imgData = CFDataCreateMutable(0, 0); +// CGImageDestinationRef imgDst = CGImageDestinationCreateWithData(imgData, kUTTypeJPEG2000, 1, 0); +// CGImageDestinationAddImage(imgDst, img, 0); +// CGImageDestinationFinalize(imgDst); +// CFRelease(imgDst); +// QPixmap pixmap=QPixmap::fromMacCGImageRef(img); +//#else QPixmap pixmap = QPixmap::grabWindow(this->winId()); +//#endif QString a=PREF.Get("{home}")+"/Screenshots"; QDir dir(a); if (!dir.exists()){ diff --git a/newprofile.ui b/newprofile.ui index 9da50078..0c242664 100644 --- a/newprofile.ui +++ b/newprofile.ui @@ -7,7 +7,7 @@ 0 0 607 - 392 + 395 @@ -24,7 +24,7 @@ - 2 + 3 @@ -591,7 +591,11 @@ p, li { white-space: pre-wrap; } - + + + 999.990000000000009 + +