edfreader
index
/data/pluto/python/EDFlib-Python/src/EDFlib/edfreader.py

#############################################################################
#
# Copyright (c) 2020 - 2023 Teunis van Beelen
# All rights reserved.
#
# Email: teuniz@protonmail.com
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of the copyright holder nor the names of its
#       contributors may be used to endorse or promote products derived from
#       this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES
# LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#############################################################################

 
Modules
       
array
io
numpy
os
sys

 
Classes
       
builtins.Exception(builtins.BaseException)
EDFexception
builtins.object
EDFreader

 
class EDFexception(builtins.Exception)
    Common base class for all non-exit exceptions.
 
 
Method resolution order:
EDFexception
builtins.Exception
builtins.BaseException
builtins.object

Methods defined here:
__init__(self, message)
Initialize self.  See help(type(self)) for accurate signature.

Data descriptors defined here:
__weakref__
list of weak references to the object (if defined)

Methods inherited from builtins.Exception:
__new__(*args, **kwargs) from builtins.type
Create and return a new object.  See help(type) for accurate signature.

Methods inherited from builtins.BaseException:
__delattr__(self, name, /)
Implement delattr(self, name).
__getattribute__(self, name, /)
Return getattr(self, name).
__reduce__(...)
helper for pickle
__repr__(self, /)
Return repr(self).
__setattr__(self, name, value, /)
Implement setattr(self, name, value).
__setstate__(...)
__str__(self, /)
Return str(self).
with_traceback(...)
Exception.with_traceback(tb) --
set self.__traceback__ to tb and return self.

Data descriptors inherited from builtins.BaseException:
__cause__
exception cause
__context__
exception context
__dict__
__suppress_context__
__traceback__
args

 
class EDFreader(builtins.object)
    A reader for EDF(+) and BDF(+) files.
 
EDF header:
 
offset (hex, dec) length
---------------------------------------------------------------------
0x00      0     8 ascii : version of this data format (0)
0x08      8    80 ascii : local patient identification
0x58     88    80 ascii : local recording identification
0xA8    168     8 ascii : startdate of recording (dd.mm.yy)
0xB0    176     8 ascii : starttime of recording (hh.mm.ss)
0xB8    184     8 ascii : number of bytes in header record
0xC0    192    44 ascii : reserved
0xEC    236     8 ascii : number of data records (-1 if unknown)
0xF4    244     8 ascii : duration of a data record, in seconds
0xFC    252     4 ascii : number of signals
 
      0x00           0     ns * 16 ascii : ns * label (e.g. EEG Fpz-Cz or Body temp)
 ns * 0x10    ns *  16     ns * 80 ascii : ns * transducer type (e.g. AgAgCl electrode)
 ns * 0x60    ns *  96     ns *  8 ascii : ns * physical dimension (e.g. uV or degreeC)
 ns * 0x68    ns * 104     ns *  8 ascii : ns * physical minimum (e.g. -500 or 34)
 ns * 0x70    ns * 112     ns *  8 ascii : ns * physical maximum (e.g. 500 or 40)
 ns * 0x78    ns * 120     ns *  8 ascii : ns * digital minimum (e.g. -2048)
 ns * 0x80    ns * 128     ns *  8 ascii : ns * digital maximum (e.g. 2047)
 ns * 0x88    ns * 136     ns * 80 ascii : ns * prefiltering (e.g. HP:0.1Hz LP:75Hz N:60)
 ns * 0xD8    ns * 216     ns *  8 ascii : ns * nr of samples in each data record
 ns * 0xE0    ns * 224     ns * 32 ascii : ns * reserved
 
ns: number of signals
 
All fields are left aligned and filled up with spaces, no NULL's.
 
Only printable ASCII characters are allowed.
 
Decimal separator (if any) must be a dot. No grouping characters in numbers.
 
For more info about the EDF and EDF+ format, visit: https://edfplus.info/specs/
 
For more info about the BDF and BDF+ format, visit: https://www.teuniz.net/edfbrowser/bdfplus%20format%20description.html
 
note: In EDF, the sensitivity (e.g. uV/bit) and offset are stored using four parameters:
digital maximum and minimum, and physical maximum and minimum.
Here, digital means the raw data coming from a sensor or ADC. Physical means the units like uV.
The sensitivity in units/bit is calculated as follows:
 
units per bit = (physical max - physical min) / (digital max - digital min)
 
The digital offset is calculated as follows:
 
offset = (physical max / units per bit) - digital max
 
For a better explanation about the relation between digital data and physical data,
read the document "Coding Schemes Used with Data Converters" (PDF):
 
https://www.ti.com/general/docs/lit/getliterature.tsp?baseLiteratureNumber=sbaa042
 
note: An EDF file usually contains multiple so-called datarecords. One datarecord usually has a duration of one second (this is the default but it is not mandatory!).
In that case a file with a duration of five minutes contains 300 datarecords. The duration of a datarecord can be freely choosen but, if possible, use values from
0.1 to 1 second for easier handling. Just make sure that the total size of one datarecord, expressed in bytes, does not exceed 10MByte (15MBytes for BDF(+)).
 
The RECOMMENDATION of a maximum datarecordsize of 61440 bytes in the EDF and EDF+ specification was usefull in the time people were still using DOS as their main operating system.
Using DOS and fast (near) pointers (16-bit pointers), the maximum allocatable block of memory was 64KByte.
This is not a concern anymore so the maximum datarecord size now is limited to 10MByte for EDF(+) and 15MByte for BDF(+). This helps to accommodate for higher samplingrates
used by modern Analog to Digital Converters.
 
EDF header character encoding: The EDF specification says that only (printable) ASCII characters are allowed.
When writing the header info, EDFlib will assume you are using Latin1 encoding and it will automatically convert
characters with accents, umlauts, tilde, etc. to their "normal" equivalent without the accent/umlaut/tilde/etc.
in order to create a valid EDF file.
 
The description/name of an EDF+ annotation on the other hand, is encoded in UTF-8.
 
Annotations: onset and duration are expressed in units of 100 nano-Sec.
 
author: Teunis van Beelen
 
  Methods defined here:
__init__(self, path:str)
Creates an instance of an EDF reader.
 
Path is the path to the EDF file.
close(self) -> int
Closes the file.
 
Returns 0 on success, otherwise -1.
fseek(self, s:int, offset:int, whence:int) -> int
Sets the file offset / sample position.
 
The fseek() function sets the sample position indicator for the edfsignal pointed to by s.
The new position, measured in samples, is obtained by adding offset samples to the position specified by whence.
If whence is set to EDFSEEK_SET, EDFSEEK_CUR, or EDFSEEK_END, the offset is relative to the start of the file,
the current position indicator, or end-of-file, respectively.
Returns the current offset.
Note that every signal has it's own independent sample position indicator and fseek() affects only one of them.
 
s is the signal number (zero-based).
ftell(self, s:int) -> int
Returns the sample pointer position.
 
The ftell() function obtains the current value of the sample position indicator for the edfsignal pointed to by s.
Returns the current offset relative to the start of the file.
Note that every signal has it's own independent sample position indicator and ftell() affects only one of them.
 
s is the signal number (zero-based).
getAdministrationCode(self) -> str
Returns the patient's name.
 
Is always empty if filetype is EDF or BDF.
getDigitalMaximum(self, s:int) -> int
Returns the digital minimum of a signal.
 
note: In EDF, the sensitivity (e.g. uV/bit) and offset are stored using four parameters:
digital maximum and minimum, and physical maximum and minimum.
Here, digital means the raw data coming from a sensor or ADC. Physical means the units like uV.
The sensitivity in units/bit is calculated as follows:
 
units per bit = (physical max - physical min) / (digital max - digital min)
 
The digital offset is calculated as follows:
 
offset = (physical max / units per bit) - digital max
 
s is the signal number (zero-based).
getDigitalMinimum(self, s:int) -> int
Returns the digital minimum of a signal.
 
note: In EDF, the sensitivity (e.g. uV/bit) and offset are stored using four parameters:
digital maximum and minimum, and physical maximum and minimum.
Here, digital means the raw data coming from a sensor or ADC. Physical means the units like uV.
The sensitivity in units/bit is calculated as follows:
 
units per bit = (physical max - physical min) / (digital max - digital min)
 
The digital offset is calculated as follows:
 
offset = (physical max / units per bit) - digital max
 
s is the signal number (zero-based).
getEquipment(self) -> str
Returns the name or code of the technician who performed the recording.
 
Is always empty if filetype is EDF or BDF.
getFileDuration(self) -> int
Returns the duration of the file (recording time).
 
File duration expressed in units of 100 nanoSeconds.
getFileType(self) -> int
Gets the file type.
 
File type can be one of the following:
EDFLIB_FILETYPE_EDF      = 0
EDFLIB_FILETYPE_EDFPLUS  = 1
EDFLIB_FILETYPE_BDF      = 2
EDFLIB_FILETYPE_BDFPLUS  = 3
getLongDataRecordDuration(self) -> int
Returns the duration of a datarecord.
 
The duration of a recording = number of datarecords * datarecord duration.
Duration is expressed in units of 100 nanoSeconds.
getNumDataRecords(self) -> int
Returns the number of datarecords in the file.
 
The duration of a recording = number of datarecords * datarecord duration.
getNumSignals(self) -> int
Returns the number of signals in the file.
getPatient(self) -> str
Returns the patient field of the file header.
 
Is always empty if filetype is EDF+ or BDF+.
getPatientAdditional(self) -> str
Returns the additional info (if any) of the patient field of the file header.
 
Is always empty if filetype is EDF or BDF.
getPatientBirthDate(self) -> str
Returns the patient's birthdate.
 
Is always empty if filetype is EDF or BDF.
getPatientCode(self) -> str
Returns the patient code.
 
Is always empty if filetype is EDF or BDF.
getPatientGender(self) -> str
Returns the patient's gender.
 
Is always empty if filetype is EDF or BDF.
getPatientName(self) -> str
Returns the patient's name.
 
Is always empty if filetype is EDF or BDF.
getPhysicalDimension(self, s:int) -> str
Returns the physical dimension (unit) of a signal.
 
e.g. "uV", "mmHg", "mV", etc.
s is the signal number (zero-based).
getPhysicalMaximum(self, s:int) -> float
Returns the physical maximum of a signal.
 
note: In EDF, the sensitivity (e.g. uV/bit) and offset are stored using four parameters:
digital maximum and minimum, and physical maximum and minimum.
Here, digital means the raw data coming from a sensor or ADC. Physical means the units like uV.
The sensitivity in units/bit is calculated as follows:
 
units per bit = (physical max - physical min) / (digital max - digital min)
 
The digital offset is calculated as follows:
 
offset = (physical max / units per bit) - digital max
 
s is the signal number (zero-based).
getPhysicalMinimum(self, s:int) -> float
Returns the physical minimum of a signal.
 
note: In EDF, the sensitivity (e.g. uV/bit) and offset are stored using four parameters:
digital maximum and minimum, and physical maximum and minimum.
Here, digital means the raw data coming from a sensor or ADC. Physical means the units like uV.
The sensitivity in units/bit is calculated as follows:
 
units per bit = (physical max - physical min) / (digital max - digital min)
 
The digital offset is calculated as follows:
 
offset = (physical max / units per bit) - digital max
 
s is the signal number (zero-based).
getPreFilter(self, s:int) -> str
Returns the pre-filtering description of a signal.
 
e.g.: "LPF:250Hz", "HPF:0.05Hz", "N:60Hz"
s is the signal number (zero-based).
getRecording(self) -> str
Returns the recording field of the file header.
 
Is always empty if filetype is EDF+ or BDF+.
getRecordingAdditional(self) -> str
Returns the additional info (if any) of the recording field of the file header.
 
Is always empty if filetype is EDF or BDF.
getReserved(self) -> str
Returns the reserved field of the file header.
getSampelsPerDataRecord(self, s:int) -> int
Returns the number of samples of a signal in a datarecord.
 
s is the signal number (zero-based).
The returned value equals the samplerate only if the datarecord duration equals one second.
Samplerate can be calculated as follows: fs = samples per record / datarecord duration.
The total number of samples of a signal in a file = samples per record * datarecords.
getSampleFrequency(self, s:int) -> float
Returns the samplefrequency of a signal.
 
s is the signal number (zero-based).
getSignalLabel(self, s:int) -> str
Returns the label (name) of a signal.
 
s is the signal number (zero-based).
getSignalReserved(self, s:int) -> str
Returns the reserved field in the header of a signal.
 
s is the signal number (zero-based).
getStartDateDay(self) -> int
Returns the days of the startdate of the recording.
getStartDateMonth(self) -> int
Returns the month of the startdate of the recording.
getStartDateTime(self) -> datetime.datetime
Returns a datetime structure containing the startdate and starttime of the recording.
getStartDateYear(self) -> int
Returns the year of the startdate of the recording.
getStartTimeHour(self) -> int
Returns the hours of the starttime of the recording.
getStartTimeMinute(self) -> int
Returns the minutes of the starttime of the recording.
getStartTimeSecond(self) -> int
Returns the seconds of the starttime of the recording.
getStartTimeSubSecond(self) -> int
Returns the sub-second part of the starttime of the recording.
 
The returned value is always less than one second and is expressed
in units of 100 nanoSeconds.
getTechnician(self) -> str
Returns the name or code of the technician who performed the recording.
 
Is always empty if filetype is EDF or BDF.
getTotalSamples(self, s:int) -> int
Returns the total number of samples of a signal in the recording.
 
s is the signal number (zero-based).
The returned value equals the samplerate only if the datarecord duration equals one second.
Samplerate can be calculated as follows: fs = samples per record / datarecord duration.
The total number of samples of a signal in a file = samples per record * datarecords.
getTransducer(self, s:int) -> str
Returns the label (name) of a signal.
 
s is the signal number (zero-based).
readSamples(self, s:int, buf:<built-in function array>, n:int) -> int
Read samples.
 
Fills buf with n samples from edfsignal s, starting from the current sample position indicator.
Buf must be a one-dimensional numpy array of size >= n. Array datatype must be int32, float_ or float64.
For EDF, dataype int16 can also be used.
The sample position indicator of edfsignal s will be increased with the number of samples read.
If buf is of type integer, the values are the "raw" digital values as stored in the EDF file
(no conversion is done to their physical values).
If buf is of type float, the values are converted to their physical values e.g. microVolts, beats per minute, mmHg, etc.
 
s is the signal number (zero-based).
buf is a one-dimensional numpy array of datatype int32, float_ or float64. For EDF, dataype int16 can also be used.
n is the number of samples to be read.
Returns the actual number of samples read into buf (this can be less than n or zero).
rewind(self, s:int)
Rewinds the sample position pointer to zero.
 
The rewind() function sets the sample position indicator for the edfsignal pointed to by s to the beginning of the file.
It is equivalent to: fseek(s, 0, EDFSEEK_SET)
Note that every signal has it's own independent sample position indicator and ftell() affects only one of them.
 
s is the signal number (zero-based).
version(self) -> int
If version is 1.00 then it will return 100.

Data descriptors defined here:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

Data and other attributes defined here:
EDFAnnotationStruct = <class 'edfreader.annotation'>
annotation(onset, duration, description)
EDFLIB_DATARECORD_SIZE_TOO_BIG = -26
EDFLIB_DIGMAX_LOWER_THAN_DIGMIN = -24
EDFLIB_DIGMIN_IS_DIGMAX = -23
EDFLIB_DO_NOT_READ_ANNOTATIONS = 0
EDFLIB_FILETYPE_BDF = 2
EDFLIB_FILETYPE_BDFPLUS = 3
EDFLIB_FILETYPE_EDF = 0
EDFLIB_FILETYPE_EDFPLUS = 1
EDFLIB_FILETYPE_ERROR = -7
EDFLIB_FILE_ALREADY_OPENED = -6
EDFLIB_FILE_CLOSED = -13
EDFLIB_FILE_CONTAINS_FORMAT_ERRORS = -3
EDFLIB_FILE_IS_DISCONTINUOUS = -10
EDFLIB_FILE_READ_ERROR = -5
EDFLIB_FILE_WRITE_ERROR = -8
EDFLIB_INVALID_ARGUMENT = -12
EDFLIB_INVALID_READ_ANNOTS_VALUE = -11
EDFLIB_MALLOC_ERROR = -1
EDFLIB_MAXFILES_REACHED = -4
EDFLIB_MAXSIGNALS = 640
EDFLIB_MAX_ANNOTATION_LEN = 512
EDFLIB_NO_SAMPLES_IN_RECORD = -22
EDFLIB_NO_SIGNALS = -20
EDFLIB_NO_SUCH_FILE_OR_DIRECTORY = -2
EDFLIB_NUMBER_OF_SIGNALS_INVALID = -9
EDFLIB_PHYSMIN_IS_PHYSMAX = -25
EDFLIB_READ_ALL_ANNOTATIONS = 2
EDFLIB_READ_ANNOTATIONS = 1
EDFLIB_TIME_DIMENSION = 10000000
EDFLIB_TOO_MANY_SIGNALS = -21
EDFLIB_VERSION = 108
EDFSEEK_CUR = 1
EDFSEEK_END = 2
EDFSEEK_SET = 0