edfize 0.1.0.beta2 → 0.1.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -16
- data/lib/edfize/edf.rb +92 -165
- data/lib/edfize/signal.rb +24 -0
- data/lib/edfize/tests/runner.rb +0 -11
- data/lib/edfize/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66faa96463f7845c7d3216c16b6d71dfedaf5a47
|
4
|
+
data.tar.gz: 835ab39c4653438fbebeb739b3330e160606b3ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4bb06a14848520c8f1245731bee2ddabc66308628b5a7ffcee722afab44b52153fbbb27aa53bf7e8cd5b13781fc0d1d02aed7a3288688d9215d9c770aec98ea2
|
7
|
+
data.tar.gz: 0307a0be76a76bce43a328d1c5dd7049db317c09d896d3f3b7731ba62f32932df5c4cb87ea1d7e656f16c9d61fc446c08b44098e7c2845215c9fb324c7802882
|
data/README.md
CHANGED
@@ -8,17 +8,9 @@ Ruby gem used to load, validate, and parse European Data Format files. Used for
|
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
gem 'edfize'
|
14
|
-
|
15
|
-
And then execute:
|
16
|
-
|
17
|
-
$ bundle
|
18
|
-
|
19
|
-
Or install it yourself as:
|
11
|
+
Use `gem install edfize` to update Edfize to the latest stable
|
20
12
|
|
21
|
-
|
13
|
+
Use `gem install edfize --pre` to update Edfize to the latest prerelease
|
22
14
|
|
23
15
|
## Usage
|
24
16
|
|
@@ -58,12 +50,6 @@ Use `edfize version` to check the version of Edfize.
|
|
58
50
|
|
59
51
|
edfize version
|
60
52
|
|
61
|
-
### Upgrade Edfize
|
62
|
-
|
63
|
-
Use `gem install edfize` to update Edfize to the latest stable
|
64
|
-
|
65
|
-
Use `gem install edfize --pre` to update Edfize to the latest prerelease
|
66
|
-
|
67
53
|
### Example of how to Load and Analyze EDFs in a Ruby Script
|
68
54
|
|
69
55
|
The following Ruby file demonstrates how to make use of the Edfize gem to load EDF signals into arrays for analysis.
|
data/lib/edfize/edf.rb
CHANGED
@@ -2,38 +2,49 @@ require 'edfize/signal'
|
|
2
2
|
|
3
3
|
module Edfize
|
4
4
|
class Edf
|
5
|
+
# EDF File Path
|
5
6
|
attr_reader :filename
|
6
7
|
|
7
|
-
|
8
|
-
attr_reader
|
8
|
+
# Header Information
|
9
|
+
attr_reader :version
|
10
|
+
attr_reader :local_patient_identification
|
11
|
+
attr_reader :local_recording_identification
|
12
|
+
attr_reader :start_date_of_recording
|
13
|
+
attr_reader :start_time_of_recording
|
14
|
+
attr_reader :number_of_bytes_in_header
|
15
|
+
attr_reader :reserved
|
16
|
+
attr_reader :number_of_data_records
|
17
|
+
attr_reader :duration_of_a_data_record
|
18
|
+
attr_reader :number_of_signals
|
9
19
|
|
10
20
|
attr_accessor :signals
|
11
21
|
|
12
|
-
|
22
|
+
HEADER_CONFIG = {
|
23
|
+
version: { size: 8, after_read: :to_i, name: 'Version' },
|
24
|
+
local_patient_identification: { size: 80, after_read: :strip, name: 'Local Patient Identification' },
|
25
|
+
local_recording_identification: { size: 80, after_read: :strip, name: 'Local Recording Identification' },
|
26
|
+
start_date_of_recording: { size: 8, name: 'Start Date of Recording', description: '(dd.mm.yy)' },
|
27
|
+
start_time_of_recording: { size: 8, name: 'Start Time of Recording', description: '(hh.mm.ss)'},
|
28
|
+
number_of_bytes_in_header: { size: 8, after_read: :to_i, name: 'Number of Bytes in Header' },
|
29
|
+
reserved: { size: 44, name: 'Reserved' },
|
30
|
+
number_of_data_records: { size: 8, after_read: :to_i, name: 'Number of Data Records' },
|
31
|
+
duration_of_a_data_record: { size: 8, after_read: :to_i, name: 'Duration of a Data Record', units: 'second' },
|
32
|
+
number_of_signals: { size: 4, after_read: :to_i, name: 'Number of Signals' }
|
33
|
+
}
|
34
|
+
|
35
|
+
HEADER_OFFSET = HEADER_CONFIG.collect{|k,h| h[:size]}.inject(:+)
|
13
36
|
|
14
|
-
|
15
|
-
|
16
|
-
HEADER_OFFSET = 256
|
17
37
|
SIZE_OF_SAMPLE_IN_BYTES = 2
|
18
38
|
|
39
|
+
# Used by tests
|
40
|
+
RESERVED_SIZE = HEADER_CONFIG[:reserved][:size]
|
41
|
+
|
19
42
|
def initialize(filename)
|
20
43
|
@filename = filename
|
21
44
|
@signals = []
|
22
45
|
|
23
46
|
read_header
|
24
|
-
|
25
|
-
# Other
|
26
|
-
get_number_of_data_records
|
27
|
-
signal_labels
|
28
|
-
transducer_types
|
29
|
-
physical_dimensions
|
30
|
-
physical_minimums
|
31
|
-
physical_maximums
|
32
|
-
digital_minimums
|
33
|
-
digital_maximums
|
34
|
-
prefilterings
|
35
|
-
samples_per_data_records
|
36
|
-
reserved_areas
|
47
|
+
read_signal_header
|
37
48
|
end
|
38
49
|
|
39
50
|
def load_signals
|
@@ -41,11 +52,11 @@ module Edfize
|
|
41
52
|
end
|
42
53
|
|
43
54
|
def size_of_header
|
44
|
-
HEADER_OFFSET + ns * (
|
55
|
+
HEADER_OFFSET + ns * Signal::SIGNAL_CONFIG.collect{|k,h| h[:size]}.inject(:+)
|
45
56
|
end
|
46
57
|
|
47
58
|
def expected_size_of_header
|
48
|
-
|
59
|
+
@number_of_bytes_in_header
|
49
60
|
end
|
50
61
|
|
51
62
|
# Total File Size In Bytes
|
@@ -62,32 +73,41 @@ module Edfize
|
|
62
73
|
expected_data_size + size_of_header
|
63
74
|
end
|
64
75
|
|
76
|
+
def section_value_to_string(section)
|
77
|
+
self.instance_variable_get("@#{section}").to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
def section_units(section)
|
81
|
+
units = HEADER_CONFIG[section][:units].to_s
|
82
|
+
result = if units == ''
|
83
|
+
''
|
84
|
+
else
|
85
|
+
" #{units}" + (self.instance_variable_get("@#{section}") == 1 ? '' : 's')
|
86
|
+
end
|
87
|
+
result
|
88
|
+
end
|
89
|
+
|
90
|
+
def section_description(section)
|
91
|
+
description = HEADER_CONFIG[section][:description].to_s
|
92
|
+
result = if description == ''
|
93
|
+
''
|
94
|
+
else
|
95
|
+
" #{description}"
|
96
|
+
end
|
97
|
+
result
|
98
|
+
end
|
99
|
+
|
65
100
|
def print_header
|
66
101
|
puts "\nEDF : #{@filename}"
|
67
102
|
puts "Total File Size : #{edf_size} bytes"
|
68
103
|
puts "\nHeader Information"
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
puts "Start Date of Recording : #{header_start_date_of_recording} (dd.mm.yy)"
|
73
|
-
puts "Start Time of Recording : #{header_start_time_of_recording} (hh.mm.ss)"
|
74
|
-
puts "Reserved : '#{@reserved}'"
|
75
|
-
puts "Number of Data Records : #{number_of_data_records}"
|
76
|
-
puts "Duration of a Data Record : #{duration_of_a_data_record.to_i} second#{'s' unless duration_of_a_data_record.to_i == 1}"
|
77
|
-
puts "Number of Signals (NS) : #{number_of_signals}"
|
104
|
+
HEADER_CONFIG.each do |section, hash|
|
105
|
+
puts "#{hash[:name]}#{' '*(31 - hash[:name].size)}: " + section_value_to_string(section) + section_units(section) + section_description(section)
|
106
|
+
end
|
78
107
|
puts "\nSignal Information"
|
79
108
|
signals.each_with_index do |signal, index|
|
80
109
|
puts "\n Position : #{index + 1}"
|
81
|
-
|
82
|
-
puts " Physical Dimension : #{signal.physical_dimension}"
|
83
|
-
puts " Transducer Type : #{signal.transducer_type}"
|
84
|
-
puts " Physical Minimum : #{signal.physical_minimum}"
|
85
|
-
puts " Physical Maximum : #{signal.physical_maximum}"
|
86
|
-
puts " Digital Minimum : #{signal.digital_minimum}"
|
87
|
-
puts " Digital Maximum : #{signal.digital_maximum}"
|
88
|
-
puts " Prefiltering : #{signal.prefiltering}"
|
89
|
-
puts " Samples Per Data Record : #{signal.samples_per_data_record}"
|
90
|
-
puts " Reserved Area : '#{signal.reserved_area}'"
|
110
|
+
signal.print_header
|
91
111
|
end
|
92
112
|
puts "\nGeneral Information"
|
93
113
|
puts "Size of Header (bytes) : #{size_of_header}"
|
@@ -102,158 +122,65 @@ module Edfize
|
|
102
122
|
protected
|
103
123
|
|
104
124
|
def read_header
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
def header_version
|
109
|
-
IO.binread(@filename, 8)
|
110
|
-
end
|
111
|
-
|
112
|
-
# 80 ascii : local patient identification (mind item 3 of the additional EDF+ specs)
|
113
|
-
def header_local_patient_identification
|
114
|
-
IO.binread(@filename, 80, 8)
|
115
|
-
end
|
116
|
-
|
117
|
-
# 80 ascii : local recording identification (mind item 4 of the additional EDF+ specs)
|
118
|
-
def header_local_recording_identification
|
119
|
-
IO.binread(@filename, 80, 88)
|
120
|
-
end
|
121
|
-
|
122
|
-
# 8 ascii : startdate of recording (dd.mm.yy) (mind item 2 of the additional EDF+ specs)
|
123
|
-
def header_start_date_of_recording
|
124
|
-
IO.binread(@filename, 8, 168)
|
125
|
-
end
|
126
|
-
|
127
|
-
# 8 ascii : starttime of recording (hh.mm.ss)
|
128
|
-
def header_start_time_of_recording
|
129
|
-
IO.binread(@filename, 8, 176)
|
130
|
-
end
|
131
|
-
|
132
|
-
# 8 ascii : number of bytes in header record
|
133
|
-
def number_of_bytes_in_header
|
134
|
-
IO.binread(@filename, 8, 184)
|
135
|
-
end
|
136
|
-
|
137
|
-
# 44 ascii : reserved
|
138
|
-
def read_reserved
|
139
|
-
@reserved = IO.binread(@filename, RESERVED_SIZE, 192)
|
140
|
-
end
|
141
|
-
|
142
|
-
# 8 ascii : number of data records (-1 if unknown, obey item 10 of the additional EDF+ specs)
|
143
|
-
def get_number_of_data_records
|
144
|
-
@number_of_data_records = IO.binread(@filename, 8, RESERVED_SIZE + 192).to_i
|
145
|
-
end
|
146
|
-
|
147
|
-
# 8 ascii : duration of a data record, in seconds
|
148
|
-
def duration_of_a_data_record
|
149
|
-
IO.binread(@filename, 8, 244)
|
150
|
-
end
|
151
|
-
|
152
|
-
# 4 ascii : number of signals (ns) in data record
|
153
|
-
def number_of_signals
|
154
|
-
IO.binread(@filename, 4, 252)
|
155
|
-
end
|
156
|
-
|
157
|
-
def ns
|
158
|
-
Integer(self.number_of_signals) rescue 0
|
159
|
-
end
|
160
|
-
|
161
|
-
# ns * 16 ascii : ns * label (e.g. EEG Fpz-Cz or Body temp) (mind item 9 of the additional EDF+ specs)
|
162
|
-
def signal_labels
|
163
|
-
offset = HEADER_OFFSET
|
164
|
-
(0..ns-1).to_a.each do |signal_number|
|
165
|
-
@signals[signal_number] ||= Signal.new()
|
166
|
-
@signals[signal_number].label = IO.binread(@filename, 16, offset+(signal_number*16))
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
# ns * 80 ascii : ns * transducer type (e.g. AgAgCl electrode)
|
171
|
-
def transducer_types
|
172
|
-
offset = HEADER_OFFSET + ns * 16
|
173
|
-
(0..ns-1).to_a.each do |signal_number|
|
174
|
-
@signals[signal_number] ||= Signal.new()
|
175
|
-
@signals[signal_number].transducer_type = IO.binread(@filename, 80, offset+(signal_number*80))
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
# ns * 8 ascii : ns * physical dimension (e.g. uV or degreeC)
|
180
|
-
def physical_dimensions
|
181
|
-
offset = HEADER_OFFSET + ns * (16 + 80)
|
182
|
-
(0..ns-1).to_a.each do |signal_number|
|
183
|
-
@signals[signal_number] ||= Signal.new()
|
184
|
-
@signals[signal_number].physical_dimension = IO.binread(@filename, 8, offset+(signal_number*8))
|
125
|
+
HEADER_CONFIG.keys.each do |section|
|
126
|
+
read_header_section(section)
|
185
127
|
end
|
186
128
|
end
|
187
129
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
(
|
192
|
-
@signals[signal_number] ||= Signal.new()
|
193
|
-
@signals[signal_number].physical_minimum = IO.binread(@filename, 8, offset+(signal_number*8))
|
194
|
-
end
|
130
|
+
def read_header_section(section)
|
131
|
+
result = IO.binread(@filename, HEADER_CONFIG[section][:size], compute_offset(section) )
|
132
|
+
result = result.to_s.send(HEADER_CONFIG[section][:after_read]) unless HEADER_CONFIG[section][:after_read].to_s == ''
|
133
|
+
self.instance_variable_set("@#{section}", result)
|
195
134
|
end
|
196
135
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
@signals[signal_number].physical_maximum = IO.binread(@filename, 8, offset+(signal_number*8))
|
136
|
+
def compute_offset(section)
|
137
|
+
offset = 0
|
138
|
+
HEADER_CONFIG.each do |key, hash|
|
139
|
+
break if key == section
|
140
|
+
offset += hash[:size]
|
203
141
|
end
|
142
|
+
offset
|
204
143
|
end
|
205
144
|
|
206
|
-
|
207
|
-
|
208
|
-
offset = HEADER_OFFSET + ns * (16 + 80 + 8 + 8 + 8)
|
209
|
-
(0..ns-1).to_a.each do |signal_number|
|
210
|
-
@signals[signal_number] ||= Signal.new()
|
211
|
-
@signals[signal_number].digital_minimum = IO.binread(@filename, 8, offset+(signal_number*8))
|
212
|
-
end
|
145
|
+
def ns
|
146
|
+
@number_of_signals
|
213
147
|
end
|
214
148
|
|
215
|
-
|
216
|
-
def digital_maximums
|
217
|
-
offset = HEADER_OFFSET + ns * (16 + 80 + 8 + 8 + 8 + 8)
|
149
|
+
def create_signals
|
218
150
|
(0..ns-1).to_a.each do |signal_number|
|
219
151
|
@signals[signal_number] ||= Signal.new()
|
220
|
-
@signals[signal_number].digital_maximum = IO.binread(@filename, 8, offset+(signal_number*8))
|
221
152
|
end
|
222
153
|
end
|
223
154
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
@signals[signal_number] ||= Signal.new()
|
229
|
-
@signals[signal_number].prefiltering = IO.binread(@filename, 80, offset+(signal_number*80))
|
155
|
+
def read_signal_header
|
156
|
+
create_signals
|
157
|
+
Signal::SIGNAL_CONFIG.keys.each do |section|
|
158
|
+
read_signal_header_section(section)
|
230
159
|
end
|
231
160
|
end
|
232
161
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
@signals[signal_number].samples_per_data_record = IO.binread(@filename, 8, offset+(signal_number*8)).to_i
|
239
|
-
@signals[signal_number].samples = Array.new(@signals[signal_number].samples_per_data_record, 0)
|
162
|
+
def compute_signal_offset(section)
|
163
|
+
offset = 0
|
164
|
+
Signal::SIGNAL_CONFIG.each do |key, hash|
|
165
|
+
break if key == section
|
166
|
+
offset += hash[:size]
|
240
167
|
end
|
168
|
+
offset
|
241
169
|
end
|
242
170
|
|
243
|
-
|
244
|
-
|
245
|
-
offset = HEADER_OFFSET + ns * (16 + 80 + 8 + 8 + 8 + 8 + 8 + 80 + 8)
|
171
|
+
def read_signal_header_section(section)
|
172
|
+
offset = HEADER_OFFSET + ns * compute_signal_offset(section)
|
246
173
|
(0..ns-1).to_a.each do |signal_number|
|
247
|
-
|
248
|
-
|
174
|
+
section_size = Signal::SIGNAL_CONFIG[section][:size]
|
175
|
+
result = IO.binread(@filename, section_size, offset+(signal_number*section_size))
|
176
|
+
result = result.to_s.send(Signal::SIGNAL_CONFIG[section][:after_read]) unless Signal::SIGNAL_CONFIG[section][:after_read].to_s == ''
|
177
|
+
@signals[signal_number].send("#{section}=", result)
|
249
178
|
end
|
250
179
|
end
|
251
180
|
|
252
|
-
|
253
|
-
#
|
254
181
|
def get_data_records
|
255
182
|
current_read_offset = size_of_header
|
256
|
-
(0
|
183
|
+
(0..ns-1).to_a.each do |data_record_index|
|
257
184
|
@signals.each do |signal|
|
258
185
|
# 16-bit signed integer size = 2 Bytes = 2 ASCII characters
|
259
186
|
read_size = signal.samples_per_data_record * SIZE_OF_SAMPLE_IN_BYTES
|
data/lib/edfize/signal.rb
CHANGED
@@ -5,5 +5,29 @@ module Edfize
|
|
5
5
|
:digital_minimum, :digital_maximum,
|
6
6
|
:prefiltering, :samples_per_data_record,
|
7
7
|
:reserved_area, :samples
|
8
|
+
|
9
|
+
SIGNAL_CONFIG = {
|
10
|
+
label: { size: 16, after_read: :strip, name: 'Label' },
|
11
|
+
transducer_type: { size: 80, after_read: :strip, name: 'Transducer Type' },
|
12
|
+
physical_dimension: { size: 8, after_read: :strip, name: 'Physical Dimension' },
|
13
|
+
physical_minimum: { size: 8, after_read: :to_f, name: 'Physical Minimum' },
|
14
|
+
physical_maximum: { size: 8, after_read: :to_f, name: 'Physical Maximum' },
|
15
|
+
digital_minimum: { size: 8, after_read: :to_i, name: 'Digital Minimum' },
|
16
|
+
digital_maximum: { size: 8, after_read: :to_i, name: 'Digital Maximum' },
|
17
|
+
prefiltering: { size: 80, after_read: :strip, name: 'Prefiltering' },
|
18
|
+
samples_per_data_record: { size: 8, after_read: :to_i, name: 'Samples Per Data Record' },
|
19
|
+
reserved_area: { size: 32, name: 'Reserved Area' }
|
20
|
+
}
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@samples = []
|
24
|
+
end
|
25
|
+
|
26
|
+
def print_header
|
27
|
+
SIGNAL_CONFIG.each do |section, hash|
|
28
|
+
puts " #{hash[:name]}#{' '*(29 - hash[:name].size)}: " + self.send(section).to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
8
32
|
end
|
9
33
|
end
|
data/lib/edfize/tests/runner.rb
CHANGED
@@ -27,17 +27,6 @@ module Edfize
|
|
27
27
|
results.each do |result|
|
28
28
|
print_result(result)
|
29
29
|
end
|
30
|
-
|
31
|
-
# test_expected_length(edf) ? nil : failure_count += 1
|
32
|
-
# test_count += 1
|
33
|
-
|
34
|
-
# test_reserved_area_blank(edf) ? nil : failure_count += 1
|
35
|
-
# test_count += 1
|
36
|
-
|
37
|
-
# test_reserved_signal_areas_blank(edf) ? nil : failure_count += 1
|
38
|
-
# test_count += 1
|
39
|
-
|
40
|
-
# [test_count, failure_count]
|
41
30
|
end
|
42
31
|
|
43
32
|
def print_result(result)
|
data/lib/edfize/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: edfize
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.beta3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Remo Mueller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|