Skip to content

EDF / EDF+ / BDF / BDF+

EDF and BDF store polygraphic time-series data in a fixed-width header followed by fixed-size data records. EDF stores signed 16-bit samples; BDF uses the same header geometry and stores signed 24-bit samples. EDF+ and BDF+ add structured identification fields, standardized labels, annotations, and continuous/discontinuous timeline markers.

The word signal in the format specifications means one channel in a data record.

Source Authority and scope
EDF specification Base EDF header and data-record layout
EDF+ specification Normative EDF+ differences, structured fields, TAL grammar, and time keeping
EDF+ standard texts Obligatory EDF+ signal labels, dimensions, polarity rules, and annotation texts
EDF programming guidelines Explicitly non-normative implementation guidance and clarifications
BioSemi BDF description Base BDF identification, 24-bit sample layout, and BioSemi header profile
BDF+ format description BDF+ differences from EDF+; the author states that this description is not affiliated with BioSemi

Rules attributed to the programming guidelines are recommendations or clarifications rather than additions to the normative EDF/EDF+ specifications.

Dialect Extension Version bytes Main reserved prefix Ordinary sample
EDF .edf / .EDF 30 20 20 20 20 20 20 20 Free local text or spaces signed 16-bit little-endian
EDF+C .edf / .EDF 30 20 20 20 20 20 20 20 EDF+C signed 16-bit little-endian
EDF+D .edf / .EDF 30 20 20 20 20 20 20 20 EDF+D signed 16-bit little-endian
BDF .bdf / .BDF FF 42 49 4F 53 45 4D 49 24BIT in the BioSemi profile signed 24-bit little-endian
BDF+C .bdf / .BDF FF 42 49 4F 53 45 4D 49 BDF+C signed 24-bit little-endian
BDF+D .bdf / .BDF FF 42 49 4F 53 45 4D 49 BDF+D signed 24-bit little-endian

C means that every data record begins exactly where the preceding record ends. D permits gaps between data records. A base EDF reader can frame EDF+D bytes but presents the records as continuous and therefore loses the gaps. BDF+ applies the same compatibility model to BDF readers.

The BioSemi acquisition profile for base BDF places a Trigger/Status channel last.

A file contains:

  1. one main 256-byte header;
  2. ten banded per-signal header arrays totaling 256 * signal_count bytes;
  3. record_count fixed-size data records.

The total header size is

header_bytes = 256 * (signal_count + 1)

Within every data record, channels are stored channel-major: all samples of channel 0, followed by all samples of channel 1, and so on. Channels are not interleaved sample-by-sample.

Header fields have fixed byte widths and do not contain terminators. Content is left-justified and unused bytes are filled with ASCII space 0x20. A value must not spill into the following field. The format does not define a safe semantic truncation rule for over-width structured or numeric values.

EDF+ requires printable US-ASCII 0x20..0x7E throughout the header. BDF+ inherits this rule except for the first version byte, which is 0xFF. UTF-8 is used only for annotation text in data records.

Header numbers:

  • use no digit grouping;
  • use . rather than , as the decimal separator;
  • use a leading - for negative values;
  • may use scientific E notation according to the non-normative programming guidelines;
  • remain plain integer strings in fields whose value is defined as an integer.

TAL Onset and Duration fields use a stricter grammar and never permit E notation.

Offset Size Field Content
0 8 version EDF bytes 0 or BDF bytes 0xFFBIOSEMI
8 80 patient_id Local patient identification
88 80 recording_id Local recording identification
168 8 start_date dd.mm.yy
176 8 start_time hh.mm.ss
184 8 header_bytes Total header length
192 44 reserved Base-format local value or plus dialect tag
236 8 record_count Number of data records
244 8 record_duration Duration of one data record in seconds
252 4 signal_count Number of channels, including annotations signals

EDF, EDF+C, and EDF+D use the eight ASCII bytes "0 ".

BDF, BDF+C, and BDF+D use one non-ASCII byte 0xFF followed by the seven ASCII bytes BIOSEMI:

FF 42 49 4F 53 45 4D 49

Base EDF and base BDF treat the 80-byte field as local text.

EDF+ requires four leading space-separated subfields:

  1. patient administration code;
  2. sex, F or M;
  3. birth date in dd-MMM-yyyy form;
  4. patient name.

Additional local subfields may follow. BDF+ describes the same layout as five conceptual parts by naming the trailing content additional info.

Month names are uppercase English abbreviations:

JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC

Subfields contain no spaces. Internal spaces are replaced with another character such as _. Unknown, inapplicable, or anonymized subfields are represented by X.

Examples:

MCH-0234567 F 02-MAY-1951 Haagse_Harry
X X X X

Base EDF and base BDF treat the 80-byte field as local text.

EDF+ and BDF+ begin with:

Startdate <dd-MMM-yyyy> <administration-code> <technician-code> <equipment-code>

Additional recording information may follow. Unknown fields use X:

Startdate X X X X

Spaces inside individual codes are replaced with another character.

start_date uses dd.mm.yy. EDF+ defines the two-digit year window:

  • 85..99 means 1985..1999;
  • 00..84 means 2000..2084;
  • after 2084, the last two bytes are the literal yy and the four-digit date comes from recording_id.

start_time uses hh.mm.ss and contains local wall-clock time at the patient’s location. The format stores no timezone identifier or UTC offset.

For plus dialects, start_date and start_time identify the absolute whole second containing the beginning of the first data record. The first time-keeping TAL carries any fractional offset within that second.

This ASCII integer must equal:

256 * (signal_count + 1)

The first five bytes select a plus dialect:

Prefix Meaning
EDF+C Continuous EDF+
EDF+D Discontinuous EDF+
BDF+C Continuous BDF+
BDF+D Discontinuous BDF+

The remaining 39 bytes of a plus header are spaces. Base EDF permits local text in this field. The BioSemi base BDF profile uses 24BIT.

This field is an ASCII integer. Base EDF and BDF use -1 when the count is unknown. Plus dialects permit -1 only while a recording is being written; a closed plus file contains the actual non-negative record count.

This field is an ASCII number of seconds.

Base EDF recommends a whole number of seconds and a maximum data-record size of 61,440 bytes. A duration below one second is recommended when a one-second record would exceed that size.

EDF+:

  • makes the 61,440-byte record limit mandatory;
  • permits a positive duration below one second without the base EDF qualification;
  • permits zero only for an annotations-only file or for the EDF+D case where every ordinary channel contributes one sample per record at arbitrary record times.

BDF+ permits any representable duration greater than zero and sets a 15 MB maximum data-record size.

This ASCII integer counts every ordinary and annotations channel. Annotations channels occupy normal entries in all ten per-signal header bands and in every data record.

The per-signal header is banded rather than interleaved. All labels come first, then all transducer fields, and so on.

# Base offset Width per signal Field
1 256 16 label[i]
2 256 + 16 * ns 80 transducer[i]
3 256 + 96 * ns 8 physical_dim[i]
4 256 + 104 * ns 8 physical_min[i]
5 256 + 112 * ns 8 physical_max[i]
6 256 + 120 * ns 8 digital_min[i]
7 256 + 128 * ns 8 digital_max[i]
8 256 + 136 * ns 80 prefiltering[i]
9 256 + 216 * ns 8 samples_per_record[i]
10 256 + 224 * ns 32 signal_reserved[i]

The byte following band 10 is 256 + 256 * ns and therefore equals header_bytes.

The 16-byte label identifies a channel. EDF+ standard texts use:

<signal-type> <specification>

Examples include ECG II, EEG Fpz-Cz, Resp nasal, and Temp rectal.

The exact labels EDF Annotations and BDF Annotations are reserved for their corresponding plus annotation channels. They must not be used by ordinary channels.

The 80-byte field describes the sensor, such as AgAgCl electrode or thermistor. It is all spaces for an annotations channel.

The 8-byte field contains the physical unit: an optional SI prefix followed by a base unit. ECG channels normally use V, mV, or uV.

The prefix multiplies the base unit by a power of ten. Case is significant, so M is mega while m is milli:

Prefix Power Name
K 3 kilo
m -3 milli
u -6 micro
n -9 nano

These are the prefixes seen in ECG dimensions; the EDF+ standard texts define the full SI set. The micro prefix is the ASCII letter u, not the non-ASCII micro sign, because header bytes stay in printable US-ASCII. Powers use ^. Evaluation order is prefix, then power, then multiplication, then division, so Km^2 is (1000 m)^2, not 1000 * m^2.

An uncalibrated ordinary channel uses spaces. An annotations channel also uses spaces.

These ASCII numbers identify the physical values corresponding to digital_min and digital_max. They must differ. A negative-gain channel uses physical_max < physical_min; the digital extrema remain ordered.

Annotations channels may use any two distinct representable values. -1 and 1 are conventional.

These fields are ASCII integers and must satisfy:

digital_max > digital_min

Ordinary EDF channels use values representable by signed 16-bit samples, -32768..32767. Ordinary BDF channels use values representable by signed 24-bit samples, -8388608..8388607.

Annotations-channel extrema are fixed:

Dialect digital_min digital_max
EDF+ -32768 32767
BDF+ -8388608 8388607

This 80-byte field describes filters and relevant acquisition processing. EDF+ recommends forms such as:

HP:0.1Hz LP:75Hz N:50Hz

It is all spaces for an annotations channel.

For an ordinary channel, this positive ASCII integer is the number of stored samples in every data record. For positive record duration:

sample_rate[i] = samples_per_record[i] / record_duration

Different channels may have different values and therefore different sample rates.

For an annotations channel, the field reserves character storage:

Dialect Bytes available in one data record
EDF+ 2 * samples_per_record
BDF+ 3 * samples_per_record

The nominal 2-byte or 3-byte sample grouping has no meaning to the TAL parser; annotation characters remain in byte order.

Plus dialects fill this 32-byte field with spaces. Annotation channels also require spaces.

For record record_index:

channel[0][0 .. samples_per_record[0] - 1]
channel[1][0 .. samples_per_record[1] - 1]
...
channel[ns - 1][0 .. samples_per_record[ns - 1] - 1]

Record size is:

EDF record bytes = 2 * sum(samples_per_record[i])
BDF record bytes = 3 * sum(samples_per_record[i])

The body starts at header_bytes. For a known record count, its expected size is record_count * record_bytes.

Each ordinary sample is a signed 16-bit two’s-complement integer in little-endian byte order:

least-significant byte, most-significant byte

Each ordinary sample is a signed 24-bit two’s-complement integer in little-endian byte order:

bits 0..7, bits 8..15, bits 16..23

The stored range is -8388608..8388607.

Samples of one ordinary channel are equally spaced inside each data record. The first sample occurs at the onset given by that record’s time-keeping TAL.

In a +C file, record k + 1 begins at:

onset[k] + record_duration

In a +D file, the next onset may be later. Records remain in temporal order in the file.

The four extrema define an affine map:

gain = (physical_max - physical_min)
/ (digital_max - digital_min)
physical = physical_min
+ (digital - digital_min) * gain

The inverse used by an encoder is:

digital = round(
(physical - physical_min) / gain + digital_min
)

The stored digital value must remain inside the declared digital range. Negative gain is represented by reversed physical extrema, not by reversed digital extrema.

EDF+ and BDF+ both require at least one annotations channel even when there are no user annotations. The channel exists because the first annotations signal stores the onset of every data record.

Required header values:

Field EDF+ BDF+
label EDF Annotations BDF Annotations
transducer spaces spaces
physical_dim spaces spaces
physical_min/max distinct values distinct values
digital_min -32768 -8388608
digital_max 32767 8388607
prefiltering spaces spaces
signal_reserved spaces spaces

The labels shown include the trailing space that fills the 16-byte field.

Multiple annotations channels are permitted by the formats. The first one is the time-keeping channel.

An annotations buffer contains Time-stamped Annotation Lists:

TAL = Onset (0x15 Duration)? 0x14
(Annotation 0x14)* 0x00
Onset = ("+" | "-") Digits ("." Digits)?
Duration = Digits ("." Digits)?
Digits = digit { digit }
Annotation = UTF-8 text

Control bytes:

Byte Meaning
0x14 Ends the timestamp and each annotation
0x15 Separates Onset from Duration
0x00 Terminates the TAL and fills unused bytes

Onset always begins with + or -. Duration has no sign. Neither field permits exponent notation. Fractional precision is arbitrary within the available annotation bytes.

Annotation text uses UTF-8. Bytes 0x14 and 0x00 cannot occur inside the text. Other C0 controls are forbidden except TAB 0x09, LF 0x0A, and CR 0x0D.

A recurring event uses the same annotation text on every occurrence so that software can group its instances; distinct events use distinct texts.

A TAL, including its terminating 0x00, fits entirely in one data record. Unused annotation bytes are 0x00.

An atrial-fibrillation episode of 8.2 s starting 12.5 s after file start carries an onset, a duration, and one annotation:

"+12.5" 0x15 "8.2" 0x14 "Atrial fibrillation" 0x14 0x00

An instantaneous boundary marker omits the duration:

"+0" 0x14 "Recording starts" 0x14 0x00

An event before the record start uses a negative onset:

"-0.5" 0x15 "3.0" 0x14 "Asystole" 0x14 0x00

Annotations sharing one timestamp pack into a single TAL; here one event is bound to two leads with @@:

"+12.5" 0x15 "8.2" 0x14 "Atrial fibrillation@@ECG II" 0x14 "Atrial fibrillation@@ECG V1" 0x14 0x00

The first TAL of the first annotations channel starts at byte zero in every data record. Its first annotation is empty:

Onset 0x14 0x14 0x00

Onset is the number of seconds from the whole-second start_date/start_time reference to the first ordinary sample in that record.

The first record begins at +0.X, where X is its fractional offset inside the header’s whole second. When X is zero, +0 is valid.

If a record has no ordinary channels, the same TAL contains a non-empty annotation immediately after the empty time-keeping annotation to name the event defining the record onset.

EDF+ requires an annotation related to information in one particular data record to be stored in that record. A pre-interval event still follows the record’s time-keeping TAL.

BDF+ explicitly relaxes this placement rule: a record-related annotation need not be stored in the related record. All other TAL framing and time-keeping rules remain applicable.

An event spanning several data records is annotated once, in the record that contains its onset, and is not repeated in the following records.

EDF+C and BDF+C require contiguous data records. Their time-keeping TALs still exist and state every record onset.

EDF+D and BDF+D permit gaps. The gap after record k is:

onset[k + 1] - onset[k] - record_duration

for positive record duration. The onset sequence, rather than record index alone, defines absolute time in a discontinuous file.

Within a continuous segment, data records remain adjacent. A gap begins a separate segment.

EDF+ standard texts are obligatory for EDF+ and inherited by BDF+ except where the BDF+ description specifies a difference.

An ECG channel label uses ECG followed by a recognized lead:

ECG I ECG II ECG III
ECG aVR ECG aVL ECG aVF ECG -aVR
ECG V1 ECG V2 ECG V3 ECG V4 ECG V5 ECG V6
ECG V2R ECG V3R ECG V4R
ECG V7 ECG V8 ECG V9
ECG X ECG Y ECG Z

The complete value is left-justified and space-padded to 16 bytes.

Lead semantics:

  • I, II, III are the bipolar limb leads;
  • aVR, aVL, aVF are the augmented limb leads;
  • -aVR is the inverted augmented-vector lead;
  • V1..V6 are the standard precordial leads;
  • V2R, V3R, V4R are the right-sided precordial leads;
  • V7, V8, V9 are the posterior precordial leads;
  • X, Y, Z are the orthogonal Frank leads.

For EEG, EOG, EMG, and other electrode-pair labels, the physical value is the potential at the first electrode minus the potential at the second electrode. When a reference cannot be represented accurately within the label, standard texts use Ref, Ref1, and so on.

The EDF+ standard-text page defines annotation strings that plus software can recognize automatically. Cardiac entries include:

  • Sinus Tachycardia;
  • WC tachycardia;
  • NC tachycardia;
  • Bradycardia;
  • Asystole;
  • Atrial fibrillation.

Recording starts and Recording ends are instantaneous boundary annotations. Other standard-text entries in the table, including the cardiac entries above, carry a Duration.

An annotation can target a channel by appending @@ and the exact unpadded standard channel label:

Atrial fibrillation@@ECG II

A strict validator checks the following before interpreting samples:

  • the first eight bytes identify EDF or BDF;
  • all header bytes except the BDF 0xFF version byte are printable US-ASCII for a plus file;
  • header_bytes == 256 * (signal_count + 1);
  • every fixed-width band is present;
  • signal_count, record_count, and every samples_per_record are valid integers;
  • record_duration is valid for the selected dialect;
  • the reserved prefix matches the version family and is exactly a supported +C or +D tag for plus files;
  • ordinary digital extrema are ordered and fit the selected 16-bit or 24-bit sample width;
  • physical extrema differ;
  • record size uses the selected 2-byte or 3-byte sample width and respects the dialect limit;
  • an EDF+ file contains EDF Annotations and a BDF+ file contains BDF Annotations;
  • annotation-channel digital extrema and blank fields match the dialect;
  • every record begins its first annotations channel with a valid time-keeping TAL;
  • no TAL crosses a record boundary;
  • every TAL and UTF-8 annotation obeys the control-byte rules;
  • the body contains the declared number of complete records;
  • +C onsets are contiguous and +D onsets remain in temporal order.