/*
*****************************************************************************
*
* Copyright (c) 2009 - 2024 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.
*
*****************************************************************************
*/
/**
* @file edflib.h
*
* In EDF, the resolution (or 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 resolution in units per least significant 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
*
* An EDF file usually contains multiple so-called datarecords. One datarecord usually has a duration of one second (this is the default but it's not mandatory).
* In that case a file with a duration of five minutes contains 300 datarecords. The duration of a datarecord can be freely chosen 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 10 MByte (15 MBytes for BDF(+)).
*
* The recommendation of a maximum datarecord size of 61440 bytes in the EDF(+) specification was useful 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 10 MByte for EDF(+) and 15 MByte for BDF(+). This helps to accommodate for higher sampling rates
* 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 of an EDF+ annotation/event/trigger on the other hand, is always encoded in UTF-8 (which is forward compatible with ASCII).
*
* The sample frequency of a signal is calculated as follows: sf = (smp_in_datarecord * EDFLIB_TIME_DIMENSION) / datarecord_duration
*
* Annotation signals
* ==================
*
* EDF+ and BDF+ store the annotations/events/triggers in one or more signals (in order to be backwards compatible with EDF and BDF)
* and they can appear anywhere in the list of signals.
* The numbering of the signals in the file is zero based (starts at 0). Signals used for annotations are skipped by EDFlib.
* This means that the annotation signal(s) in the file are hidden.
* Use the function edf_get_annotation() to get the annotations.
*
* So, when a file contains 7 signals and the third and fifth signal are annotation signals, the library will
* report that there are only 5 signals in the file.
* The library will "map" the (zero-based) signal numbers as follows: 0->0, 1->1, 2->3, 3->4, 4->6.
* This way you don't need to worry about which signals are annotationsignals, the library will take care of it.
*
* How the library stores time values
* ==================================
*
* To avoid rounding errors and to be able to compare values, the library stores some time values in variables of type long long int.
* In order not to lose the sub-second precision, all time values are scaled with a scaling factor: 10000000.
* This will limit the time resolution to 100 nanoseconds. To calculate the amount of seconds, divide
* the timevalue by 10000000 or use the macro EDFLIB_TIME_DIMENSION which is declared in edflib.h.
* The following variables use this scaling when you open a file in read mode: "file_duration", "starttime_subsecond" and "onset".
*
* EDFlib and thread-safety
* ========================
* The following functions are always MT-unsafe:
* edfopen_file_readonly() (race condition)
* edfclose_file() (race condition)
* edflib_get_handle() (race condition)
*
* When writing to or reading from the same file, all EDFlib functions are MT-unsafe (race condition).
*
* When accessing EDFlib from different threads, use a mutex.
*
* 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
*
*/
/* compile with options "-D_LARGEFILE64_SOURCE -D_LARGEFILE_SOURCE" */
#ifndef EDFLIB_INCLUDED
#define EDFLIB_INCLUDED
#include
#include
#include
#include
/*
* If both EDFLIB_SO_DLL and EDFLIB_BUILD are defined: compile only EDFlib as a shared library (so, dll).
* When compiling on unix-like systems, add the -fvisibility=hidden to hide all symbols by
* default so that this macro can reveal them.
*
* If only EDFLIB_SO_DLL is defined: link with EDFlib as an external library (so, dll).
* EDFlib must be installed on your system (as an .so or .dll) when running your program.
*
* If both EDFLIB_SO_DLL and EDFLIB_BUILD are not defined: EDFlib will not be used as a shared library,
* it will be an integral part of your program instead.
*
*/
/*
#define EDFLIB_SO_DLL
#define EDFLIB_BUILD
*/
#if defined(EDFLIB_SO_DLL)
# if defined(EDFLIB_BUILD)
# if defined(_WIN32)
# define EDFLIB_API __declspec(dllexport)
# elif defined(__ELF__)
# define EDFLIB_API __attribute__ ((visibility ("default")))
# else
# define EDFLIB_API
# endif
# else
# if defined(_WIN32)
# define EDFLIB_API __declspec(dllimport)
# else
# define EDFLIB_API
# endif
# endif
#else
# define EDFLIB_API
#endif
#define EDFLIB_TIME_DIMENSION (10000000LL)
#define EDFLIB_MAXSIGNALS (4096)
#define EDFLIB_MAX_ANNOTATION_LEN (512)
#define EDFSEEK_SET (0)
#define EDFSEEK_CUR (1)
#define EDFSEEK_END (2)
#define EDF_ANNOT_IDX_POS_END (0)
#define EDF_ANNOT_IDX_POS_MIDDLE (1)
#define EDF_ANNOT_IDX_POS_START (2)
/* the following defines are used in the member "filetype" of the edf_hdr_struct
and as return value for the function edfopen_file_readonly() */
#define EDFLIB_FILETYPE_EDF (0)
#define EDFLIB_FILETYPE_EDFPLUS (1)
#define EDFLIB_FILETYPE_BDF (2)
#define EDFLIB_FILETYPE_BDFPLUS (3)
#define EDFLIB_MALLOC_ERROR (-1)
#define EDFLIB_NO_SUCH_FILE_OR_DIRECTORY (-2)
/* when this error occurs, try to open the file with EDFbrowser (https://www.teuniz.net/edfbrowser/),
it will give you full details about the cause of the error. It can also fix most errors. */
#define EDFLIB_FILE_CONTAINS_FORMAT_ERRORS (-3)
#define EDFLIB_MAXFILES_REACHED (-4)
#define EDFLIB_FILE_READ_ERROR (-5)
#define EDFLIB_FILE_ALREADY_OPENED (-6)
#define EDFLIB_FILETYPE_ERROR (-7)
#define EDFLIB_FILE_WRITE_ERROR (-8)
#define EDFLIB_NUMBER_OF_SIGNALS_INVALID (-9)
#define EDFLIB_FILE_IS_DISCONTINUOUS (-10)
#define EDFLIB_INVALID_READ_ANNOTS_VALUE (-11)
#define EDFLIB_ARCH_ERROR (-12)
/* values for annotations */
#define EDFLIB_DO_NOT_READ_ANNOTATIONS (0)
#define EDFLIB_READ_ANNOTATIONS (1)
#define EDFLIB_READ_ALL_ANNOTATIONS (2)
/* the following defines are possible errors returned by the first sample write action */
#define EDFLIB_NO_SIGNALS (-20)
#define EDFLIB_TOO_MANY_SIGNALS (-21)
#define EDFLIB_NO_SAMPLES_IN_RECORD (-22)
#define EDFLIB_DIGMIN_IS_DIGMAX (-23)
#define EDFLIB_DIGMAX_LOWER_THAN_DIGMIN (-24)
#define EDFLIB_PHYSMIN_IS_PHYSMAX (-25)
#define EDFLIB_DATARECORD_SIZE_TOO_BIG (-26)
#ifdef __cplusplus
extern "C" {
#endif
/**
* This structure contains the signal parameters.
*/
typedef struct edf_param_struct
{
char label[17]; /*!< Label (name) of the signal, null-terminated string. */
long long smp_in_file; /*!< Number of samples in the file. */
double phys_max; /*!< Physical maximum, usually the maximum input of the ADC. */
double phys_min; /*!< Physical minimum, usually the minimum input of the ADC. */
int dig_max; /*!< Digital maximum, usually the maximum output of the ADC, cannot not be higher than 32767 for EDF or 8388607 for BDF. */
int dig_min; /*!< Digital minimum, usually the minimum output of the ADC, cannot not be lower than -32768 for EDF or -8388608 for BDF. */
int smp_in_datarecord; /*!< Number of samplesin a datarecord, if the datarecord has a duration of one second (default), then it equals the sample rate. */
char physdimension[9]; /*!< Physical dimension (unit, e.g. uV, bpm, mA, etc.), null-terminated string. */
char prefilter[81]; /*!< Prefilter settings, null-terminated string. */
char transducer[81]; /*!< Transducer (sensor), null-terminated string. */
} edflib_param_t;
/**
* This structure is used for annotations/events/triggers.
*/
typedef struct edf_annotation_struct
{
long long onset; /*!< Onset time of the event, expressed in units of 100 nanoseconds and relative to the start of the recording. */
long long duration_l; /*!< Duration, expressed in units of 100 nanoseconds, if less than zero: unused or not applicable. */
char duration[20]; /*!< Duration, expressed in seconds, this is a null-terminated ASCII string. */
char annotation[EDFLIB_MAX_ANNOTATION_LEN + 1]; /*!< Description of the annotation/event/trigger, this is a null-terminated UTF8 string. */
} edflib_annotation_t;
/**
* This structure contains the general header info and parameters. It will be filled when calling the function edfopen_file_readonly().
*/
typedef struct edf_hdr_struct
{
int handle; /*!< A handle (identifier) used to distinguish the different files or -1 in case of an error. */
int filetype; /*!< 0: EDF, 1: EDF+, 2: BDF, 3: BDF+, a negative number indicates an error code. */
int edfsignals; /*!< Number of signals in the file, annotation channels are not included. */
long long file_duration; /*!< Duration of the file expressed in units of 100 nanoseconds. */
int startdate_day; /*!< Startdate: day: 1 - 31 */
int startdate_month; /*!< Startdate: month: 1 - 12 */
int startdate_year; /*!< Startdate: year: 1985 - 2084 */
long long starttime_subsecond; /*!< Starttime subsecond expressed in units of 100 nanoseconds. Is always less than 10000000 (one second). Only used by EDF+ and BDF+. */
int starttime_second; /*!< Starttime: second: 0 - 59 */
int starttime_minute; /*!< Starttime: minute: 0 - 59 */
int starttime_hour; /*!< Starttime: hour: 0 - 23 */
char patient[81]; /*!< Null-terminated string, contains patient field of header, is always empty when filetype is EDFPLUS or BDFPLUS. */
char recording[81]; /*!< Null-terminated string, contains recording field of header, is always empty when filetype is EDFPLUS or BDFPLUS. */
char patientcode[81]; /*!< Null-terminated string, is always empty when filetype is EDF or BDF. */
char sex[16]; /*!< Null-terminated string, is always empty when filetype is EDF or BDF. */
#if defined(__GNUC__)
char gender[16] __attribute__ ((deprecated ("use sex"))); /*!< Deprecated, use \p sex. */
#else
char gender[16]; /*!< Deprecated, use \p sex. */
#endif
char birthdate[16]; /*!< Null-terminated string, is always empty when filetype is EDF or BDF. */
int birthdate_day; /*!< Birthdate: day: 1 - 31 (zero in case of EDF or BDF). */
int birthdate_month; /*!< Birthdate: month: 1 - 12 (zero in case of EDF or BDF). */
int birthdate_year; /*!< Birthdate: year: 1800 - 3000 (zero in case of EDF or BDF). */
char patient_name[81]; /*!< Null-terminated string, is always empty when filetype is EDF or BDF. */
char patient_additional[81]; /*!< Null-terminated string, is always empty when filetype is EDF or BDF. */
char admincode[81]; /*!< Null-terminated string, is always empty when filetype is EDF or BDF. */
char technician[81]; /*!< Null-terminated string, is always empty when filetype is EDF or BDF. */
char equipment[81]; /*!< Null-terminated string, is always empty when filetype is EDF or BDF. */
char recording_additional[81]; /*!< Null-terminated string, is always empty when filetype is EDF or BDF. */
long long datarecord_duration; /*!< Duration of a datarecord expressed in units of 100 nanoseconds. */
long long datarecords_in_file; /*!< Number of datarecords in the file. */
long long annotations_in_file; /*!< Number of annotations/events/triggers in the file. */
edflib_param_t signalparam[EDFLIB_MAXSIGNALS]; /*!< array of structs containing the signal parameters. */
} edflib_hdr_t;
/***************** the following functions are used to read files **************************/
/**
* Opens an existing file for reading.
*
* @param[in] path
* null-terminated string containing the \p path to the file
*
* @param[out] edfhdr
* pointer to an \p edflib_hdr_t struct, all fields in this struct will be overwritten,
* it will be filled with all the relevant header- and signalinfo/parameters
*
* @param[in] read_annotations
* Must have one of the following values:
* - EDFLIB_DO_NOT_READ_ANNOTATIONS annotations will not be read (this can save time when opening a very large EDF+ or BDF+ file
* - EDFLIB_READ_ANNOTATIONS annotations will be read immediately, stops when an annotation has
* been found which contains the description "Recording ends"
* - EDFLIB_READ_ALL_ANNOTATIONS all annotations will be read immediately
*
* @return
* 0 on success, in case of an error it returns -1 and an error code will be set in the member "filetype" of edfhdr.
* This function is required if you want to read a file
*
* In case of a file format error (-3), try to open the file with EDFbrowser: https://www.teuniz.net/edfbrowser/
* It will give you full details about the cause of the error and it can also fix most errors.
*/
EDFLIB_API int edfopen_file_readonly(const char *path, edflib_hdr_t *edfhdr, int read_annotations);
/**
* Reads \p n samples from \p edfsignal, starting from the current sample position indicator, into \p buf (edfsignal starts at 0).
* The values are converted to their physical values e.g. microVolts, beats per minute, etc.
*
* @param[in] handle
* File handle.
* @param[in] edfsignal
* The zero-based index of the signal.
* @param[in] n
* Number of samples to read. The sample position indicator will be increased with the same amount.
* @param[out] buf
* Pointer to a buffer, size must be equal to, or bigger than, sizeof(double[n])
*
* @return
* The number of samples read (this can be less than \p n or zero!) or -1 in case of an error
*/
EDFLIB_API int edfread_physical_samples(int handle, int edfsignal, int n, double *buf);
/**
* Reads \p n samples from \p edfsignal, starting from the current sample position indicator, into \p buf (edfsignal starts at 0).
* The values are the "raw" digital values (e.g. from an ADC).
*
* @param[in] handle
* File handle.
* @param[in] edfsignal
* The zero-based index of the signal.
* @param[in] n
* Number of samples to read. The sample position indicator will be increased with the same amount.
* @param[out] buf
* Pointer to a buffer, size must be equal to, or bigger than, sizeof(double[n])
*
* @return
* The number of samples read (this can be less than \p n or zero!) or -1 in case of an error
*/
EDFLIB_API int edfread_digital_samples(int handle, int edfsignal, int n, int *buf);
/**
* Sets the sample position indicator for the edfsignal pointed to by \p edfsignal.
* The new position, measured in samples, is obtained by adding offset samples to the position specified by \p whence.
* If \p 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.
* Note that every signal has it's own independent sample position indicator and \p edfseek() affects only one of them.
*
* @param[in] handle
* File handle.
* @param[in] edfsignal
* The zero-based index of the signal.
* @param[in] offset
* Offset measured in samples.
* @param[in] whence
* Reference for \p offset:
* - EDFSEEK_SET start of the file
* - EDFSEEK_CUR current position
* - EDFSEEK_END end of the file
*
* @return
* The current offset or -1 in case of an error.
*/
EDFLIB_API long long edfseek(int handle, int edfsignal, long long offset, int whence);
/**
* Obtains the current value of the sample position indicator for the edfsignal pointed to by \p edfsignal.
* Note that every signal has it's own independent sample position indicator and \p edftell() affects only one of them.
*
* @param[in] handle
* File handle.
* @param[in] edfsignal
* The zero-based index of the signal.
*
* @return
* The current offset or -1 in case of an error.
*/
EDFLIB_API long long edftell(int handle, int edfsignal);
/**
* Sets the sample position indicator for the edfsignal pointed to by \p edfsignal to the beginning of the file.
* It is equivalent to: \p edfseek(handle, edfsignal, 0LL, EDFSEEK_SET).
* Note that every signal has it's own independent sample position indicator and \p edfrewind() affects only one of them.
*
* @param[in] handle
* File handle.
* @param[in] edfsignal
* The zero-based index of the signal.
*
* @return
* 0 on success or -1 in case of an error.
*/
EDFLIB_API int edfrewind(int handle, int edfsignal);
/**
* Fills the edflib_annotation_t structure with the annotation \p n.
* The string that describes the annotation/event is encoded in UTF-8.
* To obtain the number of annotations in a file, check edf_hdr_struct -> annotations_in_file.
*
* @param[in] handle
* File handle.
* @param[in] n
* The zero-based index number of the list of annotations.
* @param[out] annot
* Pointer to a struct that will be filled with the annotation.
*
* @return
* 0 on success or -1 in case of an error.
*/
EDFLIB_API int edf_get_annotation(int handle, int n, edflib_annotation_t *annot);
/***************** the following functions are used in read and write mode **************************/
/**
* Closes (and in case of writing, finalizes) the file.
*
* This function MUST be called when you have finished reading or writing
* This function is required after reading or writing. Failing to do so will cause
* unnecessary memory usage and in case of writing it will cause a corrupted or incomplete file.
*
* @param[in] handle
* File handle.
*
* @return
* 0 on success or -1 in case of an error.
*/
EDFLIB_API int edfclose_file(int handle);
/**
* Returns the version number of this library, multiplied by hundred. if version is "1.00" then it will return 100.
*
* @return
* The version number.
*/
EDFLIB_API int edflib_version(void);
/**
* Returns 1 if the file is in use, either for reading or writing, otherwise returns 0.
*
* @param[in] path
* Pointer to a null-terminated string that contains the path to the file.
*
* @return
* 1 if the file is in use (either for reading or writing), otherwise 0.
*/
EDFLIB_API int edflib_is_file_used(const char *path);
/**
* Returns the number of open files.
*
* @return
* The number of open files, either for reading or writing.
*/
EDFLIB_API int edflib_get_number_of_open_files(void);
/**
* Returns the handle of an open file, either for reading or writing.
*
* @param[in] file_number
* A zero based index number of the list of open files.
*
* @return
* The file handle or -1 if the file_number >= number of open files.
*/
EDFLIB_API int edflib_get_handle(int file_number);
/***************** the following functions are used to write files **************************/
/**
* Opens an new file for writing. Warning: an already existing file with the same name will be silently overwritten without advance warning!
* This function is required if you want to write a file (or use edfopen_file_writeonly_with_params())
*
* @param[in] path
* A null-terminated string containing the path and name of the file
*
* @param[in] filetype
* Must be EDFLIB_FILETYPE_EDFPLUS or EDFLIB_FILETYPE_BDFPLUS.
*
* @param[in] number_of_signals
* The number of signals you want to store into the file
* (excluding annotation signals, the library will take care of that).
*
* @return
* A file handle on success or a negative number in case of an error:
* - EDFLIB_MALLOC_ERROR
* - EDFLIB_NO_SUCH_FILE_OR_DIRECTORY
* - EDFLIB_MAXFILES_REACHED
* - EDFLIB_FILE_ALREADY_OPENED
* - EDFLIB_NUMBER_OF_SIGNALS_INVALID
* - EDFLIB_ARCH_ERROR
*/
EDFLIB_API int edfopen_file_writeonly(const char *path, int filetype, int number_of_signals);
/**
* This is a convenience function that can create a new EDF file and initializes the most important parameters.
* It assumes that all signals are sharing the same parameters (you can still change them though).
* Warning: an already existing file with the same name will be silently overwritten without advance warning!
*
* @param[in] path
* A null-terminated string containing the path and name of the file.
*
* @param[in] filetype
* Must be EDFLIB_FILETYPE_EDFPLUS or EDFLIB_FILETYPE_BDFPLUS.
*
* @param[in] number_of_signals
* The number of signals you want to store into the file
* (excluding annotation signals, the library will take care of that).
*
* @param[in] samplefrequency
* Sample frequency for all signals. (In reality, it sets the number of samples per datarecord which equals the sample frequency only when
* the datarecords have a duration of one second which is the default here.)
*
* @param[in] phys_max_min
* Physical maximum and minimum for all signals.
*
* @param[in] phys_dim
* Pointer to a NULL-terminated ASCII-string containing the physical dimension (unit) for all signals ("uV", "BPM", "mA", "Degr.", etc.).
*
* @return
* A file handle on success or a negative number in case of an error:
* - EDFLIB_MALLOC_ERROR
* - EDFLIB_NO_SUCH_FILE_OR_DIRECTORY
* - EDFLIB_MAXFILES_REACHED
* - EDFLIB_FILE_ALREADY_OPENED
* - EDFLIB_NUMBER_OF_SIGNALS_INVALID
* - EDFLIB_ARCH_ERROR
*/
EDFLIB_API int edfopen_file_writeonly_with_params(const char *path, int filetype, int number_of_signals, int samplefrequency, double phys_max_min, const char *phys_dim);
/**
* Sets the sample frequency of signal edfsignal. In reality, it sets the number of samples in a datarecord
* which equals the sample frequency only when the datarecords have a duration of one second.
* The effective sample frequency is: samplefrequency / datarecord duration
* This function is required for every signal (except when using edfopen_file_writeonly_with_params()) and can be called
* only after opening a file in write mode and before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] edfsignal
* The zero-based index of the signal.
*
* @param[in] samplefrequency
* Sample frequency, must be > 0;
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_samplefrequency(int handle, int edfsignal, int samplefrequency);
/**
* Sets the maximum physical value of signal edfsignal. (the value of the input of the ADC when the output equals the value of "digital maximum")
* It is the highest value that the equipment is able to record. It does not necessarily mean the signal recorded reaches this level.
* In other words, it is the highest value that CAN occur in the recording.
* Must be un-equal to physical minimum.
* This function is required for every signal (except when using edfopen_file_writeonly_with_params()) and can be called
* only after opening a file in write mode and before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] edfsignal
* The zero-based index of the signal.
*
* @param[in] phys_max
* Physical maximum, must be != physical minimum;
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_physical_maximum(int handle, int edfsignal, double phys_max);
/**
* Sets the minimum physical value of signal edfsignal. (the value of the input of the ADC when the output equals the value of "digital minimum")
* It is the lowest value that the equipment is able to record. It does not necessarily mean the signal recorded reaches this level.
* In other words, it is the lowest value that CAN occur in the recording.
* Must be un-equal to physical maximum.
* This function is required for every signal (except when using edfopen_file_writeonly_with_params()) and can be called
* only after opening a file in write mode and before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] edfsignal
* The zero-based index of the signal.
*
* @param[in] phys_min
* Physical minimum, must be != physical maximum;
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_physical_minimum(int handle, int edfsignal, double phys_min);
/**
* Sets the maximum digital value of signal edfsignal. The maximum value is 32767 for EDF+ and 8388607 for BDF+.
* It is the highest value that the equipment is able to record. It does not necessarily mean the signal recorded reaches this level.
* In other words, it is the highest value that CAN occur in the recording.
* Must be higher than digital minimum.
* This function is required for every signal (except when using edfopen_file_writeonly_with_params()) and can be called
* only after opening a file in write mode and before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] edfsignal
* The zero-based index of the signal.
*
* @param[in] dig_max
* Digital maximum, must be > digital minimum;
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_digital_maximum(int handle, int edfsignal, int dig_max);
/**
* Sets the minimum digital value of signal edfsignal. The minimum value is -32768 for EDF+ and -8388608 for BDF+.
* It is the lowest value that the equipment is able to record. It does not necessarily mean the signal recorded reaches this level.
* In other words, it is the lowest value that CAN occur in the recording.
* Must be lower than digital maximum.
* This function is required for every signal (except when using edfopen_file_writeonly_with_params()) and can be called
* only after opening a file in write mode and before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] edfsignal
* The zero-based index of the signal.
*
* @param[in] dig_min
* Digital minimum, must be < digital maximum;
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_digital_minimum(int handle, int edfsignal, int dig_min);
/**
* Sets the label (name) of signal \p edfsignal. ("EEG FP1", "SaO2", etc.).
* This function is recommended for every signal when you want to write a file
* and can be called only after opening a file in write mode and before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] edfsignal
* The zero-based index of the signal.
*
* @param[in] label
* A pointer to a NULL-terminated ASCII-string containing the label (name) of the signal \p edfsignal.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_label(int handle, int edfsignal, const char *label);
/**
* Sets the prefilter of signal \p edfsignal e.g. "HP:0.1Hz", "LP:75Hz N:50Hz", etc.
* This function is optional and can be called only after opening a file in writemode and
* before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] edfsignal
* The zero-based index of the signal.
*
* @param[in] prefilter
* A pointer to a NULL-terminated ASCII-string containing the prefilter text of the signal \p edfsignal.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_prefilter(int handle, int edfsignal, const char *prefilter);
/**
* Sets the transducer of signal \p edfsignal e.g. "AgAgCl cup electrodes", etc.
* This function is optional and can be called only after opening a file in writemode and
* before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] edfsignal
* The zero-based index of the signal.
*
* @param[in] transducer
* A pointer to a NULL-terminated ASCII-string containing the transducer text of the signal \p edfsignal.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_transducer(int handle, int edfsignal, const char *transducer);
/**
* Sets the physical dimension (unit) of signal \p edfsignal. ("uV", "BPM", "mA", "Degr.", etc.).
* This function is recommended for every signal when you want to write a file
* and can be called only after opening a file in write mode and before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] edfsignal
* The zero-based index of the signal.
*
* @param[in] phys_dim
* A pointer to a NULL-terminated ASCII-string containing the physical dimension (unit) of the signal \p edfsignal.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_physical_dimension(int handle, int edfsignal, const char *phys_dim);
/**
* Sets the startdate and starttime.
* If not called, the library will use the system date and time at runtime.
* This function is optional and can be called only after opening a file in write mode
* and before the first sample write action.
* Note: for anonymization purposes, the consensus is to use 1985-01-01 00:00:00 for the startdate and starttime.
*
* @param[in] handle
* File handle.
*
* @param[in] startdate_year
* 1985 - 2084 inclusive
*
* @param[in] startdate_month
* 1 - 12 inclusive
*
* @param[in] startdate_day
* 1 - 31 inclusive
*
* @param[in] starttime_hour
* 0 - 23 inclusive
*
* @param[in] starttime_minute
* 0 - 59 inclusive
*
* @param[in] starttime_second
* 0 - 59 inclusive
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_startdatetime(int handle, int startdate_year, int startdate_month, int startdate_day,
int starttime_hour, int starttime_minute, int starttime_second);
/**
* Sets the subject name
* This function is optional and can be called only after opening a file in writemode and
* before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] patientname
* A pointer to a NULL-terminated ASCII-string containing the subject name.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_patientname(int handle, const char *patientname);
/**
* Sets the subject code
* This function is optional and can be called only after opening a file in writemode and
* before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] patientcode
* A pointer to a NULL-terminated ASCII-string containing the subject code.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_patientcode(int handle, const char *patientcode);
/**
* Sets the sex of the subject. 1 is male, 0 is female.
* This function is optional and can be called only after opening a file in writemode
* and before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] sex
* 1: male, 0: female.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_sex(int handle, int sex);
#if defined(__GNUC__)
EDFLIB_API int edf_set_gender(int handle, int sex) __attribute__ ((deprecated ("use edf_set_sex()")));
#else
EDFLIB_API int edf_set_gender(int handle, int sex);
#endif
/* DEPRECATED!! USE edf_set_sex()
* Sets the sex. 1 is male, 0 is female.
* Returns 0 on success, otherwise -1
* This function is optional and can be called only after opening a file in writemode
* and before the first sample write action
*/
/**
* Sets the subject birthdate.
* This function is optional and can be called only after opening a file in write mode
* and before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] birthdate_year
* 1800 - 3000 inclusive
*
* @param[in] birthdate_month
* 1 - 12 inclusive
*
* @param[in] birthdate_day
* 1 - 31 inclusive
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_birthdate(int handle, int birthdate_year, int birthdate_month, int birthdate_day);
/**
* Sets the additional subject info
* This function is optional and can be called only after opening a file in writemode and
* before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] patient_additional
* A pointer to a NULL-terminated ASCII-string containing the additional subject info.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_patient_additional(int handle, const char *patient_additional);
/**
* Sets the administration code
* This function is optional and can be called only after opening a file in writemode and
* before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] admincode
* A pointer to a NULL-terminated ASCII-string containing the administration code.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_admincode(int handle, const char *admincode);
/**
* Sets the technicians name or code
* This function is optional and can be called only after opening a file in writemode and
* before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] technician
* A pointer to a NULL-terminated ASCII-string containing the technicians name or code.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_technician(int handle, const char *technician);
/**
* Sets the equipment brand and/or model
* This function is optional and can be called only after opening a file in writemode and
* before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] equipment
* A pointer to a NULL-terminated ASCII-string containing the equipment brand and/or model
* used for the recording.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_equipment(int handle, const char *equipment);
/**
* Sets the additional info about the recording.
* This function is optional and can be called only after opening a file in writemode and
* before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] recording_additional
* A pointer to a NULL-terminated ASCII-string containing the additional info about the recording.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_recording_additional(int handle, const char *recording_additional);
/**
* Writes n physical samples (uV, mA, Ohm) from \p buf belonging to one signal
* where n is the samplefrequency of that signal.
* Actually, n equals the number of samples per datarecord which equals the samplefrequency only
* when the datarecord duration has the default value of one second!
* The physical samples will be converted to digital samples using the
* values of physical maximum, physical minimum, digital maximum and digital minimum.
* Size of \p buf must be equal to or bigger than sizeof(double[samples per datarecord]).
* Call this function for every signal in the file. The order is important:
* When there are 4 signals in the file, the order of calling this function
* must be: signal 0, signal 1, signal 2, signal 3, signal 0, signal 1, signal 2, etc.
*
* @param[in] handle
* File handle.
*
* @param[in] buf
* A pointer to a buffer containing the samples.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edfwrite_physical_samples(int handle, double *buf);
/**
* Writes physical samples (uV, mA, Ohm) from \p buf
* \p buf must be filled with samples from all signals, starting with n samples of signal 0, n samples of signal 1, n samples of signal 2, etc.
* where n is the samplefrequency of that signal.
* Actually, n equals the number of samples per datarecord which equals the samplefrequency only
* when the datarecord duration has the default value of one second!
* The physical samples will be converted to digital samples using the
* values of physical maximum, physical minimum, digital maximum and digital minimum.
* The number of samples written equals the sum of the samples per datarecord of all signals.
*
* @param[in] handle
* File handle.
*
* @param[in] buf
* A pointer to a buffer containing the samples.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_blockwrite_physical_samples(int handle, double *buf);
/**
* Writes n "raw" digital samples from \p buf belonging to one signal
* where n is the samplefrequency of that signal.
* Actually, n equals the number of samples per datarecord which equals the samplefrequency only
* when the datarecord duration has the default value of one second!
* Size of \p buf should be equal to or bigger than sizeof(short[samples per datarecord]).
* Call this function for every signal in the file. The order is important:
* When there are 4 signals in the file, the order of calling this function
* must be: signal 0, signal 1, signal 2, signal 3, signal 0, signal 1, signal 2, etc.
*
* @param[in] handle
* File handle.
*
* @param[in] buf
* A pointer to a buffer containing the samples.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edfwrite_digital_short_samples(int handle, short *buf);
/**
* Writes n "raw" digital samples from \p buf belonging to one signal
* where n is the samplefrequency of that signal.
* Actually, n equals the number of samples per datarecord which equals the samplefrequency only
* when the datarecord duration has the default value of one second!
* The 16 (or 24 in case of BDF+) least significant bits of the samples will be written to the
* file without any conversion.
* Size of \p buf should be equal to or bigger than sizeof(int[samples per datarecord]).
* Call this function for every signal in the file. The order is important:
* When there are 4 signals in the file, the order of calling this function
* must be: signal 0, signal 1, signal 2, signal 3, signal 0, signal 1, signal 2, etc.
*
* @param[in] handle
* File handle.
*
* @param[in] buf
* A pointer to a buffer containing the samples.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edfwrite_digital_samples(int handle, int *buf);
/**
* Writes "raw" digital samples from \p buf
* \p buf must be filled with samples from all signals, starting with n samples of signal 0, n samples of signal 1, n samples of signal 2, etc.
* where n is the samplefrequency of that signal.
* Actually, n equals the number of samples per datarecord which equals the samplefrequency only
* when the datarecord duration has the default value of one second!
* One sample equals 3 bytes, order is little endian (least significant byte first).
* Encoding is second's complement, most significant bit of most significant byte is the sign-bit.
* Because the size of a 3-byte sample is 24-bit, this function can only be used when writing a BDF+ file.
* The number of samples written equals the sum of the samples per datarecord of all signals.
*
* @param[in] handle
* File handle.
*
* @param[in] buf
* A pointer to a buffer containing the samples.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_blockwrite_digital_3byte_samples(int handle, void *buf);
/**
* Writes "raw" digital samples from \p buf
* \p buf must be filled with samples from all signals, starting with n samples of signal 0, n samples of signal 1, n samples of signal 2, etc.
* where n is the samplefrequency of that signal.
* Actually, n equals the number of samples per datarecord which equals the samplefrequency only
* when the datarecord duration has the default value of one second!
* One sample equals 2 bytes, order is little endian (least significant byte first).
* Encoding is second's complement, most significant bit of most significant byte is the sign-bit.
* Because the size of a 2-byte sample is 16-bit, this function can only be used when writing an EDF+ file.
* The number of samples written equals the sum of the samples per datarecord of all signals.
*
* @param[in] handle
* File handle.
*
* @param[in] buf
* A pointer to a buffer containing the samples.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_blockwrite_digital_short_samples(int handle, short *buf);
/**
* Writes "raw" digital samples from \p buf
* \p buf must be filled with samples from all signals, starting with n samples of signal 0, n samples of signal 1, n samples of signal 2, etc.
* where n is the samplefrequency of that signal.
* Actually, n equals the number of samples per datarecord which equals the samplefrequency only
* when the datarecord duration has the default value of one second!
* The 16 (or 24 in case of BDF+) least significant bits of the samples will be written to the
* file without any conversion.
* The number of samples written equals the sum of the samples per datarecord of all signals.
*
* @param[in] handle
* File handle.
*
* @param[in] buf
* A pointer to a buffer containing the samples.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_blockwrite_digital_samples(int handle, int *buf);
/**
* Writes an annotation/event to the file.
* This function is optional and can be called only after opening a file in writemode
* and before closing the file.
*
* @param[in] handle
* File handle.
*
* @param[in] onset
* microseconds since start of recording.
*
* @param[in] duration
* microseconds, > 0 or -1 if not used.
*
* @param[in] description
* A null-terminated UTF8-string containing the text that describes the event.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edfwrite_annotation_utf8_hr(int handle, long long onset, long long duration, const char *description);
#if defined(__GNUC__)
EDFLIB_API int edfwrite_annotation_utf8(int handle, long long onset, long long duration, const char *description) __attribute__ ((deprecated ("use edfwrite_annotation_utf8_hr()")));
#else
EDFLIB_API int edfwrite_annotation_utf8(int handle, long long onset, long long duration, const char *description);
#endif
/* DEPRECATED!! USE edfwrite_annotation_utf8_hr()
* writes an annotation/event to the file
* onset is relative to the start of the file
* onset and duration are in units of 100 microseconds! resolution is 0.0001 second!
* for example: 34.071 seconds must be written as 340710
* if duration is unknown or not applicable: set a negative number (-1)
* description is a null-terminated UTF8-string containing the text that describes the event
* This function is optional and can be called only after opening a file in writemode
* and before closing the file
*/
/**
* Writes an annotation/event to the file.
* This function is optional and can be called only after opening a file in writemode
* and before closing the file.
*
* @param[in] handle
* File handle.
*
* @param[in] onset
* microseconds since start of recording.
*
* @param[in] duration
* microseconds, > 0 or -1 if not used.
*
* @param[in] description
* A null-terminated Latin1-string containing the text that describes the event.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edfwrite_annotation_latin1_hr(int handle, long long onset, long long duration, const char *description);
#if defined(__GNUC__)
EDFLIB_API int edfwrite_annotation_latin1(int handle, long long onset, long long duration, const char *description) __attribute__ ((deprecated ("use edfwrite_annotation_latin1_hr()")));
#else
EDFLIB_API int edfwrite_annotation_latin1(int handle, long long onset, long long duration, const char *description);
#endif
/* DEPRECATED!! USE edfwrite_annotation_latin1_hr()
* writes an annotation/event to the file
* onset is relative to the start of the file
* onset and duration are in units of 100 microseconds! resolution is 0.0001 second!
* for example: 34.071 seconds must be written as 340710
* if duration is unknown or not applicable: set a negative number (-1)
* description is a null-terminated Latin1-string containing the text that describes the event
* This function is optional and can be called only after opening a file in writemode
* and before closing the file
*/
/**
* Sets the datarecord duration. The default value is 1 second.
* ATTENTION: the argument \p duration is expressed in units of 10 microseconds.
* So, if you want to set the datarecord duration to 0.1 second, you must write a value of 10000.
* The datarecord duration must be in the range 0.001 to 60 seconds.
* This function can be used when you want to use a samplerate
* which is not an integer. For example, if you want to use a samplerate of 0.5 Hz,
* set the samplefrequency to 5 Hz and the datarecord duration to 10 seconds,
* or set the samplefrequency to 1 Hz and the datarecord duration to 2 seconds.
* This function is optional and can be called after opening a
* file in writemode and before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] duration
* Datarecord duration expressed in units of 10 microSecond.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_datarecord_duration(int handle, int duration);
/**
* Sets the datarecord duration to a very small value.
* ATTENTION: the argument \p duration is expressed in units of 1 microSecond.
* The datarecord duration must be in the range 1 to 9999 microseconds.
* This function can be used when you want to use a very high samplerate.
* For example, if you want to use a samplerate of 5 GHz,
* set the samplefrequency to 5000 Hz and the datarecord duration to 1 micro-second.
* Do not use this function if not necessary.
* This function was added to accommodate for high speed ADC's e.g. Digital Sampling Oscilloscopes
* This function is optional and can be called after opening a
* file in writemode and before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] duration
* Datarecord duration expressed in units of 1 microSecond.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_micro_datarecord_duration(int handle, int duration);
/**
* Sets the number of annotation signals. The default value is 1.
* This function is optional and can be called only after opening a file in writemode
* and before the first sample write action.
* Normally you don't need to change the default value. Only when the number of annotations
* you expect to write is more than the number of datarecords in the recording, you can use
* this function to increase the storage space for annotations.
*
* @param[in] handle
* File handle.
*
* @param[in] annot_signals
* Number of annotation signals, must be in the range 1 - 64 inclusive.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_number_of_annotation_signals(int handle, int annot_signals);
/**
* Sets the subsecond starttime expressed in units of 100 nanoseconds.
* Valid range is 0 to 9999999 inclusive. Default is 0.
* This function is optional and can be called only after opening a file in writemode
* and before the first sample write action.
* It is recommended to use a maximum resolution of not more than 100 microseconds.
* E.g. use 1234000 to set a starttime offset of 0.1234 seconds (instead of for example 1234217).
* In other words, leave the last 3 digits at zero.
*
* @param[in] handle
* File handle.
*
* @param[in] subsecond
* Subsecond starttime expressed in units of 100 nanoseconds.
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_subsecond_starttime(int handle, int subsecond);
/**
* Sets the preferred position of the annotation channel(s) before, after or in the middle of the list
* of regular signals. The default is to put them at the end (after the regular signals).
* This function is optional and can be called only after opening a file in writemode
* and before the first sample write action.
*
* @param[in] handle
* File handle.
*
* @param[in] pos
* Preferred position of the annotation channel(s):
* EDF_ANNOT_IDX_POS_START
* EDF_ANNOT_IDX_POS_MIDDLE
* EDF_ANNOT_IDX_POS_END
*
* @return
* 0 on success, otherwise -1.
*/
EDFLIB_API int edf_set_annot_chan_idx_pos(int handle, int pos);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif