[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 &lt=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