Expand description
§EDF+ Library for Rust
A pure Rust library for reading and writing EDF+ (European Data Format Plus) files. This library focuses specifically on EDF+ format and provides a safe, efficient API.
§Quick Start
§Reading an EDF+ file
use edfplus::{EdfReader, EdfWriter, SignalParam, Result};
fn main() -> Result<()> {
// Open an EDF+ file
let mut reader = EdfReader::open("test_data.edf")?;
// Get file information
let header = reader.header();
println!("Number of signals: {}", header.signals.len());
println!("File duration: {:.2} seconds",
header.file_duration as f64 / 10_000_000.0);
// Read first 1000 samples from signal 0
if !header.signals.is_empty() {
let samples = reader.read_physical_samples(0, 1000)?;
println!("Read {} samples", samples.len());
}
Ok(())
}
§Creating an EDF+ file
use edfplus::{EdfWriter, SignalParam, Result};
fn main() -> Result<()> {
// Create a writer
let mut writer = EdfWriter::create("test_output.edf")?;
// Set patient information
writer.set_patient_info("P001", "M", "01-JAN-1990", "Patient Name")?;
// Define signal parameters
let signal = SignalParam {
label: "EEG Fp1".to_string(),
samples_in_file: 0, // Will be calculated automatically
physical_max: 200.0,
physical_min: -200.0,
digital_max: 32767,
digital_min: -32768,
samples_per_record: 256, // Sample rate
physical_dimension: "uV".to_string(),
prefilter: "HP:0.1Hz LP:70Hz".to_string(),
transducer: "AgAgCl cup electrodes".to_string(),
};
// Add the signal
writer.add_signal(signal)?;
// Generate and write data
let mut samples = Vec::new();
for i in 0..256 {
let t = i as f64 / 256.0;
let value = 50.0 * (2.0 * std::f64::consts::PI * 10.0 * t).sin();
samples.push(value);
}
writer.write_samples(&[samples])?;
writer.finalize()?;
Ok(())
}
§Adds an annotation/event to the EDF+ file
⚠️ CRITICAL TIMING CONSTRAINT
Annotations are only saved when their onset time falls within future data records.
Once a data record is written with write_samples()
, no new annotations can be
added to that time period.
Timing Rules:
- Add annotations BEFORE writing the data records that cover their time range
- Annotations with
onset_seconds
in already-written time periods will be silently lost - This is due to the sequential write architecture - no backtracking is possible
§Arguments
onset_seconds
- Time when the event occurred (seconds since recording start)duration_seconds
- Duration of the event in seconds (None for instantaneous events)description
- UTF-8 text describing the event (max 40 chars effective)
§Description Length Limit
Warning: Annotation descriptions are subject to EDF+ format constraints:
- Maximum effective length is 40 characters in the final TAL (Time-stamped Annotations Lists) data
- Longer descriptions will be automatically truncated during file writing
- UTF-8 multi-byte characters may be truncated at byte boundaries, potentially corrupting the text
- This limit is enforced by the EDF+ standard and matches edflib behavior
// Write 5 seconds of data (5 records)
let mut writer = EdfWriter::create("annotations.edf")?;
// ✅ Good - within file duration [0.0, 5.0)
writer.add_annotation(2.5, None, "Valid event")?;
writer.add_annotation(4.999, None, "Last moment")?;
// ❌ Lost - outside file duration
writer.add_annotation(5.0, None, "Will be discarded")?;
writer.add_annotation(6.0, None, "Also discarded")?;
for i in 0..5 {
let samples = vec![0.0; 256];
writer.write_samples(&[samples])?;
}
§Working with Signal Data
§Physical vs Digital Values
EDF+ stores data as 16-bit integers but represents real-world measurements. The library automatically handles conversion between digital and physical values:
use edfplus::SignalParam;
let signal = SignalParam {
label: "Test Signal".to_string(),
samples_in_file: 1000,
physical_max: 100.0, // +100 µV
physical_min: -100.0, // -100 µV
digital_max: 32767, // +32767 (16-bit max)
digital_min: -32768, // -32768 (16-bit min)
samples_per_record: 256,
physical_dimension: "uV".to_string(),
prefilter: "".to_string(),
transducer: "".to_string(),
};
// Convert digital to physical
let digital_value = 16384; // Half of max digital value
let physical_value = signal.to_physical(digital_value);
assert!((physical_value - 50.0).abs() < 0.1); // Should be ~50 µV
// Convert physical to digital
let physical_input = 25.0; // 25 µV
let digital_output = signal.to_digital(physical_input);
assert!((digital_output - 8192).abs() <= 1); // Should be ~8192
Re-exports§
pub use error::EdfError;
pub use error::Result;
pub use types::EdfHeader;
pub use types::SignalParam;
pub use types::Annotation;
pub use reader::EdfReader;
pub use writer::EdfWriter;
Modules§
Constants§
Functions§
- version
- Library version