[med-svn] [edfbrowser] 03/05: New upstream version 1.60
Andreas Tille
tille at debian.org
Mon Sep 18 09:36:00 UTC 2017
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository edfbrowser.
commit 5e83817f3e82c7fe450def9077313fce4fe755fc
Author: Andreas Tille <tille at debian.org>
Date: Mon Sep 18 11:23:42 2017 +0200
New upstream version 1.60
---
README.txt | 71 +++-
ascii2edf.cpp | 23 +-
averager_dialog.cpp | 17 +
biosemi2bdfplus.cpp | 1 +
check_edf_file.cpp | 13 +-
doc/manual.html | 48 ++-
edf_annot_list.c | 82 ++++
edf_annot_list.h | 3 +
edf_annotations.cpp | 12 +-
edfbrowser.pro | 18 +-
edflib.c | 20 +-
edit_annotation_dock.cpp | 12 +-
edit_annotation_dock.h | 1 +
filteredblockread.cpp | 5 +
global.h | 9 +-
header_editor.cpp | 103 ++++-
images/splash.png | Bin 9305 -> 9306 bytes
import_annotations.cpp | 5 +
load_montage_dialog.cpp | 138 ++++++-
load_montage_dialog.h | 1 +
mainwindow.cpp | 108 +++++-
mainwindow.h | 5 +
mainwindow_constr.cpp | 35 +-
mit2edf.cpp | 75 +++-
mit2edf.h | 1 +
options_dialog.cpp | 83 +++-
options_dialog.h | 3 +
plif_ecg_subtract_filter.c | 416 +++++++++++++++++++++
plif_ecg_subtract_filter.h | 89 +++++
plif_ecg_subtract_filter_dialog.cpp | 301 +++++++++++++++
...e_dialog.h => plif_ecg_subtract_filter_dialog.h | 63 ++--
print_to_bdf.cpp | 21 +-
print_to_edf.cpp | 19 +-
read_write_settings.cpp | 45 ++-
save_montage_dialog.cpp | 9 +
show_actual_montage_dialog.cpp | 8 +
signal_chooser.cpp | 14 +
signals_dialog.cpp | 2 +-
spectrum_dock.cpp | 17 +
spectrumanalyzer.cpp | 17 +
version.txt | 25 ++
view_montage_dialog.cpp | 63 +++-
viewbuf.cpp | 24 +-
viewcurve.cpp | 135 ++++++-
viewcurve.h | 2 +
45 files changed, 2018 insertions(+), 144 deletions(-)
diff --git a/README.txt b/README.txt
index f1ecc56..2a72c23 100644
--- a/README.txt
+++ b/README.txt
@@ -5,7 +5,8 @@ Qt http://www.qt.io/
Minimum version 4.7.1 or later, preferable 4.8.7
-Do not use Qt 5.x.x to compile EDFbrowser, Qt5 is not yet ready for production use. It contains still bugs that affects EDFbrowser.
+If you insist on using Qt5 (which I don't recommend), it must be version >= 5.9.1.
+For info how to compile Qt5 from source, scroll down.
The GCC compiler on Linux or Mingw-w64 on windows. <http://mingw-w64.sourceforge.net/>
@@ -72,5 +73,73 @@ How to compile (step by step)
Now you can run the program by typing: edfbrowser
+In case you insist on using Qt5, the recommended way is not to use the Qt5 libraries that comes with your distro.
+Instead, download and compile the Qt5 source and use that to compile EDFbrowser.
+Here's a step by step list of instuctions:
+
+First, fulfill the requirements for Qt:
+
+http://doc.qt.io/qt-5/linux.html
+
+Debian/Ubuntu: sudo apt-get install build-essential libgl1-mesa-dev libcups2-dev libx11-dev
+
+Fedora: sudo dnf groupinstall "C Development Tools and Libraries"
+ sudo dnf install mesa-libGL-devel cups-devel libx11-dev
+
+openSUSE: sudo zypper install -t pattern devel_basis
+ sudo zypper install xorg-x11-devel cups-devel freetype-devel fontconfig-devel
+
+#############################################################################################
+# #
+# Compile a static version of the Qt5 libraries excluding all modules that are not needed. #
+# #
+# This will not mess with your system libraries. The new compiled libraries will be stored #
+# #
+# in a new and separate directory: /usr/local/Qt-5.9.1 #
+# #
+# It will not interfere with other Qt programs. #
+# #
+#############################################################################################
+
+mkdir Qt5-source
+
+cd Qt5-source
+
+wget http://ftp1.nluug.nl/languages/qt/official_releases/qt/5.9/5.9.1/single/qt-everywhere-opensource-src-5.9.1.tar.xz
+
+here is a list of download mirrors: https://download.qt.io/static/mirrorlist/
+The Qt source package you are going to need is: qt-everywhere-opensource-src-5.9.1.tar.xz
+
+tar -xvf qt-everywhere-opensource-src-5.9.1.tar.xz
+
+./configure -v -release -opensource -confirm-license -c++std c++11 -static -accessibility -fontconfig -skip qtdeclarative -skip qtconnectivity -skip qtmultimedia -no-qml-debug -qt-zlib -no-mtdev -no-journald -qt-libpng -qt-libjpeg -system-freetype -qt-harfbuzz -no-openssl -no-libproxy -no-glib -nomake examples -nomake tests -no-compile-examples -cups -no-evdev -no-dbus -no-eglfs -qreal double -no-opengl -skip qtlocation -skip qtsensors -skip qtwayland -skip qtgamepad -skip qtserialbus
+
+(takes about 1.5 minutes)
+
+make -j8
+
+(takes about 11 minutes)
+
+sudo make install
+
+Now go to the directory that contains the EDFbrowser sourcecode and enter the following commands:
+
+/usr/local/Qt-5.9.1/bin/qmake
+
+make -j8
+
+sudo make install
+
+Now you can run the program by typing: edfbrowser
+
+Congratulations!
+You have compiled a static version of EDFbrowser that can be deployed on other systems without the need
+to install the Qt libraries.
+In order to reduce the size of the executable, run the following commands:
+
+strip -s edfbrowser
+
+upx edfbrowser
+
diff --git a/ascii2edf.cpp b/ascii2edf.cpp
index 56fde18..b3d78dc 100644
--- a/ascii2edf.cpp
+++ b/ascii2edf.cpp
@@ -750,7 +750,7 @@ void UI_ASCII2EDFapp::gobuttonpressed()
fprintf(outputfile, "%-8i", 256 * edfsignals + 256);
fprintf(outputfile, " ");
fprintf(outputfile, "-1 ");
- if(samplefrequency<1.0)
+ if(dblcmp(samplefrequency, 1) < 0)
{
datrecduration = 1.0 / samplefrequency;
snprintf(str, 256, "%.8f", datrecduration);
@@ -897,7 +897,7 @@ void UI_ASCII2EDFapp::gobuttonpressed()
fputc(' ', outputfile);
}
- if(samplefrequency<1.0)
+ if(dblcmp(samplefrequency, 1) < 0)
{
for(i=0; i<edfsignals; i++)
{
@@ -1175,10 +1175,19 @@ void UI_ASCII2EDFapp::gobuttonpressed()
return;
}
- snprintf(txt_string, ASCII_MAX_LINE_LEN, "Done. EDF file is located at %s\n", outputfilename);
- QMessageBox messagewindow(QMessageBox::Information, "Ready", txt_string);
- messagewindow.setIconPixmap(QPixmap(":/images/ok.png"));
- messagewindow.exec();
+ if(datarecords < 1)
+ {
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "Error: Input file does not contain enough lines\n"
+ "to create at least one datarecord in the EDF/BDF file.");
+ messagewindow.exec();
+ }
+ else
+ {
+ snprintf(txt_string, ASCII_MAX_LINE_LEN, "Done. EDF file is located at %s\n", outputfilename);
+ QMessageBox messagewindow(QMessageBox::Information, "Ready", txt_string);
+ messagewindow.setIconPixmap(QPixmap(":/images/ok.png"));
+ messagewindow.exec();
+ }
ascii2edfDialog->setEnabled(true);
}
@@ -1687,7 +1696,7 @@ int UI_ASCII2EDFapp::check_input(void)
samplefrequency = SamplefreqSpinbox->value();
- if(samplefrequency>=1.0)
+ if(!(dblcmp(samplefrequency, 1) < 0))
{
if(samplefrequency>((double)((int)samplefrequency)))
{
diff --git a/averager_dialog.cpp b/averager_dialog.cpp
index 6342718..14febea 100644
--- a/averager_dialog.cpp
+++ b/averager_dialog.cpp
@@ -594,6 +594,23 @@ void UI_AveragerWindow::process_avg(struct signalcompblock *signalcomp)
dig_value = signalcomp->fidfuncp[k](signalcomp->fidbuf[k], dig_value);
}
+ if(signalcomp->plif_ecg_filter)
+ {
+ if(s==signalcomp->sample_start)
+ {
+ if(mainwindow->edfheaderlist[signalcomp->filenum]->viewtime<=0)
+ {
+ plif_reset_subtract_filter(signalcomp->plif_ecg_filter, 0);
+ }
+ else
+ {
+ plif_subtract_filter_state_copy(signalcomp->plif_ecg_filter, signalcomp->plif_ecg_filter_sav);
+ }
+ }
+
+ dig_value = plif_run_subtract_filter(dig_value, signalcomp->plif_ecg_filter);
+ }
+
if(signalcomp->ecg_filter != NULL)
{
if(s==signalcomp->sample_start)
diff --git a/biosemi2bdfplus.cpp b/biosemi2bdfplus.cpp
index 6534b15..0871f43 100644
--- a/biosemi2bdfplus.cpp
+++ b/biosemi2bdfplus.cpp
@@ -589,6 +589,7 @@ void UI_BIOSEMI2BDFPLUSwindow::SelectFileButton()
sprintf(str, "%.4f", (double)((datarecords * EDFLIB_TIME_DIMENSION) + ((long long)i * status_sample_duration) - annot_ptr->onset) / (double)EDFLIB_TIME_DIMENSION);
str[15] = 0;
strcpy(annot_ptr->duration, str);
+ annot_ptr->long_duration = edfplus_annotation_get_long_from_number(str);
break;
}
}
diff --git a/check_edf_file.cpp b/check_edf_file.cpp
index 88f04d9..dd3b099 100644
--- a/check_edf_file.cpp
+++ b/check_edf_file.cpp
@@ -1032,7 +1032,8 @@ struct edfhdrblock * EDFfileCheck::check_edf_file(FILE *inputfile, char *txt_str
edfhdr->edfparam[i].dig_max = n;
if(edfhdr->edfparam[i].dig_max<(edfhdr->edfparam[i].dig_min + 1))
{
- sprintf(txt_string, "Error, digital maximum of signal %i is less than or equal to digital minimum",
+ sprintf(txt_string, "Error, digital maximum of signal %i is less than or equal to digital minimum.\n"
+ "Use the header editor to fix your file. Look at the manual for the details.",
i + 1);
free(edf_hdr);
free(edfhdr->edfparam);
@@ -1817,6 +1818,16 @@ long long EDFfileCheck::get_long_duration(char *str)
long long value=0, radix;
+ if((str[0] == '+') || (str[0] == '-'))
+ {
+ for(i=0; i<7; i++)
+ {
+ str[i] = str[i+1];
+ }
+
+ str[7] = ' ';
+ }
+
for(i=0; i<8; i++)
{
if(str[i]==' ')
diff --git a/doc/manual.html b/doc/manual.html
index d801fff..86bf9a0 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -7,7 +7,7 @@
<meta name="description" content="EDFbrowser manual">
</head><body>
-<h1>EDFbrowser 1.59 manual</h1>
+<h1>EDFbrowser 1.60 manual</h1>
<p><br></p>
@@ -32,6 +32,7 @@
<li><a href="#Adjust_filters">Adjust filters</a></li>
<li><a href="#Powerspectrum">Powerspectrum</a></li>
<li><a href="#Averaging_EP">Averaging (evoked potentials or other triggers)</a></li>
+ <li><a href="#ECG_PLIF_filter">ECG powerline interference filter</a></li>
<li><a href="#ECG_detection">ECG heartrate detection</a></li>
<li><a href="#HR_statistics">Heart Rate statistics</a></li>
<li><a href="#Spike_filter">Spike filter</a></li>
@@ -451,6 +452,48 @@
<p><br><br></p>
+<h3><a name="ECG_PLIF_filter">ECG powerline interference filter</a></h3>
+
+<p>
+ The ECG powerline interference filter is inspired by the subtraction method described by:<br>
+ - Subtraction Method For Powerline Interference Removing From ECG<br>
+ Chavdar Levkov, Georgy Mihov, Ratcho Ivanov, Ivan K. Daskalov Ivaylo Christov, Ivan Dotsinsky<br>
+<br>
+ - Removal of power-line interference from the ECG: a review of the subtraction procedure<br>
+ Chavdar Levkov, Georgy Mihov, Ratcho Ivanov, Ivan Daskalov, Ivaylo Christov and Ivan Dotsinsky<br>
+<br>
+ - Accuracy of 50 Hz interference subtraction from an electrocardiogram I. A, Dotsinsky I.K. Daskalov<br>
+<br>
+ - Dynamic powerline interference subtraction from biosignals<br>
+ Ivaylo I. Christov<br>
+<br>
+ The subtraction method extracts the powerline interference noise during a<br>
+ a linear region between two consecutive QRS complexes and stores it in a buffer.<br>
+ The reference noise from the buffer is used to subtract it from the signal outside<br>
+ the linear region i.e. during the QRS complex.<br>
+ This method only works correctly when the samplefrequency of the ECG recording is an<br>
+ integer multiple of the powerline frequency.<br>
+ In case they are synchronized, this method will remove also the harmonics of the<br>
+ powerline frequency. In that case extra notch-filters for the harmonics are<br>
+ not necessary. The advantage of this method is that it will not cause ringing<br>
+ in the waveform of the QRS complex (like notch-filters do).<br>
+<br>
+ The following rules apply:<br>
+ - The samplefrequency of the ECG recording must be at least 500 Hz<br>
+ - The samplefrequency of the ECG recording must be an integer multiple of the powerline frequency<br>
+ - The physical dimension (units) of the ECG signal must be expressed in uV, mV or V.<br>
+<br>
+ Note: Good results will be achieved when the accuracy of the recording sampleclock is <=100ppm.<br>
+ A nearly perfect result will achieved when the recording sampleclock is synchronized with the<br>
+ powerlinefrequency in hardware (e.g. using a PLL).
+</p>
+
+<p><br></p>
+
+<p><a href="#Table_of_Contents">Table of Contents</a></p>
+
+<p><br><br></p>
+
<h3><a name="ECG_detection">ECG heart rate detection</a></h3>
<p>
@@ -757,6 +800,7 @@
- a wrong number of datarecords written in the header<br>
- a wrong filesize (file is not ending on the boundary of a datarecord)<br>
- a broken number i.e. a number with decimal(s) in the digital maximum/minimum field<br>
+ - the value for digital maximum is lower than or equal to digital minimum<br>
<br>
Do as follows: In EDFbrowser go to Tools -> Header editor. Select the file. Now click on the<br>
"save" button (you don't need to edit the content manually). Close the header editor.<br>
@@ -1483,7 +1527,7 @@
- if (one of) the file(s) is of type EDF+, the outputfile will be an EDF+ file as well,<br>
otherwise EDF<br>
<br>
- - when selected multiple files, the datarecordduration of these files<br>
+ - when selected multiple files, the datarecord duration of these files<br>
needs to be the same or an integer multiple of eachother<br>
<br>
- if you selected any filters, these will be applied as well
diff --git a/edf_annot_list.c b/edf_annot_list.c
index b132066..73bbaf6 100644
--- a/edf_annot_list.c
+++ b/edf_annot_list.c
@@ -144,6 +144,26 @@ struct annotationblock * edfplus_annotation_get_item(struct annotation_list *lis
}
+int edfplus_annotation_get_index_at(struct annotation_list *list, long long onset, int start)
+{
+ int i;
+
+ if(list == NULL) return -1;
+
+ if(start < 0) return -1;
+
+ for(i=start; i<list->sz; i++)
+ {
+ if(list->items[list->idx[i]].onset >= onset)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
void edfplus_annotation_sort(struct annotation_list *list, void (*callback)(void)) /* sort the annotationlist based on the onset time */
{
int i, j, p, idx;
@@ -413,6 +433,68 @@ int edfplus_annotation_get_max_annotation_strlen(struct annotation_list *list)
}
+long long edfplus_annotation_get_long_from_number(const char *str)
+{
+ int i, len, hasdot=0, dotposition=0;
+
+ long long value=0, radix;
+
+ if((str[0] == '+') || (str[0] == '-'))
+ {
+ str++;
+ }
+
+ len = strlen(str);
+
+ for(i=0; i<len; i++)
+ {
+ if(str[i]=='.')
+ {
+ hasdot = 1;
+ dotposition = i;
+ break;
+ }
+ }
+
+ if(hasdot)
+ {
+ radix = TIME_DIMENSION_II;
+
+ for(i=dotposition-1; i>=0; i--)
+ {
+ value += ((long long)(str[i] - 48)) * radix;
+ radix *= 10;
+ }
+
+ radix = TIME_DIMENSION_II / 10;
+
+ for(i=dotposition+1; i<len; i++)
+ {
+ value += ((long long)(str[i] - 48)) * radix;
+ radix /= 10;
+ }
+ }
+ else
+ {
+ radix = TIME_DIMENSION_II;
+
+ for(i=len-1; i>=0; i--)
+ {
+ value += ((long long)(str[i] - 48)) * radix;
+ radix *= 10;
+ }
+ }
+
+ if(str[-1]=='-') value = -value;
+
+ return(value);
+}
+
+
+
+
+
+
diff --git a/edf_annot_list.h b/edf_annot_list.h
index f434eab..68bed2b 100644
--- a/edf_annot_list.h
+++ b/edf_annot_list.h
@@ -45,6 +45,7 @@ struct annotationblock{
int file_num;
long long onset;
char duration[16];
+ long long long_duration;
char annotation[MAX_ANNOTATION_LEN_II + 1];
int modified;
int x_pos;
@@ -75,6 +76,8 @@ struct annotation_list * edfplus_annotation_create_list_copy(struct annotation_l
int edfplus_annotation_remove_duplicates(struct annotation_list *);
int edfplus_annotation_get_max_annotation_strlen(struct annotation_list *);
void edfplus_annotation_copy_list(struct annotation_list *, struct annotation_list *);
+long long edfplus_annotation_get_long_from_number(const char *);
+int edfplus_annotation_get_index_at(struct annotation_list *, long long, int);
#ifdef __cplusplus
} /* extern "C" */
diff --git a/edf_annotations.cpp b/edf_annotations.cpp
index 5f636a2..f148f59 100644
--- a/edf_annotations.cpp
+++ b/edf_annotations.cpp
@@ -435,7 +435,12 @@ int EDF_annotations::get_annotations(struct edfhdrblock *edf_hdr, int read_nk_tr
}
annotblock.annotation[j] = 0;
- if(duration) strcpy(annotblock.duration, duration_in_txt);
+ if(duration)
+ {
+ strcpy(annotblock.duration, duration_in_txt);
+
+ annotblock.long_duration = get_long_time(duration_in_txt);
+ }
if(edfplus_annotation_add_item(&edf_hdr->annot_list, annotblock))
{
@@ -651,7 +656,10 @@ long long EDF_annotations::get_long_time(char *str)
long long value=0, radix;
- str = str + 1;
+ if((str[0] == '+') || (str[0] == '-'))
+ {
+ str++;
+ }
len = strlen(str);
diff --git a/edfbrowser.pro b/edfbrowser.pro
index 082270a..47e8737 100644
--- a/edfbrowser.pro
+++ b/edfbrowser.pro
@@ -2,23 +2,23 @@
contains(QT_MAJOR_VERSION, 4) {
LIST = 0 1 2 3 4 5 6
-for(a, LIST):contains(QT_MINOR_VERSION, $$a):error("This project needs Qt4 version >= 4.7.1 or Qt5 version >= 5.5.1")
+for(a, LIST):contains(QT_MINOR_VERSION, $$a):error("This project needs Qt4 version >= 4.7.1 or Qt5 version >= 5.9.1")
contains(QT_MINOR_VERSION, 7) {
LIST = 0
- for(a, LIST):contains(QT_PATCH_VERSION, $$a):error("This project needs Qt4 version >= 4.7.1 or Qt5 version >= 5.5.1")
+ for(a, LIST):contains(QT_PATCH_VERSION, $$a):error("This project needs Qt4 version >= 4.7.1 or Qt5 version >= 5.9.1")
}
}
contains(QT_MAJOR_VERSION, 5) {
-LIST = 0 1 2 3 4
-for(a, LIST):contains(QT_MINOR_VERSION, $$a):error("This project needs Qt4 version >= 4.7.1 or Qt5 version >= 5.5.1")
+LIST = 0 1 2 3 4 5 6 7 8
+for(a, LIST):contains(QT_MINOR_VERSION, $$a):error("This project needs Qt4 version >= 4.7.1 or Qt5 version >= 5.9.1")
-contains(QT_MINOR_VERSION, 5) {
+contains(QT_MINOR_VERSION, 9) {
LIST = 0
- for(a, LIST):contains(QT_PATCH_VERSION, $$a):error("This project needs Qt4 version >= 4.7.1 or Qt5 version >= 5.5.1")
+ for(a, LIST):contains(QT_PATCH_VERSION, $$a):error("This project needs Qt4 version >= 4.7.1 or Qt5 version >= 5.9.1")
}
}
@@ -42,8 +42,6 @@ win32 {
QTPLUGIN += windowsprintersupport
} else:mac {
QTPLUGIN += cocoaprintersupport
-} else {
- QTPLUGIN += cupsprintersupport
}
}
@@ -127,6 +125,8 @@ HEADERS += spike_filter_dialog.h
HEADERS += mit2edf.h
HEADERS += biox2edf.h
HEADERS += edf_helper.h
+HEADERS += plif_ecg_subtract_filter.h
+HEADERS += plif_ecg_subtract_filter_dialog.h
HEADERS += third_party/fidlib/fidlib.h
HEADERS += third_party/fidlib/fidmkf.h
@@ -217,6 +217,8 @@ SOURCES += spike_filter_dialog.cpp
SOURCES += mit2edf.cpp
SOURCES += biox2edf.cpp
SOURCES += edf_helper.c
+SOURCES += plif_ecg_subtract_filter.c
+SOURCES += plif_ecg_subtract_filter_dialog.cpp
SOURCES += third_party/fidlib/fidlib.c
diff --git a/edflib.c b/edflib.c
index eb76896..31e65f4 100644
--- a/edflib.c
+++ b/edflib.c
@@ -39,7 +39,7 @@
#include "edflib.h"
-#define EDFLIB_VERSION 111
+#define EDFLIB_VERSION 112
#define EDFLIB_MAXFILES 64
@@ -2766,6 +2766,16 @@ static long long edflib_get_long_duration(char *str)
long long value=0, radix;
+ if((str[0] == '+') || (str[0] == '-'))
+ {
+ for(i=0; i<7; i++)
+ {
+ str[i] = str[i+1];
+ }
+
+ str[7] = ' ';
+ }
+
for(i=0; i<8; i++)
{
if(str[i]==' ')
@@ -4522,9 +4532,7 @@ int edfwrite_physical_samples(int handle, double *buf)
for(i=0; i<sf; i++)
{
- value = buf[i] / bitvalue;
-
- value -= phys_offset;
+ value = (buf[i] / bitvalue) - phys_offset;
if(value>digmax)
{
@@ -4662,9 +4670,7 @@ int edf_blockwrite_physical_samples(int handle, double *buf)
for(i=0; i<sf; i++)
{
- value = buf[i + buf_offset] / bitvalue;
-
- value -= phys_offset;
+ value = (buf[i + buf_offset] / bitvalue) - phys_offset;
if(value>digmax)
{
diff --git a/edit_annotation_dock.cpp b/edit_annotation_dock.cpp
index 35aada9..8def60f 100644
--- a/edit_annotation_dock.cpp
+++ b/edit_annotation_dock.cpp
@@ -148,13 +148,17 @@ void UI_AnnotationEditwindow::modifyButtonClicked()
annot->onset += mainwindow->edfheaderlist[file_num]->starttime_offset;
- if(duration_spinbox->value()>0.0)
+ if(dblcmp(duration_spinbox->value(), 0.0) > 0)
{
snprintf(annot->duration, 16, "%f", duration_spinbox->value());
+
+ annot->long_duration = edfplus_annotation_get_long_from_number(annot->duration);
}
else
{
annot->duration[0] = 0;
+
+ annot->long_duration = 0LL;
}
strncpy(annot->annotation, annot_descript_lineEdit->text().toUtf8().data(), MAX_ANNOTATION_LEN);
@@ -227,13 +231,17 @@ void UI_AnnotationEditwindow::createButtonClicked()
annotation.file_num = file_num;
- if(duration_spinbox->value()>0.0)
+ if(dblcmp(duration_spinbox->value(), 0.0) > 0)
{
snprintf(annotation.duration, 16, "%f", duration_spinbox->value());
+
+ annotation.long_duration = edfplus_annotation_get_long_from_number(annotation.duration);
}
else
{
annotation.duration[0] = 0;
+
+ annotation.long_duration = 0LL;
}
strncpy(annotation.annotation, annot_descript_lineEdit->text().toUtf8().data(), MAX_ANNOTATION_LEN);
diff --git a/edit_annotation_dock.h b/edit_annotation_dock.h
index ffacb35..5ba93b9 100644
--- a/edit_annotation_dock.h
+++ b/edit_annotation_dock.h
@@ -58,6 +58,7 @@
#include "annotations_dock.h"
#include "popup_save_cancelwindow.h"
#include "edf_annot_list.h"
+#include "utils.h"
diff --git a/filteredblockread.cpp b/filteredblockread.cpp
index dbb70d7..9e76575 100644
--- a/filteredblockread.cpp
+++ b/filteredblockread.cpp
@@ -258,6 +258,11 @@ int FilteredBlockReadClass::process_signalcomp(int datarecord_start)
dig_value = signalcomp->fidfuncp[k](signalcomp->fidbuf[k], dig_value);
}
+ if(signalcomp->plif_ecg_filter)
+ {
+ dig_value = plif_run_subtract_filter(dig_value, signalcomp->plif_ecg_filter);
+ }
+
if(signalcomp->ecg_filter != NULL)
{
dig_value = run_ecg_filter(dig_value, signalcomp->ecg_filter);
diff --git a/global.h b/global.h
index a0967d6..51d48b0 100644
--- a/global.h
+++ b/global.h
@@ -44,8 +44,9 @@
#endif
#define PROGRAM_NAME "EDFbrowser"
-#define PROGRAM_VERSION "1.59"
-#define MINIMUM_QT_VERSION 0x040701
+#define PROGRAM_VERSION "1.60"
+#define MINIMUM_QT4_VERSION 0x040701
+#define MINIMUM_QT5_VERSION 0x050901
#define MAXFILES 32
#define MAXSIGNALS 512
#define MAXFILTERS 16
@@ -85,6 +86,7 @@
#include "ecg_filter.h"
#include "z_ratio_filter.h"
#include "edf_annot_list.h"
+#include "plif_ecg_subtract_filter.h"
struct edfparamblock{
@@ -223,6 +225,9 @@ struct signalcompblock{
double spike_filter_velocity;
int spike_filter_holdoff;
struct spike_filter_settings *spike_filter;
+ int plif_ecg_subtract_filter_plf;
+ struct plif_subtract_filter_settings *plif_ecg_filter;
+ struct plif_subtract_filter_settings *plif_ecg_filter_sav;
struct zratio_filter_settings *zratio_filter;
double zratio_crossoverfreq;
int spectr_dialog[MAXSPECTRUMDIALOGS];
diff --git a/header_editor.cpp b/header_editor.cpp
index a0bc0a5..eb58a15 100644
--- a/header_editor.cpp
+++ b/header_editor.cpp
@@ -219,6 +219,8 @@ UI_headerEditorWindow::UI_headerEditorWindow(QWidget *w_parent)
pushButton2 = new QPushButton(this);
pushButton2->setGeometry(200, 510, 100, 25);
pushButton2->setText("Save");
+ pushButton2->setToolTip("By clicking on \"Save\", the file will be checked for\n"
+ "errors and, if found, it will try to correct them.");
pushButton3 = new QPushButton(this);
pushButton3->setGeometry(10, 510, 100, 25);
@@ -898,9 +900,10 @@ void UI_headerEditorWindow::read_header()
void UI_headerEditorWindow::save_hdr()
{
- int i, j, p, len, hassign;
+ int i, j, p, len, hassign, digmin=0, digmax=0, dig_ok;
char scratchpad[256],
+ scratchpad2[256],
str[256];
long long filesize,
@@ -1319,6 +1322,7 @@ void UI_headerEditorWindow::save_hdr()
strncpy(scratchpad, hdr + 256 + (edfsignals * 120) + (i * 8), 8); // digital minimum
hassign = 0;
p = 0;
+ dig_ok = 0;
if((scratchpad[0] == '+') || (scratchpad[0] == '-'))
{
hassign = 1;
@@ -1340,19 +1344,21 @@ void UI_headerEditorWindow::save_hdr()
scratchpad[8] = 0;
fseeko(file, (long long)(256 + (edfsignals * 120) + (i * 8)), SEEK_SET);
fprintf(file, "%s", scratchpad);
+ digmin = atoi(scratchpad);
+ dig_ok++;
}
- strncpy(scratchpad, hdr + 256 + (edfsignals * 128) + (i * 8), 8); // digital maximum
+ strncpy(scratchpad2, hdr + 256 + (edfsignals * 128) + (i * 8), 8); // digital maximum
hassign = 0;
p = 0;
- if((scratchpad[0] == '+') || (scratchpad[0] == '-'))
+ if((scratchpad2[0] == '+') || (scratchpad2[0] == '-'))
{
hassign = 1;
p++;
}
for(; p<8; p++)
{
- if((scratchpad[p] < '0') || (scratchpad[p] > '9'))
+ if((scratchpad2[p] < '0') || (scratchpad2[p] > '9'))
{
break;
}
@@ -1361,11 +1367,71 @@ void UI_headerEditorWindow::save_hdr()
{
for(; p<8; p++)
{
- scratchpad[p] = ' ';
+ scratchpad2[p] = ' ';
}
- scratchpad[8] = 0;
+ scratchpad2[8] = 0;
fseeko(file, (long long)(256 + (edfsignals * 128) + (i * 8)), SEEK_SET);
- fprintf(file, "%s", scratchpad);
+ fprintf(file, "%s", scratchpad2);
+ digmax = atoi(scratchpad2);
+ dig_ok++;
+ }
+ if(dig_ok == 2)
+ {
+ if(digmax < digmin)
+ {
+ dig_ok++;
+
+ fseeko(file, (long long)(256 + (edfsignals * 120) + (i * 8)), SEEK_SET);
+ fprintf(file, "%s", scratchpad2);
+
+ fseeko(file, (long long)(256 + (edfsignals * 128) + (i * 8)), SEEK_SET);
+ fprintf(file, "%s", scratchpad);
+ }
+ if(digmax == digmin)
+ {
+ if(edf)
+ {
+ if(digmax < 32767)
+ {
+ digmax++;
+ sprintf(scratchpad, "%i", digmax);
+ strcat(scratchpad, " ");
+ scratchpad[8] = 0;
+ fseeko(file, (long long)(256 + (edfsignals * 128) + (i * 8)), SEEK_SET);
+ fprintf(file, "%s", scratchpad);
+ }
+ else
+ {
+ digmin--;
+ sprintf(scratchpad, "%i", digmin);
+ strcat(scratchpad, " ");
+ scratchpad[8] = 0;
+ fseeko(file, (long long)(256 + (edfsignals * 120) + (i * 8)), SEEK_SET);
+ fprintf(file, "%s", scratchpad);
+ }
+ }
+ if(bdf)
+ {
+ if(digmax < 8388607)
+ {
+ digmax++;
+ sprintf(scratchpad, "%i", digmax);
+ strcat(scratchpad, " ");
+ scratchpad[8] = 0;
+ fseeko(file, (long long)(256 + (edfsignals * 128) + (i * 8)), SEEK_SET);
+ fprintf(file, "%s", scratchpad);
+ }
+ else
+ {
+ digmin--;
+ sprintf(scratchpad, "%i", digmin);
+ strcat(scratchpad, " ");
+ scratchpad[8] = 0;
+ fseeko(file, (long long)(256 + (edfsignals * 120) + (i * 8)), SEEK_SET);
+ fprintf(file, "%s", scratchpad);
+ }
+ }
+ }
}
strncpy(scratchpad, hdr + 256 + (edfsignals * 104) + (i * 8), 8); // physical minimum
@@ -1384,21 +1450,30 @@ void UI_headerEditorWindow::save_hdr()
fseeko(file, (long long)(256 + (edfsignals * 104) + (i * 8)), SEEK_SET);
fprintf(file, "%s", scratchpad);
- strncpy(scratchpad, hdr + 256 + (edfsignals * 112) + (i * 8), 8); // physical maximum
+ strncpy(scratchpad2, hdr + 256 + (edfsignals * 112) + (i * 8), 8); // physical maximum
for(p=7; p>0; p--)
{
- if((scratchpad[p] < '0') || (scratchpad[p] > '9'))
+ if((scratchpad2[p] < '0') || (scratchpad2[p] > '9'))
{
- scratchpad[p] = ' ';
+ scratchpad2[p] = ' ';
}
else
{
break;
}
}
- scratchpad[8] = 0;
+ scratchpad2[8] = 0;
fseeko(file, (long long)(256 + (edfsignals * 112) + (i * 8)), SEEK_SET);
- fprintf(file, "%s", scratchpad);
+ fprintf(file, "%s", scratchpad2);
+
+ if(dig_ok == 3)
+ {
+ fseeko(file, (long long)(256 + (edfsignals * 104) + (i * 8)), SEEK_SET);
+ fprintf(file, "%s", scratchpad2);
+
+ fseeko(file, (long long)(256 + (edfsignals * 112) + (i * 8)), SEEK_SET);
+ fprintf(file, "%s", scratchpad);
+ }
}
fseeko(file, (long long)(256 + (edfsignals * 224) + (i * 32)), SEEK_SET); // reserved
@@ -1455,6 +1530,10 @@ void UI_headerEditorWindow::save_hdr()
fflush(file);
read_header();
+
+ QMessageBox messagewindow(QMessageBox::Information, "Ready", "File has been saved.");
+ messagewindow.setIconPixmap(QPixmap(":/images/ok.png"));
+ messagewindow.exec();
}
diff --git a/images/splash.png b/images/splash.png
index 3c1d222..5eee368 100644
Binary files a/images/splash.png and b/images/splash.png differ
diff --git a/import_annotations.cpp b/import_annotations.cpp
index 0550064..8c0668a 100644
--- a/import_annotations.cpp
+++ b/import_annotations.cpp
@@ -728,6 +728,7 @@ int UI_ImportAnnotationswindow::import_from_xml(void)
}
annotation.annotation[MAX_ANNOTATION_LEN] = 0;
strcpy(annotation.duration, duration);
+ annotation.long_duration = edfplus_annotation_get_long_from_number(duration);
if(edfplus_annotation_add_item(&mainwindow->edfheaderlist[0]->annot_list, annotation))
{
QApplication::restoreOverrideCursor();
@@ -1484,6 +1485,8 @@ int UI_ImportAnnotationswindow::import_from_ascii(void)
{
strcpy(annotation.duration, duration);
}
+
+ annotation.long_duration = edfplus_annotation_get_long_from_number(duration);
}
}
if(edfplus_annotation_add_item(&mainwindow->edfheaderlist[0]->annot_list, annotation))
@@ -1518,6 +1521,8 @@ int UI_ImportAnnotationswindow::import_from_ascii(void)
{
strcpy(annotation.duration, duration);
}
+
+ annotation.long_duration = edfplus_annotation_get_long_from_number(duration);
}
}
if(edfplus_annotation_add_item(&mainwindow->edfheaderlist[0]->annot_list, annotation))
diff --git a/load_montage_dialog.cpp b/load_montage_dialog.cpp
index 4ff9c74..135813b 100644
--- a/load_montage_dialog.cpp
+++ b/load_montage_dialog.cpp
@@ -112,7 +112,10 @@ void UI_LoadMontagewindow::LoadButtonClicked()
size=0,
amp_cat[3],
f_ruler_cnt=0,
- holdoff=100;
+ holdoff=100,
+ plif_powerlinefrequency,
+ not_compatibel,
+ sf;
char result[XML_STRBUFLEN],
scratchpad[2048],
@@ -125,7 +128,8 @@ void UI_LoadMontagewindow::LoadButtonClicked()
double frequency=1.0,
frequency2=2.0,
ripple=1.0,
- velocity=1.0;
+ velocity=1.0,
+ dthreshold;
struct xml_handle *xml_hdl;
@@ -256,6 +260,19 @@ void UI_LoadMontagewindow::LoadButtonClicked()
mainwindow->signalcomp[k]->fidfilter_cnt = 0;
+ if(mainwindow->signalcomp[k]->plif_ecg_filter)
+ {
+ plif_free_subtract_filter(mainwindow->signalcomp[k]->plif_ecg_filter);
+
+ mainwindow->signalcomp[k]->plif_ecg_filter = NULL;
+ }
+
+ if(mainwindow->signalcomp[k]->plif_ecg_filter_sav)
+ {
+ plif_free_subtract_filter(mainwindow->signalcomp[k]->plif_ecg_filter_sav);
+
+ mainwindow->signalcomp[k]->plif_ecg_filter_sav = NULL;
+ }
if(mainwindow->signalcomp[k]->ecg_filter != NULL)
{
@@ -346,6 +363,8 @@ void UI_LoadMontagewindow::LoadButtonClicked()
newsignalcomp->screen_offset = 0;
newsignalcomp->filter_cnt = 0;
newsignalcomp->ravg_filter_cnt = 0;
+ newsignalcomp->plif_ecg_filter = NULL;
+ newsignalcomp->plif_ecg_filter_sav = NULL;
newsignalcomp->ecg_filter = NULL;
newsignalcomp->fidfilter_cnt = 0;
newsignalcomp->hasruler = 0;
@@ -686,6 +705,28 @@ void UI_LoadMontagewindow::LoadButtonClicked()
xml_go_up(xml_hdl);
break;
}
+
+ if(dblcmp(newsignalcomp->edfhdr->edfparam[newsignalcomp->edfsignal[signals_read]].bitvalue,
+ newsignalcomp->edfhdr->edfparam[newsignalcomp->edfsignal[0]].bitvalue))
+ {
+ free(newsignalcomp);
+ skip = 1;
+ signalcomps_read++;
+ xml_go_up(xml_hdl);
+ xml_go_up(xml_hdl);
+ break;
+ }
+
+ if(strcmp(newsignalcomp->edfhdr->edfparam[newsignalcomp->edfsignal[signals_read]].physdimension,
+ newsignalcomp->edfhdr->edfparam[newsignalcomp->edfsignal[0]].physdimension))
+ {
+ free(newsignalcomp);
+ skip = 1;
+ signalcomps_read++;
+ xml_go_up(xml_hdl);
+ xml_go_up(xml_hdl);
+ break;
+ }
}
newsignalcomp->sensitivity[signals_read] = newsignalcomp->edfhdr->edfparam[newsignalcomp->edfsignal[signals_read]].bitvalue / (newsignalcomp->voltpercm * mainwindow->pixelsizefactor);
@@ -1362,6 +1403,99 @@ void UI_LoadMontagewindow::LoadButtonClicked()
xml_go_up(xml_hdl);
}
+ if(!xml_goto_nth_element_inside(xml_hdl, "plif_ecg_filter", 0))
+ {
+ not_compatibel = 0;
+
+ sf = ((long long)(newsignalcomp->edfhdr->edfparam[newsignalcomp->edfsignal[0]].smp_per_record) * TIME_DIMENSION) /
+ newsignalcomp->edfhdr->long_data_record_duration;
+
+ if(xml_goto_nth_element_inside(xml_hdl, "plf", 0))
+ {
+ sprintf(str2, "There seems to be an error in this montage file.\nFile: %s line: %i", __FILE__, __LINE__);
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", str2);
+ messagewindow.exec();
+ free(newsignalcomp);
+ xml_close(xml_hdl);
+ return;
+ }
+ if(xml_get_content_of_element(xml_hdl, result, XML_STRBUFLEN))
+ {
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "There seems to be an error in this montage file.");
+ messagewindow.exec();
+ free(newsignalcomp);
+ xml_close(xml_hdl);
+ return;
+ }
+ plif_powerlinefrequency = atoi(result);
+ if((plif_powerlinefrequency != 0) && (plif_powerlinefrequency != 1))
+ {
+ sprintf(str2, "There seems to be an error in this montage file.\nFile: %s line: %i", __FILE__, __LINE__);
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", str2);
+ messagewindow.exec();
+ free(newsignalcomp);
+ xml_close(xml_hdl);
+ return;
+ }
+ plif_powerlinefrequency *= 10;
+ plif_powerlinefrequency += 50;
+ xml_go_up(xml_hdl);
+
+ if(sf % plif_powerlinefrequency) not_compatibel = 1;
+
+ strcpy(str, newsignalcomp->edfhdr->edfparam[newsignalcomp->edfsignal[0]].physdimension);
+
+ remove_trailing_spaces(str);
+
+ if(!strcmp(str, "uV"))
+ {
+ dthreshold = 10.0 / newsignalcomp->edfhdr->edfparam[newsignalcomp->edfsignal[0]].bitvalue;
+ }
+ else if(!strcmp(str, "mV"))
+ {
+ dthreshold = 1e-2 / newsignalcomp->edfhdr->edfparam[newsignalcomp->edfsignal[0]].bitvalue;
+ }
+ else if(!strcmp(str, "V"))
+ {
+ dthreshold = 1e-5 / newsignalcomp->edfhdr->edfparam[newsignalcomp->edfsignal[0]].bitvalue;
+ }
+ else
+ {
+ not_compatibel = 1;
+ }
+
+ if(!not_compatibel)
+ {
+ newsignalcomp->plif_ecg_filter = plif_create_subtract_filter(sf, plif_powerlinefrequency, dthreshold);
+ if(newsignalcomp->plif_ecg_filter == NULL)
+ {
+ sprintf(str2, "A memory allocation error occurred when creating a powerline interference removal filter.\n"
+ "File: %s line: %i", __FILE__, __LINE__);
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", str2);
+ messagewindow.exec();
+ free(newsignalcomp);
+ xml_close(xml_hdl);
+ return;
+ }
+
+ newsignalcomp->plif_ecg_filter_sav = plif_create_subtract_filter(sf, plif_powerlinefrequency, dthreshold);
+ if(newsignalcomp->plif_ecg_filter_sav == NULL)
+ {
+ sprintf(str2, "A memory allocation error occurred when creating a powerline interference removal filter.\n"
+ "File: %s line: %i", __FILE__, __LINE__);
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", str2);
+ messagewindow.exec();
+ free(newsignalcomp);
+ xml_close(xml_hdl);
+ return;
+ }
+
+ newsignalcomp->plif_ecg_subtract_filter_plf = plif_powerlinefrequency / 60;
+ }
+
+ xml_go_up(xml_hdl);
+ }
+
if(!xml_goto_nth_element_inside(xml_hdl, "ecg_filter", 0))
{
if(xml_goto_nth_element_inside(xml_hdl, "type", 0))
diff --git a/load_montage_dialog.h b/load_montage_dialog.h
index 5597f9c..e7ac858 100644
--- a/load_montage_dialog.h
+++ b/load_montage_dialog.h
@@ -57,6 +57,7 @@
#include "utc_date_time.h"
#include "utils.h"
#include "spectrum_dock.h"
+#include "plif_ecg_subtract_filter.h"
#include "third_party/fidlib/fidlib.h"
diff --git a/mainwindow.cpp b/mainwindow.cpp
index 5825f03..f37d4ae 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -876,6 +876,14 @@ void UI_Mainwindow::add_new_filter()
}
+void UI_Mainwindow::add_plif_ecg_filter()
+{
+ if(!files_open) return;
+
+ UI_PLIF_ECG_filter_dialog plifecgfilterdialog(this);
+}
+
+
void UI_Mainwindow::add_spike_filter()
{
if(!files_open) return;
@@ -1347,6 +1355,8 @@ void UI_Mainwindow::open_new_file()
str[0] = 0;
+ QAction *act;
+
struct edfhdrblock *edfhdr=NULL;
if(annot_editor_active && files_open)
@@ -1441,7 +1451,9 @@ void UI_Mainwindow::open_new_file()
{
break;
}
- recent_filesmenu->addAction(QString::fromLocal8Bit(&recent_file_path[i][0]));
+ act = new QAction(QString::fromLocal8Bit(&recent_file_path[i][0]), recent_filesmenu);
+ act->setData(QVariant(i));
+ recent_filesmenu->addAction(act);
}
present = 0;
@@ -1886,6 +1898,41 @@ void UI_Mainwindow::remove_all_filters()
}
+void UI_Mainwindow::remove_all_plif_ecg_filters()
+{
+ int i,
+ update_scr=0;
+
+ if(!files_open) return;
+
+ for(i=0; i<signalcomps; i++)
+ {
+ if(signalcomp[i]->plif_ecg_filter)
+ {
+ plif_free_subtract_filter(signalcomp[i]->plif_ecg_filter);
+
+ signalcomp[i]->plif_ecg_filter = NULL;
+
+ update_scr = 1;
+ }
+
+ if(signalcomp[i]->plif_ecg_filter_sav)
+ {
+ plif_free_subtract_filter(signalcomp[i]->plif_ecg_filter_sav);
+
+ signalcomp[i]->plif_ecg_filter_sav = NULL;
+
+ update_scr = 1;
+ }
+ }
+
+ if(update_scr)
+ {
+ setup_viewbuf();
+ }
+}
+
+
void UI_Mainwindow::remove_all_spike_filters()
{
int i,
@@ -1965,6 +2012,8 @@ void UI_Mainwindow::remove_all_signals()
remove_all_spike_filters();
+ remove_all_plif_ecg_filters();
+
for(i=0; i<signalcomps; i++)
{
free(signalcomp[i]);
@@ -2098,6 +2147,20 @@ void UI_Mainwindow::close_file_action_func(QAction *action)
signalcomp[j]->filter_cnt = 0;
+ if(signalcomp[j]->plif_ecg_filter)
+ {
+ plif_free_subtract_filter(signalcomp[j]->plif_ecg_filter);
+
+ signalcomp[j]->plif_ecg_filter = NULL;
+ }
+
+ if(signalcomp[j]->plif_ecg_filter_sav)
+ {
+ plif_free_subtract_filter(signalcomp[j]->plif_ecg_filter_sav);
+
+ signalcomp[j]->plif_ecg_filter_sav = NULL;
+ }
+
if(signalcomp[j]->spike_filter)
{
free_spike_filter(signalcomp[j]->spike_filter);
@@ -2695,6 +2758,8 @@ void UI_Mainwindow::set_display_time_whole_rec()
{
int i;
+ long long vtime=0LL;
+
if(!files_open) return;
if(viewtime_sync==VIEWTIME_SYNCED_OFFSET)
@@ -2736,7 +2801,28 @@ void UI_Mainwindow::set_display_time_whole_rec()
edfheaderlist[sel_viewtime]->viewtime = 0;
}
- pagetime = edfheaderlist[sel_viewtime]->datarecords * edfheaderlist[sel_viewtime]->long_data_record_duration;
+ for(i=0; i<files_open; i++)
+ {
+ if(edfheaderlist[i]->viewtime > vtime)
+ {
+ vtime = edfheaderlist[i]->viewtime;
+ }
+ }
+
+ for(i=0; i<files_open; i++)
+ {
+ edfheaderlist[i]->viewtime -= vtime;
+ }
+
+ pagetime = 0LL;
+
+ for(i=0; i<files_open; i++)
+ {
+ if(((edfheaderlist[i]->datarecords * edfheaderlist[i]->long_data_record_duration) - edfheaderlist[i]->viewtime) > pagetime)
+ {
+ pagetime = (edfheaderlist[i]->datarecords * edfheaderlist[i]->long_data_record_duration) - edfheaderlist[i]->viewtime;
+ }
+ }
setup_viewbuf();
}
@@ -3249,7 +3335,7 @@ long long UI_Mainwindow::get_long_time(char *str)
void UI_Mainwindow::recent_file_action_func(QAction *action)
{
- strcpy(path, action->text().toLocal8Bit().data());
+ strcpy(path, &recent_file_path[action->data().toInt()][0]);
cmdlineargument = 1;
@@ -3519,7 +3605,7 @@ struct signalcompblock * UI_Mainwindow::create_signalcomp_copy(struct signalcomp
return(NULL);
}
- memcpy(newsignalcomp, original_signalcomp, sizeof(struct signalcompblock));
+ *newsignalcomp = *original_signalcomp;
if(newsignalcomp->spike_filter)
{
@@ -3533,6 +3619,20 @@ struct signalcompblock * UI_Mainwindow::create_signalcomp_copy(struct signalcomp
}
}
+ if(newsignalcomp->plif_ecg_filter)
+ {
+ newsignalcomp->plif_ecg_filter = plif_subtract_filter_create_copy(original_signalcomp->plif_ecg_filter);
+ if(newsignalcomp->plif_ecg_filter == NULL)
+ {
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "malloc() error");
+ messagewindow.exec();
+ free(signalcomp);
+ return(NULL);
+ }
+ }
+
+ newsignalcomp->plif_ecg_filter_sav = NULL;
+
for(i=0; i<newsignalcomp->filter_cnt; i++)
{
newsignalcomp->filter[i] = create_filter_copy(original_signalcomp->filter[i]);
diff --git a/mainwindow.h b/mainwindow.h
index 66dd591..1177fe9 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -87,6 +87,7 @@
#include <QList>
#include <QProcess>
#include <QProcessEnvironment>
+#include <QVariant>
#include <stdio.h>
#include <stdlib.h>
@@ -157,6 +158,8 @@
#include "spike_filter_dialog.h"
#include "mit2edf.h"
#include "biox2edf.h"
+#include "plif_ecg_subtract_filter.h"
+#include "plif_ecg_subtract_filter_dialog.h"
#include "third_party/fidlib/fidlib.h"
@@ -488,8 +491,10 @@ private slots:
void export_to_ascii();
void check_edf_compatibility();
void add_new_filter();
+ void add_plif_ecg_filter();
void add_spike_filter();
void remove_all_filters();
+ void remove_all_plif_ecg_filters();
void remove_all_spike_filters();
// void add_new_math_func();
// void remove_all_math_funcs();
diff --git a/mainwindow_constr.cpp b/mainwindow_constr.cpp
index 25d0f63..71480e6 100644
--- a/mainwindow_constr.cpp
+++ b/mainwindow_constr.cpp
@@ -610,6 +610,9 @@ UI_Mainwindow::UI_Mainwindow()
filtermenu->addAction("Adjust", this, SLOT(filterproperties_dialog()));
filtermenu->addAction("Remove all", this, SLOT(remove_all_filters()));
filtermenu->addSeparator();
+ filtermenu->addAction("Powerline interference removal for ECG", this, SLOT(add_plif_ecg_filter()));
+ filtermenu->addAction("Remove all Powerline interference filters", this, SLOT(remove_all_plif_ecg_filters()));
+ filtermenu->addSeparator();
filtermenu->addAction("Spike", this, SLOT(add_spike_filter()));
filtermenu->addAction("Remove all spike filters", this, SLOT(remove_all_spike_filters()));
menubar->addMenu(filtermenu);
@@ -1001,29 +1004,29 @@ UI_Mainwindow::UI_Mainwindow()
}
}
- if(QT_VERSION < MINIMUM_QT_VERSION)
+ if((QT_VERSION < MINIMUM_QT4_VERSION) || ((QT_VERSION >= 0x050000) && (QT_VERSION < MINIMUM_QT5_VERSION)))
{
- QMessageBox messagewindow(QMessageBox::Warning, "Warning", "Qt version is too old");
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "EDFbrowser has been compiled with a version of Qt\n"
+ "which is too old and will likely cause problems!");
messagewindow.exec();
}
- else
- {
- int v_nr;
- char v_str[32];
+ int v_nr;
- strncpy(v_str, qVersion(), 32);
- v_str[31] = 0;
+ char v_str[32];
- v_nr = 0x10000 * atoi(v_str);
- v_nr += 0x100 * atoi(v_str + 2);
- v_nr += atoi(v_str + 4);
+ strncpy(v_str, qVersion(), 32);
+ v_str[31] = 0;
- if(v_nr < MINIMUM_QT_VERSION)
- {
- QMessageBox messagewindow(QMessageBox::Warning, "Warning", "Qt version is too old");
- messagewindow.exec();
- }
+ v_nr = 0x10000 * atoi(v_str);
+ v_nr += 0x100 * atoi(v_str + 2);
+ v_nr += atoi(v_str + 4);
+
+ if((v_nr < MINIMUM_QT4_VERSION) || ((v_nr >= 0x050000) && (v_nr < MINIMUM_QT5_VERSION)))
+ {
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "Your version of Qt is too old\n"
+ "and will likely cause problems!");
+ messagewindow.exec();
}
pixmap = new QPixmap(":/images/splash.png");
diff --git a/mit2edf.cpp b/mit2edf.cpp
index d4cb2d9..2d94d04 100644
--- a/mit2edf.cpp
+++ b/mit2edf.cpp
@@ -176,6 +176,11 @@ void UI_MIT2EDFwindow::SelectFileButton()
long long filesize;
+ union {
+ int one;
+ unsigned char four[4];
+ } var;
+
pushButton1->setEnabled(false);
strcpy(header_filename, QFileDialog::getOpenFileName(0, "Select inputfile", QString::fromLocal8Bit(recent_opendir), "MIT header files (*.hea *.HEA)").toLocal8Bit().data());
@@ -408,6 +413,7 @@ void UI_MIT2EDFwindow::SelectFileButton()
if((mit_hdr.format[j] != 212) &&
(mit_hdr.format[j] != 16) &&
+ (mit_hdr.format[j] != 32) &&
(mit_hdr.format[j] != 61))
{
snprintf(txt_string, 2048, "Error, unsupported format: %i (error 16)\n", mit_hdr.format[j]);
@@ -447,7 +453,7 @@ void UI_MIT2EDFwindow::SelectFileButton()
if(atoi(charpntr + p) != 0)
{
- mit_hdr.adc_gain[j] = atoi(charpntr + p);
+ mit_hdr.adc_gain[j] = atof(charpntr + p);
}
p = ++i;
@@ -855,11 +861,6 @@ void UI_MIT2EDFwindow::SelectFileButton()
if(l_end)
{
tmp1 += (fgetc(data_inputfile) << 8);
-
- if(tmp1 & 0x8000)
- {
- tmp1 |= 0xffff0000;
- }
}
else
{
@@ -868,6 +869,11 @@ void UI_MIT2EDFwindow::SelectFileButton()
tmp1 += fgetc(data_inputfile);
}
+ if(tmp1 & 0x8000)
+ {
+ tmp1 |= 0xffff0000;
+ }
+
buf[j * mit_hdr.sf_block + i] = tmp1;
}
}
@@ -885,6 +891,63 @@ void UI_MIT2EDFwindow::SelectFileButton()
}
}
+ if(mit_hdr.format[0] == 32)
+ {
+ blocks /= 4;
+
+ progress.setMaximum(blocks);
+
+ for(k=0; k<blocks; k++)
+ {
+ if(!(k % 100))
+ {
+ progress.setValue(k);
+
+ qApp->processEvents();
+
+ if(progress.wasCanceled() == true)
+ {
+ textEdit1->append("Conversion aborted by user.\n");
+ fclose(data_inputfile);
+ edfclose_file(hdl);
+ free(buf);
+ pushButton1->setEnabled(true);
+ return;
+ }
+ }
+
+ for(i=0; i<mit_hdr.sf_block; i++)
+ {
+ for(j=0; j<mit_hdr.chns; j++)
+ {
+ tmp1 = fgetc(data_inputfile);
+ if(tmp1 == EOF)
+ {
+ goto OUT;
+ }
+
+ var.four[0] = tmp1;
+ var.four[1] = fgetc(data_inputfile);
+ var.four[2] = fgetc(data_inputfile);
+ var.four[3] = fgetc(data_inputfile);
+
+ buf[j * mit_hdr.sf_block + i] = var.one;
+ }
+ }
+
+ if(edf_blockwrite_digital_samples(hdl, buf))
+ {
+ progress.reset();
+ textEdit1->append("A write error occurred during conversion.\n");
+ fclose(data_inputfile);
+ edfclose_file(hdl);
+ free(buf);
+ pushButton1->setEnabled(true);
+ return;
+ }
+ }
+ }
+
OUT:
progress.reset();
diff --git a/mit2edf.h b/mit2edf.h
index ad25c08..23a76aa 100644
--- a/mit2edf.h
+++ b/mit2edf.h
@@ -86,6 +86,7 @@ struct {
long long smp_period;
int format[MAXSIGNALS];
double adc_gain[MAXSIGNALS];
+ int baseline[MAXSIGNALS];
int adc_resolution[MAXSIGNALS];
int adc_zero[MAXSIGNALS];
int init_val[MAXSIGNALS];
diff --git a/options_dialog.cpp b/options_dialog.cpp
index c700d30..9901115 100644
--- a/options_dialog.cpp
+++ b/options_dialog.cpp
@@ -55,13 +55,13 @@ UI_OptionsDialog::UI_OptionsDialog(QWidget *w_parent)
scrollarea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
frame = new QFrame;
- frame->setMinimumSize(440, 780);
- frame->setMaximumSize(440, 780);
+ frame->setMinimumSize(440, 820);
+ frame->setMaximumSize(440, 820);
}
else
{
- optionsdialog->setMinimumSize(440, 780);
- optionsdialog->setMaximumSize(440, 780);
+ optionsdialog->setMinimumSize(440, 820);
+ optionsdialog->setMaximumSize(440, 820);
}
optionsdialog->setWindowTitle("Settings");
optionsdialog->setModal(true);
@@ -188,12 +188,20 @@ UI_OptionsDialog::UI_OptionsDialog(QWidget *w_parent)
AnnotMkrButton->setGeometry(240, 420, 60, 15);
AnnotMkrButton->setColor(mainwindow->maincurve->annot_marker_color);
+ label12_1 = new QLabel(tab1);
+ label12_1->setGeometry(20, 455, 200, 25);
+ label12_1->setText("Annotation duration background");
+
+ AnnotDurationButton = new SpecialButton(tab1);
+ AnnotDurationButton->setGeometry(240, 460, 60, 15);
+ AnnotDurationButton->setColor(mainwindow->maincurve->annot_duration_color);
+
label11 = new QLabel(tab1);
- label11->setGeometry(20, 455, 200, 25);
+ label11->setGeometry(20, 495, 200, 25);
label11->setText("Print in grayscale");
checkbox1 = new QCheckBox(tab1);
- checkbox1->setGeometry(200, 458, 20, 20);
+ checkbox1->setGeometry(200, 498, 20, 20);
checkbox1->setTristate(false);
if(mainwindow->maincurve->blackwhite_printing)
{
@@ -205,11 +213,11 @@ UI_OptionsDialog::UI_OptionsDialog(QWidget *w_parent)
}
label13 = new QLabel(tab1);
- label13->setGeometry(20, 495, 200, 25);
+ label13->setGeometry(20, 535, 200, 25);
label13->setText("Clip signals to pane");
checkbox4 = new QCheckBox(tab1);
- checkbox4->setGeometry(200, 498, 20, 20);
+ checkbox4->setGeometry(200, 538, 20, 20);
checkbox4->setTristate(false);
if(mainwindow->clip_to_pane)
{
@@ -221,23 +229,23 @@ UI_OptionsDialog::UI_OptionsDialog(QWidget *w_parent)
}
colorSchema_Dark_Button = new QPushButton(tab1);
- colorSchema_Dark_Button->setGeometry(140, 540, 140, 20);
+ colorSchema_Dark_Button->setGeometry(140, 580, 140, 20);
colorSchema_Dark_Button->setText("Colorschema \"Dark\"");
colorSchema_NK_Button = new QPushButton(tab1);
- colorSchema_NK_Button->setGeometry(140, 570, 140, 20);
+ colorSchema_NK_Button->setGeometry(140, 610, 140, 20);
colorSchema_NK_Button->setText("Colorschema \"NK\"");
DefaultButton = new QPushButton(tab1);
- DefaultButton->setGeometry(140, 600, 140, 20);
+ DefaultButton->setGeometry(140, 640, 140, 20);
DefaultButton->setText("Default colorschema");
saveColorSchemaButton = new QPushButton(tab1);
- saveColorSchemaButton->setGeometry(140, 630, 140, 20);
+ saveColorSchemaButton->setGeometry(140, 670, 140, 20);
saveColorSchemaButton->setText("Save colorschema");
loadColorSchemaButton = new QPushButton(tab1);
- loadColorSchemaButton->setGeometry(140, 660, 140, 20);
+ loadColorSchemaButton->setGeometry(140, 700, 140, 20);
loadColorSchemaButton->setText("Load colorschema");
QObject::connect(BgColorButton, SIGNAL(clicked(SpecialButton *)), this, SLOT(BgColorButtonClicked(SpecialButton *)));
@@ -251,6 +259,7 @@ UI_OptionsDialog::UI_OptionsDialog(QWidget *w_parent)
QObject::connect(Crh2ColorButton, SIGNAL(clicked(SpecialButton *)), this, SLOT(Crh2ColorButtonClicked(SpecialButton *)));
QObject::connect(FrColorButton, SIGNAL(clicked(SpecialButton *)), this, SLOT(FrColorButtonClicked(SpecialButton *)));
QObject::connect(AnnotMkrButton, SIGNAL(clicked(SpecialButton *)), this, SLOT(AnnotMkrButtonClicked(SpecialButton *)));
+ QObject::connect(AnnotDurationButton, SIGNAL(clicked(SpecialButton *)), this, SLOT(AnnotDurationButtonClicked(SpecialButton *)));
QObject::connect(checkbox1, SIGNAL(stateChanged(int)), this, SLOT(checkbox1Clicked(int)));
QObject::connect(checkbox2, SIGNAL(stateChanged(int)), this, SLOT(checkbox2Clicked(int)));
QObject::connect(checkbox3, SIGNAL(stateChanged(int)), this, SLOT(checkbox3Clicked(int)));
@@ -1256,6 +1265,12 @@ void UI_OptionsDialog::DefaultButtonClicked()
mainwindow->show_annot_markers = 1;
checkbox2->setCheckState(Qt::Checked);
+ mainwindow->maincurve->annot_duration_color.setRed(0);
+ mainwindow->maincurve->annot_duration_color.setGreen(127);
+ mainwindow->maincurve->annot_duration_color.setBlue(127);
+ mainwindow->maincurve->annot_duration_color.setAlpha(32);
+ AnnotDurationButton->setColor(mainwindow->maincurve->annot_duration_color);
+
palette.setColor(QPalette::Text, mainwindow->maincurve->text_color);
palette.setColor(QPalette::Base, mainwindow->maincurve->backgroundcolor);
@@ -1509,6 +1524,23 @@ void UI_OptionsDialog::AnnotMkrButtonClicked(SpecialButton *)
}
+void UI_OptionsDialog::AnnotDurationButtonClicked(SpecialButton *)
+{
+ QColor temp;
+
+ temp = QColorDialog::getColor(mainwindow->maincurve->annot_duration_color, tab1, "Select Color", QColorDialog::ShowAlphaChannel);
+
+ if(temp.isValid())
+ {
+ mainwindow->maincurve->annot_duration_color = temp;
+
+ AnnotDurationButton->setColor(mainwindow->maincurve->annot_duration_color);
+
+ mainwindow->maincurve->update();
+ }
+}
+
+
void UI_OptionsDialog::dspinbox4_4ValueChanged(double val)
{
mainwindow->default_amplitude = val;
@@ -1615,6 +1647,17 @@ void UI_OptionsDialog::saveColorSchemaButtonClicked()
mainwindow->maincurve->annot_marker_color.green(),
mainwindow->maincurve->annot_marker_color.blue());
+ fprintf(colorfile, " <annot_duration_color>\n"
+ " <red>%i</red>\n"
+ " <green>%i</green>\n"
+ " <blue>%i</blue>\n"
+ " <alpha>%i</alpha>\n"
+ " </annot_duration_color>\n",
+ mainwindow->maincurve->annot_duration_color.red(),
+ mainwindow->maincurve->annot_duration_color.green(),
+ mainwindow->maincurve->annot_duration_color.blue(),
+ mainwindow->maincurve->annot_duration_color.alpha());
+
fprintf(colorfile, " <signal_color>%i</signal_color>\n",
mainwindow->maincurve->signal_color);
@@ -1697,6 +1740,8 @@ void UI_OptionsDialog::loadColorSchemaButtonClicked()
mainwindow->get_rgbcolor_settings(xml_hdl, "annot_marker_color", 0, &mainwindow->maincurve->annot_marker_color);
+ mainwindow->get_rgbcolor_settings(xml_hdl, "annot_duration_color", 0, &mainwindow->maincurve->annot_duration_color);
+
if(xml_goto_nth_element_inside(xml_hdl, "signal_color", 0))
{
xml_close(xml_hdl);
@@ -1860,6 +1905,8 @@ void UI_OptionsDialog::update_interface(void)
AnnotMkrButton->setColor(mainwindow->maincurve->annot_marker_color);
+ AnnotDurationButton->setColor(mainwindow->maincurve->annot_duration_color);
+
if(mainwindow->maincurve->blackwhite_printing)
{
checkbox1->setCheckState(Qt::Checked);
@@ -1928,6 +1975,11 @@ void UI_OptionsDialog::loadColorSchema_NK()
mainwindow->maincurve->annot_marker_color.setGreen(0);
mainwindow->maincurve->annot_marker_color.setBlue(0);
+ mainwindow->maincurve->annot_duration_color.setRed(0);
+ mainwindow->maincurve->annot_duration_color.setGreen(127);
+ mainwindow->maincurve->annot_duration_color.setBlue(127);
+ mainwindow->maincurve->annot_duration_color.setAlpha(32);
+
mainwindow->maincurve->signal_color = 2;
mainwindow->maincurve->floating_ruler_color = 7;
@@ -1978,6 +2030,11 @@ void UI_OptionsDialog::loadColorSchema_Dark()
mainwindow->maincurve->annot_marker_color.setGreen(255);
mainwindow->maincurve->annot_marker_color.setBlue(255);
+ mainwindow->maincurve->annot_duration_color.setRed(0);
+ mainwindow->maincurve->annot_duration_color.setGreen(127);
+ mainwindow->maincurve->annot_duration_color.setBlue(127);
+ mainwindow->maincurve->annot_duration_color.setAlpha(32);
+
mainwindow->maincurve->signal_color = 12;
mainwindow->maincurve->floating_ruler_color = 10;
diff --git a/options_dialog.h b/options_dialog.h
index 82c535f..44ea74f 100644
--- a/options_dialog.h
+++ b/options_dialog.h
@@ -126,6 +126,7 @@ SpecialButton *BgColorButton,
*Crh2ColorButton,
*FrColorButton,
*AnnotMkrButton,
+ *AnnotDurationButton,
*slabel2_1,
*slabel2_3;
@@ -141,6 +142,7 @@ QLabel *label1,
*label10,
*label11,
*label12,
+ *label12_1,
*label13,
*label4_1,
*label4_2,
@@ -212,6 +214,7 @@ void Crh1ColorButtonClicked(SpecialButton *);
void Crh2ColorButtonClicked(SpecialButton *);
void FrColorButtonClicked(SpecialButton *);
void AnnotMkrButtonClicked(SpecialButton *);
+void AnnotDurationButtonClicked(SpecialButton *);
void checkbox1Clicked(int);
void checkbox2Clicked(int);
void checkbox3Clicked(int);
diff --git a/plif_ecg_subtract_filter.c b/plif_ecg_subtract_filter.c
new file mode 100644
index 0000000..98ca4d5
--- /dev/null
+++ b/plif_ecg_subtract_filter.c
@@ -0,0 +1,416 @@
+/*
+***************************************************************************
+*
+* Author: Teunis van Beelen
+*
+* Copyright (C) 2017 Teunis van Beelen
+*
+* Email: teuniz at gmail.com
+*
+**************************************************************************
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+***************************************************************************
+*
+* Inspired by:
+*
+* - Subtraction Method For Powerline Interference Removing From ECG
+* Chavdar Levkov, Georgy Mihov, Ratcho Ivanov, Ivan K. Daskalov
+* Ivaylo Christov, Ivan Dotsinsky
+*
+* - Removal of power-line interference from the ECG: a review of the
+* subtraction procedure
+* Chavdar Levkov, Georgy Mihov, Ratcho Ivanov, Ivan Daskalov,
+* Ivaylo Christov and Ivan Dotsinsky
+*
+* - Accuracy of 50 Hz interference subtraction from an electrocardiogram
+* I. A, Dotsinsky I.K. Daskalov
+*
+* - Dynamic powerline interference subtraction from biosignals
+* Ivaylo I. Christov
+*
+*
+* The subtraction method extracts the powerline interference noise during a
+* a linear region between two consecutive QRS complexes and stores it in a buffer.
+* Buffersize equals one complete powerlinefrequency cycle (20 milli-Sec.).
+* The reference noise from the buffer is used to subtract it from the signal outside
+* the linear region i.e. during the QRS complex.
+* This method only works correctly when the ratio of the samplefrequency and the
+* powerline frequency is an integer multiple.
+* In case they are synchronized, this method will remove also the harmonics of the
+* powerline frequency. In that case extra notch-filters for the harmonics are
+* not necessary. The advantage of this method is that it will not cause ringing
+* in the waveform of the QRS complex (like notch-filters do).
+*
+***************************************************************************
+*/
+
+
+
+
+#include "plif_ecg_subtract_filter.h"
+
+
+/*
+ *
+ * sf: samplefrequency (must be >= 500Hz and must be an integer multiple of the powerline frequency)
+ *
+ * pwlf: powerline frequency (must be set to 50Hz or 60Hz)
+ *
+ * lt: linear region threshold, MUST BE SET TO 5uV!
+ * used to detect linear region between two consecutive QRS complexes
+ *
+ */
+struct plif_subtract_filter_settings * plif_create_subtract_filter(int sf, int pwlf, double lt)
+{
+ int i;
+
+ struct plif_subtract_filter_settings *st;
+
+/* perform some sanity checks */
+ if(sf < 500) return NULL; /* we need at least the samplefrequency considered the "gold standard" */
+
+ if((pwlf != 50) && (pwlf != 60)) return NULL; /* powerline frequency must be either 50 or 60Hz */
+
+ if(sf % pwlf) return NULL; /* ratio between the samplefrequency and the powerline frequency must be an integer multiple */
+
+ if((lt < 1) || (lt > 100000)) return NULL; /* range for the linear detection threshold */
+
+ st = (struct plif_subtract_filter_settings *) calloc(1, sizeof(struct plif_subtract_filter_settings));
+ if(st==NULL) return NULL;
+
+ st->sf = sf;
+ st->tpl = sf / pwlf; /* the number of samples in one cycle of the powerline frequency */
+ st->ravg_idx = 0;
+ st->buf_idx = 0;
+ st->linear_threshold = lt; /* the threshold to detect the linear region */
+ st->ravg_buf = (double *)calloc(1, sizeof(double) * st->tpl);
+ if(st->ravg_buf == NULL) /* buffer for the running average filter */
+ {
+ free(st);
+ return NULL;
+ }
+ st->ref_buf = (double *)calloc(1, sizeof(double) * st->tpl);
+ if(st->ref_buf == NULL) /* buffer for the reference noise, used to be subtracted from the ECG signal */
+ {
+ free(st->ravg_buf);
+ free(st);
+ return NULL;
+ }
+ for(i=0;i<PLIF_NBUFS; i++)
+ {
+ st->ravg_noise_buf[i] = (double *)calloc(1, sizeof(double) * st->tpl);
+ if(st->ravg_noise_buf[i] == NULL) /* buffers used for the noise extraction */
+ {
+ free(st->ravg_buf);
+ free(st->ref_buf);
+ free(st);
+ return NULL;
+ }
+ }
+ for(i=0;i<PLIF_NBUFS; i++)
+ {
+ st->input_buf[i] = (double *)calloc(1, sizeof(double) * st->tpl);
+ if(st->input_buf[i] == NULL) /* inputbuffers used for detecting the linear region */
+ {
+ free(st->ravg_buf);
+ free(st->ref_buf);
+ free(st);
+ return NULL;
+ }
+ }
+
+ return st;
+}
+
+
+double plif_run_subtract_filter(double new_input, struct plif_subtract_filter_settings *st)
+{
+ int i, j, pre, linear_buf_idx, linear_bufs, linear;
+
+ double ravg_val, fd_max, fd_min, dtmp, thr, ret_val;
+
+ if(st == NULL)
+ {
+ return 0;
+ }
+
+ /* running average filter */
+ st->ravg_buf[st->ravg_idx] = new_input;
+
+ ravg_val = 0;
+
+ for(i=0; i<st->tpl; i++)
+ {
+ ravg_val += st->ravg_buf[i];
+ }
+
+ ravg_val /= st->tpl;
+
+ /* delay the input with half tpl samples */
+ new_input = st->ravg_buf[(st->ravg_idx + (st->tpl / 2)) % st->tpl];
+
+ ret_val = new_input - st->ref_buf[st->ravg_idx];
+
+ st->input_buf[st->buf_idx][st->ravg_idx] = new_input;
+
+ st->ravg_noise_buf[st->buf_idx][st->ravg_idx] = new_input - ravg_val; /* store the noise extracted from the signal into the buffers */
+
+ if(++st->ravg_idx >= st->tpl) /* buffer full? if so, check for linearity */
+ {
+ st->ravg_idx = 0;
+
+ fd_max = 1e-9;
+ fd_min = 1e9;
+
+ pre = (st->buf_idx - 1 + PLIF_NBUFS) % PLIF_NBUFS; /* index to the buffer before */
+
+ for(i=0; i<st->tpl; i++) /* compare this buffer with buffer before for their max and min values */
+ { /* distance between the 1th differences equals tpl in order to exclude the powerline noise from the detection */
+ dtmp = st->input_buf[st->buf_idx][i] - st->input_buf[pre][i];
+
+ if(dtmp > fd_max) fd_max = dtmp;
+
+ if(dtmp < fd_min) fd_min = dtmp;
+ }
+
+ st->linear_diff[st->buf_idx] = fd_max - fd_min; /* for every buffer we store the maximum difference (related to the buffer before) */
+
+ for(j=0; j<39; j++)
+ {
+ thr = (j + 1) * st->linear_threshold; /* first we try with the lowest threshold possible (5uV) */
+ /* if we can't find a linear region of at least 60 milli-seconds long, */
+ /* we increase the threshold and try again */
+ for(i=0, linear=0, linear_bufs=0; i<(PLIF_NBUFS - 1); i++)
+ {
+ linear_buf_idx = st->buf_idx - i + PLIF_NBUFS;
+ linear_buf_idx %= PLIF_NBUFS;
+
+ if(st->linear_diff[linear_buf_idx] < thr) linear_bufs++;
+ else linear_bufs = 0;
+
+ if(linear_bufs == 5) /* we need five consegutive buffers (100 milli-sec.) to pass the threshold limit */
+ {
+ linear = 1;
+
+ break;
+ }
+ }
+
+ if(linear) break;
+ }
+
+ if(linear) /* are we in a linear region? */
+ {
+ for(j=0; j<3; j++) /* average three buffers from the five (don't use the first and the last buffer) containing the extracted noise */
+ {
+ linear_buf_idx += j + PLIF_NBUFS;
+ linear_buf_idx %= PLIF_NBUFS;
+
+ if(!j)
+ {
+ for(i=0; i<st->tpl; i++)
+ {
+ st->ref_buf[i] = st->ravg_noise_buf[linear_buf_idx][i];
+ }
+ }
+ else
+ {
+ for(i=0; i<st->tpl; i++)
+ {
+ st->ref_buf[i] += st->ravg_noise_buf[linear_buf_idx][i];
+ }
+ }
+ }
+
+ for(i=0; i<st->tpl; i++)
+ {
+ st->ref_buf[i] /= j; /* calculate the average */
+ }
+ }
+
+ st->buf_idx++; /* increment the index */
+ st->buf_idx %= PLIF_NBUFS; /* check boundary and roll-over if necessary */
+ }
+
+ return ret_val;
+}
+
+
+struct plif_subtract_filter_settings * plif_subtract_filter_create_copy(struct plif_subtract_filter_settings *st_ori)
+{
+ int i;
+
+ struct plif_subtract_filter_settings *st;
+
+ if(st_ori == NULL)
+ {
+ return NULL;
+ }
+
+ st = (struct plif_subtract_filter_settings *) calloc(1, sizeof(struct plif_subtract_filter_settings));
+ if(st==NULL) return NULL;
+
+ *st = *st_ori;
+
+ st->ravg_buf = (double *)calloc(1, sizeof(double) * st->tpl);
+ if(st->ravg_buf == NULL)
+ {
+ free(st);
+ return NULL;
+ }
+ memcpy(st->ravg_buf, st_ori->ravg_buf, sizeof(double) * st->tpl);
+
+ st->ref_buf = (double *)calloc(1, sizeof(double) * st->tpl);
+ if(st->ref_buf == NULL)
+ {
+ free(st->ravg_buf);
+ free(st);
+ return NULL;
+ }
+ memcpy(st->ref_buf, st_ori->ref_buf, sizeof(double) * st->tpl);
+
+ for(i=0;i<PLIF_NBUFS; i++)
+ {
+ st->ravg_noise_buf[i] = (double *)calloc(1, sizeof(double) * st->tpl);
+ if(st->ravg_noise_buf[i] == NULL)
+ {
+ free(st->ravg_buf);
+ free(st->ref_buf);
+ free(st);
+ return NULL;
+ }
+ memcpy(st->ravg_noise_buf[i], st_ori->ravg_noise_buf[i], sizeof(double) * st->tpl);
+ }
+
+ for(i=0;i<PLIF_NBUFS; i++)
+ {
+ st->input_buf[i] = (double *)calloc(1, sizeof(double) * st->tpl);
+ if(st->input_buf[i] == NULL)
+ {
+ free(st->ravg_buf);
+ free(st->ref_buf);
+ free(st);
+ return NULL;
+ }
+ memcpy(st->input_buf[i], st_ori->input_buf[i], sizeof(double) * st->tpl);
+ }
+
+ for(i=0;i<PLIF_NBUFS; i++)
+ {
+ st->linear_diff[i] = st_ori->linear_diff[i];
+ }
+
+ return st;
+}
+
+
+void plif_free_subtract_filter(struct plif_subtract_filter_settings *st)
+{
+ int i;
+
+ if(st == NULL)
+ {
+ return;
+ }
+
+ free(st->ravg_buf);
+ for(i=0; i<PLIF_NBUFS; i++)
+ {
+ free(st->ravg_noise_buf[i]);
+ free(st->input_buf[i]);
+ }
+ free(st->ref_buf);
+ free(st);
+}
+
+
+void plif_reset_subtract_filter(struct plif_subtract_filter_settings *st, double reference)
+{
+ int i, j;
+
+ if(st == NULL)
+ {
+ return;
+ }
+
+ st->ravg_idx = 0;
+ st->buf_idx = 0;
+
+ for(j=0; j<st->tpl; j++)
+ {
+ st->ravg_buf[j] = reference;
+ }
+
+ for(i=0; i<PLIF_NBUFS; i++)
+ {
+ memset(st->ravg_noise_buf[i], 0, sizeof(double) * st->tpl);
+
+ for(j=0; j<st->tpl; j++)
+ {
+ st->input_buf[i][j] = reference;
+ }
+
+ st->linear_diff[i] = 1e9;
+ }
+
+ memset(st->ref_buf, 0, sizeof(double) * st->tpl);
+}
+
+
+void plif_subtract_filter_state_copy(struct plif_subtract_filter_settings *dest, struct plif_subtract_filter_settings *src)
+{
+ int i;
+
+ if((dest == NULL) || (src == NULL)) return;
+
+ if(dest->sf != src->sf) return;
+
+ if(dest->tpl != src->tpl) return;
+
+ dest->ravg_idx = src->ravg_idx;
+ dest->buf_idx = src->buf_idx;
+ dest->linear_threshold = src->linear_threshold;
+
+ memcpy(dest->ravg_buf, src->ravg_buf, sizeof(double) * dest->tpl);
+ memcpy(dest->ref_buf, src->ref_buf, sizeof(double) * dest->tpl);
+
+ for(i=0; i<PLIF_NBUFS; i++)
+ {
+ memcpy(dest->ravg_noise_buf[i], src->ravg_noise_buf[i], sizeof(double) * dest->tpl);
+ memcpy(dest->input_buf[i], src->input_buf[i], sizeof(double) * dest->tpl);
+
+ dest->linear_diff[i] = src->linear_diff[i];
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plif_ecg_subtract_filter.h b/plif_ecg_subtract_filter.h
new file mode 100644
index 0000000..36258ce
--- /dev/null
+++ b/plif_ecg_subtract_filter.h
@@ -0,0 +1,89 @@
+/*
+***************************************************************************
+*
+* Author: Teunis van Beelen
+*
+* Copyright (C) 2017 Teunis van Beelen
+*
+* Email: teuniz at gmail.com
+*
+***************************************************************************
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+***************************************************************************
+*/
+
+
+#ifndef PLIF_ECG_subtract_filter_INCLUDED
+#define PLIF_ECG_subtract_filter_INCLUDED
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+
+#define PLIF_NBUFS 40
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+struct plif_subtract_filter_settings{
+ int sf;
+ int tpl;
+ double *ravg_buf;
+ double *ravg_noise_buf[PLIF_NBUFS];
+ double *input_buf[PLIF_NBUFS];
+ double *ref_buf;
+ double linear_threshold;
+ double linear_diff[PLIF_NBUFS];
+ int ravg_idx;
+ int buf_idx;
+ };
+
+
+
+struct plif_subtract_filter_settings * plif_create_subtract_filter(int, int, double);
+double plif_run_subtract_filter(double, struct plif_subtract_filter_settings *);
+void plif_reset_subtract_filter(struct plif_subtract_filter_settings *, double);
+void plif_free_subtract_filter(struct plif_subtract_filter_settings *);
+void plif_subtract_filter_state_copy(struct plif_subtract_filter_settings *, struct plif_subtract_filter_settings *);
+struct plif_subtract_filter_settings * plif_subtract_filter_create_copy(struct plif_subtract_filter_settings *);
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plif_ecg_subtract_filter_dialog.cpp b/plif_ecg_subtract_filter_dialog.cpp
new file mode 100644
index 0000000..1492fb0
--- /dev/null
+++ b/plif_ecg_subtract_filter_dialog.cpp
@@ -0,0 +1,301 @@
+/*
+***************************************************************************
+*
+* Author: Teunis van Beelen
+*
+* Copyright (C) 2017 Teunis van Beelen
+*
+* Email: teuniz at gmail.com
+*
+***************************************************************************
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+***************************************************************************
+*/
+
+
+
+#include "plif_ecg_subtract_filter_dialog.h"
+
+
+
+UI_PLIF_ECG_filter_dialog::UI_PLIF_ECG_filter_dialog(QWidget *w_parent)
+{
+ int i, n, s, sf=0;
+
+ char str[256];
+
+ QListWidgetItem *item;
+
+ QList<QListWidgetItem *> selectedlist;
+
+
+ mainwindow = (UI_Mainwindow *)w_parent;
+
+ plifecgfilterdialog = new QDialog;
+
+ plifecgfilterdialog->setMinimumSize(620, 365);
+ plifecgfilterdialog->setMaximumSize(620, 365);
+ plifecgfilterdialog->setWindowTitle("Add a powerline interference filter");
+ plifecgfilterdialog->setModal(true);
+ plifecgfilterdialog->setAttribute(Qt::WA_DeleteOnClose, true);
+
+ plfLabel = new QLabel(plifecgfilterdialog);
+ plfLabel->setGeometry(20, 90, 200, 25);
+ plfLabel->setText("Powerline frequency");
+
+ plfBox = new QComboBox(plifecgfilterdialog);
+ plfBox->setGeometry(240, 90, 150, 25);
+ plfBox->addItem(" 50 Hz");
+ plfBox->addItem(" 60 Hz");
+
+ listlabel = new QLabel(plifecgfilterdialog);
+ listlabel->setGeometry(440, 20, 100, 25);
+ listlabel->setText("Select signals:");
+
+ list = new QListWidget(plifecgfilterdialog);
+ list->setGeometry(440, 45, 160, 300);
+ list->setSelectionBehavior(QAbstractItemView::SelectRows);
+ list->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ list->setToolTip("Only signals with a physical dimension V, mV or uV and\n"
+ "a samplerate of >= 500Hz and\n"
+ "an integer ratio to 50 or 60 Hz will be listed here.");
+
+ CancelButton = new QPushButton(plifecgfilterdialog);
+ CancelButton->setGeometry(300, 320, 100, 25);
+ CancelButton->setText("&Close");
+
+ ApplyButton = new QPushButton(plifecgfilterdialog);
+ ApplyButton->setGeometry(20, 270, 100, 25);
+ ApplyButton->setText("&Apply");
+ ApplyButton->setVisible(false);
+
+ helpButton = new QPushButton(plifecgfilterdialog);
+ helpButton->setGeometry(20, 320, 100, 25);
+ helpButton->setText("Help");
+
+ for(i=0; i<mainwindow->signalcomps; i++)
+ {
+ sf = ((long long)mainwindow->signalcomp[i]->edfhdr->edfparam[mainwindow->signalcomp[i]->edfsignal[0]].smp_per_record * TIME_DIMENSION) /
+ mainwindow->signalcomp[i]->edfhdr->long_data_record_duration;
+
+ if(sf < 500) /* don't list signals that have low samplerate */
+ {
+ continue;
+ }
+
+ if((sf % 50) && (sf % 60)) /* don't list signals that can not be filtered */
+ {
+ continue;
+ }
+
+ strcpy(str, mainwindow->signalcomp[i]->edfhdr->edfparam[mainwindow->signalcomp[i]->edfsignal[0]].physdimension);
+
+ remove_trailing_spaces(str);
+
+ if(strcmp(str, "uV") && strcmp(str, "mV") && strcmp(str, "V"))
+ {
+ continue;
+ }
+
+ item = new QListWidgetItem;
+ if(mainwindow->signalcomp[i]->alias[0] != 0)
+ {
+ item->setText(mainwindow->signalcomp[i]->alias);
+ }
+ else
+ {
+ item->setText(mainwindow->signalcomp[i]->signallabel);
+ }
+ item->setData(Qt::UserRole, QVariant(i));
+ list->addItem(item);
+ }
+
+ n = list->count();
+
+ for(i=0; i<n; i++)
+ {
+ item = list->item(i);
+ s = item->data(Qt::UserRole).toInt();
+
+ if(mainwindow->signalcomp[s]->plif_ecg_filter != NULL)
+ {
+ plfBox->setCurrentIndex(mainwindow->signalcomp[s]->plif_ecg_subtract_filter_plf);
+
+ item->setSelected(true);
+ }
+ else
+ {
+ item->setSelected(false);
+ }
+ }
+
+ QObject::connect(ApplyButton, SIGNAL(clicked()), this, SLOT(ApplyButtonClicked()));
+ QObject::connect(CancelButton, SIGNAL(clicked()), plifecgfilterdialog, SLOT(close()));
+ QObject::connect(list, SIGNAL(itemSelectionChanged()), this, SLOT(ApplyButtonClicked()));
+ QObject::connect(plfBox, SIGNAL(currentIndexChanged(int)), this, SLOT(ApplyButtonClicked()));
+ QObject::connect(helpButton, SIGNAL(clicked()), this, SLOT(helpbuttonpressed()));
+
+ plifecgfilterdialog->exec();
+}
+
+
+void UI_PLIF_ECG_filter_dialog::ApplyButtonClicked()
+{
+ int i, s, n, sf, plf, err=0;
+
+ char str[256];
+
+ double dthreshold=0;
+
+ QListWidgetItem *item;
+
+ QList<QListWidgetItem *> selectedlist;
+
+ for(i=0; i<mainwindow->signalcomps; i++)
+ {
+ if(mainwindow->signalcomp[i]->plif_ecg_filter)
+ {
+ plif_free_subtract_filter(mainwindow->signalcomp[i]->plif_ecg_filter);
+
+ mainwindow->signalcomp[i]->plif_ecg_filter = NULL;
+ }
+
+ if(mainwindow->signalcomp[i]->plif_ecg_filter_sav)
+ {
+ plif_free_subtract_filter(mainwindow->signalcomp[i]->plif_ecg_filter_sav);
+
+ mainwindow->signalcomp[i]->plif_ecg_filter_sav = NULL;
+ }
+ }
+
+ if(plfBox->currentIndex() == 0)
+ {
+ plf = 50;
+ }
+ else
+ {
+ plf = 60;
+ }
+
+ selectedlist = list->selectedItems();
+
+ n = selectedlist.size();
+
+ for(i=0; i<n; i++)
+ {
+ item = selectedlist.at(i);
+ s = item->data(Qt::UserRole).toInt();
+
+ sf = ((long long)mainwindow->signalcomp[s]->edfhdr->edfparam[mainwindow->signalcomp[s]->edfsignal[0]].smp_per_record * TIME_DIMENSION) /
+ mainwindow->signalcomp[s]->edfhdr->long_data_record_duration;
+
+ if(sf % plf)
+ {
+ err = 1;
+ sprintf(str, "Samplefrequency (%iHz) is not an integer multiple of the powerline frequency (%iHz)", sf, plf);
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", str);
+ messagewindow.exec();
+ break;
+ }
+
+ strcpy(str, mainwindow->signalcomp[s]->edfhdr->edfparam[mainwindow->signalcomp[s]->edfsignal[0]].physdimension);
+
+ remove_trailing_spaces(str);
+
+ if(!strcmp(str, "uV"))
+ {
+ dthreshold = 5.0 / mainwindow->signalcomp[s]->edfhdr->edfparam[mainwindow->signalcomp[s]->edfsignal[0]].bitvalue;
+ }
+ else if(!strcmp(str, "mV"))
+ {
+ dthreshold = 5e-3 / mainwindow->signalcomp[s]->edfhdr->edfparam[mainwindow->signalcomp[s]->edfsignal[0]].bitvalue;
+ }
+ else if(!strcmp(str, "V"))
+ {
+ dthreshold = 5e-6 / mainwindow->signalcomp[s]->edfhdr->edfparam[mainwindow->signalcomp[s]->edfsignal[0]].bitvalue;
+ }
+
+ mainwindow->signalcomp[s]->plif_ecg_filter = plif_create_subtract_filter(sf, plf, dthreshold);
+ if(mainwindow->signalcomp[s]->plif_ecg_filter == NULL)
+ {
+ err = 1;
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "An error occurred while creating a powerline interference filter.");
+ messagewindow.exec();
+ break;
+ }
+
+ mainwindow->signalcomp[s]->plif_ecg_filter_sav = plif_create_subtract_filter(sf, plf, dthreshold);
+ if(mainwindow->signalcomp[s]->plif_ecg_filter_sav == NULL)
+ {
+ err = 1;
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "An error occurred while creating a powerline interference filter.");
+ messagewindow.exec();
+ break;
+ }
+
+ mainwindow->signalcomp[s]->plif_ecg_subtract_filter_plf = plfBox->currentIndex();
+ }
+
+ if(err)
+ {
+ for(i=0; i<mainwindow->signalcomps; i++)
+ {
+ if(mainwindow->signalcomp[i]->plif_ecg_filter)
+ {
+ plif_free_subtract_filter(mainwindow->signalcomp[i]->plif_ecg_filter);
+
+ mainwindow->signalcomp[i]->plif_ecg_filter = NULL;
+ }
+
+ if(mainwindow->signalcomp[i]->plif_ecg_filter_sav)
+ {
+ plif_free_subtract_filter(mainwindow->signalcomp[i]->plif_ecg_filter_sav);
+
+ mainwindow->signalcomp[i]->plif_ecg_filter_sav = NULL;
+ }
+ }
+ }
+
+ mainwindow->setup_viewbuf();
+}
+
+
+void UI_PLIF_ECG_filter_dialog::helpbuttonpressed()
+{
+#ifdef Q_OS_LINUX
+ QDesktopServices::openUrl(QUrl("file:///usr/share/doc/edfbrowser/manual.html#ECG_PLIF_filter"));
+#endif
+
+#ifdef Q_OS_WIN32
+ char p_path[MAX_PATH_LENGTH];
+
+ strcpy(p_path, "file:///");
+ strcat(p_path, mainwindow->specialFolder(CSIDL_PROGRAM_FILES).toLocal8Bit().data());
+ strcat(p_path, "\\EDFbrowser\\manual.html#ECG_PLIF_filter");
+ QDesktopServices::openUrl(QUrl(p_path));
+#endif
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/load_montage_dialog.h b/plif_ecg_subtract_filter_dialog.h
similarity index 65%
copy from load_montage_dialog.h
copy to plif_ecg_subtract_filter_dialog.h
index 5597f9c..0a86045 100644
--- a/load_montage_dialog.h
+++ b/plif_ecg_subtract_filter_dialog.h
@@ -3,7 +3,7 @@
*
* Author: Teunis van Beelen
*
-* Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Teunis van Beelen
+* Copyright (C) 2017 Teunis van Beelen
*
* Email: teuniz at gmail.com
*
@@ -27,38 +27,36 @@
-
-#ifndef LOADMONTAGEFORM1_H
-#define LOADMONTAGEFORM1_H
+#ifndef ADD_PLIF_ECG_FILTERFORM1_H
+#define ADD_PLIF_ECG_FILTERFORM1_H
#include <QtGlobal>
#include <QApplication>
#include <QObject>
-#include <QDialog>
-#include <QLabel>
-#include <QFileDialog>
-#include <QPushButton>
#include <QListWidget>
+#include <QListWidgetItem>
#include <QList>
-#include <QMessageBox>
+#include <QDialog>
+#include <QPushButton>
+#include <QComboBox>
+#include <QDoubleSpinBox>
+#include <QLabel>
+#include <QSpinBox>
#include <QString>
+#include <QMessageBox>
+#include <QVariant>
+#include <QDesktopServices>
+#include <QUrl>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "global.h"
#include "mainwindow.h"
-#include "xml.h"
-#include "filter.h"
-#include "spike_filter.h"
-#include "utc_date_time.h"
+#include "plif_ecg_subtract_filter.h"
#include "utils.h"
-#include "spectrum_dock.h"
-
-#include "third_party/fidlib/fidlib.h"
@@ -66,40 +64,47 @@ class UI_Mainwindow;
-class UI_LoadMontagewindow : public QObject
+class UI_PLIF_ECG_filter_dialog : public QObject
{
Q_OBJECT
public:
- UI_LoadMontagewindow(QWidget *parent, char *path = NULL);
+ UI_PLIF_ECG_filter_dialog(QWidget *parent=0);
UI_Mainwindow *mainwindow;
private:
- QDialog *LoadMontageDialog;
-
- QLabel *label1;
+QDialog *plifecgfilterdialog;
- QListWidget *filelist;
+QPushButton *CancelButton,
+ *ApplyButton,
+ *helpButton;
- QPushButton *CloseButton,
- *LoadButton;
+QListWidget *list;
- char mtg_path[MAX_PATH_LENGTH];
+QLabel *listlabel,
+ *plfLabel;
- void strip_types_from_label(char *);
+QComboBox *plfBox;
private slots:
- void LoadButtonClicked();
+void ApplyButtonClicked();
+void helpbuttonpressed();
};
-#endif // LOADMONTAGEFORM1_H
+#endif // ADD_PLIF_ECG_FILTERFORM1_H
+
+
+
+
+
+
diff --git a/print_to_bdf.cpp b/print_to_bdf.cpp
index 7413069..af9363f 100644
--- a/print_to_bdf.cpp
+++ b/print_to_bdf.cpp
@@ -165,8 +165,8 @@ void print_screen_to_bdf(UI_Mainwindow *mainwindow)
{
if(duration % signalcomp[i]->edfhdr->long_data_record_duration)
{
- QMessageBox messagewindow(QMessageBox::Critical, "Error", "This combination of files can not be printed to EDF\n"
- "because the quotient of the datarecordblock durations is not an integer.");
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "This combination of files can not be printed to BDF\n"
+ "because the datarecordblock durations are not an integer multiple.");
messagewindow.exec();
return;
}
@@ -1024,6 +1024,23 @@ void print_screen_to_bdf(UI_Mainwindow *mainwindow)
dig_value = signalcomp[i]->fidfuncp[p](signalcomp[i]->fidbuf[p], dig_value);
}
+ if(signalcomp[i]->plif_ecg_filter)
+ {
+ if(smpls_written[i]==signalcomp[i]->sample_start)
+ {
+ if(mainwindow->edfheaderlist[signalcomp[i]->filenum]->viewtime<=0)
+ {
+ plif_reset_subtract_filter(signalcomp[i]->plif_ecg_filter, 0);
+ }
+ else
+ {
+ plif_subtract_filter_state_copy(signalcomp[i]->plif_ecg_filter, signalcomp[i]->plif_ecg_filter_sav);
+ }
+ }
+
+ dig_value = plif_run_subtract_filter(dig_value, signalcomp[i]->plif_ecg_filter);
+ }
+
if(signalcomp[i]->ecg_filter != NULL)
{
if(smpls_written[i]==signalcomp[i]->sample_start)
diff --git a/print_to_edf.cpp b/print_to_edf.cpp
index 87b7ec4..ce7fa35 100644
--- a/print_to_edf.cpp
+++ b/print_to_edf.cpp
@@ -166,7 +166,7 @@ void print_screen_to_edf(UI_Mainwindow *mainwindow)
if(duration % signalcomp[i]->edfhdr->long_data_record_duration)
{
QMessageBox messagewindow(QMessageBox::Critical, "Error", "This combination of files can not be printed to EDF\n"
- "because the quotient of the datarecordblock durations is not an integer.");
+ "because the datarecordblock durations are not an integer multiple.");
messagewindow.exec();
return;
}
@@ -986,6 +986,23 @@ void print_screen_to_edf(UI_Mainwindow *mainwindow)
dig_value = signalcomp[i]->fidfuncp[p](signalcomp[i]->fidbuf[p], dig_value);
}
+ if(signalcomp[i]->plif_ecg_filter)
+ {
+ if(smpls_written[i]==signalcomp[i]->sample_start)
+ {
+ if(mainwindow->edfheaderlist[signalcomp[i]->filenum]->viewtime<=0)
+ {
+ plif_reset_subtract_filter(signalcomp[i]->plif_ecg_filter, 0);
+ }
+ else
+ {
+ plif_subtract_filter_state_copy(signalcomp[i]->plif_ecg_filter, signalcomp[i]->plif_ecg_filter_sav);
+ }
+ }
+
+ dig_value = plif_run_subtract_filter(dig_value, signalcomp[i]->plif_ecg_filter);
+ }
+
if(signalcomp[i]->ecg_filter != NULL)
{
if(smpls_written[i]==signalcomp[i]->sample_start)
diff --git a/read_write_settings.cpp b/read_write_settings.cpp
index d65f24e..a3f5737 100644
--- a/read_write_settings.cpp
+++ b/read_write_settings.cpp
@@ -43,10 +43,13 @@ void UI_Mainwindow::get_rgbcolor_settings(struct xml_handle *xml_hdl, const char
if(xml_goto_nth_element_inside(xml_hdl, "red", 0))
{
+ xml_go_up(xml_hdl);
return;
}
if(xml_get_content_of_element(xml_hdl, result, XML_STRBUFLEN))
{
+ xml_go_up(xml_hdl);
+ xml_go_up(xml_hdl);
return;
}
tmp_color.setRed(atoi(result));
@@ -55,10 +58,13 @@ void UI_Mainwindow::get_rgbcolor_settings(struct xml_handle *xml_hdl, const char
if(xml_goto_nth_element_inside(xml_hdl, "green", 0))
{
+ xml_go_up(xml_hdl);
return;
}
if(xml_get_content_of_element(xml_hdl, result, XML_STRBUFLEN))
{
+ xml_go_up(xml_hdl);
+ xml_go_up(xml_hdl);
return;
}
tmp_color.setGreen(atoi(result));
@@ -67,18 +73,32 @@ void UI_Mainwindow::get_rgbcolor_settings(struct xml_handle *xml_hdl, const char
if(xml_goto_nth_element_inside(xml_hdl, "blue", 0))
{
+ xml_go_up(xml_hdl);
return;
}
if(xml_get_content_of_element(xml_hdl, result, XML_STRBUFLEN))
{
+ xml_go_up(xml_hdl);
+ xml_go_up(xml_hdl);
return;
}
tmp_color.setBlue(atoi(result));
+ xml_go_up(xml_hdl);
+
+ if(!xml_goto_nth_element_inside(xml_hdl, "alpha", 0))
+ {
+ if(!xml_get_content_of_element(xml_hdl, result, XML_STRBUFLEN))
+ {
+ tmp_color.setAlpha(atoi(result));
+ }
+
+ xml_go_up(xml_hdl);
+ }
+
*rgb_color = tmp_color;
xml_go_up(xml_hdl);
- xml_go_up(xml_hdl);
}
@@ -141,6 +161,8 @@ void UI_Mainwindow::read_color_settings()
get_rgbcolor_settings(xml_hdl, "annot_marker_color", 0, &maincurve->annot_marker_color);
+ get_rgbcolor_settings(xml_hdl, "annot_duration_color", 0, &maincurve->annot_duration_color);
+
if(xml_goto_nth_element_inside(xml_hdl, "signal_color", 0))
{
xml_close(xml_hdl);
@@ -253,6 +275,8 @@ void UI_Mainwindow::read_recent_file_settings()
cfg_path[0] = 0;
+ QAction *act;
+
#ifdef Q_OS_WIN32
strcpy(cfg_path, specialFolder(CSIDL_APPDATA).toLocal8Bit().data());
strcat(cfg_path, "\\");
@@ -350,7 +374,9 @@ void UI_Mainwindow::read_recent_file_settings()
{
strncpy(&recent_file_path[0][0], result, MAX_PATH_LENGTH);
recent_file_path[0][MAX_PATH_LENGTH - 1] = 0;
- recent_filesmenu->addAction(QString::fromLocal8Bit(&recent_file_path[0][0]));
+ act = new QAction(QString::fromLocal8Bit(&recent_file_path[0][0]), recent_filesmenu);
+ act->setData(QVariant(0));
+ recent_filesmenu->addAction(act);
for(i=1; i<MAX_RECENTFILES; i++)
{
@@ -369,7 +395,9 @@ void UI_Mainwindow::read_recent_file_settings()
}
strncpy(&recent_file_path[i][0], result, MAX_PATH_LENGTH);
recent_file_path[i][MAX_PATH_LENGTH - 1] = 0;
- recent_filesmenu->addAction(QString::fromLocal8Bit(&recent_file_path[i][0]));
+ act = new QAction(QString::fromLocal8Bit(&recent_file_path[i][0]), recent_filesmenu);
+ act->setData(QVariant(i));
+ recent_filesmenu->addAction(act);
}
}
@@ -1768,6 +1796,17 @@ void UI_Mainwindow::write_settings()
maincurve->annot_marker_color.green(),
maincurve->annot_marker_color.blue());
+ fprintf(cfgfile, " <annot_duration_color>\n"
+ " <red>%i</red>\n"
+ " <green>%i</green>\n"
+ " <blue>%i</blue>\n"
+ " <alpha>%i</alpha>\n"
+ " </annot_duration_color>\n",
+ maincurve->annot_duration_color.red(),
+ maincurve->annot_duration_color.green(),
+ maincurve->annot_duration_color.blue(),
+ maincurve->annot_duration_color.alpha());
+
fprintf(cfgfile, " <signal_color>%i</signal_color>\n",
maincurve->signal_color);
diff --git a/save_montage_dialog.cpp b/save_montage_dialog.cpp
index f5c3d5e..48f9bab 100644
--- a/save_montage_dialog.cpp
+++ b/save_montage_dialog.cpp
@@ -228,6 +228,15 @@ void UI_SaveMontagewindow::SaveButtonClicked()
fprintf(mtgfile, " </fidfilter>\n");
}
+ if(mainwindow->signalcomp[i]->plif_ecg_filter != NULL)
+ {
+ fprintf(mtgfile, " <plif_ecg_filter>\n");
+
+ fprintf(mtgfile, " <plf>%i</plf>\n", mainwindow->signalcomp[i]->plif_ecg_subtract_filter_plf);
+
+ fprintf(mtgfile, " </plif_ecg_filter>\n");
+ }
+
if(mainwindow->signalcomp[i]->ecg_filter != NULL)
{
fprintf(mtgfile, " <ecg_filter>\n");
diff --git a/show_actual_montage_dialog.cpp b/show_actual_montage_dialog.cpp
index 7425f17..9357dbc 100644
--- a/show_actual_montage_dialog.cpp
+++ b/show_actual_montage_dialog.cpp
@@ -321,6 +321,14 @@ UI_ShowActualMontagewindow::UI_ShowActualMontagewindow(QWidget *w_parent)
filterItem->appendRow(new QStandardItem(txtbuf));
}
+ if(mainwindow->signalcomp[i]->plif_ecg_filter != NULL)
+ {
+ sprintf(txtbuf, "Powerline interference removal: %iHz",
+ (mainwindow->signalcomp[i]->plif_ecg_subtract_filter_plf * 10) + 50);
+
+ filterItem->appendRow(new QStandardItem(txtbuf));
+ }
+
if(mainwindow->signalcomp[i]->zratio_filter != NULL)
{
sprintf(txtbuf, "Z-ratio cross-over frequency is %.1f Hz", mainwindow->signalcomp[i]->zratio_crossoverfreq);
diff --git a/signal_chooser.cpp b/signal_chooser.cpp
index 3a1fc97..1878477 100644
--- a/signal_chooser.cpp
+++ b/signal_chooser.cpp
@@ -465,6 +465,20 @@ void UI_SignalChooser::signalDelete()
mainwindow->signalcomp[sigcomp_nr]->fidfilter_cnt = 0;
+ if(mainwindow->signalcomp[sigcomp_nr]->plif_ecg_filter)
+ {
+ plif_free_subtract_filter(mainwindow->signalcomp[sigcomp_nr]->plif_ecg_filter);
+
+ mainwindow->signalcomp[sigcomp_nr]->plif_ecg_filter = NULL;
+ }
+
+ if(mainwindow->signalcomp[sigcomp_nr]->plif_ecg_filter_sav)
+ {
+ plif_free_subtract_filter(mainwindow->signalcomp[sigcomp_nr]->plif_ecg_filter_sav);
+
+ mainwindow->signalcomp[sigcomp_nr]->plif_ecg_filter_sav = NULL;
+ }
+
free(mainwindow->signalcomp[sigcomp_nr]);
for(i=sigcomp_nr; i<mainwindow->signalcomps - 1; i++)
diff --git a/signals_dialog.cpp b/signals_dialog.cpp
index 0846513..593a343 100644
--- a/signals_dialog.cpp
+++ b/signals_dialog.cpp
@@ -435,7 +435,7 @@ void UI_Signalswindow::AddButtonClicked()
if(bitvalue!=0.0)
{
- if(bitvalue!=mainwindow->edfheaderlist[row]->edfparam[s].bitvalue)
+ if(dblcmp(bitvalue, mainwindow->edfheaderlist[row]->edfparam[s].bitvalue))
{
QMessageBox messagewindow(QMessageBox::Warning, "Warning",
"It is only possible to make combinations/derivations with signals which:\n"
diff --git a/spectrum_dock.cpp b/spectrum_dock.cpp
index de19c0a..de47c47 100644
--- a/spectrum_dock.cpp
+++ b/spectrum_dock.cpp
@@ -914,6 +914,23 @@ void UI_SpectrumDockWindow::update_curve()
dig_value = signalcomp->fidfuncp[k](signalcomp->fidbuf[k], dig_value);
}
+ if(signalcomp->plif_ecg_filter)
+ {
+ if(s==signalcomp->sample_start)
+ {
+ if(mainwindow->edfheaderlist[signalcomp->filenum]->viewtime<=0)
+ {
+ plif_reset_subtract_filter(signalcomp->plif_ecg_filter, 0);
+ }
+ else
+ {
+ plif_subtract_filter_state_copy(signalcomp->plif_ecg_filter, signalcomp->plif_ecg_filter_sav);
+ }
+ }
+
+ dig_value = plif_run_subtract_filter(dig_value, signalcomp->plif_ecg_filter);
+ }
+
if(signalcomp->ecg_filter != NULL)
{
if(s==signalcomp->sample_start)
diff --git a/spectrumanalyzer.cpp b/spectrumanalyzer.cpp
index 4933c80..2bb2d6b 100644
--- a/spectrumanalyzer.cpp
+++ b/spectrumanalyzer.cpp
@@ -695,6 +695,23 @@ void UI_FreqSpectrumWindow::run()
dig_value = signalcomp->fidfuncp[k](signalcomp->fidbuf[k], dig_value);
}
+ if(signalcomp->plif_ecg_filter)
+ {
+ if(s==signalcomp->sample_start)
+ {
+ if(mainwindow->edfheaderlist[signalcomp->filenum]->viewtime<=0)
+ {
+ plif_reset_subtract_filter(signalcomp->plif_ecg_filter, 0);
+ }
+ else
+ {
+ plif_subtract_filter_state_copy(signalcomp->plif_ecg_filter, signalcomp->plif_ecg_filter_sav);
+ }
+ }
+
+ dig_value = plif_run_subtract_filter(dig_value, signalcomp->plif_ecg_filter);
+ }
+
if(signalcomp->ecg_filter != NULL)
{
if(s==signalcomp->sample_start)
diff --git a/version.txt b/version.txt
index fa73b17..bb4337e 100644
--- a/version.txt
+++ b/version.txt
@@ -1,4 +1,29 @@
+ version 1.60 to be defined
+ --------------
+
+ - Improve parameter checking for signal composition when loading a montage file.
+
+ - Added a powerline interference removal filter for ECG signals.
+
+ - Added the possibility to fix the EDF-header in case the value for digital maximum
+ is lower than or equal to digital minimum.
+
+ - When "whole recording" is selected for the Timescale and multiple files are opened,
+ make sure all recordings are completely visible.
+
+ - Fix erroneous reading of the datarecord duration field in the header
+ when the number has a sign.
+
+ - Annotations: show the duration using an overlay in the background color.
+
+ - Annotations: show the date when "relative" is unchecked in the annotations list and
+ viewtime/fileposition indicator is set to "date real (relative)" in the Settings dialog.
+
+ - Added a workaround for KDE Plasma 5 bug 345023.
+
+
+
version 1.59 May 8, 2017
--------------
diff --git a/view_montage_dialog.cpp b/view_montage_dialog.cpp
index e1e37b8..1080220 100644
--- a/view_montage_dialog.cpp
+++ b/view_montage_dialog.cpp
@@ -109,7 +109,9 @@ void UI_ViewMontagewindow::SelectButtonClicked()
type,
size,
polarity=1,
- holdoff=100;
+ holdoff=100,
+ plif_powerlinefrequency,
+ plif_linear_threshold;
char result[XML_STRBUFLEN],
composition_txt[2048],
@@ -830,6 +832,65 @@ void UI_ViewMontagewindow::SelectButtonClicked()
xml_go_up(xml_hdl);
}
+ if(!xml_goto_nth_element_inside(xml_hdl, "plif_ecg_filter", 0))
+ {
+ if(xml_goto_nth_element_inside(xml_hdl, "plf", 0))
+ {
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "There seems to be an error in this montage file.");
+ messagewindow.exec();
+ xml_close(xml_hdl);
+ return;
+ }
+ if(xml_get_content_of_element(xml_hdl, result, XML_STRBUFLEN))
+ {
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "There seems to be an error in this montage file.");
+ messagewindow.exec();
+ xml_close(xml_hdl);
+ return;
+ }
+ plif_powerlinefrequency = atoi(result);
+ if((plif_powerlinefrequency != 0) && (plif_powerlinefrequency != 1))
+ {
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "There seems to be an error in this montage file.");
+ messagewindow.exec();
+ xml_close(xml_hdl);
+ return;
+ }
+ plif_powerlinefrequency *= 10;
+ plif_powerlinefrequency += 50;
+ xml_go_up(xml_hdl);
+
+ if(xml_goto_nth_element_inside(xml_hdl, "linear_threshold", 0))
+ {
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "There seems to be an error in this montage file.");
+ messagewindow.exec();
+ xml_close(xml_hdl);
+ return;
+ }
+ if(xml_get_content_of_element(xml_hdl, result, XML_STRBUFLEN))
+ {
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "There seems to be an error in this montage file.");
+ messagewindow.exec();
+ xml_close(xml_hdl);
+ return;
+ }
+ plif_linear_threshold = atoi(result);
+ if((plif_linear_threshold < 10) || (plif_linear_threshold > 200))
+ {
+ QMessageBox messagewindow(QMessageBox::Critical, "Error", "There seems to be an error in this montage file.");
+ messagewindow.exec();
+ xml_close(xml_hdl);
+ return;
+ }
+ xml_go_up(xml_hdl);
+
+ sprintf(composition_txt, "Powerline interference removal: %iHz threshold: %iuV", plif_powerlinefrequency, plif_linear_threshold);
+
+ filterItem->appendRow(new QStandardItem(composition_txt));
+
+ xml_go_up(xml_hdl);
+ }
+
if(!xml_goto_nth_element_inside(xml_hdl, "ecg_filter", 0))
{
if(xml_goto_nth_element_inside(xml_hdl, "type", 0))
diff --git a/viewbuf.cpp b/viewbuf.cpp
index d4974ac..4286bef 100644
--- a/viewbuf.cpp
+++ b/viewbuf.cpp
@@ -84,6 +84,16 @@ void UI_Mainwindow::setup_viewbuf()
}
}
+ if(signalcomp[i]->plif_ecg_filter)
+ {
+ hasprefilter = 1;
+
+ if(pre_time < 2.0)
+ {
+ pre_time = 2.0;
+ }
+ }
+
if(signalcomp[i]->ravg_filter_cnt)
{
hasprefilter = 1;
@@ -135,7 +145,7 @@ void UI_Mainwindow::setup_viewbuf()
{
for(i=0; i<signalcomps; i++)
{
- if((signalcomp[i]->filter_cnt) || (signalcomp[i]->spike_filter) || (signalcomp[i]->ravg_filter_cnt) || (signalcomp[i]->fidfilter_cnt) || (signalcomp[i]->ecg_filter != NULL) || (signalcomp[i]->zratio_filter != NULL))
+ if((signalcomp[i]->filter_cnt) || (signalcomp[i]->spike_filter) || (signalcomp[i]->ravg_filter_cnt) || (signalcomp[i]->fidfilter_cnt) || (signalcomp[i]->plif_ecg_filter != NULL) || (signalcomp[i]->ecg_filter != NULL) || (signalcomp[i]->zratio_filter != NULL))
{
signalcomp[i]->edfhdr->prefiltertime = (long long)(pre_time * ((double)TIME_DIMENSION));
if(signalcomp[i]->edfhdr->prefiltertime>signalcomp[i]->edfhdr->viewtime)
@@ -322,7 +332,7 @@ void UI_Mainwindow::setup_viewbuf()
for(i=0; i<signalcomps; i++)
{
- if((!signalcomp[i]->filter_cnt) && (!signalcomp[i]->spike_filter) && (!signalcomp[i]->ravg_filter_cnt) && (!signalcomp[i]->fidfilter_cnt) && (signalcomp[i]->ecg_filter == NULL) && (signalcomp[i]->zratio_filter == NULL)) continue;
+ if((!signalcomp[i]->filter_cnt) && (!signalcomp[i]->spike_filter) && (!signalcomp[i]->ravg_filter_cnt) && (!signalcomp[i]->fidfilter_cnt) && (!signalcomp[i]->plif_ecg_filter) && (signalcomp[i]->ecg_filter == NULL) && (signalcomp[i]->zratio_filter == NULL)) continue;
for(s=0; s<signalcomp[i]->samples_in_prefilterbuf; s++)
{
@@ -395,6 +405,11 @@ void UI_Mainwindow::setup_viewbuf()
dig_value = signalcomp[i]->fidfuncp[j](signalcomp[i]->fidbuf[j], dig_value);
}
+ if(signalcomp[i]->plif_ecg_filter)
+ {
+ dig_value = plif_run_subtract_filter(dig_value, signalcomp[i]->plif_ecg_filter);
+ }
+
if(signalcomp[i]->ecg_filter != NULL)
{
if(s == 0)
@@ -442,6 +457,11 @@ void UI_Mainwindow::setup_viewbuf()
memcpy(signalcomp[i]->fidbuf2[j], signalcomp[i]->fidbuf[j], fid_run_bufsize(signalcomp[i]->fid_run[j]));
}
+ if(signalcomp[i]->plif_ecg_filter)
+ {
+ plif_subtract_filter_state_copy(signalcomp[i]->plif_ecg_filter_sav, signalcomp[i]->plif_ecg_filter);
+ }
+
if(signalcomp[i]->ecg_filter != NULL)
{
ecg_filter_save_buf(signalcomp[i]->ecg_filter);
diff --git a/viewcurve.cpp b/viewcurve.cpp
index 04ddfe6..1bd0bec 100644
--- a/viewcurve.cpp
+++ b/viewcurve.cpp
@@ -74,6 +74,7 @@ ViewCurve::ViewCurve(QWidget *w_parent) : QWidget(w_parent)
crosshair_2.color = Qt::cyan;
floating_ruler_color = Qt::red;
annot_marker_color = Qt::white;
+ annot_duration_color = QColor(0, 127, 127, 32);
crosshair_1.active = 0;
crosshair_2.active = 0;
@@ -677,7 +678,7 @@ void ViewCurve::mouseReleaseEvent(QMouseEvent *release_event)
for(i=0; i<signalcomps; i++)
{
mainwindow->signalcomp[i]->screen_offset = mainwindow->signalcomp[i]->screen_offset * zoomfactor;
- mainwindow->signalcomp[i]->screen_offset += (((double)h * (zoomfactor - 1.0) * (double)(i + 1.0)) / (double)(signalcomps + 1.0));
+ mainwindow->signalcomp[i]->screen_offset += (((double)h * (zoomfactor - 1.0) * (double)(i + 1)) / (double)(signalcomps + 1));
mainwindow->signalcomp[i]->screen_offset -= ((double)mouse_press_coordinate_y * zoomfactor);
mainwindow->signalcomp[i]->voltpercm = mainwindow->signalcomp[i]->voltpercm / ((double)h / (double)(m_y - mouse_press_coordinate_y));
@@ -1400,7 +1401,9 @@ void ViewCurve::drawCurve_stage_2(QPainter *painter, int w_width, int w_height,
m_pagetime,
vert_ruler_offset,
vertical_distance,
- marker_x;
+ marker_x,
+ marker_x2,
+ annot_list_sz=0;
char *viewbuf,
string[600],
@@ -1416,6 +1419,10 @@ void ViewCurve::drawCurve_stage_2(QPainter *painter, int w_width, int w_height,
struct annotation_list *annot_list;
+ struct annotationblock *annot;
+
+ struct date_time_struct date_time_str;
+
QFont paintersfont;
if(mainwindow->exit_in_progress)
@@ -1467,6 +1474,42 @@ void ViewCurve::drawCurve_stage_2(QPainter *painter, int w_width, int w_height,
painter->fillRect(0, 0, w, h, backgroundcolor);
+ if(mainwindow->show_annot_markers)
+ {
+ for(i=0; i<mainwindow->files_open; i++)
+ {
+ annot_list = &mainwindow->edfheaderlist[i]->annot_list;
+
+ annot_list_sz = edfplus_annotation_size(annot_list);
+
+ for(j=0; j<annot_list_sz; j++)
+ {
+ annot = edfplus_annotation_get_item(annot_list, j);
+
+ if(annot->long_duration)
+ {
+ l_tmp = annot->onset - mainwindow->edfheaderlist[i]->starttime_offset;
+
+ if(((l_tmp + annot->long_duration) > (mainwindow->edfheaderlist[i]->viewtime - TIME_DIMENSION)) && (!annot->hided) && (!annot->hided_in_list))
+ {
+ if(l_tmp > (mainwindow->edfheaderlist[i]->viewtime + mainwindow->pagetime))
+ {
+ break;
+ }
+
+ l_tmp -= mainwindow->edfheaderlist[i]->viewtime;
+
+ marker_x = (int)((((double)w) / mainwindow->pagetime) * l_tmp);
+
+ marker_x2 = (int)((((double)w) / mainwindow->pagetime) * annot->long_duration);
+
+ painter->fillRect(marker_x, 0, marker_x2, h, annot_duration_color);
+ }
+ }
+ }
+ }
+ }
+
m_pagetime = (int)(mainwindow->pagetime / TIME_DIMENSION);
time_ppixel = mainwindow->pagetime / w;
@@ -1866,8 +1909,6 @@ void ViewCurve::drawCurve_stage_2(QPainter *painter, int w_width, int w_height,
}
}
- struct annotationblock *annot;
-
if(mainwindow->show_annot_markers)
{
annot_marker_pen->setColor(annot_marker_color);
@@ -1885,7 +1926,9 @@ void ViewCurve::drawCurve_stage_2(QPainter *painter, int w_width, int w_height,
{
annot_list = &mainwindow->edfheaderlist[i]->annot_list;
- for(j=0; j<annot_list->sz; j++)
+ annot_list_sz = edfplus_annotation_size(annot_list);
+
+ for(j=0; j<annot_list_sz; j++)
{
annot = edfplus_annotation_get_item(annot_list, j);
@@ -1927,7 +1970,18 @@ void ViewCurve::drawCurve_stage_2(QPainter *painter, int w_width, int w_height,
}
else
{
- snprintf(string, MAX_ANNOTATION_LEN + 32, "%i:%02i:%02i.%04i",
+ if(mainwindow->viewtime_indicator_type == 2)
+ {
+ utc_to_date_time((annot->onset / TIME_DIMENSION) + mainwindow->edfheaderlist[i]->utc_starttime, &date_time_str);
+
+ snprintf(string, 32, "%2i-%s ", date_time_str.day, date_time_str.month_str);
+ }
+ else
+ {
+ string[0] = 0;
+ }
+
+ snprintf(string + strlen(string), MAX_ANNOTATION_LEN + 32, "%i:%02i:%02i.%04i",
(int)((((annot->onset + mainwindow->edfheaderlist[i]->l_starttime) / TIME_DIMENSION)/ 3600) % 24),
(int)((((annot->onset + mainwindow->edfheaderlist[i]->l_starttime) / TIME_DIMENSION) % 3600) / 60),
(int)(((annot->onset + mainwindow->edfheaderlist[i]->l_starttime) / TIME_DIMENSION) % 60),
@@ -2348,14 +2402,7 @@ void ViewCurve::drawCurve_stage_1(QPainter *painter, int w_width, int w_height,
for(i=0; i<signalcomps; i++)
{
- if(signalcomp[i]->samples_on_screen < (w / 2))
- {
- signalcomp[i]->sample_pixel_ratio = (((double)signalcomp[i]->edfhdr->edfparam[signalcomp[i]->edfsignal[0]].smp_per_record / signalcomp[i]->edfhdr->data_record_duration) * ((double)mainwindow->pagetime / (double)TIME_DIMENSION)) / (double)w;
- }
- else
- {
- signalcomp[i]->sample_pixel_ratio = (double)signalcomp[i]->samples_on_screen / (double)w;
- }
+ signalcomp[i]->sample_pixel_ratio = (double)signalcomp[i]->samples_on_screen / (double)w;
}
if((viewbuf==NULL)||(screensamples==NULL))
@@ -2578,6 +2625,23 @@ void ViewCurve::drawCurve_stage_1(QPainter *painter, int w_width, int w_height,
dig_value = signalcomp[i]->fidfuncp[k](signalcomp[i]->fidbuf[k], dig_value);
}
+ if(signalcomp[i]->plif_ecg_filter)
+ {
+ if(s==signalcomp[i]->sample_start)
+ {
+ if(mainwindow->edfheaderlist[signalcomp[i]->filenum]->viewtime<=0)
+ {
+ plif_reset_subtract_filter(signalcomp[i]->plif_ecg_filter, 0);
+ }
+ else
+ {
+ plif_subtract_filter_state_copy(signalcomp[i]->plif_ecg_filter, signalcomp[i]->plif_ecg_filter_sav);
+ }
+ }
+
+ dig_value = plif_run_subtract_filter(dig_value, signalcomp[i]->plif_ecg_filter);
+ }
+
if(signalcomp[i]->ecg_filter != NULL)
{
if(s==signalcomp[i]->sample_start)
@@ -2682,14 +2746,14 @@ void ViewCurve::drawCurve_stage_1(QPainter *painter, int w_width, int w_height,
{
x1 = (int)((double)s / signalcomp[i]->sample_pixel_ratio);
y1 = signalcomp[i]->oldvalue;
- x2 = (int)(((double)s + 1.0) / signalcomp[i]->sample_pixel_ratio);
+ x2 = (int)(((double)(s + 1)) / signalcomp[i]->sample_pixel_ratio);
y2 = value;
- if(signalcomp[i]->samples_on_screen < (w / 2))
+ if(signalcomp[i]->samples_on_screen < w)
{
if(linear_interpol)
{
- x1 = (int)(((double)s - 1.0) / signalcomp[i]->sample_pixel_ratio);
+ x1 = (int)(((double)(s - 1)) / signalcomp[i]->sample_pixel_ratio);
x2 = (int)((double)s / signalcomp[i]->sample_pixel_ratio);
graphicBuf[screensamples[i]].graphicLine[i].x1 = x1 - signalcomp[i]->pixels_shift;
@@ -3047,6 +3111,23 @@ void drawCurve_stage_1_thread::run()
dig_value = signalcomp->fidfuncp[k](signalcomp->fidbuf[k], dig_value);
}
+ if(signalcomp->plif_ecg_filter)
+ {
+ if(s==signalcomp->sample_start)
+ {
+ if(mainwindow->edfheaderlist[signalcomp->filenum]->viewtime<=0)
+ {
+ plif_reset_subtract_filter(signalcomp->plif_ecg_filter, 0);
+ }
+ else
+ {
+ plif_subtract_filter_state_copy(signalcomp->plif_ecg_filter, signalcomp->plif_ecg_filter_sav);
+ }
+ }
+
+ dig_value = plif_run_subtract_filter(dig_value, signalcomp->plif_ecg_filter);
+ }
+
if(signalcomp->ecg_filter != NULL)
{
if(s==signalcomp->sample_start)
@@ -3151,14 +3232,14 @@ void drawCurve_stage_1_thread::run()
{
x1 = (int)((double)s / signalcomp->sample_pixel_ratio);
y1 = signalcomp->oldvalue;
- x2 = (int)(((double)s + 1.0) / signalcomp->sample_pixel_ratio);
+ x2 = (int)(((double)(s + 1)) / signalcomp->sample_pixel_ratio);
y2 = value;
- if(signalcomp->samples_on_screen < (w / 2))
+ if(signalcomp->samples_on_screen < w)
{
if(linear_interpol)
{
- x1 = (int)(((double)s - 1.0) / signalcomp->sample_pixel_ratio);
+ x1 = (int)(((double)(s - 1)) / signalcomp->sample_pixel_ratio);
x2 = (int)((double)s / signalcomp->sample_pixel_ratio);
graphicBuf[*screensamples].graphicLine[i].x1 = x1 - signalcomp->pixels_shift;
@@ -3973,6 +4054,20 @@ void ViewCurve::RemovesignalButton()
mainwindow->signalcomp[signal_nr]->filter_cnt = 0;
+ if(mainwindow->signalcomp[signal_nr]->plif_ecg_filter)
+ {
+ plif_free_subtract_filter(mainwindow->signalcomp[signal_nr]->plif_ecg_filter);
+
+ mainwindow->signalcomp[signal_nr]->plif_ecg_filter = NULL;
+ }
+
+ if(mainwindow->signalcomp[signal_nr]->plif_ecg_filter_sav)
+ {
+ plif_free_subtract_filter(mainwindow->signalcomp[signal_nr]->plif_ecg_filter_sav);
+
+ mainwindow->signalcomp[signal_nr]->plif_ecg_filter_sav = NULL;
+ }
+
if(mainwindow->signalcomp[signal_nr]->spike_filter)
{
free_spike_filter(mainwindow->signalcomp[signal_nr]->spike_filter);
diff --git a/viewcurve.h b/viewcurve.h
index df6af25..5c574f3 100644
--- a/viewcurve.h
+++ b/viewcurve.h
@@ -66,6 +66,7 @@
#include "utils.h"
#include "adjustfiltersettings.h"
#include "spike_filter.h"
+#include "plif_ecg_subtract_filter.h"
#include "ravg_filter.h"
#include "ecg_filter.h"
#include "statistics_dialog.h"
@@ -180,6 +181,7 @@ public:
baseline_color,
text_color,
annot_marker_color,
+ annot_duration_color,
backup_color_1,
backup_color_2,
backup_color_3,
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/edfbrowser.git
More information about the debian-med-commit
mailing list