OSCAR-code/SleepLib/loader_plugins/prs1_loader.cpp

1335 lines
46 KiB
C++
Raw Normal View History

2011-06-26 08:30:44 +00:00
/*
SleepLib PRS1 Loader Implementation
Author: Mark Watkins <jedimark64@users.sourceforge.net>
License: GPL
*/
2011-07-30 00:36:31 +00:00
#include <QApplication>
2011-06-26 08:30:44 +00:00
#include <QString>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QMessageBox>
#include <QProgressBar>
2011-07-01 10:10:44 +00:00
#include <QDebug>
2011-07-27 09:21:53 +00:00
#include <cmath>
2011-06-26 08:30:44 +00:00
#include "prs1_loader.h"
#include "SleepLib/session.h"
2011-07-10 14:23:07 +00:00
const int PRS1_MAGIC_NUMBER=2;
const int PRS1_SUMMARY_FILE=1;
const int PRS1_EVENT_FILE=2;
const int PRS1_WAVEFORM_FILE=5;
2011-06-26 08:30:44 +00:00
//********************************************************************************************
/// IMPORTANT!!!
//********************************************************************************************
// Please INCREMENT the prs1_data_version in prs1_loader.h when making changes to this loader
// that change loader behaviour or modify channels.
//********************************************************************************************
extern QProgressBar *qprogress;
QHash<int,QString> ModelMap;
2011-06-26 08:30:44 +00:00
PRS1::PRS1(Profile *p,MachineID id):CPAP(p,id)
{
m_class=prs1_class_name;
properties["Brand"]="Philips Respironics";
properties["Model"]="System One";
//SleepFlags= { CPAP_RERA, PRS1_VSnore2, CPAP_FlowLimit, CPAP_Hypopnea, CPAP_Obstructive, CPAP_ClearAirway, CPAP_CSR };
}
PRS1::~PRS1()
{
}
PRS1Loader::PRS1Loader()
{
2011-07-27 09:21:53 +00:00
m_buffer=NULL;
2011-06-26 08:30:44 +00:00
}
PRS1Loader::~PRS1Loader()
{
for (QHash<QString,Machine *>::iterator i=PRS1List.begin(); i!=PRS1List.end(); i++) {
delete i.value();
2011-06-26 08:30:44 +00:00
}
}
Machine *PRS1Loader::CreateMachine(QString serial,Profile *profile)
{
if (!profile)
return NULL;
2011-07-01 10:10:44 +00:00
qDebug() << "Create Machine " << serial;
2011-06-26 08:30:44 +00:00
QVector<Machine *> ml=profile->GetMachines(MT_CPAP);
2011-06-26 08:30:44 +00:00
bool found=false;
QVector<Machine *>::iterator i;
for (i=ml.begin(); i!=ml.end(); i++) {
2011-06-26 08:30:44 +00:00
if (((*i)->GetClass()=="PRS1") && ((*i)->properties["Serial"]==serial)) {
PRS1List[serial]=*i; //static_cast<CPAP *>(*i);
found=true;
break;
}
}
if (found) return *i;
2011-06-26 08:30:44 +00:00
//assert(PRS1List.find(serial)==PRS1List.end())
Machine *m=new PRS1(profile,0);
PRS1List[serial]=m;
profile->AddMachine(m);
m->properties["Serial"]=serial;
return m;
}
bool isdigit(QChar c)
{
if ((c>='0') && (c<='9')) return true;
return false;
}
2011-07-15 13:30:41 +00:00
int PRS1Loader::Open(QString & path,Profile *profile)
2011-06-26 08:30:44 +00:00
{
QString newpath;
QString pseries="P-Series";
if (path.endsWith("/"+pseries)) {
newpath=path;
} else {
newpath=path+"/"+pseries;
}
QDir dir(newpath);
if ((!dir.exists() || !dir.isReadable()))
return 0;
2011-07-27 09:21:53 +00:00
//qDebug() << "PRS1Loader::Open newpath=" << newpath;
2011-06-26 08:30:44 +00:00
dir.setFilter(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoSymLinks);
dir.setSorting(QDir::Name);
QFileInfoList flist=dir.entryInfoList();
QList<QString> SerialNumbers;
QList<QString>::iterator sn;
2011-06-26 08:30:44 +00:00
for (int i=0;i<flist.size();i++) {
QFileInfo fi=flist.at(i);
QString filename=fi.fileName();
if ((filename[0]=='P') && (isdigit(filename[1])) && (isdigit(filename[2]))) {
SerialNumbers.push_back(filename);
} else if (filename.toLower()=="last.txt") { // last.txt points to the current serial number
QString file=fi.canonicalFilePath();
QFile f(file);
if (!fi.isReadable()) {
2011-07-01 10:10:44 +00:00
qDebug() << "PRS1Loader: last.txt exists but I couldn't read it!";
2011-06-26 08:30:44 +00:00
continue;
}
if (!f.open(QIODevice::ReadOnly)) {
2011-07-01 10:10:44 +00:00
qDebug() << "PRS1Loader: last.txt exists but I couldn't open it!";
2011-06-26 08:30:44 +00:00
continue;
}
last=f.readLine(64);
last=last.trimmed();
f.close();
}
}
if (SerialNumbers.empty()) return 0;
2011-07-27 09:21:53 +00:00
m_buffer=new unsigned char [max_load_buffer_size]; //allocate once and reuse.
2011-06-26 08:30:44 +00:00
Machine *m;
for (sn=SerialNumbers.begin(); sn!=SerialNumbers.end(); sn++) {
QString s=*sn;
m=CreateMachine(s,profile);
try {
if (m) OpenMachine(m,newpath+"/"+(*sn),profile);
} catch(OneTypePerDay e) {
profile->DelMachine(m);
PRS1List.erase(PRS1List.find(s));
2011-06-26 08:30:44 +00:00
QMessageBox::warning(NULL,"Import Error","This Machine Record cannot be imported in this profile.\nThe Day records overlap with already existing content.",QMessageBox::Ok);
delete m;
}
}
2011-07-27 09:21:53 +00:00
delete [] m_buffer;
2011-06-26 08:30:44 +00:00
return PRS1List.size();
}
2011-07-15 13:30:41 +00:00
2011-06-26 08:30:44 +00:00
bool PRS1Loader::ParseProperties(Machine *m,QString filename)
{
QFile f(filename);
if (!f.open(QIODevice::ReadOnly))
return false;
QString line;
QHash<QString,QString> prop;
2011-06-26 08:30:44 +00:00
QString s=f.readLine();
QChar sep='=';
QString key,value;
while (!f.atEnd()) {
key=s.section(sep,0,0); //BeforeFirst(sep);
if (key==s) continue;
value=s.section(sep,1).trimmed(); //AfterFirst(sep).Strip();
if (value==s) continue;
prop[key]=value;
s=f.readLine();
}
bool ok;
QString pt=prop["ProductType"];
int i=pt.toInt(&ok,0);
2011-06-26 08:30:44 +00:00
if (ok) {
if (ModelMap.find(i)!=ModelMap.end()) {
m->properties["SubModel"]=ModelMap[i];
}
}
if (prop["SerialNumber"]!=m->properties["Serial"]) {
2011-07-01 10:10:44 +00:00
qDebug() << "Serial Number in PRS1 properties.txt doesn't match directory structure";
} else prop.erase(prop.find("SerialNumber")); // already got it stored.
2011-06-26 08:30:44 +00:00
for (QHash<QString,QString>::iterator i=prop.begin(); i!=prop.end(); i++) {
m->properties[i.key()]=i.value();
2011-06-26 08:30:44 +00:00
}
f.close();
return true;
}
int PRS1Loader::OpenMachine(Machine *m,QString path,Profile *profile)
{
2011-07-01 10:10:44 +00:00
qDebug() << "Opening PRS1 " << path;
2011-06-26 08:30:44 +00:00
QDir dir(path);
if (!dir.exists() || (!dir.isReadable()))
return false;
dir.setFilter(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoSymLinks);
dir.setSorting(QDir::Name);
QFileInfoList flist=dir.entryInfoList();
QString filename;
if (qprogress) qprogress->setValue(0);
QList<QString> paths;
2011-06-26 08:30:44 +00:00
for (int i=0;i<flist.size();i++) {
QFileInfo fi=flist.at(i);
filename=fi.fileName();
if ((filename[0].toLower()=='p') && (isdigit(filename[1]))) {
paths.push_back(fi.canonicalFilePath());
} else if (filename.toLower()=="properties.txt") {
ParseProperties(m,fi.canonicalFilePath());
} else if (filename.toLower()=="e") {
// don't really give a crap about .004 files yet.
}
//if (qprogress) qprogress->Pulse();
}
SessionID session;
long ext;
typedef QVector<QString> StringList;
QMap<SessionID,StringList> sessfiles;
2011-06-26 08:30:44 +00:00
int size=paths.size();
int cnt=0;
bool ok;
for (QList<QString>::iterator p=paths.begin(); p!=paths.end(); p++) {
2011-06-26 08:30:44 +00:00
dir.setPath(*p);
if (!dir.exists() || !dir.isReadable()) continue;
flist=dir.entryInfoList();
for (int i=0;i<flist.size();i++) {
QFileInfo fi=flist.at(i);
2011-07-03 09:13:54 +00:00
QString ext_s=fi.fileName().section(".",-1);
QString session_s=fi.fileName().section(".",0,-2);
2011-06-26 08:30:44 +00:00
ext=ext_s.toLong(&ok);
if (!ok) continue;
session=session_s.toLong(&ok);
if (!ok) continue;
if (sessfiles[session].capacity()==0) sessfiles[session].resize(3);
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();
}
cnt++;
2011-06-30 10:56:22 +00:00
if (qprogress) qprogress->setValue((float(cnt)/float(size)*33.0));
2011-07-30 00:36:31 +00:00
QApplication::processEvents();
2011-06-26 08:30:44 +00:00
}
}
size=sessfiles.size();
if (size==0)
return 0;
cnt=0;
for (QMap<SessionID,StringList>::iterator s=sessfiles.begin(); s!=sessfiles.end(); s++) {
session=s.key();
2011-06-26 08:30:44 +00:00
cnt++;
2011-06-30 10:56:22 +00:00
if (qprogress) qprogress->setValue(33.0+(float(cnt)/float(size)*33.0));
2011-07-30 00:36:31 +00:00
QApplication::processEvents();
2011-06-26 08:30:44 +00:00
if (m->SessionExists(session)) continue;
if (s.value()[0].isEmpty()) continue;
2011-06-26 08:30:44 +00:00
Session *sess=new Session(m,session);
if (!OpenSummary(sess,s.value()[0])) {
//qWarning() << "PRS1Loader: Dodgy summary file " << s.value()[0];
2011-06-26 08:30:44 +00:00
delete sess;
continue;
2011-06-26 08:30:44 +00:00
}
//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];
2011-06-26 08:30:44 +00:00
}
}
if (!s.value()[2].isEmpty()) {
if (!OpenWaveforms(sess,s.value()[2])) {
qWarning() << "PRS1Loader: Couldn't open event file " << s.value()[2];
2011-06-26 08:30:44 +00:00
}
}
2011-08-05 07:52:32 +00:00
if ((sess->last() - sess->first()) == 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";
2011-06-26 08:30:44 +00:00
delete sess;
continue;
}
ChannelID e[]={
CPAP_Obstructive, CPAP_Hypopnea, CPAP_ClearAirway, CPAP_RERA, CPAP_FlowLimit, CPAP_VSnore,
CPAP_CSR, PRS1_VSnore2
};
for (unsigned i=0;i<sizeof(e)/sizeof(ChannelID);i++) {
sess->count(e[i]);
sess->max(e[i]);
sess->min(e[i]);
sess->avg(e[i]);
sess->p90(e[i]);
sess->cph(e[i]);
sess->sph(e[i]);
}
sess->setCph(CPAP_AHI,sess->cph(CPAP_Obstructive)+sess->cph(CPAP_Hypopnea)+sess->cph(CPAP_ClearAirway));
sess->setSph(CPAP_AHI,sess->sph(CPAP_Obstructive)+sess->sph(CPAP_Hypopnea)+sess->sph(CPAP_ClearAirway));
ChannelID a[]={
CPAP_FlowRate, CPAP_MaskPressure, CPAP_Leak, CPAP_Snore, CPAP_EPAP,
CPAP_IPAP, CPAP_TidalVolume, CPAP_RespiratoryRate,
CPAP_PatientTriggeredBreaths,CPAP_MinuteVentilation, CPAP_IPAP_Low,
CPAP_IPAP_High, CPAP_FlowLimitGraph, CPAP_PressureSupport, CPAP_PressurePulse
};
for (unsigned i=0;i<sizeof(a)/sizeof(ChannelID);i++) {
if (sess->eventlist.contains(a[i])) {
sess->min(a[i]);
sess->max(a[i]);
sess->avg(a[i]);
sess->wavg(a[i]);
sess->p90(a[i]);
sess->cph(a[i]);
}
}
2011-07-12 12:43:10 +00:00
if (sess->count(CPAP_IPAP)>0) {
//sess->summaryCPAP_Mode]!=MODE_ASV)
sess->settings[CPAP_Mode]=MODE_BIPAP;
2011-07-12 12:43:10 +00:00
if (sess->settings[PRS1_PressureReliefType].toInt()!=PR_NONE) {
sess->settings[PRS1_PressureReliefType]=PR_BIFLEX;
2011-06-26 08:30:44 +00:00
}
sess->setAvg(CPAP_Pressure,(sess->avg(CPAP_EPAP)+sess->avg(CPAP_IPAP))/2.0);
sess->setWavg(CPAP_Pressure,(sess->wavg(CPAP_EPAP)+sess->wavg(CPAP_IPAP))/2.0);
sess->setMin(CPAP_Pressure,sess->min(CPAP_EPAP));
sess->setMax(CPAP_Pressure,sess->min(CPAP_IPAP));
sess->set90p(CPAP_Pressure,sess->min(CPAP_IPAP));
sess->p90(CPAP_EPAP);
sess->p90(CPAP_IPAP);
2011-06-26 08:30:44 +00:00
} else {
sess->avg(CPAP_Pressure);
sess->wavg(CPAP_Pressure);
sess->p90(CPAP_Pressure);
sess->min(CPAP_Pressure);
sess->max(CPAP_Pressure);
sess->cph(CPAP_Pressure);
if (!sess->settings.contains(PRS1_PressureMin)) {
sess->settings[CPAP_BrokenSummary]=true;
2011-07-03 02:43:50 +00:00
//sess->set_last(sess->first());
if (sess->min(CPAP_Pressure)==sess->max(CPAP_Pressure)) {
sess->settings[CPAP_Mode]=MODE_CPAP; // no ramp
2011-06-26 08:30:44 +00:00
} else {
sess->settings[CPAP_Mode]=MODE_UNKNOWN;
2011-06-26 08:30:44 +00:00
}
sess->settings[PRS1_PressureReliefType]=PR_UNKNOWN;
2011-06-26 08:30:44 +00:00
}
}
if (sess->settings[CPAP_Mode]==MODE_CPAP) {
sess->settings[PRS1_PressureMax]=sess->settings[PRS1_PressureMin];
2011-06-26 08:30:44 +00:00
}
//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);
2011-06-26 08:30:44 +00:00
}
QString s;
s.sprintf("%i",prs1_data_version);
m->properties["DataVersion"]=s;
m->Save(); // Save any new sessions to disk in our format */
2011-06-26 08:30:44 +00:00
if (qprogress) qprogress->setValue(100);
2011-07-30 00:36:31 +00:00
//qDebug() << "OpenMachine Done";
2011-06-26 08:30:44 +00:00
return true;
}
bool PRS1Loader::OpenSummary(Session *session,QString filename)
{
2011-07-18 02:33:25 +00:00
int size,seconds,br,htype,version,sequence;
qint64 timestamp;
2011-06-26 08:30:44 +00:00
unsigned char header[24];
unsigned char ext,sum;
2011-07-01 10:10:44 +00:00
//qDebug() << "Opening PRS1 Summary " << filename;
2011-06-26 08:30:44 +00:00
QFile f(filename);
2011-07-10 14:23:07 +00:00
2011-06-26 08:30:44 +00:00
if (!f.open(QIODevice::ReadOnly))
return false;
if (!f.exists())
return false;
int hl=16;
br=f.read((char *)header,hl);
2011-07-10 14:23:07 +00:00
if (header[0]!=PRS1_MAGIC_NUMBER)
2011-06-26 08:30:44 +00:00
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];
2011-07-10 14:23:07 +00:00
htype=header[3]; // 00 = normal // 01=waveform // could be a bool?
version=header[4];
2011-07-18 02:33:25 +00:00
sequence=sequence;
version=version; // don't need it here?
htype=htype; // shut the warning up.. this is useless.
2011-07-10 14:23:07 +00:00
if (ext!=PRS1_SUMMARY_FILE)
2011-06-26 08:30:44 +00:00
return false;
size-=(hl+2);
2011-07-10 14:23:07 +00:00
// Calculate header checksum and compare to verify header
2011-06-26 08:30:44 +00:00
sum=0;
for (int i=0; i<hl-1; i++) sum+=header[i];
if (sum!=header[hl-1])
return false;
if (size<=19) {
2011-07-20 16:48:21 +00:00
// qDebug() << "Ignoring short session file " << filename;
2011-06-26 08:30:44 +00:00
return false;
}
qint64 date=timestamp*1000;
2011-06-26 08:30:44 +00:00
2011-07-10 14:23:07 +00:00
//memset(m_buffer,0,size);
2011-06-26 08:30:44 +00:00
unsigned char * buffer=m_buffer;
br=f.read((char *)buffer,size);
if (br<size) {
return false;
}
if (size<0x30)
return true;
session->set_first(date);
2011-07-10 14:23:07 +00:00
2011-06-26 08:30:44 +00:00
double max;
session->settings[PRS1_PressureMin]=(EventDataType)buffer[0x03]/10.0;
session->settings[PRS1_PressureMax]=max=(EventDataType)buffer[0x04]/10.0;
2011-07-10 14:23:07 +00:00
int offset=0;
if (buffer[0x05]!=0) { // This is a time value for ASV stuff
// non zero adds extra fields..
offset=4;
}
session->settings[PRS1_RampTime]=(int)buffer[offset+0x06]; // Minutes. Convert to seconds/hours here?
session->settings[PRS1_RampPressure]=(EventDataType)buffer[offset+0x07]/10.0;
2011-06-26 08:30:44 +00:00
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;
2011-06-26 08:30:44 +00:00
// This is incorrect..
2011-07-10 14:23:07 +00:00
if (buffer[offset+0x08] & 0x80) { // Flex Setting
if (buffer[offset+0x08] & 0x08) {
if (max>0) session->settings[PRS1_PressureReliefType]=(int)PR_AFLEX;
else session->settings[PRS1_PressureReliefType]=(int)PR_CFLEXPLUS;
} else session->settings[PRS1_PressureReliefType]=(int)PR_CFLEX;
} else session->settings[PRS1_PressureReliefType]=(int)PR_NONE;
session->settings[PRS1_PressureReliefSetting]=(int)buffer[offset+0x08] & 3;
session->settings[PRS1_HumidifierSetting]=(int)buffer[offset+0x09]&0x0f;
session->settings[PRS1_HumidifierStatus]=(buffer[offset+0x09]&0x80)==0x80;
session->settings[PRS1_SystemLockStatus]=(buffer[offset+0x0a]&0x80)==0x80;
session->settings[PRS1_SystemOneResistanceStatus]=(buffer[offset+0x0a]&0x40)==0x40;
session->settings[PRS1_SystemOneResistanceSetting]=(int)buffer[offset+0x0a]&7;
session->settings[PRS1_HoseDiameter]=(int)((buffer[offset+0x0a]&0x08)?15:22);
session->settings[PRS1_AutoOff]=(buffer[offset+0x0c]&0x10)==0x10;
session->settings[PRS1_MaskAlert]=(buffer[offset+0x0c]&0x08)==0x08;
session->settings[PRS1_ShowAHI]=(buffer[offset+0x0c]&0x04)==0x04;
2011-07-10 14:23:07 +00:00
2011-07-21 02:17:43 +00:00
unsigned duration=buffer[offset+0x14] | (buffer[0x15] << 8);
//session->settings[CPAP_Duration]=(int)duration;
2011-07-01 10:10:44 +00:00
//qDebug() << "ID: " << session->session() << " " << duration;
//float hours=float(duration)/3600.0;
//session->set_hours(hours);
2011-07-10 14:23:07 +00:00
if (!duration)
return false;
2011-07-03 02:43:50 +00:00
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;
2011-06-26 08:30:44 +00:00
if (max==0) {
session->settings[PRS1_PressureAvg]=session->settings[PRS1_PressureMin];
2011-06-26 08:30:44 +00:00
}
// Not using these because sometimes this summary is broken.
2011-07-21 02:17:43 +00:00
/*/if (size==0x4d) {
2011-06-26 08:30:44 +00:00
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);
2011-07-21 02:17:43 +00:00
}*/
2011-06-26 08:30:44 +00:00
return true;
}
2011-07-10 14:23:07 +00:00
// v2 event parser.
2011-07-18 02:33:25 +00:00
bool PRS1Loader::Parse002(Session *session,unsigned char *buffer,int size,qint64 timestamp)
2011-06-26 08:30:44 +00:00
{
ChannelID Codes[]={
PRS1_Unknown00, PRS1_Unknown01, CPAP_Pressure, CPAP_EPAP, CPAP_PressurePulse, CPAP_RERA, CPAP_Obstructive, CPAP_ClearAirway,
2011-06-26 08:30:44 +00:00
PRS1_Unknown08, PRS1_Unknown09, CPAP_Hypopnea, PRS1_Unknown0B, CPAP_FlowLimit, CPAP_VSnore, PRS1_Unknown0E, CPAP_CSR, PRS1_Unknown10,
CPAP_Leak, PRS1_Unknown12
};
int ncodes=sizeof(Codes)/sizeof(ChannelID);
2011-06-26 08:30:44 +00:00
2011-07-27 09:21:53 +00:00
EventList * Code[0x20]={NULL};
2011-07-10 14:23:07 +00:00
EventDataType data[10];
2011-06-26 08:30:44 +00:00
session->updateFirst(timestamp);
2011-07-18 02:33:25 +00:00
//qint64 start=timestamp;
qint64 t=timestamp;
qint64 tt;
2011-06-26 08:30:44 +00:00
int pos=0;
int cnt=0;
2011-07-18 02:33:25 +00:00
short delta;//,duration;
2011-06-26 08:30:44 +00:00
while (pos<size) {
2011-07-10 14:23:07 +00:00
unsigned char code=buffer[pos++];
if (code>=ncodes) {
qDebug() << "Illegal PRS1 code " << hex << int(code) << " appeared at " << hex << pos+16;
return false;
}
2011-07-20 16:01:31 +00:00
//if (code==0xe) {
// pos+=2;
//} else
delta=0;
2011-06-26 08:30:44 +00:00
if (code!=0x12) {
2011-07-20 16:01:31 +00:00
//delta=buffer[pos];
2011-07-18 02:33:25 +00:00
//duration=buffer[pos+1];
2011-07-20 16:01:31 +00:00
delta=buffer[pos+1] << 8 | buffer[pos];
2011-06-26 08:30:44 +00:00
pos+=2;
t+=delta*1000;
tt=t;//+(delta*1000);
2011-07-20 16:01:31 +00:00
//QDateTime d=QDateTime::fromMSecsSinceEpoch(t);
//qDebug()<< d.toString("yyyy-MM-dd HH:mm:ss") << ": " << hex << pos+15 << " " << hex << int(code) << int(delta);
2011-06-26 08:30:44 +00:00
}
2011-06-26 08:30:44 +00:00
cnt++;
2011-07-18 02:33:25 +00:00
//int fc=0;
2011-06-26 08:30:44 +00:00
switch (code) {
2011-07-27 09:21:53 +00:00
case 0x00: // Unknown 00
if (!Code[0]) {
Code[0]=new EventList(PRS1_Unknown00,EVL_Event);
session->eventlist[PRS1_Unknown00].push_back(Code[0]);
}
Code[0]->AddEvent(t,buffer[pos++]);
2011-07-10 14:23:07 +00:00
break;
2011-07-27 09:21:53 +00:00
case 0x01: // Unknown
if (!Code[1]) {
Code[1]=new EventList(PRS1_Unknown01,EVL_Event);
session->eventlist[PRS1_Unknown01].push_back(Code[1]);
}
Code[1]->AddEvent(t,0);
2011-07-10 14:23:07 +00:00
break;
2011-06-26 08:30:44 +00:00
case 0x02: // Pressure
2011-07-27 09:21:53 +00:00
if (!Code[2]) {
Code[2]=new EventList(CPAP_Pressure,EVL_Event,0.1);
session->eventlist[CPAP_Pressure].push_back(Code[2]);
}
Code[2]->AddEvent(t,buffer[pos++]);
2011-06-26 08:30:44 +00:00
break;
2011-07-12 12:43:10 +00:00
case 0x03: // BIPAP Pressure
2011-07-27 09:21:53 +00:00
if (!Code[3]) {
Code[3]=new EventList(CPAP_EPAP,EVL_Event,0.1);
session->eventlist[CPAP_EPAP].push_back(Code[3]);
Code[4]=new EventList(CPAP_IPAP,EVL_Event,0.1);
session->eventlist[CPAP_IPAP].push_back(Code[4]);
Code[5]=new EventList(CPAP_PressureSupport,EVL_Event,0.1);
session->eventlist[CPAP_PressureSupport].push_back(Code[5]);
2011-07-27 09:21:53 +00:00
}
Code[3]->AddEvent(t,data[0]=buffer[pos++]);
Code[4]->AddEvent(t,data[1]=buffer[pos++]);
Code[5]->AddEvent(t,data[1]-data[0]);
2011-07-12 12:43:10 +00:00
break;
2011-06-26 08:30:44 +00:00
case 0x04: // Pressure Pulse
2011-07-27 09:21:53 +00:00
if (!Code[6]) {
Code[6]=new EventList(CPAP_PressurePulse,EVL_Event);
session->eventlist[CPAP_PressurePulse].push_back(Code[6]);
2011-07-27 09:21:53 +00:00
}
Code[6]->AddEvent(t,buffer[pos++]);
2011-07-20 16:01:31 +00:00
//qDebug() << hex << data[0];
2011-06-26 08:30:44 +00:00
break;
case 0x05: // RERA
2011-07-27 09:21:53 +00:00
data[0]=buffer[pos++];
tt=t-(data[0]*1000);
2011-07-27 09:21:53 +00:00
if (!Code[7]) {
Code[7]=new EventList(CPAP_RERA,EVL_Event);
session->eventlist[CPAP_RERA].push_back(Code[7]);
}
Code[7]->AddEvent(tt,data[0]);
break;
2011-06-26 08:30:44 +00:00
case 0x06: // Obstructive Apoanea
2011-07-27 09:21:53 +00:00
data[0]=buffer[pos++];
tt=t-(data[0]*1000);
2011-07-27 09:21:53 +00:00
if (!Code[8]) {
Code[8]=new EventList(CPAP_Obstructive,EVL_Event);
session->eventlist[CPAP_Obstructive].push_back(Code[8]);
}
Code[8]->AddEvent(tt,data[0]);
break;
2011-06-26 08:30:44 +00:00
case 0x07: // Clear Airway
2011-07-27 09:21:53 +00:00
data[0]=buffer[pos++];
tt=t-(data[0]*1000);
2011-07-27 09:21:53 +00:00
if (!Code[9]) {
Code[9]=new EventList(CPAP_ClearAirway,EVL_Event);
session->eventlist[CPAP_ClearAirway].push_back(Code[9]);
}
Code[9]->AddEvent(tt,data[0]);
break;
2011-07-12 12:43:10 +00:00
case 0x0a: // Hypopnea
2011-07-27 09:21:53 +00:00
data[0]=buffer[pos++];
tt=t-(data[0]*1000);
2011-07-27 09:21:53 +00:00
if (!Code[10]) {
Code[10]=new EventList(CPAP_Hypopnea,EVL_Event);
session->eventlist[CPAP_Hypopnea].push_back(Code[10]);
}
Code[10]->AddEvent(tt,data[0]);
break;
2011-06-26 08:30:44 +00:00
case 0x0c: // Flow Limitation
2011-07-10 14:23:07 +00:00
data[0]=buffer[pos++];
tt=t-(data[0]*1000);
2011-07-27 09:21:53 +00:00
if (!Code[11]) {
Code[11]=new EventList(CPAP_FlowLimit,EVL_Event);
session->eventlist[CPAP_FlowLimit].push_back(Code[11]);
}
Code[11]->AddEvent(tt,data[0]);
2011-06-26 08:30:44 +00:00
break;
2011-07-27 09:21:53 +00:00
case 0x0b: // Hypopnea related code
2011-07-11 05:46:12 +00:00
data[0]=buffer[pos++];
data[1]=buffer[pos++];
2011-07-27 09:21:53 +00:00
if (!Code[12]) {
Code[12]=new EventList(PRS1_Unknown0B,EVL_Event);
session->eventlist[PRS1_Unknown0B].push_back(Code[12]);
}
// FIXME
Code[12]->AddEvent(t,data[0]);
2011-07-11 05:46:12 +00:00
break;
2011-06-26 08:30:44 +00:00
case 0x0d: // Vibratory Snore
2011-07-27 09:21:53 +00:00
if (!Code[13]) {
Code[13]=new EventList(CPAP_VSnore,EVL_Event);
session->eventlist[CPAP_VSnore].push_back(Code[13]);
}
Code[13]->AddEvent(t,0);
2011-06-26 08:30:44 +00:00
break;
2011-07-27 09:21:53 +00:00
case 0x11: // Leak Rate & Snore Graphs
2011-06-26 08:30:44 +00:00
data[0]=buffer[pos++];
data[1]=buffer[pos++];
2011-07-27 09:21:53 +00:00
if (!Code[14]) {
Code[14]=new EventList(CPAP_Leak,EVL_Event);
session->eventlist[CPAP_Leak].push_back(Code[14]);
Code[15]=new EventList(CPAP_Snore,EVL_Event);
session->eventlist[CPAP_Snore].push_back(Code[15]);
}
Code[14]->AddEvent(t,data[0]);
Code[15]->AddEvent(t,data[1]);
2011-07-10 14:23:07 +00:00
if (data[1]>0) {
2011-07-27 09:21:53 +00:00
if (!Code[16]) {
Code[16]=new EventList(PRS1_VSnore2,EVL_Event);
session->eventlist[PRS1_VSnore2].push_back(Code[16]);
}
Code[16]->AddEvent(t,data[1]);
2011-06-26 08:30:44 +00:00
}
break;
case 0x0e: // Unknown
2011-07-20 16:01:31 +00:00
data[0]=((char *)buffer)[pos++];
data[1]=buffer[pos++]; //(buffer[pos+1] << 8) | buffer[pos];
//data[0]/=10.0;
//pos+=2;
2011-07-10 14:23:07 +00:00
data[2]=buffer[pos++];
2011-07-27 09:21:53 +00:00
if (!Code[17]) {
Code[17]=new EventList(PRS1_Unknown0E,EVL_Event);
session->eventlist[PRS1_Unknown0E].push_back(Code[17]);
}
Code[17]->AddEvent(t,data[0]);
2011-07-20 16:01:31 +00:00
//qDebug() << hex << data[0] << data[1] << data[2];
2011-07-27 09:21:53 +00:00
//session->AddEvent(new Event(t,cpapcode, 0, data, 3));
//tt-=data[1]*1000;
//session->AddEvent(new Event(t,CPAP_CSR, data, 2));
2011-07-10 14:23:07 +00:00
break;
2011-06-26 08:30:44 +00:00
case 0x10: // Unknown
data[0]=buffer[pos++]; // << 8) | buffer[pos];
data[1]=buffer[pos++];
data[2]=buffer[pos++];
2011-07-27 09:21:53 +00:00
if (!Code[20]) {
Code[20]=new EventList(PRS1_Unknown10,EVL_Event);
session->eventlist[PRS1_Unknown10].push_back(Code[20]);
}
Code[20]->AddEvent(t,data[0]);
2011-06-26 08:30:44 +00:00
break;
case 0x0f: // Cheyne Stokes Respiration
data[0]=buffer[pos+1]<<8 | buffer[pos];
pos+=2;
data[1]=buffer[pos++];
tt=t-data[1]*1000;
2011-07-27 09:21:53 +00:00
if (!Code[23]) {
Code[23]=new EventList(CPAP_CSR,EVL_Event);
session->eventlist[CPAP_CSR].push_back(Code[23]);
}
Code[23]->AddEvent(tt,data[0]);
2011-06-26 08:30:44 +00:00
break;
case 0x12: // Summary
data[0]=buffer[pos++];
data[1]=buffer[pos++];
data[2]=buffer[pos+1]<<8 | buffer[pos];
pos+=2;
2011-07-27 09:21:53 +00:00
if (!Code[24]) {
Code[24]=new EventList(PRS1_Unknown12,EVL_Event);
session->eventlist[PRS1_Unknown12].push_back(Code[24]);
}
Code[24]->AddEvent(t,data[0]);
2011-06-26 08:30:44 +00:00
break;
default:
// ERROR!!!
2011-07-10 14:23:07 +00:00
qWarning() << "Some new fandangled PRS1 code detected " << hex << int(code) << " at " << pos+15;
2011-07-03 11:49:47 +00:00
return false;
2011-06-26 08:30:44 +00:00
}
}
session->updateLast(t);
2011-07-27 09:21:53 +00:00
2011-06-26 08:30:44 +00:00
return true;
}
2011-07-18 02:33:25 +00:00
bool PRS1Loader::Parse002ASV(Session *session,unsigned char *buffer,int size,qint64 timestamp)
2011-07-10 14:23:07 +00:00
{
ChannelID Codes[]={
PRS1_Unknown00, PRS1_Unknown01, CPAP_Pressure, CPAP_EPAP, CPAP_PressurePulse, CPAP_Obstructive,
2011-07-27 09:21:53 +00:00
CPAP_ClearAirway, CPAP_Hypopnea, PRS1_Unknown08, CPAP_FlowLimit, PRS1_Unknown0A, CPAP_CSR,
PRS1_Unknown0C, CPAP_VSnore, PRS1_Unknown0E, PRS1_Unknown0F, PRS1_Unknown10,
2011-07-10 14:23:07 +00:00
CPAP_Leak, PRS1_Unknown12
};
int ncodes=sizeof(Codes)/sizeof(ChannelID);
2011-07-10 14:23:07 +00:00
2011-07-27 09:21:53 +00:00
EventList * Code[0x20]={NULL};
//for (int i=0;i<0x20;i++) Code[i]=NULL;
session->updateFirst(timestamp);
2011-07-10 14:23:07 +00:00
EventDataType data[10];
2011-07-18 02:33:25 +00:00
//qint64 start=timestamp;
2011-07-10 14:23:07 +00:00
qint64 t=timestamp;
qint64 tt;
int pos=0;
int cnt=0;
2011-07-18 02:33:25 +00:00
short delta;//,duration;
2011-07-10 16:20:51 +00:00
QDateTime d;
2011-07-10 14:23:07 +00:00
while (pos<size) {
unsigned char code=buffer[pos++];
if (code>=ncodes) {
qDebug() << "Illegal PRS1 code " << hex << int(code) << " appeared at " << hex << pos+16;
return false;
}
2011-07-10 16:20:51 +00:00
//QDateTime d=QDateTime::fromMSecsSinceEpoch(t);
//qDebug()<< d.toString("yyyy-MM-dd HH:mm:ss") << ": " << hex << pos+15 << " " << hex << int(code) ;
2011-07-19 16:44:03 +00:00
if (code==0) {
} else
2011-07-10 14:23:07 +00:00
if (code!=0x12) {
delta=buffer[pos];
2011-07-18 02:33:25 +00:00
//duration=buffer[pos+1];
2011-07-10 14:23:07 +00:00
//delta=buffer[pos+1] << 8 | buffer[pos];
pos+=2;
t+=delta*1000;
}
ChannelID cpapcode=Codes[(int)code];
2011-07-18 02:33:25 +00:00
//EventDataType PS;
2011-07-10 14:23:07 +00:00
tt=t;
cnt++;
int fc=0;
2011-07-27 09:21:53 +00:00
2011-07-10 14:23:07 +00:00
switch (code) {
2011-07-10 16:20:51 +00:00
case 0x00: // Unknown (ASV Pressure value)
2011-07-10 14:23:07 +00:00
// offset?
data[0]=buffer[pos++];
fc++;
2011-07-10 16:20:51 +00:00
if (!buffer[pos-1]) { // WTH???
2011-07-10 14:23:07 +00:00
data[1]=buffer[pos++];
fc++;
}
if (!buffer[pos-1]) {
data[2]=buffer[pos++];
fc++;
}
break;
2011-07-27 09:21:53 +00:00
case 0x01: // Unknown
if (!Code[1]) {
Code[1]=new EventList(cpapcode,EVL_Event,0.1);
session->eventlist[cpapcode].push_back(Code[1]);
}
Code[1]->AddEvent(t,0);
break;
2011-07-10 14:23:07 +00:00
case 0x02: // Pressure
2011-07-27 09:21:53 +00:00
data[0]=buffer[pos++];
if (!Code[2]) {
Code[2]=new EventList(cpapcode,EVL_Event,0.1);
session->eventlist[cpapcode].push_back(Code[2]);
}
Code[2]->AddEvent(t,data[0]);
2011-07-10 14:23:07 +00:00
break;
case 0x04: // Pressure Pulse
data[0]=buffer[pos++];
2011-07-27 09:21:53 +00:00
if (!Code[3]) {
Code[3]=new EventList(cpapcode,EVL_Event);
session->eventlist[cpapcode].push_back(Code[3]);
}
Code[3]->AddEvent(t,data[0]);
2011-07-10 14:23:07 +00:00
break;
case 0x05:
2011-07-27 09:21:53 +00:00
data[0]=buffer[pos++];
tt-=data[0]*1000; // Subtract Time Offset
if (!Code[4]) {
Code[4]=new EventList(cpapcode,EVL_Event);
session->eventlist[cpapcode].push_back(Code[4]);
}
Code[4]->AddEvent(tt,data[0]);
break;
2011-07-10 14:23:07 +00:00
case 0x06:
2011-07-27 09:21:53 +00:00
data[0]=buffer[pos++];
tt-=data[0]*1000; // Subtract Time Offset
if (!Code[5]) {
Code[5]=new EventList(cpapcode,EVL_Event);
session->eventlist[cpapcode].push_back(Code[5]);
}
Code[5]->AddEvent(tt,data[0]);
break;
2011-07-10 14:23:07 +00:00
case 0x07:
2011-07-27 09:21:53 +00:00
data[0]=buffer[pos++];
tt-=data[0]*1000; // Subtract Time Offset
if (!Code[6]) {
Code[6]=new EventList(cpapcode,EVL_Event);
session->eventlist[cpapcode].push_back(Code[6]);
}
Code[6]->AddEvent(tt,data[0]);
break;
case 0x08: // ASV Codes
data[0]=buffer[pos++];
tt-=data[0]*1000; // Subtract Time Offset
if (!Code[10]) {
Code[10]=new EventList(cpapcode,EVL_Event);
session->eventlist[cpapcode].push_back(Code[10]);
}
Code[10]->AddEvent(tt,data[0]);
break;
case 0x09: // ASV Codes
data[0]=buffer[pos++];
tt-=data[0]*1000; // Subtract Time Offset
if (!Code[11]) {
Code[11]=new EventList(cpapcode,EVL_Event);
session->eventlist[cpapcode].push_back(Code[11]);
}
Code[11]->AddEvent(tt,data[0]);
break;
case 0x0a:
data[0]=buffer[pos++];
tt-=data[0]*1000; // Subtract Time Offset
if (!Code[7]) {
Code[7]=new EventList(cpapcode,EVL_Event);
session->eventlist[cpapcode].push_back(Code[7]);
}
Code[7]->AddEvent(tt,data[0]);
break;
2011-07-10 14:23:07 +00:00
case 0x0c:
data[0]=buffer[pos++];
tt-=data[0]*1000; // Subtract Time Offset
2011-07-27 09:21:53 +00:00
if (!Code[8]) {
Code[8]=new EventList(cpapcode,EVL_Event);
session->eventlist[cpapcode].push_back(Code[8]);
}
Code[8]->AddEvent(tt,data[0]);
2011-07-10 14:23:07 +00:00
break;
2011-07-27 09:21:53 +00:00
2011-07-10 14:23:07 +00:00
case 0x0b: // Cheyne Stokes
data[0]=((unsigned char *)buffer)[pos+1]<<8 | ((unsigned char *)buffer)[pos];
2011-07-27 09:21:53 +00:00
//data[0]*=2;
2011-07-10 14:23:07 +00:00
pos+=2;
data[1]=((unsigned char *)buffer)[pos]; //|buffer[pos+1] << 8
pos+=1;
//tt-=delta;
tt-=data[1]*1000;
2011-07-27 09:21:53 +00:00
if (!Code[9]) {
Code[9]=new EventList(cpapcode,EVL_Event,2.0);
session->eventlist[cpapcode].push_back(Code[9]);
}
Code[9]->AddEvent(tt,data[0]);
//session->AddEvent(new Event(tt,cpapcode, data[0], data, 2));
2011-07-10 14:23:07 +00:00
break;
case 0x0d: // All the other ASV graph stuff.
2011-07-27 09:21:53 +00:00
if (!Code[12]) {
Code[12]=new EventList(CPAP_IPAP,EVL_Event,0.1);
session->eventlist[CPAP_IPAP].push_back(Code[12]);
2011-07-27 09:21:53 +00:00
Code[13]=new EventList(CPAP_IPAP_Low,EVL_Event,0.1);
session->eventlist[CPAP_IPAP_Low].push_back(Code[13]);
2011-07-27 09:21:53 +00:00
Code[14]=new EventList(CPAP_IPAP_High,EVL_Event,0.1);
session->eventlist[CPAP_IPAP_High].push_back(Code[14]);
2011-07-27 09:21:53 +00:00
Code[15]=new EventList(CPAP_Leak,EVL_Event);
session->eventlist[CPAP_Leak].push_back(Code[15]);
Code[16]=new EventList(CPAP_RespiratoryRate,EVL_Event);
session->eventlist[CPAP_RespiratoryRate].push_back(Code[16]);
Code[17]=new EventList(CPAP_PatientTriggeredBreaths,EVL_Event);
session->eventlist[CPAP_PatientTriggeredBreaths].push_back(Code[17]);
Code[18]=new EventList(CPAP_MinuteVentilation,EVL_Event);
session->eventlist[CPAP_MinuteVentilation].push_back(Code[18]);
Code[19]=new EventList(CPAP_TidalVolume,EVL_Event,10.0);
session->eventlist[CPAP_TidalVolume].push_back(Code[19]);
Code[20]=new EventList(CPAP_Snore,EVL_Event);
session->eventlist[CPAP_Snore].push_back(Code[20]);
Code[22]=new EventList(CPAP_EPAP,EVL_Event,0.1);
session->eventlist[CPAP_EPAP].push_back(Code[22]);
2011-07-27 09:21:53 +00:00
Code[23]=new EventList(CPAP_PressureSupport,EVL_Event,0.1);
session->eventlist[CPAP_PressureSupport].push_back(Code[23]);
2011-07-10 16:20:51 +00:00
2011-07-27 09:21:53 +00:00
}
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
Code[19]->AddEvent(t,buffer[pos++]); // Tidal Volume
Code[20]->AddEvent(t,data[2]=buffer[pos++]); // Snore
if (data[2]>0) {
if (!Code[21]) {
Code[21]=new EventList(CPAP_VSnore,EVL_Event);
session->eventlist[CPAP_VSnore].push_back(Code[21]);
}
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
2011-07-10 14:23:07 +00:00
break;
case 0x03: // BIPAP Pressure
2011-07-27 09:21:53 +00:00
qDebug() << "0x03 Observed in ASV data!!????";
2011-07-10 14:23:07 +00:00
data[0]=buffer[pos++];
data[1]=buffer[pos++];
2011-07-27 09:21:53 +00:00
/*data[0]/=10.0;
2011-07-10 14:23:07 +00:00
data[1]/=10.0;
2011-07-27 09:21:53 +00:00
session->AddEvent(new Event(t,CPAP_EAP, 0, data, 1));
session->AddEvent(new Event(t,CPAP_IAP, 0, &data[1], 1)); */
2011-07-10 14:23:07 +00:00
break;
2011-07-27 09:21:53 +00:00
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++]);
2011-07-10 14:23:07 +00:00
break;
case 0x0e: // Unknown
2011-07-27 09:21:53 +00:00
qDebug() << "0x0E Observed in ASV data!!????";
2011-07-10 14:23:07 +00:00
data[0]=buffer[pos++]; // << 8) | buffer[pos];
2011-07-27 09:21:53 +00:00
//session->AddEvent(new Event(t,cpapcode, 0, data, 1));
2011-07-10 14:23:07 +00:00
break;
case 0x10: // Unknown
2011-07-27 09:21:53 +00:00
qDebug() << "0x10 Observed in ASV data!!????";
2011-07-10 14:23:07 +00:00
data[0]=buffer[pos++]; // << 8) | buffer[pos];
data[1]=buffer[pos++];
data[2]=buffer[pos++];
2011-07-27 09:21:53 +00:00
//session->AddEvent(new Event(t,cpapcode, 0, data, 3));
2011-07-10 14:23:07 +00:00
break;
case 0x0f:
2011-07-27 09:21:53 +00:00
qDebug() << "0x0f Observed in ASV data!!????";
2011-07-10 14:23:07 +00:00
data[0]=buffer[pos+1]<<8 | buffer[pos];
pos+=2;
data[1]=buffer[pos]; //|buffer[pos+1] << 8
pos+=1;
tt-=data[1]*1000;
2011-07-27 09:21:53 +00:00
//session->AddEvent(new Event(tt,cpapcode, 0, data, 2));
2011-07-10 14:23:07 +00:00
break;
case 0x12: // Summary
2011-07-27 09:21:53 +00:00
qDebug() << "0x12 Observed in ASV data!!????";
2011-07-10 14:23:07 +00:00
data[0]=buffer[pos++];
data[1]=buffer[pos++];
data[2]=buffer[pos+1]<<8 | buffer[pos];
pos+=2;
2011-07-27 09:21:53 +00:00
//session->AddEvent(new Event(t,cpapcode, 0, data,3));
2011-07-10 14:23:07 +00:00
break;
default:
// ERROR!!!
qWarning() << "Some new fandangled PRS1 code detected " << hex << int(code) << " at " << pos+15;
return false;
}
}
2011-07-27 09:21:53 +00:00
/*EventList *a;
for (int i=0;i<24;i++){
a=Code[i];
if (a) {
int v=ceil(a->max()/5);
a->setMax(v*5);
v=floor(a->min()/5);
a->setMin(v*5);
}
}*/
session->updateLast(t);
2011-07-10 14:23:07 +00:00
return true;
}
2011-06-26 08:30:44 +00:00
bool PRS1Loader::OpenEvents(Session *session,QString filename)
{
2011-07-10 14:23:07 +00:00
int size,sequence,seconds,br,version;
qint64 timestamp;
2011-06-26 08:30:44 +00:00
unsigned char header[24]; // use m_buffer?
2011-07-10 14:23:07 +00:00
unsigned char ext,htype;
2011-06-26 08:30:44 +00:00
QFile f(filename);
if (!f.open(QIODevice::ReadOnly))
return false;
int hl=16;
br=f.read((char *)header,hl);
2011-07-10 14:23:07 +00:00
if (header[0]!=PRS1_MAGIC_NUMBER)
2011-06-26 08:30:44 +00:00
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];
2011-07-10 14:23:07 +00:00
htype=header[3]; // 00 = normal // 01=waveform // could be a bool?
version=header[4];// | header[4];
2011-06-26 08:30:44 +00:00
2011-07-18 02:33:25 +00:00
htype=htype;
sequence=sequence;
2011-07-10 14:23:07 +00:00
if (ext!=PRS1_EVENT_FILE) // 2 == Event file
2011-06-26 08:30:44 +00:00
return false;
//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; i<hl-1; i++) sum+=header[i];
if (sum!=header[hl-1]) return false;
unsigned char *buffer=(unsigned char *)m_buffer;
br=f.read((char *)buffer,size);
if (br<size) {
return false;
}
2011-07-10 14:23:07 +00:00
if (version==0) {
2011-07-18 02:33:25 +00:00
if (!Parse002(session,buffer,size,timestamp*1000L)) {
2011-07-10 14:23:07 +00:00
qDebug() << "Couldn't Parse PRS1 Event File " << filename;
return false;
}
} else if (version==5) {
2011-07-18 02:33:25 +00:00
if (!Parse002ASV(session,buffer,size,timestamp*1000L)) {
2011-07-10 14:23:07 +00:00
qDebug() << "Couldn't Parse PRS1 (ASV) Event File " << filename;
return false;
}
}
2011-06-26 08:30:44 +00:00
return true;
}
2011-07-21 11:02:23 +00:00
struct WaveHeaderList {
quint16 interleave;
quint8 sample_format;
WaveHeaderList(quint16 i,quint8 f){ interleave=i; sample_format=f; }
};
2011-06-26 08:30:44 +00:00
bool PRS1Loader::OpenWaveforms(Session *session,QString filename)
2011-07-21 11:02:23 +00:00
{
//int sequence,seconds,br,htype,version,numsignals;
QFile file(filename);
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Couldn't open waveform file" << filename;
return false;
}
int pos,br,size=file.size();
if (size>max_load_buffer_size) {
qWarning() << "Waveform too big, increase max_load_buffer_size in PRS1Loader sourcecode" << filename;
return false;
}
br=file.read((char *)m_buffer,size);
if (br<size) {
qWarning() << "Couldn't read waveform data.. Disk error?" << filename;
return false;
}
// Look at the initial header and assume this header size for the lot.
if ((m_buffer[0]!=2) || (m_buffer[6]!=0x05)) {
qWarning() << "Not correct waveform format" << filename;
return false;
}
2011-07-27 09:21:53 +00:00
//quint8 version=m_buffer[4];
2011-07-21 11:02:23 +00:00
quint32 start=m_buffer[0xb] | m_buffer[0xc] << 8 | m_buffer[0xd] << 16 | m_buffer[0x0e] << 24;
2011-07-27 09:21:53 +00:00
session->updateFirst(qint64(start)*1000L);
2011-07-27 09:21:53 +00:00
2011-07-21 11:02:23 +00:00
quint16 num_signals=m_buffer[0x12] | m_buffer[0x13] << 8;
if (num_signals>2) {
qWarning() << "More than 2 Waveforms in " << filename;
return false;
}
pos=0x14+(num_signals-1)*3;
vector<WaveHeaderList> whl;
// add the in reverse...
for (int i=0;i<num_signals;i++) {
quint16 interleave=m_buffer[pos] | m_buffer[pos+1] << 8;
quint8 sample_format=m_buffer[pos+2];
whl.push_back(WaveHeaderList(interleave,sample_format));
pos-=3;
}
int hl=15+5+(num_signals*3);
quint16 duration,length;
quint32 timestamp,lasttimestamp;
pos=0;
int block=0;
lasttimestamp=start;
duration=0;
int corrupt=0;
char waveform[num_signals][500000];
int wlength[num_signals];
2011-07-27 09:21:53 +00:00
qint64 wdur[num_signals];
//EventList *evl[num_signals];
2011-07-21 11:02:23 +00:00
for (int i=0;i<num_signals;i++) {
wlength[i]=0;
wdur[i]=0;
2011-07-27 09:21:53 +00:00
//evl[i]=NULL;
2011-07-21 11:02:23 +00:00
//waveform[i].resize(500000);
}
2011-07-27 09:21:53 +00:00
//SampleFormat *wf[num_signals];
//EventList *FlowData=EventList(CPAP_FlowRate, EVL_Waveform,1,0,-128,128);
//EventList *MaskData=EventList(CPAP_MaskPressure, EVL_Waveform,1,0,-128,128);
ChannelID wc[2]={CPAP_FlowRate,CPAP_MaskPressure};
2011-07-21 11:02:23 +00:00
do {
timestamp=m_buffer[pos+0xb] | m_buffer[pos+0xc] << 8 | m_buffer[pos+0xd] << 16 | m_buffer[pos+0x0e] << 24;
register unsigned char sum8=0;
for (int i=0;i<hl;i++) sum8+=m_buffer[pos+i];
if (m_buffer[pos+hl]!=sum8) {
if (block==0) {
qDebug() << "Faulty Header Checksum, aborting" << filename;
return false;
}
if (++corrupt>3) {
qDebug() << "3 Faulty Waveform Headers in a row.. Aborting" << filename;
return false;
}
qDebug() << "Faulty Header Checksum, skipping block" << block;
pos+=length;
continue;
} else {
int diff=timestamp-(lasttimestamp+duration);
if (block==1) {
if (diff>0)
start-=diff;
}
2011-07-21 11:02:23 +00:00
length=m_buffer[pos+0x1] | m_buffer[pos+0x2] << 8; // block length in bytes
duration=m_buffer[pos+0xf] | m_buffer[pos+0x10] << 8; // block duration in seconds
2011-07-22 16:38:22 +00:00
if (diff<0) {
qDebug() << "Padding waveform to keep sync" << block;
//diff=abs(diff);
for (int i=0;i<num_signals;i++) {
for (int j=0;j<diff;j++) {
for (int k=0;k<whl[i].interleave;k++)
waveform[i][wlength[i]++]=0;
}
wdur[i]+=diff;
}
} else
if (diff>0 && wlength[0]>0) {
2011-07-27 09:21:53 +00:00
qDebug() << "Timestamp resync" << block << diff << corrupt << duration << timestamp-lasttimestamp << filename;
2011-07-21 11:02:23 +00:00
for (int i=0;i<num_signals;i++) {
2011-07-27 09:21:53 +00:00
double rate=(double(wdur[i])*1000.0)/double(wlength[i]);
double gain;
if (i==1) gain=0.1; else gain=1;
EventList *a=new EventList(wc[i],EVL_Waveform,gain,0,0,0,rate);
if (whl[i].sample_format)
a->AddWaveform(qint64(start)*1000L,(unsigned char *)waveform[i],wlength[i],qint64(wdur[i])*1000L);
else {
a->AddWaveform(qint64(start)*1000L,(char *)waveform[i],wlength[i],qint64(wdur[i])*1000L);
}
if (wc[i]==CPAP_FlowRate) {
a->setMax(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); */
2011-07-21 11:02:23 +00:00
}
2011-07-27 09:21:53 +00:00
session->eventlist[wc[i]].push_back(a);
session->updateLast(start+(qint64(wdur[i])*1000L));
2011-07-21 11:02:23 +00:00
wlength[i]=0;
wdur[i]=0;
}
start=timestamp;
corrupt=0;
2011-07-22 16:38:22 +00:00
}
2011-07-21 11:02:23 +00:00
}
pos+=hl+1;
//qDebug() << (duration*num_signals*whl[0].interleave) << duration;
2011-07-21 11:02:23 +00:00
if (num_signals==1) { // no interleave.. this is much quicker.
int bs=duration*whl[0].interleave;
memcpy((char *)&(waveform[0])[wlength[0]],(char *)&m_buffer[pos],bs);
wlength[0]+=bs;
pos+=bs;
} else {
for (int i=0;i<duration;i++) {
for (int s=0;s<num_signals;s++) {
memcpy((char *)&(waveform[s])[wlength[s]],(char *)&m_buffer[pos],whl[s].interleave);
wlength[s]+=whl[s].interleave;
pos+=whl[s].interleave;
}
}
}
for (int i=0;i<num_signals;i++) wdur[i]+=duration;
pos+=2;
block++;
lasttimestamp=timestamp;
} while (pos<size);
for (int i=0;i<num_signals;i++) {
2011-07-27 09:21:53 +00:00
double rate=(double(wdur[i])*1000.0)/double(wlength[i]);
double gain;
if (i==1) gain=0.1; else gain=1;
EventList *a=new EventList(wc[i],EVL_Waveform,gain,0,0,0,rate);
if (whl[i].sample_format)
a->AddWaveform(qint64(start)*1000L,(unsigned char *)waveform[i],wlength[i],qint64(wdur[i])*1000L);
else {
a->AddWaveform(qint64(start)*1000L,(char *)waveform[i],wlength[i],qint64(wdur[i])*1000L);
2011-07-20 16:01:31 +00:00
}
2011-07-27 09:21:53 +00:00
if (wc[i]==CPAP_FlowRate) {
a->setMax(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); */
2011-06-26 08:30:44 +00:00
}
2011-07-27 09:21:53 +00:00
session->eventlist[wc[i]].push_back(a);
session->updateLast(start+qint64(wdur[i])*1000L);
2011-07-10 14:23:07 +00:00
}
2011-06-26 08:30:44 +00:00
return true;
2011-07-27 09:21:53 +00:00
}
2011-06-26 08:30:44 +00:00
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]="BiPAP autoSV Advanced";
2011-06-26 08:30:44 +00:00
};
bool initialized=false;
void PRS1Loader::Register()
{
if (initialized) return;
2011-07-01 10:10:44 +00:00
qDebug() << "Registering PRS1Loader";
2011-06-26 08:30:44 +00:00
RegisterLoader(new PRS1Loader());
InitModelMap();
initialized=true;
}