diff --git a/Htmldocs/release_notes.html b/Htmldocs/release_notes.html index d7a6f4c4..3171480c 100644 --- a/Htmldocs/release_notes.html +++ b/Htmldocs/release_notes.html @@ -7,6 +7,17 @@
+
+ Changes and fixes in OSCAR v1.X.Y
+
Portions of OSCAR are © 2019-2020 by
+ The OSCAR Team
Changes and fixes in OSCAR v1.1.1
Portions of OSCAR are © 2019-2020 by
diff --git a/README b/README
index 999b4680..a3011b4b 100644
--- a/README
+++ b/README
@@ -19,10 +19,10 @@ All systems need a C++ compiler and linker and the QT platform download.
Windows Building: See Building/Windows/BUILD-WIN.md
-----------------
-MacOS Building: See Building/Windows/BUILD-mac.md
+MacOS Building: See Building/MacOS/BUILD-mac.md
---------------
-Linux Building: See Building/Windows/BUILD-Linux-md
+Linux Building: See Building/Linux/BUILD-Linux.md
---------------
Software Licensing Information
@@ -30,9 +30,9 @@ Software Licensing Information
OSCAR is released under the GNU GPL v3 License. Please see below for a note on giving correct attribution
in redistribution of derivatives.
-It is built using Qt SDK (Open Source Edition), available from http://qt.io.
+It is built using Qt SDK (Open Source Edition), available from https://qt.io.
-Redistribution of derivatives ( a note added by Mark Watins )
+Redistribution of derivatives ( a note added by Mark Watkins )
-----------------------------
Mark Watkins created this software to help lessen the exploitation of others. Seeing his work being used to exploit others
is incredibly un-motivational, and incredibly disrespectful of all the work he put into this project.
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index 57845487..a7f84e7b 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -295,6 +295,7 @@ static const PRS1TestedModel s_PRS1TestedModels[] = {
{ "960T", 5, 2, "BiPAP autoSV Advanced 30 (System One 60 Series)" }, // omits "(System One 60 Series)" on official reports
{ "900X110", 5, 3, "DreamStation BiPAP autoSV" },
{ "900X120", 5, 3, "DreamStation BiPAP autoSV" },
+ { "900X150", 5, 3, "DreamStation BiPAP autoSV" },
{ "1061401", 3, 0, "BiPAP S/T (C Series)" },
{ "1061T", 3, 3, "BiPAP S/T 30 (System One 60 Series)" },
@@ -3602,23 +3603,33 @@ bool PRS1DataChunk::ParseEventsF0V23()
break;
}
startpos = pos;
- if (code != 0x12) { // This one event has no timestamp in F0V6
- t += data[pos] | (data[pos+1] << 8);
+ if (code != 0x12 && code != 0x01) { // This one event has no timestamp in F0V6
+ elapsed = data[pos] | (data[pos+1] << 8);
+ if (elapsed > 0x7FFF) UNEXPECTED_VALUE(elapsed, "<32768s"); // check whether this is generally unsigned, since 0x01 isn't
+ t += elapsed;
pos += 2;
}
switch (code) {
- case 0x00: // ??? So far only seen on 451P and 551P occasionally, usually no more than once per session
- // A nonzero delta corresponds to an N-second gap in data (value was 0x85, only seen once). Look for more.
- if (sessionid != 122) CHECK_VALUE(data[startpos], 0); // skip the onc occurrence already seen
- CHECK_VALUE(data[startpos+1], 0);
- if (data[pos] < 0x80 || data[pos] > 0x85) {
- UNEXPECTED_VALUE(data[pos], "0x80-0x85");
- DUMP_EVENT();
- }
+ case 0x00: // Humidifier setting change (logged in summary in 60 series)
+ ParseHumidifierSetting50Series(data[pos]);
if (this->familyVersion == 3) DUMP_EVENT();
break;
- //case 0x01: // never seen
+ case 0x01: // Time elapsed?
+ // Only seen once, on a 550P.
+ // It looks almost like a time-elapsed event 4 found in F0V4 summaries, but
+ // 0xFFCC looks like it represents a time adjustment of -52 seconds,
+ // since the subsequent 0x11 statistics event has a time offset of 172 seconds,
+ // and counting this as -52 seconds results in a total session time that
+ // matches the summary and waveform data. Very weird.
+ CHECK_VALUE(data[pos], 0xCC);
+ CHECK_VALUE(data[pos+1], 0xFF);
+ elapsed = data[pos] | (data[pos+1] << 8);
+ if (elapsed & 0x8000) {
+ elapsed = (~0xFFFF | elapsed); // sign extend 16-bit number to native int
+ }
+ t += elapsed;
+ break;
case 0x02: // Pressure adjustment
// See notes in ParseEventsF0V6.
this->AddEvent(new PRS1PressureSetEvent(t, data[pos]));
@@ -5083,7 +5094,7 @@ bool PRS1DataChunk::ParseSummaryF0V4(void)
CHECK_VALUE(chunk_size, 1); // and the only record in the session.
if (this->sessionid == 1) UNEXPECTED_VALUE(this->sessionid, ">1");
break;
- case 7: // Humidifier setting change
+ case 7: // Humidifier setting change (logged in events in 50 series)
tt += data[pos] | (data[pos+1] << 8); // This adds to the total duration (otherwise it won't match report)
this->ParseHumidifierSetting60Series(data[pos+2], data[pos+3]);
break;
@@ -6783,8 +6794,7 @@ void PRS1DataChunk::ParseHumidifierSettingV3(unsigned char byte1, unsigned char
// All variations seen.
} else if (family == 5) {
if (tubepresent) {
- if (tubetemp != 0 && tubetemp > 4) UNEXPECTED_VALUE(tubetemp, "<= 4");
- // All humidity levels seen.
+ // All tube temperature and humidity levels seen.
} else if (humidadaptive) {
// All humidity levels seen.
} else if (humidfixed) {
@@ -8689,7 +8699,7 @@ bool PRS1DataChunk::ReadHeader(QFile & f)
// Do a few early sanity checks before any variable-length header data.
if (this->blockSize == 0) {
- qWarning() << this->m_path << "blocksize 0?";
+ qWarning() << this->m_path << "@" << hex << this->m_filepos << "blocksize 0, skipping remainder of file";
break;
}
if (this->fileVersion < 2 || this->fileVersion > 3) {
diff --git a/oscar/statistics.cpp b/oscar/statistics.cpp
index a1c2b63b..df4b00a3 100644
--- a/oscar/statistics.cpp
+++ b/oscar/statistics.cpp
@@ -39,7 +39,7 @@ QString resizeHTMLPixmap(QPixmap &pixmap, int width, int height) {
QBuffer buffer(&byteArray); // use buffer to store pixmap into byteArray
buffer.open(QIODevice::WriteOnly);
pixmap.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation).save(&buffer, "PNG");
- return QString("");
+ return QString("
");
}
QString formatTime(float time)
@@ -624,39 +624,41 @@ QString Statistics::getUserInfo () {
return "";
QString address = p_profile->user->address();
- address.replace("\n", "
");
+ address.replace("\n", "
");
QString userinfo = "";
if (!p_profile->user->firstName().isEmpty()) {
- userinfo = tr("Name: %1, %2").arg(p_profile->user->lastName()).arg(p_profile->user->firstName()) + "
";
+ userinfo = tr("Name: %1, %2").arg(p_profile->user->lastName()).arg(p_profile->user->firstName()) + "
";
if (!p_profile->user->DOB().isNull()) {
- userinfo += tr("DOB: %1").arg(p_profile->user->DOB().toString(MedDateFormat)) + "
";
+ userinfo += tr("DOB: %1").arg(p_profile->user->DOB().toString(MedDateFormat)) + "
";
}
if (!p_profile->user->phone().isEmpty()) {
- userinfo += tr("Phone: %1").arg(p_profile->user->phone()) + "
";
+ userinfo += tr("Phone: %1").arg(p_profile->user->phone()) + "
";
}
if (!p_profile->user->email().isEmpty()) {
- userinfo += tr("Email: %1").arg(p_profile->user->email()) + "
";
+ userinfo += tr("Email: %1").arg(p_profile->user->email()) + "
";
}
if (!p_profile->user->address().isEmpty()) {
- userinfo += tr("Address:")+"
"+address;
+ userinfo += tr("Address:")+"
"+address;
}
}
- while (userinfo.length() > 0 && userinfo.endsWith("
")) // Strip trailing newlines
- userinfo = userinfo.mid(0, userinfo.length()-5);
+ while (userinfo.length() > 0 && userinfo.endsWith("
")) // Strip trailing newlines
+ userinfo = userinfo.mid(0, userinfo.length()-4);
return userinfo;
}
-const QString table_width = "width=100%";
+const QString table_width = "width='100%'";
// Create the page header in HTML. Includes everything from