/* ***************************************************************************** * * 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