From 8863523018e2ef2dc8eec8c020b85b5d369f7dc8 Mon Sep 17 00:00:00 2001 From: Mark Watkins Date: Sun, 15 Jan 2012 16:20:10 +1000 Subject: [PATCH] Initial PRS1 Unintentional Leaks hack for all CPAP modes. Minor crash fix to UserFlags calculations --- Graphs/gLineChart.cpp | 6 +- Graphs/gSegmentChart.cpp | 7 +- Graphs/gSummaryChart.cpp | 4 +- SleepLib/calcs.cpp | 530 +++++++++++++++++++++++++++++++++++---- UpdaterWindow.ui | 9 +- overview.cpp | 1 + 6 files changed, 500 insertions(+), 57 deletions(-) diff --git a/Graphs/gLineChart.cpp b/Graphs/gLineChart.cpp index 73ce27d9..2d2fa36c 100644 --- a/Graphs/gLineChart.cpp +++ b/Graphs/gLineChart.cpp @@ -338,9 +338,9 @@ void gLineChart::paint(gGraph & w,int left, int top, int width, int height) done=false; - if (!accel) { - lines->setSize(1.5); - } else lines->setSize(1); +// if (!accel) { + lines->setSize(1.5); +// } else lines->setSize(1); if (el.type()==EVL_Waveform) { // Waveform Plot if (idx>sam) idx-=sam; diff --git a/Graphs/gSegmentChart.cpp b/Graphs/gSegmentChart.cpp index a26cd343..41c4cbe4 100644 --- a/Graphs/gSegmentChart.cpp +++ b/Graphs/gSegmentChart.cpp @@ -212,12 +212,13 @@ void gTAPGraph::SetDay(Day *d) bool rfirst=true; //bool changed; EventDataType gain=1,offset=0; + QHash >::iterator ei; for (QVector::iterator s=m_day->begin();s!=m_day->end();s++) { if (!(*s)->enabled()) continue; - if ((*s)->eventlist.find(m_code)==(*s)->eventlist.end()) continue; - for (int q=0;q<(*s)->eventlist[m_code].size();q++) { - EventList &el=*(*s)->eventlist[m_code][q]; + if ((ei=(*s)->eventlist.find(m_code))==(*s)->eventlist.end()) continue; + for (int q=0;qsettings_avg(code); break; case ST_SETWAVG: tmp=day->settings_wavg(code); break; case ST_SETSUM: tmp=day->settings_sum(code); break; - default: break; + default: + tmp=0; + break; } if (suboffset>0) { tmp-=suboffset; diff --git a/SleepLib/calcs.cpp b/SleepLib/calcs.cpp index d8c09b69..ffa163fc 100644 --- a/SleepLib/calcs.cpp +++ b/SleepLib/calcs.cpp @@ -545,7 +545,10 @@ void FlowParser::calc(bool calcResp, bool calcTv, bool calcTi, bool calcTe, bool void FlowParser::flagEvents() { + if (!PROFILE.cpap->userEventFlagging()) return; + int numbreaths=breaths.size(); + if (numbreaths<5) return; EventDataType val,mx,mn; QVector br; @@ -577,7 +580,7 @@ void FlowParser::flagEvents() const EventDataType perc=0.6; int idx=float(br.size())*perc; - nth_element(br.begin(),br.begin()+idx,br.end()); + nth_element(br.begin(),br.begin()+idx,br.end()-1); EventDataType peak=br[idx];//*(br.begin()+idx); @@ -906,69 +909,512 @@ int calcAHIGraph(Session *session) return cnt; } +struct TimeValue { + TimeValue() { + time=0; + value=0; + } + TimeValue(qint64 t, EventStoreType v) { + time=t; + value=v; + } + TimeValue(const TimeValue & copy) { + time=copy.time; + value=copy.value; + } + qint64 time; + EventStoreType value; +}; +bool operator<(const TimeValue &p1, const TimeValue & p2) { + return p1.time < p2.time; +} + +struct zMaskProfile { +public: + zMaskProfile(MaskType type, QString name); + + void reset() { pressureleaks.clear(); } + void scanLeaks(Session * session); + void scanPressure(Session * session); + void updatePressureMin(); + void updateProfile(Session * session); + + QMap pressuremin; + + QVector Pressure; + +protected: + void scanLeakList(EventList * leak); + void scanPressureList(EventList * el); + + MaskType m_type; + QString m_name; + + + // QHash > + QHash > pressureleaks; + + //QHash pressurecount; + +}; +zMaskProfile::zMaskProfile(MaskType type, QString name) + :m_type(type), m_name(name) +{ +} + +void zMaskProfile::scanPressureList(EventList * el) +{ + qint64 start=el->first(); + int count=el->count(); + EventStoreType * dptr=el->rawData(); + EventStoreType * eptr=dptr+count; + quint32 * tptr=el->rawTime(); + qint64 time; + EventStoreType pressure; + + for (;dptr < eptr; dptr++) { + time=start+ *tptr++; + pressure=*dptr; + Pressure.push_back(TimeValue(time,pressure)); + } +} +void zMaskProfile::scanPressure(Session * session) +{ + Pressure.clear(); + + int prescnt=0; + EventStoreType pressure; + if (session->eventlist.contains(CPAP_Pressure)) { + prescnt=session->count(CPAP_Pressure); + Pressure.reserve(prescnt); + for (int j=0;jeventlist[CPAP_Pressure].size();j++) { + QVector & el=session->eventlist[CPAP_Pressure]; + for (int e=0;eeventlist.contains(CPAP_IPAP)) { + prescnt=session->count(CPAP_IPAP); + Pressure.reserve(prescnt); + for (int j=0;jeventlist[CPAP_IPAP].size();j++) { + QVector & el=session->eventlist[CPAP_IPAP]; + for (int e=0;efirst(); + int count=el->count(); + EventStoreType * dptr=el->rawData(); + EventStoreType * eptr=dptr+count; + quint32 * tptr=el->rawTime(); + EventDataType gain=el->gain(); + + EventStoreType pressure,leak; + + EventDataType fleak; + QMap::iterator pmin; + qint64 ti; + bool found; + for (;dptr ti) && (p1.time <= ti)) { + pressure=p1.value; + found=true; + break; + } else if (p2.time==ti) { + pressure=p2.value; + found=true; + break; + } + } + if (found) { + pressureleaks[pressure][leak]++; + pmin=pressuremin.find(pressure); + fleak=EventDataType(leak) * gain; + if (pmin==pressuremin.end()) { + pressuremin[pressure]=fleak; + } else { + if (pmin.value() > fleak) + pmin.value()=fleak; + } + } else { + int i=5; + } + } + + +} +void zMaskProfile::scanLeaks(Session * session) +{ + QVector & elv=session->eventlist[CPAP_LeakTotal]; + for (int i=0;i >::iterator it; + + EventStoreType pressure; + //EventDataType perc=0.1; + //EventDataType tmp,l1,l2; + //int idx1, idx2, + int lks; + double SN; + double percentile=0.75; + + double p=100.0*percentile; + double nth,nthi; + + int sum1,sum2,w1,w2,N,k; + + for (it=pressureleaks.begin();it!=pressureleaks.end();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++) { + SN+=lit.value(); + } + + + nth=double(SN)*percentile; // index of the position in the unweighted set would be + nthi=floor(nth); + + sum1=0,sum2=0; + w1,w2=0; + + EventDataType v1=0,v2; + + N=leakmap.size(); + k=0; + + bool found=false; + for (QMap::iterator lit=leakmap.begin();lit!=leakmap.end();lit++,k++) { + //for (k=0;k < N;k++) { + v1=lit.key(); + w1=lit.value(); + sum1+=w1; + + if (sum1 > nthi) { + pressuremin[pressure]=v1; + found=true; + break; + } + if (sum1 == nthi) { + break; // boundary condition + } + } + if (found) continue; + if (k>=N) { + pressuremin[pressure]=v1; + continue; + } + + v2=(leakmap.begin()+(k+1)).key(); + w2=(leakmap.begin()+(k+1)).value(); + sum2=sum1+w2; + // value lies between v1 and v2 + + double px=100.0/double(SN); // Percentile represented by one full value + + // calculate percentile ranks + double p1=px * (double(sum1)-(double(w1)/2.0)); + double p2=px * (double(sum2)-(double(w2)/2.0)); + + // calculate linear interpolation + double v=v1 + ((p-p1)/(p2-p1)) * (v2-v1); + pressuremin[pressure]=v; + + } + + +// for (it=pressureleaks.begin();it!=pressureleaks.end();it++) { +// pressure=it.key(); +// QMap & leakmap = it.value(); +// lks=leakmap.size(); +// tmp=perc * EventDataType(lks); +// idx1=floor(tmp); +// idx2=ceil(tmp); +// if (idx1==idx2) { +// pressuremin[pressure]=(leakmap.begin()+int(idx1)).key(); +// } else if (idx1==lks-1) { +// pressuremin[pressure]=(leakmap.begin()+int(idx1)).key(); +// } else { +// l1=(leakmap.begin()+int(idx1)).key(); +// l2=(leakmap.begin()+int(idx2)).key(); +// tmp=l1+(l2-l1)*(tmp-idx1); +// pressuremin[pressure]=tmp; +// } + +// } + //QMap pressuremin; +} + +void zMaskProfile::updateProfile(Session * session) +{ + scanPressure(session); + scanLeaks(session); + updatePressureMin(); +} + +zMaskProfile maskProfile(Mask_NasalPillows,"ResMed Swift FX"); + int calcLeaks(Session *session) { if (session->machine()->GetType()!=MT_CPAP) return 0; if (session->eventlist.contains(CPAP_Leak)) return 0; // abort if already there if (!session->eventlist.contains(CPAP_LeakTotal)) return 0; // can't calculate without this.. - if (session->settings[CPAP_Mode].toInt()>=MODE_APAP) return 0; // Don't bother calculating when in APAP mode + // maskProfile.reset(); + maskProfile.updateProfile(session); - const qint64 winsize=3600000; + EventList *leak=session->AddEventList(CPAP_Leak,EVL_Event,1); - //qint64 first=session->first(), last=session->last(), f; - - EventList *leak=new EventList(EVL_Event); - session->eventlist[CPAP_Leak].push_back(leak); - - const int rbsize=128; - EventDataType rbuf[rbsize],tmp,median; - qint64 rtime[rbsize],ti; - int rpos=0; - int tcnt=0; - QVector med; - - qint64 start; - quint32 * tptr; - EventStoreType * dptr; - EventDataType gain; for (int i=0;ieventlist[CPAP_LeakTotal].size();i++) { EventList & el=*session->eventlist[CPAP_LeakTotal][i]; - gain=el.gain(); - dptr=el.rawData(); + EventDataType gain=el.gain(),tmp,val; + int count = el.count(); + EventStoreType *dptr=el.rawData(); + EventStoreType *eptr=dptr+count; + quint32 * tptr=el.rawTime(); + qint64 start=el.first(),ti; + EventStoreType pressure; tptr=el.rawTime(); start=el.first(); - for (unsigned j=0;j 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.pressuremin[pressure]; - med.clear(); - for (int k=0;k ti-winsize) // if fits in time window, add to the list - med.push_back(rbuf[k]); + if (val < 0) { + val=0; + } + + leak->AddEvent(ti,val); } - int idx=float(med.size() * 0.0); - if (idx>=med.size()) idx--; - nth_element(med.begin(),med.begin()+idx,med.end()); - median=tmp-(*(med.begin()+idx)); - - if (median<0) median=0; - leak->AddEvent(ti,median); - - rpos=rpos % rbsize; } } return leak->count(); + + + //if (session->settings[CPAP_Mode].toInt()>=MODE_BIPAP) return 0; // Don't bother calculating when in APAP mode + + +// int prescnt=0; +// EventStoreType pressure; +// if (session->eventlist.contains(CPAP_Pressure)) { +// prescnt=session->count(CPAP_Pressure); +// Pressure.reserve(prescnt); +// for (int j=0;jeventlist[CPAP_Pressure].size();j++) { +// QVector & el=session->eventlist[CPAP_Pressure]; +// for (int e=0;eeventlist.contains(CPAP_IPAP)) { +// prescnt=session->count(CPAP_IPAP); +// Pressure.reserve(prescnt); +// for (int j=0;jeventlist[CPAP_IPAP].size();j++) { +// QVector & el=session->eventlist[CPAP_IPAP]; +// for (int e=0;efirst(), last=session->last(), f; + +// + +// qint64 start,ti; +// quint32 * tptr; +// EventStoreType * dptr; +// EventDataType gain,val,tmp; + + +// int pcnt=0; + +// for (int i=0;ieventlist[CPAP_LeakTotal].size();i++) { +// EventList & el=*session->eventlist[CPAP_LeakTotal][i]; +// gain=el.gain(); +// dptr=el.rawData(); +// tptr=el.rawTime(); +// start=el.first(); +// pcnt=0; + +// for (unsigned j=0;j ti) && (p1.time < ti)) { +// pressure=p1.value; +// break; +// } +// } + +// if (pcnt > 0) pcnt--; + +// // leak and pressure intersection + + + +// pressureleaks[pressure].push_back(tmp); +// // pressure current leak value is at.. +// } +// } +// if (pressureleaks.size()>2) { +// int i=5; +// } +// int size=0,siz,idx; +// EventDataType perc=0.05; +// EventDataType f1,f2,fidx,diff; +// QMap presleakmin; +// QHash >::iterator it; +// for (it=pressureleaks.begin();it!=pressureleaks.end();it++) { +// pressure=it.key(); +// QVector & leaks=it.value(); +// siz=leaks.size(); +// // Only keep mask pressure value if there is enough samples to be reasonably accurate + +//// if ((pls<60) || (siz > 10)) { +// // Grab the 'perc' percentile of leak data +// fidx=float(siz) * perc; +// idx=fidx; +// diff=fidx-float(idx); +// nth_element(leaks.begin(),leaks.begin()+idx,leaks.end()); +// f1=leaks.at(idx); +// if (idx < (siz-1)) { +// nth_element(leaks.begin(),leaks.begin()+idx+1,leaks.end()); +// f2=leaks.at(idx+1); +// val=f1 + (f2-f1)*diff; +// } else val=f1; +// presleakmin[pressure]=val; +// // } + +// size+=siz; +// } + +// QMap::iterator pit=presleakmin.begin(),npit; +// EventStoreType k; + +// EventDataType p1,p2,v1,v2; +// bool skip; + +// //////////////////////////////////////////////////////////////////////// +// // Generate the Mask Profile +// //////////////////////////////////////////////////////////////////////// +// if (pressureleaks.size()>presleakmin.size()) { +// for (it=pressureleaks.begin();it!=pressureleaks.end();it++) { +// pressure=it.key(); + +// skip=false; +// for (pit=presleakmin.begin();pit!=presleakmin.end();pit++) { +// k=pit.key(); +// // if already in pressure table, skip this record. +// if (k==pressure) { +// skip=true; +// break; +// } +// if (k > pressure) break; +// } +// if (skip) continue; + +// // rewind a step if not at the beginning already +// npit=pit; +// if (pit!=presleakmin.begin()) pit--; + +// if (npit==presleakmin.end()) { // last entry.. +// //can do better here by calculate the general trend +// presleakmin[pressure]=pit.value(); +// break; +// } +// if (npit==pit) { // first entry missing (unusual) +// presleakmin[pressure]=pit.value(); +// continue; +// } + +// p1=pit.key(); +// v1=pit.value(); +// p2=npit.key(); +// v2=npit.value(); + +// // Interpolate the pressure value in between the two valid data samples. +// val= v1+ ((pressure-p1)/(p2-p1) * (v2-v1)); +// presleakmin[pressure]=val; +// } + +// } + +// // Secret Evil algorithm :) +// QList values; +// values.reserve(presleakmin.size()); +// for (npit=presleakmin.begin();npit!=presleakmin.end();npit++) { +// values.push_back(npit.value()); +// } +// qSort(values); +// int i=0; +// for (npit=presleakmin.begin();npit!=presleakmin.end();npit++) { +// npit.value()=values.at(i++); +// } +// if (presleakmin.size()>2) { +// int i=5; +// } + } diff --git a/UpdaterWindow.ui b/UpdaterWindow.ui index 522d5c4a..e9ded06f 100644 --- a/UpdaterWindow.ui +++ b/UpdaterWindow.ui @@ -171,7 +171,7 @@ - &No Thanks + Maybe &Later @@ -188,13 +188,6 @@ - - - - &Remind Me Later - - - diff --git a/overview.cpp b/overview.cpp index d28cc176..8c8c53cf 100644 --- a/overview.cpp +++ b/overview.cpp @@ -223,6 +223,7 @@ Overview::Overview(QWidget *parent,gGraphView * shared) : mv->addSlice(CPAP_MinuteVent,QColor("green"),ST_max,maxperc); MV->AddLayer(mv); + // should merge... tgmv=new SummaryChart(tr("L/m"),GT_LINE); tgmv->addSlice(CPAP_TgMV,QColor("light blue"),ST_MIN); tgmv->addSlice(CPAP_TgMV,QColor("blue"),ST_mid,0.5);