Initial Oximeter Live view Save Feature

This commit is contained in:
Mark Watkins 2011-08-08 01:37:01 +10:00
parent e2f304c2e6
commit cafdfc5563
7 changed files with 139 additions and 68 deletions

View File

@ -44,6 +44,8 @@ public:
void setMin(EventDataType v) { m_min=v; }
void setMax(EventDataType v) { m_max=v; }
void setRate(EventDataType v) { m_rate=v; }
void setCode(ChannelID id) { m_code=id; }
inline const EventDataType & min() { return m_min; }
inline const EventDataType & max() { return m_max; }
inline const EventDataType & gain() { return m_gain; }

View File

@ -30,7 +30,7 @@ CPAP_CSR, CPAP_VSnore, CPAP_PressurePulse, CPAP_Mode, CPAP_FlowRate, CPAP_MaskPr
CPAP_EPAP, CPAP_IPAP, CPAP_IPAP_Low, CPAP_IPAP_High, CPAP_PressureSupport, CPAP_Snore, CPAP_Leak,
CPAP_RespiratoryRate, CPAP_TidalVolume, CPAP_MinuteVentilation, CPAP_PatientTriggeredBreaths,
CPAP_FlowLimitGraph, CPAP_TherapyPressure, CPAP_ExpiratoryPressure, CPAP_AHI, CPAP_BrokenSummary,
CPAP_BrokenWaveform, CPAP_Pulse, CPAP_SPO2;
CPAP_BrokenWaveform, CPAP_Pulse, CPAP_SPO2, CPAP_Plethy;
ChannelID RMS9_PressureReliefType, RMS9_PressureReliefSetting, RMS9_Empty1, RMS9_Empty2;
ChannelID PRS1_PressureMin,PRS1_PressureMax, PRS1_PressureMinAchieved, PRS1_PressureMaxAchieved,
@ -176,8 +176,9 @@ void InitMapsWithoutAwesomeInitializerLists()
PRS1_VSnore2=CPAP_CODES.Get(CT_Event,QObject::tr("Vibratory Snore (Type 2)"),QObject::tr("VS2"),"VS2");
// CPAP Integrated oximetery codes..
CPAP_Pulse=CPAP_CODES.Get(CT_Event,QObject::tr("Pulse Rate"),QObject::tr("Pulse"),"CPPR");
CPAP_SPO2=CPAP_CODES.Get(CT_Event,QObject::tr("SpO2"),QObject::tr("SpO2"),"CPSP");
CPAP_Pulse=CPAP_CODES.Get(CT_Graph,QObject::tr("Pulse Rate"),QObject::tr("Pulse"),"CPPR");
CPAP_SPO2=CPAP_CODES.Get(CT_Graph,QObject::tr("SpO2"),QObject::tr("SpO2"),"CPSP");
CPAP_Plethy=CPAP_CODES.Get(CT_Graph,QObject::tr("Plethysomagram"),QObject::tr("Plethysomagram"),"CPPL");
OXI_Pulse=OXI_CODES.Get(CT_Graph,QObject::tr("Pulse Rate"),QObject::tr("PR"),"PR"),
OXI_SPO2=OXI_CODES.Get(CT_Graph,QObject::tr("Oxygen Saturation"),QObject::tr("SPO2"),"SPO2"),

View File

@ -119,7 +119,7 @@ CPAP_CSR, CPAP_VSnore, CPAP_PressurePulse, CPAP_Mode, CPAP_FlowRate, CPAP_MaskPr
CPAP_EPAP, CPAP_IPAP, CPAP_IPAP_Low, CPAP_IPAP_High, CPAP_PressureSupport, CPAP_Snore, CPAP_Leak,
CPAP_RespiratoryRate, CPAP_TidalVolume, CPAP_MinuteVentilation, CPAP_PatientTriggeredBreaths,
CPAP_FlowLimitGraph, CPAP_TherapyPressure, CPAP_ExpiratoryPressure, CPAP_AHI, CPAP_BrokenSummary,
CPAP_BrokenWaveform, CPAP_Pulse, CPAP_SPO2;
CPAP_BrokenWaveform, CPAP_Pulse, CPAP_SPO2, CPAP_Plethy;
extern ChannelID RMS9_PressureReliefType, RMS9_PressureReliefSetting, RMS9_Empty1, RMS9_Empty2;
extern ChannelID PRS1_PressureMin,PRS1_PressureMax, PRS1_PressureMinAchieved, PRS1_PressureMaxAchieved,

View File

@ -35,20 +35,23 @@ public:
const SessionID & session() {
return s_session;
};
}
qint64 first() {
return s_first;
};
}
qint64 last() {
return s_last;
};
}
qint64 length() {
return s_last-s_first;
}
void SetSessionID(SessionID s) {
s_session=s;
};
}
void set_first(qint64 d) {
if (!s_first) s_first=d;
else if (d<s_first) s_first=d;
};
}
void set_last(qint64 d) {
if (d<=s_first) {
qWarning() << "Session::set_last() d<=s_first";
@ -56,20 +59,20 @@ public:
}
if (!s_last) s_last=d;
else if (s_last<d) s_last=d;
};
}
double hours() {
double t=(s_last-s_first)/3600000.0;
return t;
};
}
void SetChanged(bool val) {
s_changed=val;
s_events_loaded=val; // dirty hack putting this here
};
}
bool IsChanged() {
return s_changed;
};
}
QHash<ChannelID,QVector<EventList *> > eventlist;
QHash<ChannelID,QVariant> settings;

View File

@ -77,7 +77,9 @@ Daily::Daily(QWidget *parent,QGLWidget * shared, MainWindow *mw)
scrollArea=new MyScrollArea(ui->graphMainArea,this);
ui->graphLayout->addWidget(scrollArea,1);
ui->graphLayout->setSpacing(0);
ui->graphLayout->setMargin(0);
ui->graphLayout->setContentsMargins(0,0,0,0);
scrollArea->setWidgetResizable(true);
scrollArea->setAutoFillBackground(false);
@ -130,6 +132,7 @@ Daily::Daily(QWidget *parent,QGLWidget * shared, MainWindow *mw)
INTSPO2=new gGraphWindow(parental,tr("SPO2"),SF); // Integrated Pulse
PULSE=new gGraphWindow(parental,tr("Pulse"),SF);
SPO2=new gGraphWindow(parental,tr("SPO2"),SF);
PLETHY=new gGraphWindow(parental,tr("Plethysomogram"),SF);
TAP=new gGraphWindow(NULL,"",(QGLWidget* )NULL);
TAP_EAP=new gGraphWindow(NULL,"",(QGLWidget* )NULL);
@ -268,6 +271,11 @@ Daily::Daily(QWidget *parent,QGLWidget * shared, MainWindow *mw)
INTSPO2->AddLayer(AddCPAP(new gLineChart(CPAP_SPO2,Qt::blue,true)));
INTSPO2->setMinimumHeight(min_height);
PLETHY->AddLayer(new gXAxis());
PLETHY->AddLayer(new gYAxis());
PLETHY->AddLayer(AddOXI(new gLineChart(OXI_Plethysomogram,Qt::darkCyan,true)));
PLETHY->setMinimumHeight(min_height);
PULSE->AddLayer(new gXAxis());
PULSE->AddLayer(new gYAxis());
PULSE->AddLayer(AddOXI(new gLineChart(OXI_Pulse,Qt::red,true)));
@ -356,11 +364,16 @@ Daily::Daily(QWidget *parent,QGLWidget * shared, MainWindow *mw)
//AddGraph(OF);
AddGraph(PULSE);
AddGraph(SPO2);
AddGraph(PLETHY);
//SPO2->LinkZoom(OF);
//PULSE->LinkZoom(OF);
SPO2->LinkZoom(PULSE);
SPO2->LinkZoom(PLETHY);
PULSE->LinkZoom(SPO2);
PULSE->LinkZoom(PLETHY);
PLETHY->LinkZoom(PULSE);
PLETHY->LinkZoom(SPO2);
//OF->LinkZoom(PULSE);
//OF->LinkZoom(SPO2);
@ -634,9 +647,9 @@ void Daily::Load(QDate date)
GraphAction[i]->setVisible(false);
Graphs[i]->hide();
} else {
Graphs[i]->ResetBounds();
GraphAction[i]->setVisible(true);
if (GraphAction[i]->isChecked()) {
Graphs[i]->ResetBounds();
Graphs[i]->show();
vis++;
} else {
@ -645,7 +658,10 @@ void Daily::Load(QDate date)
}
}
if (!cpap) {
GraphAction[0]->setVisible(false);
SF->hide();
vis--;
}
//splitter->layout();
for (int i=0;i<Graphs.size();i++) {
@ -668,6 +684,8 @@ void Daily::Load(QDate date)
// }
// splitter->blockSignals(true);
splitter->setSpacing(0);
splitter->setMargin(0);
splitter->layout();
scrollArea->setUpdatesEnabled(true);
scrollArea->update();
@ -760,6 +778,7 @@ void Daily::Load(QDate date)
// 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 (pref["EnableGraphSnapshots"].toBool()) { // AHI Pie Chart
html+="</tr>\n<tr><td colspan=4 align=center><i>"+tr("Event Breakdown")+"</i></td></tr>\n";
@ -771,9 +790,11 @@ void Daily::Load(QDate date)
pixmap.save(&buffer, "PNG");
html += "<tr><td colspan=4 align=center><img src=\"data:image/png;base64," + byteArray.toBase64() + "\"></td></tr>\n";
}
}
html+="</table>"
"<table cellspacing=0 cellpadding=0 border=0 width='100%'>\n"
"<tr height='2'><td colspan=5 height='2'><hr></td></tr>\n";
"<table cellspacing=0 cellpadding=0 border=0 width='100%'>\n";
if (cpap || oxi) {
html+="<tr height='2'><td colspan=5 height='2'><hr></td></tr>\n";
/* if (mode==MODE_BIPAP) {
html+="<tr><td colspan=4 align='center'><i>"+tr("90%&nbsp; EPAP ")+QString().sprintf("%.2f",eap90)+tr("cmH2O")+"</td></tr>\n"
@ -787,12 +808,11 @@ void Daily::Load(QDate date)
//html+=("<tr><td colspan=4 align=center>&nbsp;</td></tr>\n");
html+=("<tr><td> </td><td><b>Min</b></td><td><b>Avg</b></td><td><b>90%</b></td><td><b>Max</b></td></tr>");
ChannelID chans[]={CPAP_Pressure,CPAP_EPAP,CPAP_IPAP,CPAP_PressureSupport,CPAP_PatientTriggeredBreaths, CPAP_MinuteVentilation,CPAP_RespiratoryRate,CPAP_FlowLimitGraph,CPAP_Leak,CPAP_Snore,CPAP_TidalVolume,CPAP_Pulse,CPAP_SPO2};
ChannelID chans[]={CPAP_Pressure,CPAP_EPAP,CPAP_IPAP,CPAP_PressureSupport,CPAP_PatientTriggeredBreaths, CPAP_MinuteVentilation,CPAP_RespiratoryRate,CPAP_FlowLimitGraph,CPAP_Leak,CPAP_Snore,CPAP_TidalVolume,CPAP_Pulse,CPAP_SPO2,OXI_Pulse,OXI_SPO2};
int numchans=sizeof(chans)/sizeof(ChannelID);
for (int i=0;i<numchans;i++) {
ChannelID code=chans[i];
if (cpap->channelExists(code)) {
if (cpap && cpap->channelExists(code)) {
html+="<tr><td align=left>"+channel[code].label();
html+="</td><td>"+a.sprintf("%.2f",cpap->min(code));
html+="</td><td>"+a.sprintf("%.2f",cpap->wavg(code));
@ -800,58 +820,25 @@ void Daily::Load(QDate date)
html+="</td><td>"+a.sprintf("%.2f",cpap->max(code));
html+="</td><tr>";
}
if (oxi && oxi->channelExists(code)) {
html+="<tr><td align=left>"+channel[code].label();
html+="</td><td>"+a.sprintf("%.2f",oxi->min(code));
html+="</td><td>"+a.sprintf("%.2f",oxi->wavg(code));
html+="</td><td>"+a.sprintf("%.2f",oxi->p90(code));
html+="</td><td>"+a.sprintf("%.2f",oxi->max(code));
html+="</td><tr>";
}
}
/*html+="<tr><td align=left>"+tr("Snore:");
html+="</td><td>"+a.sprintf("%.2f",cpap->min(CPAP_Snore));
html+="</td><td>"+a.sprintf("%.2f",cpap->avg(CPAP_Snore));
html+="</td><td>"+a.sprintf("%.2f",cpap->max(CPAP_Snore))+("</td><tr>");
*/
} else {
html+="<tr><td colspan=5 align=center><i>"+tr("No CPAP data available")+"</i></td></tr>";
html+="<tr><td colspan=5 align=center><i>"+tr("No data available")+"</i></td></tr>";
html+="<tr><td colspan=5>&nbsp;</td></tr>\n";
}
// Instead of doing this, check whether any data exists..
// and show based on this factor.
bool merge_oxi_graphs=true;
if (!merge_oxi_graphs) {
//spo2->isEmpty() ? SPO2->hide() : SPO2->show();
//pulse->isEmpty() ? PULSE->hide() : PULSE->show();
} else {
//pulse->isEmpty() && spo2->isEmpty() ? PULSE->hide() : PULSE->show();
}
if (oxi) {
html+="<tr><td>"+tr("Pulse:");
html+="</td><td>"+a.sprintf("%.2fbpm",oxi->min(OXI_Pulse));
html+="</td><td>"+a.sprintf("%.2fbpm",oxi->wavg(OXI_Pulse));
html+="</td><td>"+a.sprintf("%.2fbpm",oxi->p90(OXI_Pulse));
html+="</td><td>"+a.sprintf("%.2fbpm",oxi->max(OXI_Pulse));
html+="</td><tr>";
html+="<tr><td>"+tr("SpO2:");
html+="</td><td>"+a.sprintf("%.2f%%",oxi->min(OXI_SPO2));
html+="</td><td>"+a.sprintf("%.2f%%",oxi->wavg(OXI_SPO2));
html+="</td><td>"+a.sprintf("%.2f%%",oxi->p90(OXI_SPO2));
html+="</td><td>"+a.sprintf("%.2f%%",oxi->max(OXI_SPO2));
html+="</td><tr>";
//html+=wxT("<tr><td colspan=4>&nbsp;</td></tr>\n");
//PULSE->show();
//SPO2->show();
} else {
//PULSE->hide();
//SPO2->hide();
}
html+="</table>";
html+="<table cellspacing=0 cellpadding=0 border=0 width='100%'>\n";
if (cpap) {
html+="</table>"
"<table cellspacing=0 cellpadding=0 border=0 width='100%'>\n";
if (pref["EnableGraphSnapshots"].toBool()) {
if (cpap->channelExists(CPAP_Pressure)) {
html+=("<tr><td colspan=4 align=center><i>")+tr("Time@Pressure")+("</i></td></tr>\n");

View File

@ -88,7 +88,8 @@ private:
gFlagsGroup *fg;
gGraphWindow *PRD,*FRW,*G_AHI,*TAP,*LEAK,*SF,*TAP_EAP,*TAP_IAP,*PULSE,*SPO2,
*SNORE,*RR,*MP,*MV,*TV,*FLG,*PTB,*OF,*INTPULSE,*INTSPO2, *THPR;
*SNORE,*RR,*MP,*MV,*TV,*FLG,*PTB,*OF,*INTPULSE,*INTSPO2, *THPR,
*PLETHY;
gLineChart *pressure, *epap, *ipap;

View File

@ -174,6 +174,8 @@ void Oximetry::on_RunButton_toggled(bool checked)
lasttime=QDateTime::currentMSecsSinceEpoch();
starttime=lasttime;
session->SetSessionID(lasttime/1000L);
day->setFirst(lasttime);
day->setLast(lasttime+30000);
session->set_first(lasttime);
@ -245,6 +247,81 @@ void Oximetry::on_RunButton_toggled(bool checked)
PLETHY->updateGL();
SPO2->updateGL();
PULSE->updateGL();
qint64 d=session->length();
// if (d<=30000)
// return;
if (QMessageBox::question(this,"Keep This Recording?","Would you like to keep this oximeter recording?",QMessageBox::Yes|QMessageBox::No)==QMessageBox::Yes) {
qDebug() << "Saving oximeter session data";
Session *sess=new Session(mach,starttime/1000L);
/*ev_spo2->setCode(CPAP_SPO2);
ev_pulse->setCode(CPAP_Pulse);
ev_plethy->setCode(CPAP_Plethy); */
sess->eventlist[OXI_SPO2].push_back(ev_spo2);
sess->eventlist[OXI_Pulse].push_back(ev_pulse);
sess->eventlist[OXI_Plethysomogram].push_back(ev_plethy);
//Session *sess=session;
sess->SetSessionID(starttime/1000L);
sess->setMin(OXI_Pulse,ev_pulse->min());
sess->setMax(OXI_Pulse,ev_pulse->min());
sess->setFirst(OXI_Pulse,ev_pulse->first());
sess->setLast(OXI_Pulse,ev_pulse->last());
sess->avg(OXI_Pulse);
sess->wavg(OXI_Pulse);
sess->p90(OXI_Pulse);
//sess->min(OXI_Pulse);
//sess->max(OXI_Pulse);
sess->setMin(OXI_SPO2,ev_spo2->min());
sess->setMax(OXI_SPO2,ev_spo2->max());
sess->setFirst(OXI_SPO2,ev_spo2->first());
sess->setLast(OXI_SPO2,ev_spo2->last());
sess->avg(OXI_SPO2);
sess->wavg(OXI_SPO2);
sess->p90(OXI_SPO2);
//sess->min(OXI_SPO2);
//sess->max(OXI_SPO2);
sess->setMin(OXI_SPO2,ev_spo2->min());
sess->setMax(OXI_SPO2,ev_spo2->max());
sess->setFirst(OXI_SPO2,ev_spo2->first());
sess->setLast(OXI_SPO2,ev_spo2->last());
//sess->min(OXI_Plethysomogram);
//sess->max(OXI_Plethysomogram);
sess->avg(OXI_Plethysomogram);
sess->wavg(OXI_Plethysomogram);
sess->p90(OXI_Plethysomogram);
sess->setMin(OXI_Plethysomogram,ev_plethy->min());
sess->setMax(OXI_Plethysomogram,ev_plethy->max());
sess->setFirst(OXI_Plethysomogram,ev_plethy->first());
sess->setLast(OXI_Plethysomogram,ev_plethy->last());
sess->updateFirst(sess->first(OXI_Pulse));
sess->updateLast(sess->last(OXI_Pulse));
sess->updateFirst(sess->first(OXI_SPO2));
sess->updateLast(sess->last(OXI_SPO2));
sess->updateFirst(sess->first(OXI_Plethysomogram));
sess->updateLast(sess->last(OXI_Plethysomogram));
sess->SetChanged(true);
mach->AddSession(sess,profile);
mach->Save();
session->eventlist.clear();
ev_plethy=new EventList(OXI_Plethysomogram,EVL_Waveform,1,0,0,0,1000.0/50.0);
session->eventlist[OXI_Plethysomogram].push_back(ev_plethy);
ev_pulse=new EventList(OXI_Pulse,EVL_Event,1);
session->eventlist[OXI_Pulse].push_back(ev_pulse);
ev_spo2=new EventList(OXI_SPO2,EVL_Event,1);
session->eventlist[OXI_SPO2].push_back(ev_spo2);
}
}
}
@ -627,7 +704,7 @@ void Oximetry::on_ImportButton_clicked()
day->AddSession(session);
// As did these
ev_plethy=new EventList(OXI_Plethysomogram,EVL_Waveform,1,0,0,0,1.0/50.0);
ev_plethy=new EventList(OXI_Plethysomogram,EVL_Waveform,1,0,0,0,1000.0/50.0);
session->eventlist[OXI_Plethysomogram].push_back(ev_plethy);
ev_pulse=new EventList(OXI_Pulse,EVL_Event,1);