API Reference

Module

BiosemiDataFormatModule
BiosemiDataFormat

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.

source

Data Structures

BiosemiDataFormat.BiosemiHeaderType
BiosemiHeader

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 information
  • text2::String: Recording information
  • start_date::String: Recording start date (DD.MM.YYYY)
  • start_time::String: Recording start time (HH.MM.SS)
  • num_bytes_header::Int: Header size in bytes
  • data_format::String: Data format (typically "24BIT")
  • num_data_records::Int: Number of data records
  • duration_data_records::Int: Duration of each record in seconds
  • num_channels::Int: Number of channels (including status channel)
  • channel_labels::Vector{String}: Channel names/labels
  • transducer_type::Vector{String}: Transducer type for each channel
  • channel_unit::Vector{String}: Physical units for each channel
  • physical_min::Vector{Int}: Physical minimum values
  • physical_max::Vector{Int}: Physical maximum values
  • digital_min::Vector{Int}: Digital minimum values
  • digital_max::Vector{Int}: Digital maximum values
  • pre_filter::Vector{String}: Pre-filtering information
  • num_samples::Vector{Int}: Number of samples per record per channel
  • reserved::Vector{String}: Reserved header space
  • scale_factor::Vector{Float32}: Scale factors for data conversion
  • sample_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 ranges
  • sample_rate is calculated from num_samples and duration_data_records
source
BiosemiDataFormat.BiosemiTriggersType
BiosemiTriggers

Data structure containing trigger and status channel information.

Fields

  • raw::Vector{Int16}: Raw trigger values for each sample
  • idx::Vector{Int}: Sample indices where triggers occur
  • val::Vector{Int}: Trigger values at trigger events
  • count::OrderedDict{Int,Int}: Count of each trigger value
  • time::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 change
  • time matrix has columns: triggervalue, timesinceprevioustrigger
source
BiosemiDataFormat.BiosemiDataType
BiosemiData

Complete data structure containing BioSemi BDF file data and metadata.

Fields

  • filename::String: Source filename
  • header::BiosemiHeader: File header information
  • data::Matrix{Float32}: EEG data matrix (samples × channels)
  • time::StepRangeLen{Float64}: Time vector for each sample
  • triggers::BiosemiTriggers: Trigger event information
  • status::Vector{Int16}: Status channel values for each sample

Notes

  • data matrix excludes the status channel (stored separately in status)
  • time vector starts at 0 and increments by 1/sample_rate
  • status contains the status channel values (typically 0)
source

File I/O Functions

Reading BDF Files

BiosemiDataFormat.read_bdfFunction
read_bdf(filename; header_only=false, channels=[])

Read BioSemi Data Format (BDF) files into Julia data structures.

Arguments

  • filename::String: Path to the BDF file
  • header_only::Bool=false: If true, only read header information and return BiosemiHeader
  • 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 status
  • BiosemiHeader: If header_only=true

Data Structure

The returned BiosemiData contains:

  • header: File metadata and channel information
  • data: EEG data matrix (samples × selected_channels)
  • time: Time vector starting at 0
  • triggers: Trigger event information
  • status: 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 format
  • crop_bdf: Reduce data length
  • select_channels_bdf: Select channels after reading
source

Writing BDF Files

BiosemiDataFormat.write_bdfFunction
write_bdf(bdf_in, filename="")

Write BioSemi BDF data structure to a BDF file.

Arguments

  • bdf_in::BiosemiData: Data structure to write
  • filename::String="": Output filename. If empty, uses bdf_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 files
  • crop_bdf: Reduce data before writing
  • select_channels_bdf: Select channels before writing
source

Data Processing Functions

Cropping and Slicing

BiosemiDataFormat.crop_bdfFunction
crop_bdf(bdf_in, crop_type, val)

Reduce the length of BDF data by cropping (non-mutating).

Arguments

  • bdf_in::BiosemiData: Input data structure
  • crop_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 version
  • downsample_bdf: Reduce sampling rate
source
BiosemiDataFormat.crop_bdf!Function
crop_bdf!(bdf, crop_type, val)

Reduce the length of BDF data by cropping (in-place).

Arguments

  • bdf::BiosemiData: Data structure to modify
  • crop_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

Returns

  • Nothing: Modifies bdf 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 version
  • downsample_bdf!: Reduce sampling rate
  • merge_bdf: Combine multiple files
source
BiosemiDataFormat.time_rangeFunction
time_range(sample_rate, num_data_records)

Generate time vector for BDF data.

Arguments

  • sample_rate::Int: Sampling rate in Hz
  • num_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
source
BiosemiDataFormat.trigger_infoFunction
trigger_info(trig_raw, sample_rate)

Extract trigger event information from raw trigger channel data.

Arguments

  • trig_raw::Vector{Int16}: Raw trigger values for each sample
  • sample_rate::Int: Sampling rate in Hz

Returns

  • BiosemiTriggers: Structured trigger information containing:
    • raw: Original trigger values
    • idx: Sample indices where triggers occur
    • val: Trigger values at trigger events
    • count: Count of each trigger value
    • time: Trigger timing matrix [value, timesinceprevious]

Algorithm

  1. Finds sample indices where trigger values change (diff ≥ 1)
  2. Extracts trigger values at those indices
  3. Calculates time intervals between consecutive triggers
  4. Counts occurrences of each trigger value
  5. 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
source

Downsampling

BiosemiDataFormat.downsample_bdfFunction
downsample_bdf(bdf_in, dec)

Reduce the sampling rate of BDF data by an integer factor (non-mutating).

Arguments

  • bdf_in::BiosemiData: Input data structure
  • dec::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 version
  • crop_bdf: Reduce data length
source
BiosemiDataFormat.downsample_bdf!Function
downsample_bdf!(bdf, dec)

Reduce the sampling rate of BDF data by an integer factor (in-place).

Arguments

  • bdf::BiosemiData: Data structure to modify
  • dec::Int: Downsampling factor (must be power of 2)

Returns

  • Nothing: Modifies bdf 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 version
  • crop_bdf: Reduce data length
  • merge_bdf: Combine multiple files
source

Merging

BiosemiDataFormat.merge_bdfFunction
merge_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 files
  • crop_bdf: Reduce data length
  • select_channels_bdf: Select specific channels
source

Channel Management Functions

Channel Selection

BiosemiDataFormat.select_channels_bdfFunction
select_channels_bdf(bdf_in, channels)

Select specific channels from BDF data (non-mutating).

Arguments

  • bdf_in::BiosemiData: Input data structure
  • channels::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 version
  • delete_channels_bdf: Remove specific channels
source
BiosemiDataFormat.select_channels_bdf!Function
select_channels_bdf!(bdf, channels)

Select specific channels from BDF data (in-place).

Arguments

  • bdf::BiosemiData: Data structure to modify
  • channels::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: Modifies bdf 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 version
  • delete_channels_bdf!: Remove specific channels
  • read_bdf: Read with channel selection
source

Channel Deletion

BiosemiDataFormat.delete_channels_bdfFunction
delete_channels_bdf(bdf_in, channels)

Remove specific channels from BDF data (non-mutating).

Arguments

  • bdf_in::BiosemiData: Input data structure
  • channels::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 version
  • select_channels_bdf: Keep specific channels
source
BiosemiDataFormat.delete_channels_bdf!Function
delete_channels_bdf!(bdf, channels)

Remove specific channels from BDF data (in-place).

Arguments

  • bdf::BiosemiData: Data structure to modify
  • channels::Union{Vector{<:Union{Int,String}}, Int, String}: Channels to remove

Returns

  • Nothing: Modifies bdf 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 version
  • select_channels_bdf!: Keep specific channels
  • read_bdf: Read with channel selection
source

Channel Utilities

BiosemiDataFormat.channel_indexFunction
channel_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 labels
  • channels::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
source