API Reference
Module
BiosemiDataFormat
— ModuleBiosemiDataFormat
Julia package for reading, writing, and processing BioSemi 24-bit EEG data files (BDF format).
This package provides functionality to:
- Read BDF files into Julia data structures
- Write Julia data structures back to BDF format
- Crop data by time or trigger events
- Downsample data by integer factors
- Select or reduce the number of channels
- Merge multiple BDF files
- Process trigger and status channel information
File Format
BioSemi BDF files store 24-bit EEG data with metadata including channel information, sampling rates, and trigger events. See the BioSemi BDF specification for detailed format information.
Quick Start
using BiosemiDataFormat
# Read a BDF file
dat = read_bdf("eeg_data.bdf")
# Select specific channels
dat_selected = select_channels_bdf(dat, ["Fp1", "Cz", "O1"])
# Crop data to specific time range
dat_cropped = crop_bdf(dat, "triggers", [100, 200])
# Write modified data
write_bdf(dat_cropped, "processed_data.bdf")
License
This package is licensed under the MIT License.
Data Structures
BiosemiDataFormat.BiosemiHeader
— TypeBiosemiHeader
Data structure containing BioSemi BDF file header information.
Fields
id1::Vector{UInt8}
: File identifier (first byte)id2::Vector{UInt8}
: File identifier (remaining bytes)text1::String
: Subject informationtext2::String
: Recording informationstart_date::String
: Recording start date (DD.MM.YYYY)start_time::String
: Recording start time (HH.MM.SS)num_bytes_header::Int
: Header size in bytesdata_format::String
: Data format (typically "24BIT")num_data_records::Int
: Number of data recordsduration_data_records::Int
: Duration of each record in secondsnum_channels::Int
: Number of channels (including status channel)channel_labels::Vector{String}
: Channel names/labelstransducer_type::Vector{String}
: Transducer type for each channelchannel_unit::Vector{String}
: Physical units for each channelphysical_min::Vector{Int}
: Physical minimum valuesphysical_max::Vector{Int}
: Physical maximum valuesdigital_min::Vector{Int}
: Digital minimum valuesdigital_max::Vector{Int}
: Digital maximum valuespre_filter::Vector{String}
: Pre-filtering informationnum_samples::Vector{Int}
: Number of samples per record per channelreserved::Vector{String}
: Reserved header spacescale_factor::Vector{Float32}
: Scale factors for data conversionsample_rate::Vector{Int}
: Sampling rate for each channel
Notes
- The last channel is always the status/trigger channel
scale_factor
is automatically calculated from physical and digital rangessample_rate
is calculated fromnum_samples
andduration_data_records
BiosemiDataFormat.BiosemiTriggers
— TypeBiosemiTriggers
Data structure containing trigger and status channel information.
Fields
raw::Vector{Int16}
: Raw trigger values for each sampleidx::Vector{Int}
: Sample indices where triggers occurval::Vector{Int}
: Trigger values at trigger eventscount::OrderedDict{Int,Int}
: Count of each trigger valuetime::Matrix{Float64}
: Trigger timing information (value × time)
Notes
raw
contains trigger values for every sample (0 for no trigger)idx
contains sample indices where trigger values changetime
matrix has columns: triggervalue, timesinceprevioustrigger
BiosemiDataFormat.BiosemiData
— TypeBiosemiData
Complete data structure containing BioSemi BDF file data and metadata.
Fields
filename::String
: Source filenameheader::BiosemiHeader
: File header informationdata::Matrix{Float32}
: EEG data matrix (samples × channels)time::StepRangeLen{Float64}
: Time vector for each sampletriggers::BiosemiTriggers
: Trigger event informationstatus::Vector{Int16}
: Status channel values for each sample
Notes
data
matrix excludes the status channel (stored separately instatus
)time
vector starts at 0 and increments by 1/sample_ratestatus
contains the status channel values (typically 0)
File I/O Functions
Reading BDF Files
BiosemiDataFormat.read_bdf
— Functionread_bdf(filename; header_only=false, channels=[])
Read BioSemi Data Format (BDF) files into Julia data structures.
Arguments
filename::String
: Path to the BDF fileheader_only::Bool=false
: Iftrue
, only read header information and returnBiosemiHeader
channels::Vector{<:Union{Int,String}}=[]
: Specific channels to read- Empty vector (default): read all channels
- Vector of integers: channel indices (1-based, excluding status channel)
- Vector of strings: channel labels (e.g., ["Fp1", "Cz"])
- Use
-1
to include only the trigger/status channel - Mix of types allowed (e.g., [1, "Fp1", -1])
Returns
BiosemiData
: Complete data structure with header, data, time, triggers, and statusBiosemiHeader
: Ifheader_only=true
Data Structure
The returned BiosemiData
contains:
header
: File metadata and channel informationdata
: EEG data matrix (samples × selected_channels)time
: Time vector starting at 0triggers
: Trigger event informationstatus
: Status channel values
Examples
# Read entire file
dat = read_bdf("data.bdf")
# Read only header
hdr = read_bdf("data.bdf", header_only=true)
# Read specific channels by index
dat = read_bdf("data.bdf", channels=[1, 3, 5])
# Read specific channels by label
dat = read_bdf("data.bdf", channels=["Fp1", "Cz", "A1"])
# Read only trigger channel
dat = read_bdf("data.bdf", channels=[-1])
# Mix of channel types
dat = read_bdf("data.bdf", channels=[1, "Fp1", -1])
Notes
- Channel indices are 1-based
- The status/trigger channel is always included automatically
- Data is automatically scaled using header calibration information
- Trigger information is extracted from the status channel
- File format follows BioSemi BDF specification
See also
write_bdf
: Write data back to BDF formatcrop_bdf
: Reduce data lengthselect_channels_bdf
: Select channels after reading
Writing BDF Files
BiosemiDataFormat.write_bdf
— Functionwrite_bdf(bdf_in, filename="")
Write BioSemi BDF data structure to a BDF file.
Arguments
bdf_in::BiosemiData
: Data structure to writefilename::String=""
: Output filename. If empty, usesbdf_in.filename
File Format
Writes data in BioSemi BDF 24-bit format with:
- 256-byte header containing metadata
- 24-bit data samples for each channel
- Status channel as the last channel
- Proper scaling and calibration information
Header Information
The header includes:
- File identification and metadata
- Channel information (labels, units, ranges)
- Sampling rate and data record information
- Pre-filtering and transducer information
Examples
# Write to specified filename
write_bdf(dat, "output.bdf")
# Arguments
- `bdf_in::BiosemiData`: Data structure to write
- `filename::String=""`: Output filename (uses original filename if empty)
# Returns
- `Nothing`: Writes file to disk
# Examples
julia
Write to a new file
writebdf(dat, "processeddata.bdf")
Write using original filename
dat.filename = "mydata.bdf" writebdf(dat)
Write processed data
datcropped = cropbdf(dat, "triggers", [100, 200]) writebdf(datcropped, "cropped_data.bdf") ```
Notes
- Creates a new BDF file in standard BioSemi format
- Automatically applies scale factors and converts to 24-bit format
- Preserves all header information and metadata
- If no filename is provided, uses the filename stored in the data structure
- Overwrites existing files without warning
See also
read_bdf
: Read BDF filescrop_bdf
: Reduce data before writingselect_channels_bdf
: Select channels before writing
Data Processing Functions
Cropping and Slicing
BiosemiDataFormat.crop_bdf
— Functioncrop_bdf(bdf_in, crop_type, val)
Reduce the length of BDF data by cropping (non-mutating).
Arguments
bdf_in::BiosemiData
: Input data structurecrop_type::String
: Cropping method ("records" or "triggers")val::Vector{Int}
: Cropping parameters
Returns
BiosemiData
: New cropped data structure
Examples
# Crop and get new structure
dat_cropped = crop_bdf(dat, "records", [10 20])
dat_cropped = crop_bdf(dat, "triggers", [1 2])
Notes
- Returns a new data structure (original unchanged)
- Calls
crop_bdf!
internally - Useful when you want to preserve the original data
See also
crop_bdf!
: In-place versiondownsample_bdf
: Reduce sampling rate
BiosemiDataFormat.crop_bdf!
— Functioncrop_bdf!(bdf, crop_type, val)
Reduce the length of BDF data by cropping (in-place).
Arguments
bdf::BiosemiData
: Data structure to modifycrop_type::String
: Cropping method"records"
: Crop by data record numbers"triggers"
: Crop between trigger events
val::Vector{Int}
: Cropping parameters- For
"records"
: startrecord to endrecord (1-based) - For
"triggers"
: starttrigger to endtrigger
- For
Returns
Nothing
: Modifiesbdf
in-place
Examples
# Crop between records 10-20
crop_bdf!(dat, "records", [10 20])
# Crop between first occurrence of trigger 1 and last of trigger 2
crop_bdf!(dat, "triggers", [1 2])
Notes
- Modifies the original data structure
- Updates header information (numdatarecords)
- Recalculates time vector and trigger information
- Records are 1-based indexing
- Use
crop_bdf
for non-mutating version
See also
crop_bdf
: Non-mutating versiondownsample_bdf!
: Reduce sampling ratemerge_bdf
: Combine multiple files
BiosemiDataFormat.time_range
— Functiontime_range(sample_rate, num_data_records)
Generate time vector for BDF data.
Arguments
sample_rate::Int
: Sampling rate in Hznum_data_records::Int
: Number of data records
Returns
StepRangeLen{Float64}
: Time vector starting at 0
Time Calculation
- Time starts at 0 seconds
- Increments by 1/sample_rate for each sample
- Ends at (numdatarecords - 1/sample_rate) seconds
- Total duration = numdatarecords seconds
Examples
# 60 seconds of data at 256 Hz
time = time_range(256, 60)
@assert length(time) == 256 * 60
@assert first(time) == 0.0
@assert last(time) == 59.99609375 # (60 - 1/256)
Notes
- This is an internal function used by other functions
- Time vector length matches the number of data samples
- Useful for plotting and time-based operations
BiosemiDataFormat.trigger_info
— Functiontrigger_info(trig_raw, sample_rate)
Extract trigger event information from raw trigger channel data.
Arguments
trig_raw::Vector{Int16}
: Raw trigger values for each samplesample_rate::Int
: Sampling rate in Hz
Returns
BiosemiTriggers
: Structured trigger information containing:raw
: Original trigger valuesidx
: Sample indices where triggers occurval
: Trigger values at trigger eventscount
: Count of each trigger valuetime
: Trigger timing matrix [value, timesinceprevious]
Algorithm
- Finds sample indices where trigger values change (diff ≥ 1)
- Extracts trigger values at those indices
- Calculates time intervals between consecutive triggers
- Counts occurrences of each trigger value
- Creates timing matrix with trigger values and intervals
Notes
- This is an internal function used by
read_bdf
- Trigger events are detected when the trigger value increases
- Time intervals are calculated in seconds
- The first trigger has time interval 0
Downsampling
BiosemiDataFormat.downsample_bdf
— Functiondownsample_bdf(bdf_in, dec)
Reduce the sampling rate of BDF data by an integer factor (non-mutating).
Arguments
bdf_in::BiosemiData
: Input data structuredec::Int
: Downsampling factor (must be power of 2)
Returns
BiosemiData
: New downsampled data structure
Examples
# Downsample and get new structure
dat_ds = downsample_bdf(dat, 2)
dat_ds = downsample_bdf(dat, 4)
Notes
- Returns a new data structure (original unchanged)
- Calls
downsample_bdf!
internally - Useful when you want to preserve the original data
See also
downsample_bdf!
: In-place versioncrop_bdf
: Reduce data length
BiosemiDataFormat.downsample_bdf!
— Functiondownsample_bdf!(bdf, dec)
Reduce the sampling rate of BDF data by an integer factor (in-place).
Arguments
bdf::BiosemiData
: Data structure to modifydec::Int
: Downsampling factor (must be power of 2)
Returns
Nothing
: Modifiesbdf
in-place
Examples
# Downsample by factor of 2
downsample_bdf!(dat, 2)
# Downsample by factor of 4
downsample_bdf!(dat, 4)
# Check the new sampling rate
println("Original sampling rate: ", dat.header.sample_rate[1], " Hz")
Notes
- Modifies the original data structure
- Applies anti-aliasing filter using DSP.resample
- Reduces data length by factor
dec
- Updates header information (samplerate, numsamples)
- Recalculates time vector and trigger information
- Downsampling factor must be a power of 2
See also
downsample_bdf
: Non-mutating versioncrop_bdf
: Reduce data lengthmerge_bdf
: Combine multiple files
Merging
BiosemiDataFormat.merge_bdf
— Functionmerge_bdf(bdfs)
Merge multiple BDF data structures into a single file.
Arguments
bdfs::Array{BiosemiData}
: Array of BDF data structures to merge
Returns
BiosemiData
: Merged data structure
Requirements
- All files must have the same number of channels
- All files must have identical channel labels
- All files must have the same sampling rate
Examples
# Merge two BDF files
file1 = "session1.bdf"
file2 = "session2.bdf"
dat1 = read_bdf(file1)
dat2 = read_bdf(file2)
dat_merged = merge_bdf([dat1, dat2])
# Merge multiple files
files = ["session1.bdf", "session2.bdf", "session3.bdf"]
data_arrays = [read_bdf(f) for f in files]
dat_merged = merge_bdf(data_arrays)
Notes
- Files are concatenated in the order provided
- Header information is taken from the first file
- Trigger information is recalculated for the merged data
- Time vector is updated to reflect the total duration
- Original files are not modified
See also
read_bdf
: Read individual BDF filescrop_bdf
: Reduce data lengthselect_channels_bdf
: Select specific channels
Channel Management Functions
Channel Selection
BiosemiDataFormat.select_channels_bdf
— Functionselect_channels_bdf(bdf_in, channels)
Select specific channels from BDF data (non-mutating).
Arguments
bdf_in::BiosemiData
: Input data structurechannels::Union{Vector{<:Union{Int,String}}, Int, String}
: Channels to keep
Returns
BiosemiData
: New data structure with selected channels
Examples
# Select and get new structure
dat_selected = select_channels_bdf(dat, [1, 3, 5])
dat_selected = select_channels_bdf(dat, ["Fp1", "Cz"])
Notes
- Returns a new data structure (original unchanged)
- Calls
select_channels_bdf!
internally - Useful when you want to preserve the original data
See also
select_channels_bdf!
: In-place versiondelete_channels_bdf
: Remove specific channels
BiosemiDataFormat.select_channels_bdf!
— Functionselect_channels_bdf!(bdf, channels)
Select specific channels from BDF data (in-place).
Arguments
bdf::BiosemiData
: Data structure to modifychannels::Vector{<:Union{Int,String}}
: Channels to keep- Vector of integers: channel indices (1-based, excluding status channel)
- Vector of strings: channel labels (e.g., ["Fp1", "Cz"])
- Mix of types allowed (e.g., [1, "Fp1"])
Returns
Nothing
: Modifiesbdf
in-place
Examples
# Select channels by index
select_channels_bdf!(dat, [1, 3, 5])
# Select channels by label
select_channels_bdf!(dat, ["Fp1", "Cz", "A1"])
# Mix of channel types
select_channels_bdf!(dat, [1, "Fp1"])
Notes
- Modifies the original data structure
- The status/trigger channel is always included automatically
- Updates header information (numchannels, channellabels, etc.)
- Channel indices are 1-based
- Use
select_channels_bdf
for non-mutating version
See also
select_channels_bdf
: Non-mutating versiondelete_channels_bdf!
: Remove specific channelsread_bdf
: Read with channel selection
Channel Deletion
BiosemiDataFormat.delete_channels_bdf
— Functiondelete_channels_bdf(bdf_in, channels)
Remove specific channels from BDF data (non-mutating).
Arguments
bdf_in::BiosemiData
: Input data structurechannels::Union{Vector{<:Union{Int,String}}, Int, String}
: Channels to remove
Returns
BiosemiData
: New data structure with channels removed
Examples
# Remove channels and get new structure
dat_reduced = delete_channels_bdf(dat, [2, 4])
dat_reduced = delete_channels_bdf(dat, ["Fp2", "F8"])
Notes
- Returns a new data structure (original unchanged)
- Calls
delete_channels_bdf!
internally - Useful when you want to preserve the original data
See also
delete_channels_bdf!
: In-place versionselect_channels_bdf
: Keep specific channels
BiosemiDataFormat.delete_channels_bdf!
— Functiondelete_channels_bdf!(bdf, channels)
Remove specific channels from BDF data (in-place).
Arguments
bdf::BiosemiData
: Data structure to modifychannels::Union{Vector{<:Union{Int,String}}, Int, String}
: Channels to remove
Returns
Nothing
: Modifiesbdf
in-place
Examples
# Remove channels by index
delete_channels_bdf!(dat, [2, 4])
# Remove channels by label
delete_channels_bdf!(dat, ["Fp2", "F8"])
# Mix of channel types
delete_channels_bdf!(dat, [2, "Fp2"])
Notes
- Modifies the original data structure
- The status/trigger channel cannot be removed
- Updates header information (numchannels, channellabels, etc.)
- Channel indices are 1-based
- Use
delete_channels_bdf
for non-mutating version
See also
delete_channels_bdf
: Non-mutating versionselect_channels_bdf!
: Keep specific channelsread_bdf
: Read with channel selection
Channel Utilities
BiosemiDataFormat.channel_index
— Functionchannel_index(labels, channels)
Convert channel specifications to channel indices.
Examples
# Single channel by label
idx = channel_index(["A1", "A2", "A3"], "A2") # Returns [2, 3]
# Single channel by index
idx = channel_index(["A1", "A2", "A3"], 2) # Returns [2, 3]
# Multiple channels by index
idx = channel_index(["A1", "A2", "A3"], [1, 3]) # Returns [1, 3, 3]
# Multiple channels by label
idx = channel_index(["A1", "A2", "A3"], ["A1", "A3"]) # Returns [1, 3, 3]
# Mixed types
idx = channel_index(["A1", "A2", "A3"], [1, "A2"]) # Returns [1, 2, 3]
# Trigger channel only
idx = channel_index(["A1", "A2", "A3"], [-1]) # Returns [3, 3]
Arguments
labels::Vector{<:AbstractString}
: Available channel labelschannels::Union{Vector{<:Union{Int,String}}, Int, String}
: Channel specifications
Returns
Vector{Int}
: Channel indices including the status channel
Notes
- This is an internal function used by other functions
- Channel indices are 1-based
- The status channel is always included automatically
- Returns indices in the order specified
- Useful for channel selection and deletion operations