Task 170: .EDF File Format
Task 170: .EDF File Format
File Format Specifications for .EDF
The .EDF (European Data Format) is a standard for storing multichannel biological and physical signals, such as EEG or ECG data. It consists of a header record followed by data records containing the signal samples. The header is entirely in ASCII format, with fields padded with spaces. Data records contain 2-byte integers (samples) in two's complement format. The specifications are detailed below, based on the official documentation.
List of all properties of the .EDF file format intrinsic to its file system
These properties refer to the fields in the header record, which define the file's structure, metadata, and signal characteristics. The header is divided into a fixed part (256 bytes) and a variable part (256 bytes per signal). All fields are ASCII strings unless noted, and numerical values are stored as ASCII for easy readability. Here's the comprehensive list:
- Version of data format: 8 bytes, ASCII string representing the format version (typically "0").
- Local patient identification: 80 bytes, ASCII string for patient details (e.g., code, sex, birthdate, name).
- Local recording identification: 80 bytes, ASCII string for recording details (e.g., start date, administration code, technician, equipment).
- Start date of recording: 8 bytes, ASCII string in "dd.mm.yy" format.
- Start time of recording: 8 bytes, ASCII string in "hh.mm.ss" format.
- Number of bytes in header record: 8 bytes, ASCII integer representing the total header size (must be 256 + (ns * 256), where ns is the number of signals).
- Reserved: 44 bytes, ASCII string (unused in standard EDF; filled with spaces).
- Number of data records: 8 bytes, ASCII integer (-1 if unknown).
- Duration of a data record: 8 bytes, ASCII floating-point number in seconds (typically an integer like "1").
- Number of signals (ns): 4 bytes, ASCII integer representing the number of signals in each data record.
For each of the ns signals (repeated ns times, 256 bytes each):
- Signal label: 16 bytes, ASCII string (e.g., "EEG Fpz-Cz" or "Body temp").
- Transducer type: 80 bytes, ASCII string (e.g., "AgAgCl electrode").
- Physical dimension: 8 bytes, ASCII string (e.g., "uV" or "degreeC").
- Physical minimum: 8 bytes, ASCII floating-point number (e.g., "-500").
- Physical maximum: 8 bytes, ASCII floating-point number (e.g., "500").
- Digital minimum: 8 bytes, ASCII integer (e.g., "-2048"; corresponds to the A/D converter's min value).
- Digital maximum: 8 bytes, ASCII integer (e.g., "2047"; corresponds to the A/D converter's max value).
- Prefiltering: 80 bytes, ASCII string (e.g., "HP:0.1Hz LP:75Hz").
- Number of samples in each data record: 8 bytes, ASCII integer (samples per signal per data record).
- Reserved (per signal): 32 bytes, ASCII string (unused; filled with spaces).
Two direct download links for .EDF files
- https://physionet.org/files/sleep-edfx/1.0.0/sleep-cassette/SC4001E0-PSG.edf
- https://physionet.org/files/sleep-edfx/1.0.0/sleep-cassette/SC4002E0-PSG.edf
Ghost blog embedded HTML JavaScript for drag-and-drop .EDF file parsing
This is an embeddable HTML snippet with JavaScript that can be placed in a blog post (e.g., on Ghost CMS). It creates a drag-and-drop area where users can drop an .EDF file. The script parses the header and displays all properties on the screen in a readable format. It uses the FileReader API to read the file as an ArrayBuffer and extracts ASCII strings.
Python class for .EDF file handling
This class can open an .EDF file, decode and read the header properties, print them to the console, and write a new .EDF file (header only, with placeholder data records if needed). For simplicity, writing assumes empty data records; extend for full data support.
class EDFHandler:
def __init__(self, filename=None):
self.properties = {}
self.signals = []
if filename:
self.read(filename)
def read(self, filename):
with open(filename, 'rb') as f:
header = f.read(256)
offset = 0
def read_ascii(len_):
nonlocal offset
val = header[offset:offset + len_].decode('ascii').strip()
offset += len_
return val
self.properties['version'] = read_ascii(8)
self.properties['patient_id'] = read_ascii(80)
self.properties['recording_id'] = read_ascii(80)
self.properties['start_date'] = read_ascii(8)
self.properties['start_time'] = read_ascii(8)
self.properties['header_bytes'] = int(read_ascii(8))
self.properties['reserved'] = read_ascii(44)
self.properties['num_data_records'] = int(read_ascii(8))
self.properties['duration'] = float(read_ascii(8))
self.properties['num_signals'] = int(read_ascii(4))
signal_header = f.read(256 * self.properties['num_signals'])
offset = 0
for _ in range(self.properties['num_signals']):
def read_ascii_sig(len_):
nonlocal offset
val = signal_header[offset:offset + len_].decode('ascii').strip()
offset += len_
return val
sig = {
'label': read_ascii_sig(16),
'transducer': read_ascii_sig(80),
'phys_dim': read_ascii_sig(8),
'phys_min': float(read_ascii_sig(8)),
'phys_max': float(read_ascii_sig(8)),
'dig_min': int(read_ascii_sig(8)),
'dig_max': int(read_ascii_sig(8)),
'prefiltering': read_ascii_sig(80),
'nr_samples': int(read_ascii_sig(8)),
'reserved_signal': read_ascii_sig(32)
}
self.signals.append(sig)
def print_properties(self):
for key, val in self.properties.items():
print(f"{key.capitalize().replace('_', ' ')}: {val}")
for idx, sig in enumerate(self.signals):
print(f"\nSignal {idx + 1}:")
for key, val in sig.items():
print(f" {key.capitalize().replace('_', ' ')}: {val}")
def write(self, filename, data_records=b''): # data_records is optional byte string for data
header = (
self.properties['version'].ljust(8) +
self.properties['patient_id'].ljust(80) +
self.properties['recording_id'].ljust(80) +
self.properties['start_date'].ljust(8) +
self.properties['start_time'].ljust(8) +
str(self.properties['header_bytes']).ljust(8) +
self.properties['reserved'].ljust(44) +
str(self.properties['num_data_records']).ljust(8) +
str(self.properties['duration']).ljust(8) +
str(self.properties['num_signals']).ljust(4)
).encode('ascii')
signal_header = b''
for sig in self.signals:
signal_header += (
sig['label'].ljust(16) +
sig['transducer'].ljust(80) +
sig['phys_dim'].ljust(8) +
str(sig['phys_min']).ljust(8) +
str(sig['phys_max']).ljust(8) +
str(sig['dig_min']).ljust(8) +
str(sig['dig_max']).ljust(8) +
sig['prefiltering'].ljust(80) +
str(sig['nr_samples']).ljust(8) +
sig['reserved_signal'].ljust(32)
).encode('ascii')
with open(filename, 'wb') as f:
f.write(header + signal_header + data_records)
# Example usage:
# edf = EDFHandler('example.edf')
# edf.print_properties()
# edf.write('output.edf')
Java class for .EDF file handling
This class can open an .EDF file, decode and read the header properties, print them to the console, and write a new .EDF file (header only, with placeholder data). It uses RandomAccessFile
for reading/writing.
import java.io.*;
import java.nio.charset.StandardCharsets;
public class EDFHandler {
private String version;
private String patientId;
private String recordingId;
private String startDate;
private String startTime;
private int headerBytes;
private String reserved;
private int numDataRecords;
private double duration;
private int numSignals;
private Signal[] signals;
static class Signal {
String label;
String transducer;
String physDim;
double physMin;
double physMax;
int digMin;
int digMax;
String prefiltering;
int nrSamples;
String reservedSignal;
}
public EDFHandler(String filename) throws IOException {
read(filename);
}
private void read(String filename) throws IOException {
try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
byte[] header = new byte[256];
raf.readFully(header);
int offset = 0;
version = new String(header, offset, 8, StandardCharsets.ASCII).trim(); offset += 8;
patientId = new String(header, offset, 80, StandardCharsets.ASCII).trim(); offset += 80;
recordingId = new String(header, offset, 80, StandardCharsets.ASCII).trim(); offset += 80;
startDate = new String(header, offset, 8, StandardCharsets.ASCII).trim(); offset += 8;
startTime = new String(header, offset, 8, StandardCharsets.ASCII).trim(); offset += 8;
headerBytes = Integer.parseInt(new String(header, offset, 8, StandardCharsets.ASCII).trim()); offset += 8;
reserved = new String(header, offset, 44, StandardCharsets.ASCII).trim(); offset += 44;
numDataRecords = Integer.parseInt(new String(header, offset, 8, StandardCharsets.ASCII).trim()); offset += 8;
duration = Double.parseDouble(new String(header, offset, 8, StandardCharsets.ASCII).trim()); offset += 8;
numSignals = Integer.parseInt(new String(header, offset, 4, StandardCharsets.ASCII).trim());
byte[] signalHeader = new byte[256 * numSignals];
raf.readFully(signalHeader);
offset = 0;
signals = new Signal[numSignals];
for (int i = 0; i < numSignals; i++) {
signals[i] = new Signal();
signals[i].label = new String(signalHeader, offset, 16, StandardCharsets.ASCII).trim(); offset += 16;
signals[i].transducer = new String(signalHeader, offset, 80, StandardCharsets.ASCII).trim(); offset += 80;
signals[i].physDim = new String(signalHeader, offset, 8, StandardCharsets.ASCII).trim(); offset += 8;
signals[i].physMin = Double.parseDouble(new String(signalHeader, offset, 8, StandardCharsets.ASCII).trim()); offset += 8;
signals[i].physMax = Double.parseDouble(new String(signalHeader, offset, 8, StandardCharsets.ASCII).trim()); offset += 8;
signals[i].digMin = Integer.parseInt(new String(signalHeader, offset, 8, StandardCharsets.ASCII).trim()); offset += 8;
signals[i].digMax = Integer.parseInt(new String(signalHeader, offset, 8, StandardCharsets.ASCII).trim()); offset += 8;
signals[i].prefiltering = new String(signalHeader, offset, 80, StandardCharsets.ASCII).trim(); offset += 80;
signals[i].nrSamples = Integer.parseInt(new String(signalHeader, offset, 8, StandardCharsets.ASCII).trim()); offset += 8;
signals[i].reservedSignal = new String(signalHeader, offset, 32, StandardCharsets.ASCII).trim(); offset += 32;
}
}
}
public void printProperties() {
System.out.println("Version: " + version);
System.out.println("Patient ID: " + patientId);
System.out.println("Recording ID: " + recordingId);
System.out.println("Start Date: " + startDate);
System.out.println("Start Time: " + startTime);
System.out.println("Header Bytes: " + headerBytes);
System.out.println("Reserved: " + reserved);
System.out.println("Num Data Records: " + numDataRecords);
System.out.println("Duration: " + duration);
System.out.println("Num Signals: " + numSignals);
for (int i = 0; i < numSignals; i++) {
System.out.println("\nSignal " + (i + 1) + ":");
System.out.println(" Label: " + signals[i].label);
System.out.println(" Transducer: " + signals[i].transducer);
System.out.println(" Physical Dimension: " + signals[i].physDim);
System.out.println(" Physical Min: " + signals[i].physMin);
System.out.println(" Physical Max: " + signals[i].physMax);
System.out.println(" Digital Min: " + signals[i].digMin);
System.out.println(" Digital Max: " + signals[i].digMax);
System.out.println(" Prefiltering: " + signals[i].prefiltering);
System.out.println(" Num Samples: " + signals[i].nrSamples);
System.out.println(" Reserved: " + signals[i].reservedSignal);
}
}
public void write(String filename, byte[] dataRecords) throws IOException { // dataRecords optional
try (RandomAccessFile raf = new RandomAccessFile(filename, "rw")) {
String headerStr = String.format("%-8s%-80s%-80s%-8s%-8s%-8s%-44s%-8s%-8s%-4s",
version, patientId, recordingId, startDate, startTime,
headerBytes, reserved, numDataRecords, duration, numSignals);
raf.write(headerStr.getBytes(StandardCharsets.ASCII));
for (Signal sig : signals) {
String sigStr = String.format("%-16s%-80s%-8s%-8s%-8s%-8s%-8s%-80s%-8s%-32s",
sig.label, sig.transducer, sig.physDim, sig.physMin, sig.physMax,
sig.digMin, sig.digMax, sig.prefiltering, sig.nrSamples, sig.reservedSignal);
raf.write(sigStr.getBytes(StandardCharsets.ASCII));
}
raf.write(dataRecords);
}
}
// Example usage:
// public static void main(String[] args) throws IOException {
// EDFHandler edf = new EDFHandler("example.edf");
// edf.printProperties();
// edf.write("output.edf", new byte[0]);
// }
}
JavaScript class for .EDF file handling
This class is for Node.js (using fs
). It can open an .EDF file, decode and read the header properties, print them to the console, and write a new .EDF file (header only, with placeholder data).
const fs = require('fs');
class EDFHandler {
constructor(filename) {
if (filename) {
this.read(filename);
}
}
read(filename) {
const buffer = fs.readFileSync(filename);
let offset = 0;
const readAscii = (len) => {
const val = buffer.slice(offset, offset + len).toString('ascii').trim();
offset += len;
return val;
};
this.version = readAscii(8);
this.patientId = readAscii(80);
this.recordingId = readAscii(80);
this.startDate = readAscii(8);
this.startTime = readAscii(8);
this.headerBytes = parseInt(readAscii(8));
this.reserved = readAscii(44);
this.numDataRecords = parseInt(readAscii(8));
this.duration = parseFloat(readAscii(8));
this.numSignals = parseInt(readAscii(4));
this.signals = [];
for (let i = 0; i < this.numSignals; i++) {
this.signals.push({
label: readAscii(16),
transducer: readAscii(80),
physDim: readAscii(8),
physMin: parseFloat(readAscii(8)),
physMax: parseFloat(readAscii(8)),
digMin: parseInt(readAscii(8)),
digMax: parseInt(readAscii(8)),
prefiltering: readAscii(80),
nrSamples: parseInt(readAscii(8)),
reservedSignal: readAscii(32)
});
}
}
printProperties() {
console.log(`Version: ${this.version}`);
console.log(`Patient ID: ${this.patientId}`);
console.log(`Recording ID: ${this.recordingId}`);
console.log(`Start Date: ${this.startDate}`);
console.log(`Start Time: ${this.startTime}`);
console.log(`Header Bytes: ${this.headerBytes}`);
console.log(`Reserved: ${this.reserved}`);
console.log(`Num Data Records: ${this.numDataRecords}`);
console.log(`Duration: ${this.duration}`);
console.log(`Num Signals: ${this.numSignals}`);
this.signals.forEach((sig, idx) => {
console.log(`\nSignal ${idx + 1}:`);
console.log(` Label: ${sig.label}`);
console.log(` Transducer: ${sig.transducer}`);
console.log(` Physical Dimension: ${sig.physDim}`);
console.log(` Physical Min: ${sig.physMin}`);
console.log(` Physical Max: ${sig.physMax}`);
console.log(` Digital Min: ${sig.digMin}`);
console.log(` Digital Max: ${sig.digMax}`);
console.log(` Prefiltering: ${sig.prefiltering}`);
console.log(` Num Samples: ${sig.nrSamples}`);
console.log(` Reserved: ${sig.reservedSignal}`);
});
}
write(filename, dataRecords = Buffer.alloc(0)) {
let header = Buffer.alloc(256, ' ');
let offset = 0;
header.write(this.version.padEnd(8), offset, 'ascii'); offset += 8;
header.write(this.patientId.padEnd(80), offset, 'ascii'); offset += 80;
header.write(this.recordingId.padEnd(80), offset, 'ascii'); offset += 80;
header.write(this.startDate.padEnd(8), offset, 'ascii'); offset += 8;
header.write(this.startTime.padEnd(8), offset, 'ascii'); offset += 8;
header.write(this.headerBytes.toString().padEnd(8), offset, 'ascii'); offset += 8;
header.write(this.reserved.padEnd(44), offset, 'ascii'); offset += 44;
header.write(this.numDataRecords.toString().padEnd(8), offset, 'ascii'); offset += 8;
header.write(this.duration.toString().padEnd(8), offset, 'ascii'); offset += 8;
header.write(this.numSignals.toString().padEnd(4), offset, 'ascii');
let signalHeader = Buffer.alloc(256 * this.numSignals, ' ');
offset = 0;
this.signals.forEach(sig => {
signalHeader.write(sig.label.padEnd(16), offset, 'ascii'); offset += 16;
signalHeader.write(sig.transducer.padEnd(80), offset, 'ascii'); offset += 80;
signalHeader.write(sig.physDim.padEnd(8), offset, 'ascii'); offset += 8;
signalHeader.write(sig.physMin.toString().padEnd(8), offset, 'ascii'); offset += 8;
signalHeader.write(sig.physMax.toString().padEnd(8), offset, 'ascii'); offset += 8;
signalHeader.write(sig.digMin.toString().padEnd(8), offset, 'ascii'); offset += 8;
signalHeader.write(sig.digMax.toString().padEnd(8), offset, 'ascii'); offset += 8;
signalHeader.write(sig.prefiltering.padEnd(80), offset, 'ascii'); offset += 80;
signalHeader.write(sig.nrSamples.toString().padEnd(8), offset, 'ascii'); offset += 8;
signalHeader.write(sig.reservedSignal.padEnd(32), offset, 'ascii'); offset += 32;
});
fs.writeFileSync(filename, Buffer.concat([header, signalHeader, dataRecords]));
}
}
// Example usage:
// const edf = new EDFHandler('example.edf');
// edf.printProperties();
// edf.write('output.edf');
C class (C++) for .EDF file handling
This is a C++ class (since C lacks native classes; using structs/functions would be equivalent). It can open an .EDF file, decode and read the header properties, print them to the console, and write a new .EDF file (header only, with placeholder data).
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iomanip>
#include <cstring>
class EDFHandler {
public:
std::string version;
std::string patientId;
std::string recordingId;
std::string startDate;
std::string startTime;
int headerBytes;
std::string reserved;
int numDataRecords;
double duration;
int numSignals;
struct Signal {
std::string label;
std::string transducer;
std::string physDim;
double physMin;
double physMax;
int digMin;
int digMax;
std::string prefiltering;
int nrSamples;
std::string reservedSignal;
};
std::vector<Signal> signals;
EDFHandler(const std::string& filename) {
read(filename);
}
void read(const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
if (!file) {
std::cerr << "Error opening file." << std::endl;
return;
}
char header[256];
file.read(header, 256);
char buf[81]; // Temp buffer
std::memcpy(buf, header + 0, 8); buf[8] = '\0'; version = std::string(buf);
std::memcpy(buf, header + 8, 80); buf[80] = '\0'; patientId = std::string(buf);
std::memcpy(buf, header + 88, 80); buf[80] = '\0'; recordingId = std::string(buf);
std::memcpy(buf, header + 168, 8); buf[8] = '\0'; startDate = std::string(buf);
std::memcpy(buf, header + 176, 8); buf[8] = '\0'; startTime = std::string(buf);
std::memcpy(buf, header + 184, 8); buf[8] = '\0'; headerBytes = std::stoi(buf);
std::memcpy(buf, header + 192, 44); buf[44] = '\0'; reserved = std::string(buf);
std::memcpy(buf, header + 236, 8); buf[8] = '\0'; numDataRecords = std::stoi(buf);
std::memcpy(buf, header + 244, 8); buf[8] = '\0'; duration = std::stod(buf);
std::memcpy(buf, header + 252, 4); buf[4] = '\0'; numSignals = std::stoi(buf);
// Trim spaces
version.erase(version.find_last_not_of(' ') + 1);
patientId.erase(patientId.find_last_not_of(' ') + 1);
// Similarly trim others...
char* signalHeader = new char[256 * numSignals];
file.read(signalHeader, 256 * numSignals);
int offset = 0;
for (int i = 0; i < numSignals; ++i) {
Signal sig;
std::memcpy(buf, signalHeader + offset, 16); buf[16] = '\0'; sig.label = std::string(buf); offset += 16;
std::memcpy(buf, signalHeader + offset, 80); buf[80] = '\0'; sig.transducer = std::string(buf); offset += 80;
std::memcpy(buf, signalHeader + offset, 8); buf[8] = '\0'; sig.physDim = std::string(buf); offset += 8;
std::memcpy(buf, signalHeader + offset, 8); buf[8] = '\0'; sig.physMin = std::stod(buf); offset += 8;
std::memcpy(buf, signalHeader + offset, 8); buf[8] = '\0'; sig.physMax = std::stod(buf); offset += 8;
std::memcpy(buf, signalHeader + offset, 8); buf[8] = '\0'; sig.digMin = std::stoi(buf); offset += 8;
std::memcpy(buf, signalHeader + offset, 8); buf[8] = '\0'; sig.digMax = std::stoi(buf); offset += 8;
std::memcpy(buf, signalHeader + offset, 80); buf[80] = '\0'; sig.prefiltering = std::string(buf); offset += 80;
std::memcpy(buf, signalHeader + offset, 8); buf[8] = '\0'; sig.nrSamples = std::stoi(buf); offset += 8;
std::memcpy(buf, signalHeader + offset, 32); buf[32] = '\0'; sig.reservedSignal = std::string(buf); offset += 32;
// Trim
sig.label.erase(sig.label.find_last_not_of(' ') + 1);
// Similarly for others...
signals.push_back(sig);
}
delete[] signalHeader;
}
void printProperties() {
std::cout << "Version: " << version << std::endl;
std::cout << "Patient ID: " << patientId << std::endl;
std::cout << "Recording ID: " << recordingId << std::endl;
std::cout << "Start Date: " << startDate << std::endl;
std::cout << "Start Time: " << startTime << std::endl;
std::cout << "Header Bytes: " << headerBytes << std::endl;
std::cout << "Reserved: " << reserved << std::endl;
std::cout << "Num Data Records: " << numDataRecords << std::endl;
std::cout << "Duration: " << duration << std::endl;
std::cout << "Num Signals: " << numSignals << std::endl;
for (size_t i = 0; i < signals.size(); ++i) {
std::cout << "\nSignal " << (i + 1) << ":" << std::endl;
std::cout << " Label: " << signals[i].label << std::endl;
std::cout << " Transducer: " << signals[i].transducer << std::endl;
std::cout << " Physical Dimension: " << signals[i].physDim << std::endl;
std::cout << " Physical Min: " << signals[i].physMin << std::endl;
std::cout << " Physical Max: " << signals[i].physMax << std::endl;
std::cout << " Digital Min: " << signals[i].digMin << std::endl;
std::cout << " Digital Max: " << signals[i].digMax << std::endl;
std::cout << " Prefiltering: " << signals[i].prefiltering << std::endl;
std::cout << " Num Samples: " << signals[i].nrSamples << std::endl;
std::cout << " Reserved: " << signals[i].reservedSignal << std::endl;
}
}
void write(const std::string& filename, const char* dataRecords = "", size_t dataSize = 0) {
std::ofstream file(filename, std::ios::binary);
if (!file) {
std::cerr << "Error opening file for writing." << std::endl;
return;
}
char header[256];
std::memset(header, ' ', 256);
std::sprintf(header + 0, "%-8s", version.c_str());
std::sprintf(header + 8, "%-80s", patientId.c_str());
std::sprintf(header + 88, "%-80s", recordingId.c_str());
std::sprintf(header + 168, "%-8s", startDate.c_str());
std::sprintf(header + 176, "%-8s", startTime.c_str());
std::sprintf(header + 184, "%-8d", headerBytes);
std::sprintf(header + 192, "%-44s", reserved.c_str());
std::sprintf(header + 236, "%-8d", numDataRecords);
std::sprintf(header + 244, "%-8.1f", duration);
std::sprintf(header + 252, "%-4d", numSignals);
file.write(header, 256);
for (const auto& sig : signals) {
char sigHeader[256];
std::memset(sigHeader, ' ', 256);
std::sprintf(sigHeader + 0, "%-16s", sig.label.c_str());
std::sprintf(sigHeader + 16, "%-80s", sig.transducer.c_str());
std::sprintf(sigHeader + 96, "%-8s", sig.physDim.c_str());
std::sprintf(sigHeader + 104, "%-8.1f", sig.physMin);
std::sprintf(sigHeader + 112, "%-8.1f", sig.physMax);
std::sprintf(sigHeader + 120, "%-8d", sig.digMin);
std::sprintf(sigHeader + 128, "%-8d", sig.digMax);
std::sprintf(sigHeader + 136, "%-80s", sig.prefiltering.c_str());
std::sprintf(sigHeader + 216, "%-8d", sig.nrSamples);
std::sprintf(sigHeader + 224, "%-32s", sig.reservedSignal.c_str());
file.write(sigHeader, 256);
}
file.write(dataRecords, dataSize);
}
};
// Example usage:
// int main() {
// EDFHandler edf("example.edf");
// edf.printProperties();
// edf.write("output.edf");
// return 0;
// }