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("logo"); } 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 through QString Statistics::generateHeader(bool onScreen) { - QString html = QString("")+ - "" - "" + "" "