Added optional second EventList data field, upgraded Session Load/Store code, Modified oximetery Pulse/SPO2 flag to record extra field, Retain mask info in Preferences Tab

This commit is contained in:
Mark Watkins 2011-12-02 15:54:25 +10:00
parent 9b0d27629a
commit 19e9528c4b
13 changed files with 222 additions and 118 deletions

View File

@ -410,12 +410,13 @@ int calcPulseChange(Session *session)
change=5; change=5;
} }
EventList *pc=new EventList(EVL_Event); EventList *pc=new EventList(EVL_Event,1,0,0,0,0,true);
pc->setFirst(session->first(OXI_Pulse)); pc->setFirst(session->first(OXI_Pulse));
qint64 lastt; qint64 lastt;
EventDataType lv=0; EventDataType lv=0;
int li=0; int li=0;
int max;
for (int e=0;e<it.value().size();e++) { for (int e=0;e<it.value().size();e++) {
EventList & el=*(it.value()[e]); EventList & el=*(it.value()[e]);
@ -426,6 +427,7 @@ int calcPulseChange(Session *session)
lastt=0; lastt=0;
lv=change; lv=change;
max=0;
for (unsigned j=i+1;j<el.count();j++) { // scan ahead in the window for (unsigned j=i+1;j<el.count();j++) { // scan ahead in the window
time2=el.time(j); time2=el.time(j);
@ -434,13 +436,14 @@ int calcPulseChange(Session *session)
tmp=qAbs(val2-val); tmp=qAbs(val2-val);
if (tmp > lv) { if (tmp > lv) {
lastt=time2; lastt=time2;
if (tmp>max) max=tmp;
//lv=tmp; //lv=tmp;
li=j; li=j;
} }
} }
if (lastt>0) { if (lastt>0) {
qint64 len=(lastt-time)/1000.0; qint64 len=(lastt-time)/1000.0;
pc->AddEvent(lastt,len); pc->AddEvent(lastt,len,tmp);
i=li; i=li;
} }
@ -481,7 +484,7 @@ int calcSPO2Drop(Session *session)
change=3; change=3;
} }
EventList *pc=new EventList(EVL_Event); EventList *pc=new EventList(EVL_Event,1,0,0,0,0,true);
qint64 lastt; qint64 lastt;
EventDataType lv=0; EventDataType lv=0;
int li=0; int li=0;
@ -489,10 +492,9 @@ int calcSPO2Drop(Session *session)
const unsigned ringsize=10; const unsigned ringsize=10;
EventDataType ring[ringsize]; EventDataType ring[ringsize];
int rp=0; int rp=0;
int min;
for (int e=0;e<it.value().size();e++) { for (int e=0;e<it.value().size();e++) {
EventList & el=*(it.value()[e]); EventList & el=*(it.value()[e]);
for (unsigned i=0;i<el.count();i++) { for (unsigned i=0;i<el.count();i++) {
val=el.data(i); val=el.data(i);
if (!val) continue; if (!val) continue;
@ -516,10 +518,11 @@ int calcSPO2Drop(Session *session)
lv=val; lv=val;
min=val;
for (unsigned j=i+1;j<el.count();j++) { // scan ahead in the window for (unsigned j=i+1;j<el.count();j++) { // scan ahead in the window
time2=el.time(j); time2=el.time(j);
val2=el.data(j); val2=el.data(j);
if (val2<min) min=val2;
if (val2 <= (val-change)) { if (val2 <= (val-change)) {
lastt=time2; lastt=time2;
li=j; li=j;
@ -533,7 +536,7 @@ int calcSPO2Drop(Session *session)
if (lastt>0) { if (lastt>0) {
qint64 len=(lastt-time); qint64 len=(lastt-time);
if (len>=window) { if (len>=window) {
pc->AddEvent(lastt,len/1000); pc->AddEvent(lastt,len/1000,val-min);
i=li; i=li;
} }

View File

@ -7,16 +7,17 @@
#include <QDebug> #include <QDebug>
#include "event.h" #include "event.h"
EventList::EventList(EventListType et,EventDataType gain, EventDataType offset, EventDataType min, EventDataType max,double rate) EventList::EventList(EventListType et,EventDataType gain, EventDataType offset, EventDataType min, EventDataType max,double rate,bool second_field)
:m_type(et),m_gain(gain),m_offset(offset),m_min(min),m_max(max),m_rate(rate) :m_type(et),m_gain(gain),m_offset(offset),m_min(min),m_max(max),m_rate(rate),m_second_field(second_field)
{ {
m_first=m_last=0; m_first=m_last=0;
m_count=0; m_count=0;
if (min==max) { // Update Min & Max unless forceably set here.. if (min==max) { // Update Min & Max unless forceably set here..
m_update_minmax=true; m_update_minmax=true;
m_min=999999999; m_min2=m_min=999999999;
m_max=-999999999; m_max2=m_max=-999999999;
} else { } else {
m_update_minmax=false; m_update_minmax=false;
} }
@ -39,11 +40,22 @@ EventDataType EventList::data(quint32 i)
{ {
return EventDataType(m_data[i])*m_gain; return EventDataType(m_data[i])*m_gain;
} }
EventDataType EventList::data2(quint32 i)
{
return EventDataType(m_data2[i]);
}
void EventList::AddEvent(qint64 time, EventStoreType data) void EventList::AddEvent(qint64 time, EventStoreType data, EventStoreType data2)
{ {
// Apply gain & offset // Apply gain & offset
m_data.push_back(data); m_data.push_back(data);
if (m_second_field) {
m_data2.push_back(data2);
if (m_min2>data2) m_min2=data2;
if (m_max2<data2) m_max2=data2;
}
EventDataType val=EventDataType(data)*m_gain+m_offset; EventDataType val=EventDataType(data)*m_gain+m_offset;
if (m_update_minmax) { if (m_update_minmax) {
if (m_min>val) m_min=val; if (m_min>val) m_min=val;
@ -58,14 +70,14 @@ void EventList::AddEvent(qint64 time, EventStoreType data)
// Crud.. Update all the previous records // Crud.. Update all the previous records
// This really shouldn't happen. // This really shouldn't happen.
qint64 t=(m_first-time); qint32 t=(m_first-time);
for (quint32 i=0;i<m_count;i++) { for (quint32 i=0;i<m_count;i++) {
m_time[i]-=t & 0xffffffff; m_time[i]-=t;
} }
m_first=time; m_first=time;
} }
if (m_last < time) m_last=time; if (m_last < time) m_last=time;
quint32 t=(time-m_first) & 0xffffffff; quint32 t=(time-m_first);
m_time.push_back(t); m_time.push_back(t);
m_count++; m_count++;

View File

@ -17,10 +17,10 @@ class EventList
{ {
friend class Session; friend class Session;
public: public:
EventList(EventListType et,EventDataType gain=1.0, EventDataType offset=0.0, EventDataType min=0.0, EventDataType max=0.0, double rate=0.0); EventList(EventListType et,EventDataType gain=1.0, EventDataType offset=0.0, EventDataType min=0.0, EventDataType max=0.0, double rate=0.0,bool second_field=false);
~EventList(); ~EventList();
void AddEvent(qint64 time, EventStoreType data); void AddEvent(qint64 time, EventStoreType data, EventStoreType data2=0);
void AddWaveform(qint64 start, qint16 * data, int recs, qint64 duration); void AddWaveform(qint64 start, qint16 * data, int recs, qint64 duration);
void AddWaveform(qint64 start, unsigned char * data, int recs, qint64 duration); void AddWaveform(qint64 start, unsigned char * data, int recs, qint64 duration);
void AddWaveform(qint64 start, char * data, int recs, qint64 duration); void AddWaveform(qint64 start, char * data, int recs, qint64 duration);
@ -29,9 +29,12 @@ public:
void setCount(quint32 count) { m_count=count; } void setCount(quint32 count) { m_count=count; }
inline EventStoreType raw(int i) { return m_data[i]; } inline EventStoreType raw(int i) { return m_data[i]; }
inline EventStoreType raw2(int i) { return m_data2[i]; }
EventDataType data(quint32 i); EventDataType data(quint32 i);
EventDataType data2(quint32 i);
qint64 time(quint32 i); qint64 time(quint32 i);
bool hasSecondField() { return m_second_field; }
inline const qint64 & first() { return m_first; } inline const qint64 & first() { return m_first; }
inline const qint64 & last() { return m_last; } inline const qint64 & last() { return m_last; }
inline qint64 duration() { return m_last-m_first; } inline qint64 duration() { return m_last-m_first; }
@ -43,11 +46,15 @@ public:
void setOffset(EventDataType v) { m_offset=v; } void setOffset(EventDataType v) { m_offset=v; }
void setMin(EventDataType v) { m_min=v; } void setMin(EventDataType v) { m_min=v; }
void setMax(EventDataType v) { m_max=v; } void setMax(EventDataType v) { m_max=v; }
void setMin2(EventDataType v) { m_min2=v; }
void setMax2(EventDataType v) { m_max2=v; }
void setRate(EventDataType v) { m_rate=v; } void setRate(EventDataType v) { m_rate=v; }
//void setCode(ChannelID id) { m_code=id; } //void setCode(ChannelID id) { m_code=id; }
inline const EventDataType & min() { return m_min; } inline const EventDataType & min() { return m_min; }
inline const EventDataType & max() { return m_max; } inline const EventDataType & max() { return m_max; }
inline const EventDataType & min2() { return m_min2; }
inline const EventDataType & max2() { return m_max2; }
inline const EventDataType & gain() { return m_gain; } inline const EventDataType & gain() { return m_gain; }
inline const EventDataType & offset() { return m_offset; } inline const EventDataType & offset() { return m_offset; }
inline const EventDataType & rate() { return m_rate; } inline const EventDataType & rate() { return m_rate; }
@ -59,24 +66,27 @@ public:
void setDimension(QString dimension) { m_dimension=dimension; } void setDimension(QString dimension) { m_dimension=dimension; }
QVector<EventStoreType> & getData() { return m_data; } QVector<EventStoreType> & getData() { return m_data; }
QVector<EventStoreType> & getData2() { return m_data2; }
QVector<quint32> & getTime() { return m_time; } QVector<quint32> & getTime() { return m_time; }
protected: protected:
QVector<quint32> m_time; // 32bitalize this.. add offsets to m_first QVector<quint32> m_time; // 32bitalize this.. add offsets to m_first
QVector<EventStoreType> m_data; QVector<EventStoreType> m_data;
QVector<EventStoreType> m_data2;
//ChannelID m_code; //ChannelID m_code;
EventListType m_type; EventListType m_type;
quint32 m_count; quint32 m_count;
EventDataType m_gain; EventDataType m_gain;
EventDataType m_offset; EventDataType m_offset;
EventDataType m_min; EventDataType m_min,m_min2;
EventDataType m_max; EventDataType m_max,m_max2;
EventDataType m_rate; // Waveform sample rate EventDataType m_rate; // Waveform sample rate
QString m_dimension; QString m_dimension;
qint64 m_first,m_last; qint64 m_first,m_last;
bool m_update_minmax; bool m_update_minmax;
bool m_second_field;
}; };

View File

@ -24,9 +24,7 @@ typedef qint16 EventStoreType;
class BoundsError {}; class BoundsError {};
class OldDBVersion {}; class OldDBVersion {};
// This is the uber important database version for SleepyHeads internal storage const quint32 magic=0xC73216AB; // Magic number for Sleepyhead Data Files.. Don't touch!
// Increment this after stuffing with Session's save & load code.
const quint16 dbversion=6;
//const int max_number_event_fields=10; //const int max_number_event_fields=10;

View File

@ -68,7 +68,7 @@ Preference::Preference(Preferences * pref,QString code, PrefType type, QString l
void Preference::setValue(QVariant v) void Preference::setValue(QVariant v)
{ {
if (!m_pref) { if (!m_pref) {
qDebug() << "Bad Preferences object"; qDebug() << "Bad Preferences object" << m_code;
return; return;
} }
if (m_pref) if (m_pref)
@ -76,7 +76,7 @@ void Preference::setValue(QVariant v)
} }
QVariant & Preference::value() { QVariant & Preference::value() {
if (!m_pref) { if (!m_pref) {
qDebug() << "Bad Preferences object"; qDebug() << "Bad Preferences object" << m_code;
return m_defaultValue; return m_defaultValue;
} }
QHash<QString,QVariant>::iterator i=m_pref->find(m_code); QHash<QString,QVariant>::iterator i=m_pref->find(m_code);

View File

@ -16,6 +16,15 @@
using namespace std; using namespace std;
const quint16 filetype_summary=0;
const quint16 filetype_data=1;
// This is the uber important database version for SleepyHeads internal storage
// Increment this after stuffing with Session's save & load code.
const quint16 summary_version=6;
const quint16 events_version=7;
Session::Session(Machine * m,SessionID session) Session::Session(Machine * m,SessionID session)
{ {
if (!session) { if (!session) {
@ -103,9 +112,6 @@ bool Session::Store(QString path)
return a; return a;
} }
const quint16 filetype_summary=0;
const quint16 filetype_data=1;
bool Session::StoreSummary(QString filename) bool Session::StoreSummary(QString filename)
{ {
@ -117,7 +123,7 @@ bool Session::StoreSummary(QString filename)
out.setByteOrder(QDataStream::LittleEndian); out.setByteOrder(QDataStream::LittleEndian);
out << (quint32)magic; out << (quint32)magic;
out << (quint16)dbversion; out << (quint16)summary_version;
out << (quint16)filetype_summary; out << (quint16)filetype_summary;
out << (quint32)s_machine->id(); out << (quint32)s_machine->id();
@ -180,7 +186,7 @@ bool Session::LoadSummary(QString filename)
} }
in >> t16; // DB Version in >> t16; // DB Version
if (t16!=dbversion) { if (t16!=summary_version) {
throw OldDBVersion(); throw OldDBVersion();
//qWarning() << "Old dbversion "<< t16 << "summary file.. Sorry, you need to purge and reimport"; //qWarning() << "Old dbversion "<< t16 << "summary file.. Sorry, you need to purge and reimport";
return false; return false;
@ -241,7 +247,7 @@ bool Session::StoreEvents(QString filename)
out.setByteOrder(QDataStream::LittleEndian); out.setByteOrder(QDataStream::LittleEndian);
out << (quint32)magic; // Magic Number out << (quint32)magic; // Magic Number
out << (quint16)dbversion; // File Version out << (quint16)events_version; // File Version
out << (quint16)filetype_data; // File type 1 == Event out << (quint16)filetype_data; // File type 1 == Event
out << (quint32)s_machine->id();// Machine ID out << (quint32)s_machine->id();// Machine ID
@ -269,6 +275,11 @@ bool Session::StoreEvents(QString filename)
out << e.min(); out << e.min();
out << e.max(); out << e.max();
out << e.dimension(); out << e.dimension();
out << e.hasSecondField();
if (e.hasSecondField()) {
out << e.min2();
out << e.max2();
}
} }
} }
for (i=eventlist.begin(); i!=eventlist.end(); i++) { for (i=eventlist.begin(); i!=eventlist.end(); i++) {
@ -278,6 +289,11 @@ bool Session::StoreEvents(QString filename)
for (quint32 c=0;c<e.count();c++) { for (quint32 c=0;c<e.count();c++) {
out << e.raw(c); out << e.raw(c);
} }
if (e.hasSecondField()) {
for (quint32 c=0;c<e.count();c++) {
out << e.raw2(c);
}
}
if (e.type()!=EVL_Waveform) { if (e.type()!=EVL_Waveform) {
for (quint32 c=0;c<e.count();c++) { for (quint32 c=0;c<e.count();c++) {
out << e.getTime()[c]; out << e.getTime()[c];
@ -308,14 +324,17 @@ bool Session::LoadEvents(QString filename)
quint16 t16; quint16 t16;
quint8 t8; quint8 t8;
//qint16 i16; //qint16 i16;
qint16 version;
in >> t32; // Magic Number in >> t32; // Magic Number
if (t32!=magic) { if (t32!=magic) {
qWarning() << "Wrong Magic number in " << filename; qWarning() << "Wrong Magic number in " << filename;
return false; return false;
} }
in >> t16; // File Version
if (t16!=dbversion) { in >> version; // File Version
if (version<6) { // prior to version 6 is too old to deal with
throw OldDBVersion(); throw OldDBVersion();
//qWarning() << "Old dbversion "<< t16 << "summary file.. Sorry, you need to purge and reimport"; //qWarning() << "Old dbversion "<< t16 << "summary file.. Sorry, you need to purge and reimport";
return false; return false;
@ -367,18 +386,31 @@ bool Session::LoadEvents(QString filename)
in >> mn; in >> mn;
in >> mx; in >> mx;
in >> dim; in >> dim;
EventList *elist=AddEventList(code,elt,gain,offset,mn,mx,rate); bool second_field=false;
if (version>=7) // version 7 added this field
in >> second_field;
EventList *elist=AddEventList(code,elt,gain,offset,mn,mx,rate,second_field);
elist->setDimension(dim); elist->setDimension(dim);
//eventlist[code].push_back(elist); //eventlist[code].push_back(elist);
elist->m_count=evcount; elist->m_count=evcount;
elist->m_first=ts1; elist->m_first=ts1;
elist->m_last=ts2; elist->m_last=ts2;
if (second_field) {
EventDataType min,max;
in >> min;
in >> max;
elist->setMin2(min);
elist->setMax2(max);
}
} }
} }
EventStoreType t; EventStoreType t;
quint32 x; quint32 x;
for (int i=0;i<mcsize;i++) { for (int i=0;i<mcsize;i++) {
code=mcorder[i]; code=mcorder[i];
size2=sizevec[i]; size2=sizevec[i];
@ -387,9 +419,15 @@ bool Session::LoadEvents(QString filename)
evec.m_data.reserve(evec.m_count); evec.m_data.reserve(evec.m_count);
for (quint32 c=0;c<evec.m_count;c++) { for (quint32 c=0;c<evec.m_count;c++) {
in >> t; in >> t;
//evec.m_data[c]=t;
evec.m_data.push_back(t); evec.m_data.push_back(t);
} }
if (evec.hasSecondField()) {
evec.m_data2.reserve(evec.m_count);
for (quint32 c=0;c<evec.m_count;c++) {
in >> t;
evec.m_data2.push_back(t);
}
}
//last=evec.first(); //last=evec.first();
if (evec.type()!=EVL_Waveform) { if (evec.type()!=EVL_Waveform) {
evec.m_time.reserve(evec.m_count); evec.m_time.reserve(evec.m_count);
@ -402,6 +440,13 @@ bool Session::LoadEvents(QString filename)
} }
} }
} }
file.close();
if (version<events_version) {
qDebug() << "Upgrading Events file to version" << events_version;
UpdateSummaries();
StoreEvents(filename);
}
return true; return true;
} }
@ -868,14 +913,14 @@ EventDataType Session::wavg(ChannelID id)
return v; return v;
} }
EventList * Session::AddEventList(QString chan, EventListType et,EventDataType gain,EventDataType offset,EventDataType min, EventDataType max,EventDataType rate) EventList * Session::AddEventList(QString chan, EventListType et,EventDataType gain,EventDataType offset,EventDataType min, EventDataType max,EventDataType rate,bool second_field)
{ {
schema::Channel * channel=&schema::channel[chan]; schema::Channel * channel=&schema::channel[chan];
if (!channel) { if (!channel) {
qWarning() << "Channel" << chan << "does not exist!"; qWarning() << "Channel" << chan << "does not exist!";
//return NULL; //return NULL;
} }
EventList * el=new EventList(et,gain,offset,min,max,rate); EventList * el=new EventList(et,gain,offset,min,max,rate,second_field);
eventlist[chan].push_back(el); eventlist[chan].push_back(el);
s_machine->registerChannel(chan); s_machine->registerChannel(chan);
return el; return el;

View File

@ -17,7 +17,6 @@
#include "SleepLib/event.h" #include "SleepLib/event.h"
//class EventList; //class EventList;
class Machine; class Machine;
const quint32 magic=0xC73216AB;
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 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 };
@ -137,7 +136,7 @@ public:
qint64 last(ChannelID code); qint64 last(ChannelID code);
void UpdateSummaries(); void UpdateSummaries();
EventList * AddEventList(QString chan, EventListType et, EventDataType gain=1.0, EventDataType offset=0.0, EventDataType min=0.0, EventDataType max=0.0, EventDataType rate=0.0); EventList * AddEventList(QString chan, EventListType et, EventDataType gain=1.0, EventDataType offset=0.0, EventDataType min=0.0, EventDataType max=0.0, EventDataType rate=0.0, bool second_field=false);
Machine * machine() { return s_machine; } Machine * machine() { return s_machine; }
protected: protected:
SessionID s_session; SessionID s_session;

View File

@ -162,7 +162,7 @@ Daily::Daily(QWidget *parent,gGraphView * shared, MainWindow *mw)
FRW->AddLayer(AddCPAP(los->add(new gLineOverlayBar(CPAP_Obstructive,QColor("#40c0ff"),"OA")))); FRW->AddLayer(AddCPAP(los->add(new gLineOverlayBar(CPAP_Obstructive,QColor("#40c0ff"),"OA"))));
FRW->AddLayer(AddCPAP(los->add(new gLineOverlayBar(CPAP_ClearAirway,QColor("purple"),"CA")))); FRW->AddLayer(AddCPAP(los->add(new gLineOverlayBar(CPAP_ClearAirway,QColor("purple"),"CA"))));
FRW->AddLayer(AddOXI(new gLineOverlayBar(OXI_SPO2Drop,QColor("red"),"O2"))); FRW->AddLayer(AddOXI(new gLineOverlayBar(OXI_SPO2Drop,QColor("red"),"O2")));
FRW->AddLayer(AddOXI(new gLineOverlayBar(OXI_PulseChange,QColor("blue"),"PC"))); FRW->AddLayer(AddOXI(new gLineOverlayBar(OXI_PulseChange,QColor("blue"),"PC",FT_Dot)));
FRW->AddLayer(AddCPAP(los)); FRW->AddLayer(AddCPAP(los));

104
daily.ui
View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>671</width> <width>671</width>
<height>315</height> <height>433</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -19,7 +19,7 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_9"> <layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>0</number>
</property> </property>
@ -670,66 +670,60 @@
</widget> </widget>
</widget> </widget>
</widget> </widget>
<widget class="QWidget" name=""> <widget class="QSplitter" name="splitter_4">
<layout class="QVBoxLayout" name="verticalLayout_3"> <property name="orientation">
<property name="margin"> <enum>Qt::Vertical</enum>
<number>5</number> </property>
<widget class="QWidget" name="graphMainArea" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<item> </widget>
<widget class="QWidget" name="graphMainArea" native="true"> <widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy"> <property name="maximumSize">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <size>
<horstretch>1</horstretch> <width>16777215</width>
<verstretch>0</verstretch> <height>50</height>
</sizepolicy> </size>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>82</width>
<height>33</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<property name="spacing">
<number>2</number>
</property> </property>
</widget> <property name="margin">
</item> <number>0</number>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>50</height>
</size>
</property> </property>
<property name="verticalScrollBarPolicy"> <item>
<enum>Qt::ScrollBarAlwaysOff</enum> <layout class="QHBoxLayout" name="graphToggleArea">
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>82</width>
<height>33</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<property name="spacing"> <property name="spacing">
<number>2</number> <number>2</number>
</property> </property>
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="graphToggleArea">
<property name="spacing">
<number>2</number>
</property>
</layout>
</item>
</layout> </layout>
</widget> </item>
</widget> </layout>
</item> </widget>
</layout> </widget>
</widget> </widget>
</widget> </widget>
</item> </item>

View File

@ -10,18 +10,19 @@
<p><b>What's New?</b><br/> <p><b>What's New?</b><br/>
<li>Intellipap Support</li> <li>Intellipap Support</li>
<li>Print Support in Daily View, Overview and Oximetry</li>
<li>New Graph tab in Preferences for changing individual graph settings, visibility, etc.</li>
<li>New Respiratory Rate, Tidal Volume and Minute Ventilation graphs for PRS1 users.</li>
<li>Complete rewrite of Oximetery Tab, it's now (hopefully) working much better</li> <li>Complete rewrite of Oximetery Tab, it's now (hopefully) working much better</li>
<li>Sync problems with Live serial CMS50 recording fixed.</li> <li>Oximetry PulseChange & SPO2Drop event flagging.. (Options in Preferences)</li>
<li>Preference option to Skip Login Window</li> <li>Clock problems with Live serial CMS50 recording fixed.</li>
<li>Can now change how much data is shown around events selected in the Event List.</li>
<li>Import now remembers your locations.. There is a preferences tab to edit common locations</li> <li>Import now remembers your locations.. There is a preferences tab to edit common locations</li>
<li>New Respiratory Rate graph for PRS1 users</li> <li>Can now change how much data is shown around events selected in the Event List.</li>
<li>New Graph tab in Preferences for changing individual graph settings</li> <li>Preference option to Skip Login Window</li>
<li>Quite a few other little bugfixes I've forgotten about.</li> <li>Quite a few other little bugfixes I've forgotten about.</li>
<br/> <br/>
<b>What's still missing/broken?</b><br/> <b>What's still missing/broken?</b><br/>
<li>Daily report printing still needs doing.</li> <li>Plenty of bugs, I'm sure of it..</li>
<li>Plenty more I'm sure of it..</li>
</p> </p>
<p><b>Problems & Stuff?</b><br/> <p><b>Problems & Stuff?</b><br/>

View File

@ -353,8 +353,9 @@ void CMS50Serial::on_import_process()
unsigned char a,pl,o2,lastpl=0,lasto2=0; unsigned char a,pl,o2,lastpl=0,lasto2=0;
int i=0; int i=0;
int size=data.size(); int size=data.size();
EventList * pulse=NULL; //(session->eventlist[OXI_Pulse][0]);
EventList * spo2=NULL; // (session->eventlist[OXI_SPO2][0]); EventList * pulse=(session->eventlist[OXI_Pulse][0]);
EventList * spo2=(session->eventlist[OXI_SPO2][0]);
lasttime=f2time[0].toTime_t(); lasttime=f2time[0].toTime_t();
session->SetSessionID(lasttime); session->SetSessionID(lasttime);
lasttime*=1000; lasttime*=1000;
@ -379,8 +380,12 @@ void CMS50Serial::on_import_process()
} }
if (plcnt==0) if (plcnt==0)
session->setFirst(OXI_Pulse,lasttime); session->setFirst(OXI_Pulse,lasttime);
pulse=new EventList(EVL_Event); if (pulse && pulse->count()==0) {
session->eventlist[OXI_Pulse].push_back(pulse);
} else {
pulse=new EventList(EVL_Event);
session->eventlist[OXI_Pulse].push_back(pulse);
}
} }
lastpltime=lasttime; lastpltime=lasttime;
pulse->AddEvent(lasttime,pl); pulse->AddEvent(lasttime,pl);
@ -398,8 +403,11 @@ void CMS50Serial::on_import_process()
} }
if (o2cnt==0) if (o2cnt==0)
session->setFirst(OXI_SPO2,lasttime); session->setFirst(OXI_SPO2,lasttime);
spo2=new EventList(EVL_Event); if (spo2 && spo2->count()==0) {
session->eventlist[OXI_SPO2].push_back(spo2); } else {
spo2=new EventList(EVL_Event);
session->eventlist[OXI_SPO2].push_back(spo2);
}
} }
lasto2time=lasttime; lasto2time=lasttime;
spo2->AddEvent(lasttime,o2); spo2->AddEvent(lasttime,o2);
@ -988,8 +996,8 @@ void Oximetry::on_import_complete(Session * session)
qDebug() << "Oximetry import complete"; qDebug() << "Oximetry import complete";
import_finished(); import_finished();
calcSPO2Drop(session); //calcSPO2Drop(session);
calcPulseChange(session); //calcPulseChange(session);
ui->pulseLCD->display(session->min(OXI_Pulse)); ui->pulseLCD->display(session->min(OXI_Pulse));
ui->spo2LCD->display(session->min(OXI_SPO2)); ui->spo2LCD->display(session->min(OXI_SPO2));

View File

@ -48,10 +48,10 @@ PreferencesDialog::PreferencesDialog(QWidget *parent,Profile * _profile) :
for (int i=0;i<num_masks;i++) { for (int i=0;i<num_masks;i++) {
ui->maskTypeCombo->addItem(masks[i].name); ui->maskTypeCombo->addItem(masks[i].name);
if (masktype==masks[i].name) { /*if (masktype==masks[i].name) {
ui->maskTypeCombo->setCurrentIndex(i); ui->maskTypeCombo->setCurrentIndex(i);
on_maskTypeCombo_activated(i); on_maskTypeCombo_activated(i);
} }*/
} }
QLocale locale=QLocale::system(); QLocale locale=QLocale::system();
QString shortformat=locale.dateFormat(QLocale::ShortFormat); QString shortformat=locale.dateFormat(QLocale::ShortFormat);
@ -71,6 +71,7 @@ PreferencesDialog::PreferencesDialog(QWidget *parent,Profile * _profile) :
ui->startedUsingMask->calendarWidget()->setWeekdayTextFormat(Qt::Sunday, format); ui->startedUsingMask->calendarWidget()->setWeekdayTextFormat(Qt::Sunday, format);
//ui->leakProfile->setColumnWidth(1,ui->leakProfile->width()/2); //ui->leakProfile->setColumnWidth(1,ui->leakProfile->width()/2);
{ {
@ -168,11 +169,25 @@ PreferencesDialog::PreferencesDialog(QWidget *parent,Profile * _profile) :
general["MemoryHog"]=Preference(p_profile,"MemoryHog",PT_Checkbox,"Cache Session Data","Keep session data in memory to improve load speed revisiting the date.",false); general["MemoryHog"]=Preference(p_profile,"MemoryHog",PT_Checkbox,"Cache Session Data","Keep session data in memory to improve load speed revisiting the date.",false);
general["GraphHeight"]=Preference(p_profile,"GraphHeight",PT_Checkbox,"Graph Height","Default Graph Height",160); general["GraphHeight"]=Preference(p_profile,"GraphHeight",PT_Checkbox,"Graph Height","Default Graph Height",160);
general["MaskDescription"]=Preference(p_profile,"MaskDescription",PT_Checkbox,"Mask Description","Whatever you want to record about your mask.",QString()); general["MaskDescription"]=Preference(p_profile,"MaskDescription",PT_Checkbox,"Mask Description","Whatever you want to record about your mask.",QString());
general["MaskStartDate"]=Preference(p_profile,"GraphHeight",PT_Checkbox,"Graph Height","Default Graph Height",QDate::currentDate());
if (!(p_profile)->Exists("MaskStartDate")) {
(PROFILE["MaskStartDate"]=PROFILE.FirstDay());
}
ui->startedUsingMask->setDate((*profile)["MaskStartDate"].toDate());
if (!(p_profile)->Exists("ShowLeaksMode")) {
PROFILE["ShowLeaksMode"]=0;
}
ui->leakModeCombo->setCurrentIndex((*profile)["ShowLeaksMode"].toInt());
if (!(p_profile)->Exists("MaskType")) {
PROFILE["MaskType"]=0;
}
int mt=(*profile)["MaskType"].toInt();
ui->maskTypeCombo->setCurrentIndex(mt);
on_maskTypeCombo_activated(mt);
ui->maskDescription->setText(general["MaskDescription"].value().toString()); ui->maskDescription->setText(general["MaskDescription"].value().toString());
ui->startedUsingMask->setDate(general["MaskStartDate"].value().toDate());
ui->useAntiAliasing->setChecked(general["UseAntiAliasing"].value().toBool()); ui->useAntiAliasing->setChecked(general["UseAntiAliasing"].value().toBool());
ui->useSquareWavePlots->setChecked(general["SquareWavePlots"].value().toBool()); ui->useSquareWavePlots->setChecked(general["SquareWavePlots"].value().toBool());
@ -311,6 +326,8 @@ void PreferencesDialog::Save()
(*profile)["DaySplitTime"]=ui->timeEdit->time(); (*profile)["DaySplitTime"]=ui->timeEdit->time();
(*profile)["AlwaysShowOverlayBars"]=ui->overlayFlagsCombo->currentIndex(); (*profile)["AlwaysShowOverlayBars"]=ui->overlayFlagsCombo->currentIndex();
(*profile)["ShowLeaksMode"]=ui->leakModeCombo->currentIndex();
(*profile)["MaskType"]=ui->maskTypeCombo->currentIndex();
//(*profile)["UseAntiAliasing"]=ui->genOpWidget->item(0)->checkState()==Qt::Checked; //(*profile)["UseAntiAliasing"]=ui->genOpWidget->item(0)->checkState()==Qt::Checked;
//(*profile)["MemoryHog"]=ui->memoryHogCheckbox->isChecked(); //(*profile)["MemoryHog"]=ui->memoryHogCheckbox->isChecked();
//(*profile)["EnableGraphSnapshots"]=ui->genOpWidget->item(2)->checkState()==Qt::Checked; //(*profile)["EnableGraphSnapshots"]=ui->genOpWidget->item(2)->checkState()==Qt::Checked;

View File

@ -38,7 +38,7 @@
<item> <item>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>4</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="importTab"> <widget class="QWidget" name="importTab">
<attribute name="title"> <attribute name="title">
@ -567,7 +567,20 @@ p, li { white-space: pre-wrap; }
</property> </property>
<property name="text"> <property name="text">
<string>Hmmm... Empty Space <string>Hmmm... Empty Space
Mask History could go here one day</string> Mask History could go here one day
For now a reminder for PRS1 Users
The lower leak does not yet show unintentional
leaks. It currently showns the leak line
subtracted from the 0% percentile
(aka the minimum value)
&lt;--- None of this data is being used yet!
To solve this properly requires some fairly
complex forumulae derived from the
Mask Leak Profiles
(Believe it or not, I am not a maths geek)</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>
@ -909,11 +922,15 @@ Mask History could go here one day</string>
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt; &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; } p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt; &lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Please Note:&lt;span style=&quot; font-style:italic;&quot;&gt; It is impossible to sync oximetry data with CPAP data without a valid timestamp.&lt;/span&gt;&lt;/p&gt; &lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Syncing Oximetry and CPAP Data&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;justify&quot; style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt; &lt;p align=&quot;justify&quot; style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;
&lt;p align=&quot;justify&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;CMS50 data imported from SpO2Review (from .spoR files) or the serial import method does &lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;not&lt;/span&gt; have the correct timestamp needed to do this.&lt;/p&gt; &lt;p align=&quot;justify&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;CMS50 data imported from SpO2Review (from .spoR files) or the serial import method does &lt;span style=&quot; font-weight:600; text-decoration: underline;&quot;&gt;not&lt;/span&gt; have the correct timestamp needed to sync.&lt;/p&gt;
&lt;p align=&quot;justify&quot; style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt; &lt;p align=&quot;justify&quot; style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;
&lt;p align=&quot;justify&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Live view mode (using a serial cable) is the only way to acheive an accurate sync on CMS50 oximeters.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> &lt;p align=&quot;justify&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Live view mode (using a serial cable) is one way to acheive an accurate sync on CMS50 oximeters, but does not counter for CPAP clock drift.&lt;/p&gt;
&lt;p align=&quot;justify&quot; style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;
&lt;p align=&quot;justify&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;If you start your Oximeters recording mode at &lt;span style=&quot; font-style:italic;&quot;&gt;exactly &lt;/span&gt;the same time you start your CPAP machine, you can now also achieve sync. &lt;/p&gt;
&lt;p align=&quot;justify&quot; style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;/p&gt;
&lt;p align=&quot;justify&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The serial import process takes the starting time from last nights first CPAP session. (Remember to import your CPAP data first!)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
</widget> </widget>
</item> </item>