edfize 0.1.0.beta2 → 0.1.0.beta3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|