class Edfize::Edf

Class used to load and manipulate EDFs

Constants

HEADER_CONFIG
HEADER_OFFSET
RESERVED_SIZE

Used by tests

SIZE_OF_SAMPLE_IN_BYTES

Attributes

duration_of_a_data_record[RW]
filename[R]

EDF File Path

local_patient_identification[RW]
local_recording_identification[RW]
number_of_bytes_in_header[RW]
number_of_data_records[RW]
number_of_signals[RW]
reserved[RW]
signals[RW]
start_date_of_recording[RW]
start_time_of_recording[RW]
version[RW]

Header Information

Public Class Methods

create(filename) { |edf| ... } click to toggle source
# File lib/edfize/edf.rb, line 46
def self.create(filename, &block)
  edf = new(filename)
  yield edf if block_given?
  edf
end
new(filename) click to toggle source
# File lib/edfize/edf.rb, line 52
def initialize(filename)
  @filename = filename
  @signals = []

  read_header
  read_signal_header
  self
end

Public Instance Methods

edf_size() click to toggle source

Total File Size In Bytes

# File lib/edfize/edf.rb, line 81
def edf_size
  File.size(@filename)
end
expected_data_size() click to toggle source

Data Section Size In Bytes

# File lib/edfize/edf.rb, line 86
def expected_data_size
  @signals.collect(&:samples_per_data_record).inject(:+).to_i * @number_of_data_records * SIZE_OF_SAMPLE_IN_BYTES
end
expected_edf_size() click to toggle source
# File lib/edfize/edf.rb, line 90
def expected_edf_size
  expected_data_size + size_of_header
end
expected_size_of_header() click to toggle source
# File lib/edfize/edf.rb, line 76
def expected_size_of_header
  @number_of_bytes_in_header
end
load_epoch(epoch_number, epoch_size) click to toggle source

Epoch Number is Zero Indexed, and Epoch Size is in Seconds (Not Data Records)

# File lib/edfize/edf.rb, line 66
def load_epoch(epoch_number, epoch_size)
  # reset_signals!
  load_digital_signals_by_epoch(epoch_number, epoch_size)
  calculate_physical_values!
end
load_signals() click to toggle source
# File lib/edfize/edf.rb, line 61
def load_signals
  get_data_records
end
parse_integer(string) click to toggle source
# File lib/edfize/edf.rb, line 153
def parse_integer(string)
  Integer(format("%g", string))
rescue
  nil
end
print_header() click to toggle source
section_description(section) click to toggle source
# File lib/edfize/edf.rb, line 107
def section_description(section)
  description = HEADER_CONFIG[section][:description].to_s
  if description == ""
    ""
  else
    " #{description}"
  end
end
section_units(section) click to toggle source
# File lib/edfize/edf.rb, line 98
def section_units(section)
  units = HEADER_CONFIG[section][:units].to_s
  if units == ""
    ""
  else
    " #{units}" + (instance_variable_get("@#{section}") == 1 ? "" : "s")
  end
end
section_value_to_string(section) click to toggle source
# File lib/edfize/edf.rb, line 94
def section_value_to_string(section)
  instance_variable_get("@#{section}").to_s
end
size_of_header() click to toggle source
# File lib/edfize/edf.rb, line 72
def size_of_header
  HEADER_OFFSET + ns * Signal::SIGNAL_CONFIG.collect { |_k, h| h[:size] }.inject(:+)
end
start_date() click to toggle source
# File lib/edfize/edf.rb, line 138
def start_date
  (dd, mm, yy) = start_date_of_recording.split(".")
  dd = parse_integer(dd)
  mm = parse_integer(mm)
  yy = parse_integer(yy)
  yyyy = if yy && yy >= 85
           yy + 1900
         else
           yy + 2000
         end
  Date.strptime("#{mm}/#{dd}/#{yyyy}", "%m/%d/%Y")
rescue
  nil
end
update(hash) click to toggle source
# File lib/edfize/edf.rb, line 159
def update(hash)
  hash.each do |section, value|
    update_header_section(section, value)
  end
end
update_header_section(section, value) click to toggle source
# File lib/edfize/edf.rb, line 165
def update_header_section(section, value)
  return false unless HEADER_CONFIG.keys.include?(section)
  send "#{section}=", value
  size = HEADER_CONFIG[section][:size]
  string = format("%-#{size}.#{size}s", send(section).to_s)
  IO.binwrite(filename, string, send(:compute_offset, section))
  true
end

Protected Instance Methods

calculate_physical_values!() click to toggle source
# File lib/edfize/edf.rb, line 282
def calculate_physical_values!
  @signals.each{|signal| signal.calculate_physical_values!}
end
compute_offset(section) click to toggle source
# File lib/edfize/edf.rb, line 188
def compute_offset(section)
  offset = 0
  HEADER_CONFIG.each do |key, hash|
    break if key == section
    offset += hash[:size]
  end
  offset
end
compute_signal_offset(section) click to toggle source
# File lib/edfize/edf.rb, line 219
def compute_signal_offset(section)
  offset = 0
  Signal::SIGNAL_CONFIG.each do |key, hash|
    break if key == section
    offset += hash[:size]
  end
  offset
end
create_signals() click to toggle source
# File lib/edfize/edf.rb, line 206
def create_signals
  (0..ns-1).to_a.each do |signal_number|
    @signals[signal_number] ||= Signal.new()
  end
end
data_size() click to toggle source
# File lib/edfize/edf.rb, line 286
def data_size
  IO.binread(@filename, nil, size_of_header).size
end
get_data_records() click to toggle source
# File lib/edfize/edf.rb, line 238
def get_data_records
  load_digital_signals()
  calculate_physical_values!()
end
load_digital_signals() click to toggle source

16-bit signed integer size = 2 Bytes = 2 ASCII characters 16-bit signed integer in “Little Endian” format (least significant byte first) unpack: s< 16-bit signed, (little-endian) byte order

# File lib/edfize/edf.rb, line 256
def load_digital_signals
  all_signal_data = IO.binread(@filename, nil, size_of_header).unpack("s<*")
  load_signal_data(all_signal_data, @number_of_data_records)
end
load_digital_signals_by_epoch(epoch_number, epoch_size) click to toggle source
# File lib/edfize/edf.rb, line 243
def load_digital_signals_by_epoch(epoch_number, epoch_size)
  size_of_data_record_in_bytes = @signals.collect(&:samples_per_data_record).inject(:+).to_i * SIZE_OF_SAMPLE_IN_BYTES
  data_records_to_retrieve = (epoch_size / @duration_of_a_data_record rescue 0)
  length_of_bytes_to_read = (data_records_to_retrieve+1) * size_of_data_record_in_bytes
  epoch_offset_size = epoch_number * epoch_size * size_of_data_record_in_bytes # TODO: The size in bytes of an epoch

  all_signal_data = (IO.binread(@filename, length_of_bytes_to_read, size_of_header + epoch_offset_size).unpack("s<*") rescue [])
  load_signal_data(all_signal_data, data_records_to_retrieve+1)
end
load_signal_data(all_signal_data, data_records_retrieved) click to toggle source
# File lib/edfize/edf.rb, line 261
def load_signal_data(all_signal_data, data_records_retrieved)
  all_samples_per_data_record = @signals.collect{|s| s.samples_per_data_record}
  total_samples_per_data_record = all_samples_per_data_record.inject(:+).to_i

  offset = 0
  offsets = []
  all_samples_per_data_record.each do |samples_per_data_record|
    offsets << offset
    offset += samples_per_data_record
  end

  (0..data_records_retrieved-1).to_a.each do |data_record_index|
    @signals.each_with_index do |signal, signal_index|
      read_start = data_record_index * total_samples_per_data_record + offsets[signal_index]
      (0..signal.samples_per_data_record - 1).to_a.each do |value_index|
        signal.digital_values << all_signal_data[read_start+value_index]
      end
    end
  end
end
ns() click to toggle source
# File lib/edfize/edf.rb, line 197
def ns
  @number_of_signals
end
read_header() click to toggle source
# File lib/edfize/edf.rb, line 176
def read_header
  HEADER_CONFIG.keys.each do |section|
    read_header_section(section)
  end
end
read_header_section(section) click to toggle source
# File lib/edfize/edf.rb, line 182
def read_header_section(section)
  result = IO.binread(@filename, HEADER_CONFIG[section][:size], compute_offset(section) )
  result = result.to_s.send(HEADER_CONFIG[section][:after_read]) unless HEADER_CONFIG[section][:after_read].to_s == ""
  self.instance_variable_set("@#{section}", result)
end
read_signal_header() click to toggle source
# File lib/edfize/edf.rb, line 212
def read_signal_header
  create_signals
  Signal::SIGNAL_CONFIG.keys.each do |section|
    read_signal_header_section(section)
  end
end
read_signal_header_section(section) click to toggle source
# File lib/edfize/edf.rb, line 228
def read_signal_header_section(section)
  offset = HEADER_OFFSET + ns * compute_signal_offset(section)
  (0..ns-1).to_a.each do |signal_number|
    section_size = Signal::SIGNAL_CONFIG[section][:size]
    result = IO.binread(@filename, section_size, offset+(signal_number*section_size))
    result = result.to_s.send(Signal::SIGNAL_CONFIG[section][:after_read]) unless Signal::SIGNAL_CONFIG[section][:after_read].to_s == ""
    @signals[signal_number].send("#{section}=", result)
  end
end
reset_signals!() click to toggle source
# File lib/edfize/edf.rb, line 201
def reset_signals!
  @signals = []
  read_signal_header
end