From deb49b1ba40ba17f52ef3ae0b0f233af64a3b50b Mon Sep 17 00:00:00 2001 From: Mark Watkins Date: Mon, 11 Jul 2011 02:20:51 +1000 Subject: [PATCH] Decoded ASV GraphData Stuff --- SleepLib/loader_plugins/prs1_loader.cpp | 60 ++++++++++++++-------- SleepLib/loader_plugins/resmed_loader.cpp | 21 ++++---- SleepLib/machine_common.h | 3 +- daily.cpp | 32 +++++++----- docs/PRS1 Data Format.odt | Bin 0 -> 14536 bytes 5 files changed, 71 insertions(+), 45 deletions(-) create mode 100644 docs/PRS1 Data Format.odt diff --git a/SleepLib/loader_plugins/prs1_loader.cpp b/SleepLib/loader_plugins/prs1_loader.cpp index b0a64d57..f69b74ce 100644 --- a/SleepLib/loader_plugins/prs1_loader.cpp +++ b/SleepLib/loader_plugins/prs1_loader.cpp @@ -665,6 +665,7 @@ bool PRS1Loader::Parse002ASV(Session *session,unsigned char *buffer,int size,qin int pos=0; int cnt=0; short delta,duration; + QDateTime d; while (pos=ncodes) { @@ -672,8 +673,8 @@ bool PRS1Loader::Parse002ASV(Session *session,unsigned char *buffer,int size,qin return false; } //assert(codeAddEvent(new Event(t,cpapcode, data,0)); break; - case 0x00: // Unknown (ASV Pressure value) // could this be RLE? + case 0x00: // Unknown (ASV Pressure value) // offset? data[0]=buffer[pos++]; fc++; - if (!buffer[pos-1]) { + if (!buffer[pos-1]) { // WTH??? data[1]=buffer[pos++]; fc++; } @@ -707,8 +709,8 @@ bool PRS1Loader::Parse002ASV(Session *session,unsigned char *buffer,int size,qin session->AddEvent(new Event(t,cpapcode, data,2)); break; case 0x02: // Pressure - data[0]=buffer[pos++]/10.0; - session->AddEvent(new Event(t,cpapcode, data,1)); + data[0]=buffer[pos++]/10.0; // crappy EPAP pressure value. + //session->AddEvent(new Event(t,cpapcode, data,1)); break; case 0x04: // Pressure Pulse data[0]=buffer[pos++]; @@ -743,22 +745,37 @@ bool PRS1Loader::Parse002ASV(Session *session,unsigned char *buffer,int size,qin session->AddEvent(new Event(tt,cpapcode,data,1)); break; case 0x0d: // All the other ASV graph stuff. - data[0]=buffer[pos++]; - if (buffer[pos]<=0x12) { // short type of 0d record - // Type?? - session->AddEvent(new Event(t,cpapcode, data,1)); - } else { - data[1]=buffer[pos++]; - data[2]=buffer[pos++]; - data[3]=buffer[pos++]; - data[4]=buffer[pos++]; // ?? - data[5]=buffer[pos++]; // Patient Triggered Breaths data[6]=buffer[pos++]; // Patient Triggered Breaths - data[6]=buffer[pos++]; // Patient Triggered Breaths data[6]=buffer[pos++]; // Patient Triggered Breaths - data[7]=buffer[pos++]; // Patient Triggered Breaths - data[8]=buffer[pos++]; // Patient Triggered Breaths - data[9]=buffer[pos++]; // Patient Triggered Breaths - //session->AddEvent(new Event(t,cpapcode, data,6)); + d=QDateTime::fromMSecsSinceEpoch(t); + data[0]=buffer[pos++]/10.0; + session->AddEvent(new Event(t,CPAP_IAP,&data[0],1)); //correct + data[1]=buffer[pos++]/10.0; // Low IPAP + session->AddEvent(new Event(t,CPAP_IAPLO,&data[1],1)); //correct + data[2]=buffer[pos++]/10.0; // Hi IPAP + session->AddEvent(new Event(t,CPAP_IAPHI,&data[2],1)); //correct + + // This may not be necessary.. Check: the average of IAPHI - average of IAPLO may equal the average of this. + data[2]-=data[1]; + session->AddEvent(new Event(t,CPAP_PS,&data[2],1)); //correct + + data[3]=buffer[pos++];//Leak + session->AddEvent(new Event(t,CPAP_Leak,&data[3],1)); // correct + data[4]=buffer[pos++];//Breaths Per Minute + session->AddEvent(new Event(t,CPAP_RespiratoryRate,&data[4],1)); //correct + data[5]=buffer[pos++];//Patient Triggered Breaths + session->AddEvent(new Event(t,CPAP_PatientTriggeredBreaths,&data[5],1)); //correct + data[6]=buffer[pos++];//Minute Ventilation + session->AddEvent(new Event(t,CPAP_MinuteVentilation,&data[6],1)); //correct + data[7]=buffer[pos++]*10.0; // Tidal Volume + session->AddEvent(new Event(t,CPAP_TidalVolume,&data[7],1)); //correct + data[8]=buffer[pos++]; + session->AddEvent(new Event(t,CPAP_Snore,&data[8],1)); //correct + if (data[8]>0) { + session->AddEvent(new Event(t,CPAP_VSnore,&data[8],1)); //correct } + data[9]=buffer[pos++]/10.0; // This is a pressure value + session->AddEvent(new Event(t,CPAP_EAP,&data[9],1)); //correct + + qDebug()<< d.toString("yyyy-MM-dd HH:mm:ss") << hex << session->session() << pos+15 << hex << int(code) << ": " << hex << int(data[0]) << " " << int(data[1]) << " " << int(data[2]) << " " << int(data[3]) << " " << int(data[4]) << " " << int(data[5])<< " " << int(data[6]) << " " << int(data[7]) << " " << int(data[8]) << " " << int(data[9]); break; case 0x03: // BIPAP Pressure data[0]=buffer[pos++]; @@ -1037,6 +1054,7 @@ void InitModelMap() ModelMap[34]="RemStar Pro with C-Flex+"; ModelMap[35]="RemStar Auto with A-Flex"; ModelMap[37]="RemStar BIPAP Auto with Bi-Flex"; + ModelMap[0x41]="RemStar BIPAP Auto with Bi-Flex"; }; diff --git a/SleepLib/loader_plugins/resmed_loader.cpp b/SleepLib/loader_plugins/resmed_loader.cpp index a2de2f39..03e14ff1 100644 --- a/SleepLib/loader_plugins/resmed_loader.cpp +++ b/SleepLib/loader_plugins/resmed_loader.cpp @@ -559,19 +559,22 @@ bool ResmedLoader::LoadPLD(Session *sess,EDFParser &edf) ToTimeDelta(sess,edf,edf.edfsignals[s]->data, code,recs,duration,50.0); //50.0 } else if (edf.edfsignals[s]->label=="MV") { code=CPAP_MinuteVentilation; - Waveform *w=new Waveform(edf.startdate,code,edf.edfsignals[s]->data,recs,duration,edf.edfsignals[s]->digital_minimum,edf.edfsignals[s]->digital_maximum); - edf.edfsignals[s]->data=NULL; // so it doesn't get deleted when edf gets trashed. - sess->AddWaveform(w); + ToTimeDelta(sess,edf,edf.edfsignals[s]->data, code,recs,duration); + //Waveform *w=new Waveform(edf.startdate,code,edf.edfsignals[s]->data,recs,duration,edf.edfsignals[s]->digital_minimum,edf.edfsignals[s]->digital_maximum); + //edf.edfsignals[s]->data=NULL; // so it doesn't get deleted when edf gets trashed. + //sess->AddWaveform(w); } else if (edf.edfsignals[s]->label=="RR") { code=CPAP_RespiratoryRate; - Waveform *w=new Waveform(edf.startdate,code,edf.edfsignals[s]->data,recs,duration,edf.edfsignals[s]->digital_minimum,edf.edfsignals[s]->digital_maximum); - edf.edfsignals[s]->data=NULL; // so it doesn't get deleted when edf gets trashed. - sess->AddWaveform(w); + ToTimeDelta(sess,edf,edf.edfsignals[s]->data, code,recs,duration); + //Waveform *w=new Waveform(edf.startdate,code,edf.edfsignals[s]->data,recs,duration,edf.edfsignals[s]->digital_minimum,edf.edfsignals[s]->digital_maximum); + //edf.edfsignals[s]->data=NULL; // so it doesn't get deleted when edf gets trashed. + //sess->AddWaveform(w); } else if (edf.edfsignals[s]->label=="Vt") { code=CPAP_TidalVolume; - Waveform *w=new Waveform(edf.startdate,code,edf.edfsignals[s]->data,recs,duration,edf.edfsignals[s]->digital_minimum,edf.edfsignals[s]->digital_maximum); - edf.edfsignals[s]->data=NULL; // so it doesn't get deleted when edf gets trashed. - sess->AddWaveform(w); + ToTimeDelta(sess,edf,edf.edfsignals[s]->data, code,recs,duration); + //Waveform *w=new Waveform(edf.startdate,code,edf.edfsignals[s]->data,recs,duration,edf.edfsignals[s]->digital_minimum,edf.edfsignals[s]->digital_maximum); + //edf.edfsignals[s]->data=NULL; // so it doesn't get deleted when edf gets trashed. + //sess->AddWaveform(w); } else if (edf.edfsignals[s]->label=="Leak") { code=CPAP_Leak; ToTimeDelta(sess,edf,edf.edfsignals[s]->data, code,recs,duration,1.0); diff --git a/SleepLib/machine_common.h b/SleepLib/machine_common.h index d4aad891..e100dd18 100644 --- a/SleepLib/machine_common.h +++ b/SleepLib/machine_common.h @@ -36,13 +36,14 @@ enum MachineCode//:qint16 CPAP_Obstructive, CPAP_Hypopnea, CPAP_ClearAirway, CPAP_RERA, CPAP_VSnore, CPAP_FlowLimit, CPAP_Leak, CPAP_Pressure, CPAP_EAP, CPAP_IAP, CPAP_CSR, CPAP_FlowRate, CPAP_MaskPressure, CPAP_Snore,CPAP_MinuteVentilation, CPAP_RespiratoryRate, CPAP_TidalVolume,CPAP_FlowLimitGraph, + CPAP_PatientTriggeredBreaths, CPAP_PS, CPAP_IAPLO, CPAP_IAPHI, // General CPAP Summary Information CPAP_PressureMin=0x80, CPAP_PressureMax, CPAP_RampTime, CPAP_RampStartingPressure, CPAP_Mode, CPAP_PressureReliefType, CPAP_PressureReliefSetting, CPAP_HumidifierSetting, CPAP_HumidifierStatus, CPAP_PressureMinAchieved, CPAP_PressureMaxAchieved, CPAP_PressurePercentValue, CPAP_PressurePercentName, CPAP_PressureAverage, CPAP_PressureMedian, CPAP_LeakMedian,CPAP_LeakMinimum,CPAP_LeakMaximum,CPAP_LeakAverage,CPAP_Duration, - CPAP_SnoreGraph, CPAP_SnoreMinimum, CPAP_SnoreMaximum, CPAP_SnoreAverage, CPAP_SnoreMedian, + CPAP_SnoreMinimum, CPAP_SnoreMaximum, CPAP_SnoreAverage, CPAP_SnoreMedian, BIPAP_EAPAverage,BIPAP_IAPAverage,BIPAP_EAPMin,BIPAP_EAPMax,BIPAP_IAPMin,BIPAP_IAPMax,CPAP_BrokenSummary, diff --git a/daily.cpp b/daily.cpp index 82df8744..e9c17b63 100644 --- a/daily.cpp +++ b/daily.cpp @@ -93,6 +93,9 @@ Daily::Daily(QWidget *parent,QGLContext *context) : AddCPAPData(pressure_iap=new EventData(CPAP_IAP)); AddCPAPData(pressure_eap=new EventData(CPAP_EAP)); AddCPAPData(prd=new EventData(CPAP_Pressure)); + pressure_eap->ForceMinY(0); + pressure_eap->ForceMaxY(30); + PRD=new gGraphWindow(gSplitter,tr("Pressure"),SF); PRD->AddLayer(new gXAxis()); PRD->AddLayer(new gYAxis()); @@ -104,8 +107,6 @@ Daily::Daily(QWidget *parent,QGLContext *context) : PRD->setMinimumHeight(150); AddCPAPData(leakdata=new EventData(CPAP_Leak,0)); - //leakdata->ForceMinY(0); - //leakdata->ForceMaxY(120); LEAK=new gGraphWindow(gSplitter,tr("Leaks"),SF); LEAK->AddLayer(new gXAxis()); LEAK->AddLayer(new gYAxis()); @@ -153,25 +154,25 @@ Daily::Daily(QWidget *parent,QGLContext *context) : FLG->setMinimumHeight(150); - AddCPAPData(mv=new WaveData(CPAP_MinuteVentilation)); + AddCPAPData(mv=new EventData(CPAP_MinuteVentilation)); MV=new gGraphWindow(gSplitter,tr("Minute Ventilation"),SF); MV->AddLayer(new gXAxis()); MV->AddLayer(new gYAxis()); - MV->AddLayer(new gLineChart(mv,QColor(0x20,0x20,0x7f),65536,false,false,false)); + MV->AddLayer(new gLineChart(mv,QColor(0x20,0x20,0x7f),65536,false,false,true)); MV->setMinimumHeight(150); - AddCPAPData(tv=new WaveData(CPAP_TidalVolume)); + AddCPAPData(tv=new EventData(CPAP_TidalVolume)); TV=new gGraphWindow(gSplitter,tr("Tidal Volume"),SF); TV->AddLayer(new gXAxis()); TV->AddLayer(new gYAxis()); - TV->AddLayer(new gLineChart(tv,QColor(0x7f,0x20,0x20),65536,false,false,false)); + TV->AddLayer(new gLineChart(tv,QColor(0x7f,0x20,0x20),65536,false,false,true)); TV->setMinimumHeight(150); - AddCPAPData(rr=new WaveData(CPAP_RespiratoryRate)); + AddCPAPData(rr=new EventData(CPAP_RespiratoryRate)); RR=new gGraphWindow(gSplitter,tr("Respiratory Rate"),SF); RR->AddLayer(new gXAxis()); RR->AddLayer(new gYAxis()); - RR->AddLayer(new gLineChart(rr,Qt::gray,65536,false,false,false)); + RR->AddLayer(new gLineChart(rr,Qt::gray,65536,false,false,true)); RR->setMinimumHeight(150); @@ -658,15 +659,18 @@ void Daily::Load(QDate date) SF->hide(); SNORE->hide(); } + // Instead of doing this, check whether any data exists.. + // and show based on this factor. if (cpap && (cpap->machine->GetClass()=="ResMed")) { - MV->show(); - TV->show(); - RR->show(); + //MV->show(); + //TV->show(); + //RR->show(); FLG->show(); } else { - MV->hide(); - TV->hide(); - RR->hide(); + //MV->hide(); + + //TV->hide(); + //RR->hide(); FLG->hide(); } diff --git a/docs/PRS1 Data Format.odt b/docs/PRS1 Data Format.odt new file mode 100644 index 0000000000000000000000000000000000000000..1d8542ee4b513db34f9e5a25b1db038a710a50c7 GIT binary patch literal 14536 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 i@;IpfD#;Hn?%xjaO7budz9#?x