Skip to content

Index

EDF and EDF+ recordings: their header, their annotations, and the rule that pseudonymises a header.

Every reader of an EDF header in the platform uses this package, so a recording is read the same way wherever it is read.

EDF_ANNOTATIONS_LABEL = 'EDF Annotations' module-attribute

The label EDF+ gives a signal that holds annotations rather than samples.

EDF_FIXED_HEADER_BYTES = 256 module-attribute

The size of the fixed part of an EDF header, in bytes.

EDF_UNKNOWN_START_DATE = date(1985, 1, 1) module-attribute

EDF+'s marker for an unknown or removed start date, 01.01.85 in the header.

EDF_VARIABLE_HEADER_SIZE_PER_SIGNAL = 256 module-attribute

The size of the header part each signal adds, in bytes.

NO_HEADER_DATE_YEAR = 1900 module-attribute

The year the header parser gives a recording whose header carries no usable date.

PSEUDONYMOUS_PATIENT = 'X X X X' module-attribute

The local patient identification after the rule: every EDF+ subfield unknown.

REMOVED_START_DATE = '01.01.85' module-attribute

The start date field after the rule, when dates are removed.

NotAnEdfRecording

Bases: ValueError

The file does not start with a whole EDF header.

get_validated_edf_headers(fixed_header, signal_headers)

Validate a parsed header and return the recording it describes.

The fixed header is refused if its version is not 0, if it marks an interrupted recording (EDF+D), or if its header size does not match its number of signals. A signal is refused if its number of samples per record is not a positive multiple of the record duration.

Parameters:

Name Type Description Default
fixed_header dict[str, Any]

The fixed header, as :func:parse_edf_fixed_header reads it.

required
signal_headers list[dict[str, Any]]

The signal headers, as :func:parse_edf_signal_headers reads them.

required

Returns:

Type Description
RecordingHeader

The recording's length, start and channels.

Raises:

Type Description
ValueError

If the header is refused.

header_sha256(data)

Return the SHA-256 of the EDF header at the start of data.

The header is the fixed header and every signal header; any bytes after it are ignored.

Raises:

Type Description
ValueError

If data does not start with an EDF header, or ends inside it.

is_pseudonymised(fixed_header, *, keep_dates)

Whether an EDF fixed header already follows the pseudonymisation rule.

Parameters:

Name Type Description Default
fixed_header bytes

The first 256 bytes of an EDF or EDF+ file.

required
keep_dates bool

Whether the rule keeps the start date.

required

Returns:

Type Description
bool

True if applying the rule would change nothing.

Raises:

Type Description
ValueError

If fixed_header is not the fixed header of an EDF file.

parse_edf_annotations(readable, n_signals, n_records, record_duration_seconds, samples_per_record, annotation_signal_indices)

Read the annotations of an EDF+ recording's data records.

Only the annotation signals are read: the reader seeks past every other signal. Parsing stops once :data:MAX_ANNOTATION_DENSITY annotations per second of recording have been read, and never before 100.

Parameters:

Name Type Description Default
readable SeekableReadable

The recording, positioned at the start of its data records.

required
n_signals int

The number of signals, annotation signals included.

required
n_records int

The number of data records.

required
record_duration_seconds float

The duration of one data record.

required
samples_per_record list[int]

Each signal's samples per data record.

required
annotation_signal_indices list[int]

The indices of the annotation signals.

required

Returns:

Type Description
list[tuple[int, int, str, str | None]]

(start_ms, end_ms, label, original_text) for every annotation whose

list[tuple[int, int, str, str | None]]

onset is at least 0 and at most :data:MAX_ANNOTATION_SECONDS, with

list[tuple[int, int, str, str | None]]

its duration capped at the same. label is the matching event label, or

list[tuple[int, int, str, str | None]]

"Unknown"; original_text is the text as written when nothing

list[tuple[int, int, str, str | None]]

matched, else None.

parse_edf_fixed_header(header_bytes)

Parse the 256-byte fixed part of an EDF header.

Parameters:

Name Type Description Default
header_bytes bytes

The first 256 bytes of an EDF file.

required

Returns:

Type Description
dict[str, Any]

The fields by name, as :data:EDF_FIXED_HEADER_SPEC reads them.

Raises:

Type Description
ValueError

If header_bytes is not 256 bytes long, or a numeric field is not a number.

parse_edf_signal_headers(header_bytes)

Parse the per-signal part of an EDF header.

The part stores each field for every signal in turn: all labels, then all transducers, and so on.

Parameters:

Name Type Description Default
header_bytes bytes

The 256 bytes per signal that follow the fixed header.

required

Returns:

Type Description
list[dict[str, Any]]

One dictionary of fields per signal, as :data:EDF_VARIABLE_HEADER_SPEC

list[dict[str, Any]]

reads them.

Raises:

Type Description
ValueError

If header_bytes is not a multiple of 256 bytes long, or a numeric field is not a number.

parse_edf_start_date(start_date)

Parse the dd.mm.yy start date field of an EDF header.

The two-digit year is read as the most recent year that is not in the future: 68 is 1968, not 2068. 01.01.85 therefore parses as 1985-01-01, which is also EDF+'s marker for an unknown date; the two are spelled identically, so the parser cannot tell them apart and does not try. :data:EDF_UNKNOWN_START_DATE lets a reader recognise it.

Parameters:

Name Type Description Default
start_date str

The field's text, e.g. "12.10.26".

required

Returns:

Type Description
date | None

The date, or None if the field is not a valid date.

parse_edf_start_time(start_time)

Parse the hh.mm.ss start time field of an EDF header.

Parameters:

Name Type Description Default
start_time str

The field's text, e.g. "22.30.00".

required

Returns:

Type Description
time | None

The time, or None if the field is not a valid time.

pseudonymise_file_header(path, *, keep_dates)

Apply the pseudonymisation rule to the header of an EDF file, in place.

Only the first 256 bytes are read, and they are written back only if the rule changes them.

Parameters:

Name Type Description Default
path Path

The EDF or EDF+ file.

required
keep_dates bool

Keep the start date, or replace it with EDF+'s marker for an unknown date.

required

Returns:

Type Description
bool

True if the header was changed.

Raises:

Type Description
ValueError

If the file does not start with the fixed header of an EDF file.

pseudonymise_header(fixed_header, *, keep_dates)

Apply the pseudonymisation rule to an EDF fixed header.

Parameters:

Name Type Description Default
fixed_header bytes

The first 256 bytes of an EDF or EDF+ file.

required
keep_dates bool

Keep the start date, or replace it with EDF+'s marker for an unknown date.

required

Returns:

Type Description
bytes

The rewritten 256 bytes.

Raises:

Type Description
ValueError

If fixed_header is not the 256-byte fixed header of an EDF file with version 0.

read_edf_raw_headers_from_readable(readable)

Read and parse an EDF header from the start of a file or stream.

Reads the header only, leaving readable at the start of the data records.

Parameters:

Name Type Description Default
readable Readable

The file or stream, positioned at its first byte.

required

Returns:

Type Description
tuple[dict[str, Any], list[dict[str, Any]]]

The fixed header and the signal headers.

Raises:

Type Description
ValueError

If either part cannot be parsed.

read_edf_recording_header_from_readable(readable)

Read, parse and validate an EDF header from the start of a file or stream.

Parameters:

Name Type Description Default
readable Readable

The file or stream, positioned at its first byte.

required

Returns:

Type Description
RecordingHeader

The recording's length, start and channels.

Raises:

Type Description
ValueError

If the header cannot be parsed or is refused.

read_recording(path, *, annotation_text)

Read what an ingest reports about the EDF or EDF+ file at path.

Parameters:

Name Type Description Default
path Path

The file, as the container sees it: after the rule.

required
annotation_text bool

Keep the text of annotations that match no label.

required

Annotations that are malformed, or outside the recording, are left out; only the header can make a file unreadable.

Raises:

Type Description
NotAnEdfRecording

If the file does not start with a whole EDF header.

validate_edf_recording(readable, full_file_size)

Validate the start of an EDF recording against the size of the whole file.

The header must parse, must be valid, and must account for the whole file: its size plus two bytes per sample of every data record.

Parameters:

Name Type Description Default
readable Readable

The start of the recording, at least its full header, positioned at its first byte.

required
full_file_size int

The size of the whole file in bytes.

required

Returns:

Type Description
RecordingHeader

The recording's length, start and channels.

Raises:

Type Description
ValueError

If the file is too small, its header cannot be parsed or is refused, or its size does not match the header.