edfize 0.1.0.beta8 → 0.1.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -16
- data/README.md +17 -126
- data/edfize.gemspec +0 -2
- data/lib/edfize/edf.rb +134 -164
- data/lib/edfize/signal.rb +12 -42
- data/lib/edfize/version.rb +1 -1
- data/lib/edfize.rb +10 -27
- metadata +2 -22
- data/lib/edfize/tests/check_length.rb +0 -16
- data/lib/edfize/tests/check_reserved_area.rb +0 -15
- data/lib/edfize/tests/check_reserved_signal_areas.rb +0 -17
- data/lib/edfize/tests/result.rb +0 -7
- data/lib/edfize/tests/runner.rb +0 -43
- data/lib/edfize/tests.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2067540b9f8f67b1f56d3b14932dd5941aaa8c5e
|
4
|
+
data.tar.gz: 78bd7d36d221cd07a77a9ecefb9b6f67b68d0f1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf85cdd7723bb127c1dcbfaa2cf5707b6916a65c9729ea51857d38107bb1042f1655cdf0b5584cc1a951601fe9f2b87a0deadeba56c2e654f28f033da8a70090
|
7
|
+
data.tar.gz: b120948717c189f774f6a6c58a6764d2d7d7df5a98aceca29cf3323acd70e509d97471b1519797ed507d142771d2e170d08716af8a62ed2635bba6773e7b1e94
|
data/CHANGELOG.md
CHANGED
@@ -1,21 +1,8 @@
|
|
1
1
|
## 0.1.0
|
2
2
|
- Initial EDF class to load headers and signals into Ruby objects
|
3
3
|
- `edfize` command has the following actions:
|
4
|
-
- `
|
5
|
-
|
6
|
-
|
7
|
-
- To suppress descriptive test failures, add the flag `--quiet`
|
8
|
-
- `edfize test --quiet`
|
9
|
-
- Both flags can be used together
|
10
|
-
- `edfize test --failing --quiet`
|
11
|
-
- `run`: Prints out the headers of all edfs in the current directory and subdirectories
|
4
|
+
- `check`: Validates EDFs in the current directory for errors
|
5
|
+
- `test`: Same as `check`
|
6
|
+
- `run`: Prints out the headers of all edfs in the current directory
|
12
7
|
- `help`: Displays information about `edfize` along with all available commands
|
13
8
|
- `version`: Displays the current `edfize` version
|
14
|
-
- `edfize test` checks for the following:
|
15
|
-
- Expected Length Check: The expected total size is computed from the (`number
|
16
|
-
of data records` * `total samples across all signals`) + `size of header` and
|
17
|
-
this is compared to the actual file size.
|
18
|
-
- Reserved Area Checks: The header and the individual header reserved areas are
|
19
|
-
checked to validate that they are blank. Non-blank areas are a sign that the
|
20
|
-
edf header is corrupt and that data from the signal data block have leaked into
|
21
|
-
the header itself.
|
data/README.md
CHANGED
@@ -8,35 +8,33 @@ Ruby gem used to load, validate, and parse European Data Format files. Used for
|
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
11
|
-
|
11
|
+
Add this line to your application's Gemfile:
|
12
12
|
|
13
|
-
|
13
|
+
gem 'edfize'
|
14
14
|
|
15
|
-
|
15
|
+
And then execute:
|
16
16
|
|
17
|
-
|
17
|
+
$ bundle
|
18
18
|
|
19
|
-
|
19
|
+
Or install it yourself as:
|
20
20
|
|
21
|
-
|
22
|
-
edfize test
|
21
|
+
$ gem install edfize
|
23
22
|
|
24
|
-
|
23
|
+
## Usage
|
25
24
|
|
26
|
-
|
27
|
-
- **Reserved Area Checks**: Check that reserved areas are blank. Non-blank reserved areas can indicate a sign of header or EDF file corruption.
|
25
|
+
### Validate EDFs
|
28
26
|
|
29
|
-
|
27
|
+
Use `edfize check` to check that EDFs stored in the current directory have a valid format.
|
30
28
|
|
31
|
-
-
|
32
|
-
|
29
|
+
cd <edf-directory>
|
30
|
+
edfize check
|
33
31
|
|
34
32
|
### Print Signal Header information
|
35
33
|
|
36
34
|
Use `edfize run` to print out signal header information for each EDF in the current directory.
|
37
35
|
|
38
36
|
cd <edf-directory>
|
39
|
-
edfize
|
37
|
+
edfize check
|
40
38
|
|
41
39
|
### View A List of All Available Commands for Edfize
|
42
40
|
|
@@ -50,118 +48,11 @@ Use `edfize version` to check the version of Edfize.
|
|
50
48
|
|
51
49
|
edfize version
|
52
50
|
|
53
|
-
###
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
`
|
58
|
-
```ruby
|
59
|
-
# Tutorial 01 - Load EDF and Signals
|
60
|
-
#
|
61
|
-
# gem install edfize
|
62
|
-
#
|
63
|
-
# ruby tutorial_01_load_edf_and_signals.rb
|
64
|
-
#
|
65
|
-
# The EDF exists at:
|
66
|
-
#
|
67
|
-
# https://sleepdata.org/datasets/shhs/files/edfs/shhs1?f=shhs1-200001.edf
|
68
|
-
#
|
69
|
-
|
70
|
-
require 'rubygems'
|
71
|
-
require 'edfize'
|
72
|
-
|
73
|
-
# Loads the file and reads the EDF Header
|
74
|
-
edf = Edfize::Edf.new('shhs1-200001.edf')
|
75
|
-
|
76
|
-
# Loads the data section of the EDF into Signal objects
|
77
|
-
edf.load_signals
|
78
|
-
|
79
|
-
# Print out information on the signals
|
80
|
-
puts "EDF #{edf.filename} contains the following #{edf.signals.count} signal#{'s' unless edf.signals.count == 1}:\n\n"
|
81
|
-
|
82
|
-
edf.signals.each do |signal|
|
83
|
-
puts "Signal"
|
84
|
-
puts " Label : #{signal.label}"
|
85
|
-
puts " Samples Per Data Record : #{signal.samples_per_data_record}"
|
86
|
-
puts " First 10 Physical Values : #{(signal.physical_values[0..10] + ['...']).inspect}\n\n"
|
87
|
-
end
|
88
|
-
```
|
89
|
-
|
90
|
-
When run, the code above will output the following:
|
91
|
-
|
92
|
-
```console
|
93
|
-
EDF shhs1-200001.edf contains the following 14 signals:
|
94
|
-
|
95
|
-
Signal
|
96
|
-
Label : SaO2
|
97
|
-
Samples Per Data Record : 1
|
98
|
-
First 10 Physical Values : [95.31242847333486, 95.31242847333486, 95.31242847333486, 95.31242847333486, 95.31242847333486, 95.31242847333486, 95.31242847333486, 95.31242847333486, 94.14053559166858, 94.14053559166858, 94.14053559166858, "..."]
|
99
|
-
|
100
|
-
Signal
|
101
|
-
Label : H.R.
|
102
|
-
Samples Per Data Record : 1
|
103
|
-
First 10 Physical Values : [77.34416723887999, 77.34416723887999, 77.34416723887999, 76.56595712214848, 76.56595712214848, 76.56595712214848, 75.00190737773708, 75.00190737773708, 75.00190737773708, 75.00190737773708, 75.00190737773708, "..."]
|
104
|
-
|
105
|
-
Signal
|
106
|
-
Label : EEG(sec)
|
107
|
-
Samples Per Data Record : 125
|
108
|
-
First 10 Physical Values : [-4.411764705882348, 5.392156862745111, 2.4509803921568647, 0.49019607843136725, -0.49019607843136725, -10.294117647058826, 3.4313725490196134, 12.25490196078431, -1.470588235294116, -2.4509803921568647, -8.333333333333329, "..."]
|
109
|
-
|
110
|
-
Signal
|
111
|
-
Label : ECG
|
112
|
-
Samples Per Data Record : 125
|
113
|
-
First 10 Physical Values : [0.03431372549019618, 0.03431372549019618, 0.03431372549019618, 0.03431372549019618, 0.044117647058823595, 0.044117647058823595, 0.044117647058823595, 0.044117647058823595, 0.044117647058823595, 0.03431372549019618, 0.03431372549019618, "..."]
|
114
|
-
|
115
|
-
Signal
|
116
|
-
Label : EMG
|
117
|
-
Samples Per Data Record : 125
|
118
|
-
First 10 Physical Values : [12.622549019607845, 3.7990196078431353, -3.5539215686274517, -2.5735294117647065, 8.455882352941174, 1.5931372549019613, 9.436274509803923, -8.700980392156861, -2.5735294117647065, 13.112745098039213, -12.867647058823529, "..."]
|
119
|
-
|
120
|
-
Signal
|
121
|
-
Label : EOG(L)
|
122
|
-
Samples Per Data Record : 50
|
123
|
-
First 10 Physical Values : [28.921568627450966, 17.15686274509804, 25.0, 19.117647058823536, -5.392156862745097, -9.313725490196077, -0.49019607843136725, -1.470588235294116, 1.470588235294116, -1.470588235294116, 0.49019607843136725, "..."]
|
124
|
-
|
125
|
-
Signal
|
126
|
-
Label : EOG(R)
|
127
|
-
Samples Per Data Record : 50
|
128
|
-
First 10 Physical Values : [12.25490196078431, 1.470588235294116, 10.294117647058812, 5.392156862745111, 17.15686274509804, 18.137254901960773, 25.980392156862735, 32.84313725490196, 25.0, 26.960784313725497, 22.058823529411768, "..."]
|
129
|
-
|
130
|
-
Signal
|
131
|
-
Label : EEG
|
132
|
-
Samples Per Data Record : 125
|
133
|
-
First 10 Physical Values : [-2.4509803921568647, 1.470588235294116, -9.313725490196077, -6.372549019607845, -0.49019607843136725, -10.294117647058826, -12.25490196078431, -12.25490196078431, -7.352941176470594, 1.470588235294116, 6.372549019607845, "..."]
|
134
|
-
|
135
|
-
Signal
|
136
|
-
Label : THOR RES
|
137
|
-
Samples Per Data Record : 10
|
138
|
-
First 10 Physical Values : [0.207843137254902, 0.207843137254902, 0.15294117647058825, 0.0980392156862745, 0.03529411764705881, -0.0117647058823529, -0.050980392156862786, -0.08235294117647052, -0.10588235294117654, -0.1215686274509804, -0.13725490196078427, "..."]
|
139
|
-
|
140
|
-
Signal
|
141
|
-
Label : ABDO RES
|
142
|
-
Samples Per Data Record : 10
|
143
|
-
First 10 Physical Values : [0.30980392156862746, 0.24705882352941178, 0.16078431372549018, 0.06666666666666665, -0.0039215686274509665, -0.08235294117647052, -0.1607843137254903, -0.2078431372549019, -0.2313725490196079, -0.2549019607843137, -0.2705882352941176, "..."]
|
144
|
-
|
145
|
-
Signal
|
146
|
-
Label : POSITION
|
147
|
-
Samples Per Data Record : 1
|
148
|
-
First 10 Physical Values : [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, "..."]
|
149
|
-
|
150
|
-
Signal
|
151
|
-
Label : LIGHT
|
152
|
-
Samples Per Data Record : 1
|
153
|
-
First 10 Physical Values : [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, "..."]
|
154
|
-
|
155
|
-
Signal
|
156
|
-
Label : NEW AIR
|
157
|
-
Samples Per Data Record : 10
|
158
|
-
First 10 Physical Values : [6.372549019607845, 6.372549019607845, 5.392156862745111, 3.4313725490196134, 7.35294117647058, 6.372549019607845, 8.333333333333343, 9.313725490196077, 6.372549019607845, 6.372549019607845, 7.35294117647058, "..."]
|
159
|
-
|
160
|
-
Signal
|
161
|
-
Label : OX stat
|
162
|
-
Samples Per Data Record : 1
|
163
|
-
First 10 Physical Values : [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, "..."]
|
164
|
-
```
|
51
|
+
### Upgrade Edfize
|
52
|
+
|
53
|
+
Use `gem install edfize` to update Edfize to the latest stable
|
54
|
+
|
55
|
+
Use `gem install edfize --pre` to update Edfize to the latest prerelease
|
165
56
|
|
166
57
|
## Contributing
|
167
58
|
|
data/edfize.gemspec
CHANGED
@@ -26,8 +26,6 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.test_files = spec.files.grep(%r{^(test)/})
|
27
27
|
spec.require_paths = ["lib"]
|
28
28
|
|
29
|
-
spec.add_dependency "colorize", "~> 0.7.2"
|
30
|
-
|
31
29
|
spec.add_development_dependency "bundler", "~> 1.6"
|
32
30
|
spec.add_development_dependency "rake"
|
33
31
|
end
|
data/lib/edfize/edf.rb
CHANGED
@@ -2,226 +2,196 @@ require 'edfize/signal'
|
|
2
2
|
|
3
3
|
module Edfize
|
4
4
|
class Edf
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# Header Information
|
9
|
-
attr_accessor :version
|
10
|
-
attr_accessor :local_patient_identification
|
11
|
-
attr_accessor :local_recording_identification
|
12
|
-
attr_accessor :start_date_of_recording
|
13
|
-
attr_accessor :start_time_of_recording
|
14
|
-
attr_accessor :number_of_bytes_in_header
|
15
|
-
attr_accessor :reserved
|
16
|
-
attr_accessor :number_of_data_records
|
17
|
-
attr_accessor :duration_of_a_data_record
|
18
|
-
attr_accessor :number_of_signals
|
19
|
-
|
20
|
-
attr_accessor :signals
|
21
|
-
|
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(:+)
|
36
|
-
|
37
|
-
SIZE_OF_SAMPLE_IN_BYTES = 2
|
38
|
-
|
39
|
-
# Used by tests
|
40
|
-
RESERVED_SIZE = HEADER_CONFIG[:reserved][:size]
|
41
|
-
|
42
|
-
def self.create(filename, &block)
|
43
|
-
edf = self.new(filename)
|
44
|
-
yield edf if block_given?
|
45
|
-
edf
|
46
|
-
end
|
5
|
+
attr_reader :signals
|
6
|
+
|
7
|
+
HEADER_OFFSET = 256
|
47
8
|
|
48
9
|
def initialize(filename)
|
49
10
|
@filename = filename
|
50
11
|
@signals = []
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
12
|
+
signal_labels
|
13
|
+
transducer_types
|
14
|
+
physical_dimensions
|
15
|
+
physical_minimums
|
16
|
+
physical_maximums
|
17
|
+
digital_minimums
|
18
|
+
digital_maximums
|
19
|
+
prefilterings
|
20
|
+
samples_in_data_records
|
21
|
+
reserved_areas
|
22
|
+
end
|
23
|
+
|
24
|
+
def size
|
25
|
+
File.size(@filename)
|
55
26
|
end
|
56
27
|
|
57
|
-
def
|
58
|
-
|
28
|
+
def print_header
|
29
|
+
puts @filename
|
30
|
+
puts "#{size} bytes (Total File Size)"
|
31
|
+
puts "'#{header_version}' (0)"
|
32
|
+
puts "'#{header_local_patient_identification}' (local patient identification)"
|
33
|
+
puts "'#{header_local_recording_identification}' (local recording indentification)"
|
34
|
+
puts "'#{header_start_date_of_recording}' (dd.mm.yy start date of recording)"
|
35
|
+
puts "'#{header_start_time_of_recording}' (hh.mm.ss start time of recording)"
|
36
|
+
# puts "--- RESERVED"
|
37
|
+
puts "'#{number_of_data_records}' seconds (number of data records, -1 if unknown)"
|
38
|
+
puts "'#{duration_of_a_data_record}' seconds (duration of a data record)"
|
39
|
+
puts "'#{number_of_signals}' number of signals (ns) in data record"
|
40
|
+
signals.each_with_index do |signal, index|
|
41
|
+
puts "'#{signal.label}' (signal[#{index+1}] label)"
|
42
|
+
puts "'#{signal.physical_dimension}' (signal[#{index+1}] physical_dimension)"
|
43
|
+
puts "'#{signal.transducer_type}' (signal[#{index+1}] transducer_type)"
|
44
|
+
puts "'#{signal.physical_minimum}' (signal[#{index+1}] physical_minimum)"
|
45
|
+
puts "'#{signal.physical_maximum}' (signal[#{index+1}] physical_maximum)"
|
46
|
+
puts "'#{signal.digital_minimum}' (signal[#{index+1}] digital_minimum)"
|
47
|
+
puts "'#{signal.digital_maximum}' (signal[#{index+1}] digital_maximum)"
|
48
|
+
puts "'#{signal.prefiltering}' (signal[#{index+1}] prefiltering)"
|
49
|
+
puts "'#{signal.samples_in_data_record}' (signal[#{index+1}] samples_in_data_record)"
|
50
|
+
puts "'#{signal.reserved_area}' (signal[#{index+1}] reserved_area)"
|
51
|
+
end
|
59
52
|
end
|
60
53
|
|
61
|
-
def
|
62
|
-
|
54
|
+
def header_version
|
55
|
+
IO.binread(@filename, 8)
|
63
56
|
end
|
64
57
|
|
65
|
-
|
66
|
-
|
58
|
+
# 80 ascii : local patient identification (mind item 3 of the additional EDF+ specs)
|
59
|
+
def header_local_patient_identification
|
60
|
+
IO.binread(@filename, 80, 8)
|
67
61
|
end
|
68
62
|
|
69
|
-
#
|
70
|
-
def
|
71
|
-
|
63
|
+
# 80 ascii : local recording identification (mind item 4 of the additional EDF+ specs)
|
64
|
+
def header_local_recording_identification
|
65
|
+
IO.binread(@filename, 80, 88)
|
72
66
|
end
|
73
67
|
|
74
|
-
#
|
75
|
-
def
|
76
|
-
|
68
|
+
# 8 ascii : startdate of recording (dd.mm.yy) (mind item 2 of the additional EDF+ specs)
|
69
|
+
def header_start_date_of_recording
|
70
|
+
IO.binread(@filename, 8, 168)
|
77
71
|
end
|
78
72
|
|
79
|
-
|
80
|
-
|
73
|
+
# 8 ascii : starttime of recording (hh.mm.ss)
|
74
|
+
def header_start_time_of_recording
|
75
|
+
IO.binread(@filename, 8, 176)
|
81
76
|
end
|
82
77
|
|
83
|
-
|
84
|
-
|
78
|
+
# 8 ascii : number of bytes in header record
|
79
|
+
def number_of_bytes_in_header
|
80
|
+
IO.binread(@filename, 8, 184)
|
85
81
|
end
|
86
82
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
else
|
92
|
-
" #{units}" + (self.instance_variable_get("@#{section}") == 1 ? '' : 's')
|
93
|
-
end
|
94
|
-
result
|
95
|
-
end
|
83
|
+
# 44 ascii : reserved
|
84
|
+
# def reserved
|
85
|
+
# IO.binread(@filename, 44, 192)
|
86
|
+
# end
|
96
87
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
''
|
101
|
-
else
|
102
|
-
" #{description}"
|
103
|
-
end
|
104
|
-
result
|
88
|
+
# 8 ascii : number of data records (-1 if unknown, obey item 10 of the additional EDF+ specs)
|
89
|
+
def number_of_data_records
|
90
|
+
IO.binread(@filename, 8, 236)
|
105
91
|
end
|
106
92
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
puts "\nHeader Information"
|
111
|
-
HEADER_CONFIG.each do |section, hash|
|
112
|
-
puts "#{hash[:name]}#{' '*(31 - hash[:name].size)}: " + section_value_to_string(section) + section_units(section) + section_description(section)
|
113
|
-
end
|
114
|
-
puts "\nSignal Information"
|
115
|
-
signals.each_with_index do |signal, index|
|
116
|
-
puts "\n Position : #{index + 1}"
|
117
|
-
signal.print_header
|
118
|
-
end
|
119
|
-
puts "\nGeneral Information"
|
120
|
-
puts "Size of Header (bytes) : #{size_of_header}"
|
121
|
-
puts "Size of Data (bytes) : #{data_size}"
|
122
|
-
puts "Total Size (bytes) : #{edf_size}"
|
123
|
-
|
124
|
-
puts "Expected Size of Header (bytes): #{expected_size_of_header}"
|
125
|
-
puts "Expected Size of Data (bytes): #{expected_data_size}"
|
126
|
-
puts "Expected Total Size (bytes): #{expected_edf_size}"
|
93
|
+
# 8 ascii : duration of a data record, in seconds
|
94
|
+
def duration_of_a_data_record
|
95
|
+
IO.binread(@filename, 8, 244)
|
127
96
|
end
|
128
97
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
HEADER_CONFIG.keys.each do |section|
|
133
|
-
read_header_section(section)
|
134
|
-
end
|
98
|
+
# 4 ascii : number of signals (ns) in data record
|
99
|
+
def number_of_signals
|
100
|
+
IO.binread(@filename, 4, 252)
|
135
101
|
end
|
136
102
|
|
137
|
-
def
|
138
|
-
|
139
|
-
result = result.to_s.send(HEADER_CONFIG[section][:after_read]) unless HEADER_CONFIG[section][:after_read].to_s == ''
|
140
|
-
self.instance_variable_set("@#{section}", result)
|
103
|
+
def ns
|
104
|
+
Integer(self.number_of_signals) rescue 0
|
141
105
|
end
|
142
106
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
107
|
+
# ns * 16 ascii : ns * label (e.g. EEG Fpz-Cz or Body temp) (mind item 9 of the additional EDF+ specs)
|
108
|
+
def signal_labels
|
109
|
+
offset = HEADER_OFFSET
|
110
|
+
(0..ns-1).to_a.each do |signal_number|
|
111
|
+
@signals[signal_number] ||= Signal.new()
|
112
|
+
@signals[signal_number].label = IO.binread(@filename, 16, offset+(signal_number*16))
|
148
113
|
end
|
149
|
-
offset
|
150
114
|
end
|
151
115
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
def create_signals
|
116
|
+
# ns * 80 ascii : ns * transducer type (e.g. AgAgCl electrode)
|
117
|
+
def transducer_types
|
118
|
+
offset = HEADER_OFFSET + ns * 16
|
157
119
|
(0..ns-1).to_a.each do |signal_number|
|
158
120
|
@signals[signal_number] ||= Signal.new()
|
121
|
+
@signals[signal_number].transducer_type = IO.binread(@filename, 80, offset+(signal_number*80))
|
159
122
|
end
|
160
123
|
end
|
161
124
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
125
|
+
# ns * 8 ascii : ns * physical dimension (e.g. uV or degreeC)
|
126
|
+
def physical_dimensions
|
127
|
+
offset = HEADER_OFFSET + ns * (16 + 80)
|
128
|
+
(0..ns-1).to_a.each do |signal_number|
|
129
|
+
@signals[signal_number] ||= Signal.new()
|
130
|
+
@signals[signal_number].physical_dimension = IO.binread(@filename, 8, offset+(signal_number*8))
|
166
131
|
end
|
167
132
|
end
|
168
133
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
134
|
+
# ns * 8 ascii : ns * physical minimum (e.g. -500 or 34)
|
135
|
+
def physical_minimums
|
136
|
+
offset = HEADER_OFFSET + ns * (16 + 80 + 8)
|
137
|
+
(0..ns-1).to_a.each do |signal_number|
|
138
|
+
@signals[signal_number] ||= Signal.new()
|
139
|
+
@signals[signal_number].physical_minimum = IO.binread(@filename, 8, offset+(signal_number*8))
|
174
140
|
end
|
175
|
-
offset
|
176
141
|
end
|
177
142
|
|
178
|
-
|
179
|
-
|
143
|
+
# ns * 8 ascii : ns * physical maximum (e.g. 500 or 40)
|
144
|
+
def physical_maximums
|
145
|
+
offset = HEADER_OFFSET + ns * (16 + 80 + 8 + 8)
|
180
146
|
(0..ns-1).to_a.each do |signal_number|
|
181
|
-
|
182
|
-
|
183
|
-
result = result.to_s.send(Signal::SIGNAL_CONFIG[section][:after_read]) unless Signal::SIGNAL_CONFIG[section][:after_read].to_s == ''
|
184
|
-
@signals[signal_number].send("#{section}=", result)
|
147
|
+
@signals[signal_number] ||= Signal.new()
|
148
|
+
@signals[signal_number].physical_maximum = IO.binread(@filename, 8, offset+(signal_number*8))
|
185
149
|
end
|
186
150
|
end
|
187
151
|
|
188
|
-
|
189
|
-
|
190
|
-
|
152
|
+
# ns * 8 ascii : ns * digital minimum (e.g. -2048)
|
153
|
+
def digital_minimums
|
154
|
+
offset = HEADER_OFFSET + ns * (16 + 80 + 8 + 8 + 8)
|
155
|
+
(0..ns-1).to_a.each do |signal_number|
|
156
|
+
@signals[signal_number] ||= Signal.new()
|
157
|
+
@signals[signal_number].digital_minimum = IO.binread(@filename, 8, offset+(signal_number*8))
|
158
|
+
end
|
191
159
|
end
|
192
160
|
|
193
|
-
#
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
all_samples_per_data_record = @signals.collect{|s| s.samples_per_data_record}
|
200
|
-
total_samples_per_data_record = all_samples_per_data_record.inject(:+).to_i
|
201
|
-
|
202
|
-
offset = 0
|
203
|
-
offsets = []
|
204
|
-
all_samples_per_data_record.each do |samples_per_data_record|
|
205
|
-
offsets << offset
|
206
|
-
offset += samples_per_data_record
|
161
|
+
# ns * 8 ascii : ns * digital maximum (e.g. 2047)
|
162
|
+
def digital_maximums
|
163
|
+
offset = HEADER_OFFSET + ns * (16 + 80 + 8 + 8 + 8 + 8)
|
164
|
+
(0..ns-1).to_a.each do |signal_number|
|
165
|
+
@signals[signal_number] ||= Signal.new()
|
166
|
+
@signals[signal_number].digital_maximum = IO.binread(@filename, 8, offset+(signal_number*8))
|
207
167
|
end
|
168
|
+
end
|
208
169
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
end
|
170
|
+
# ns * 80 ascii : ns * prefiltering (e.g. HP:0.1Hz LP:75Hz)
|
171
|
+
def prefilterings
|
172
|
+
offset = HEADER_OFFSET + ns * (16 + 80 + 8 + 8 + 8 + 8 + 8)
|
173
|
+
(0..ns-1).to_a.each do |signal_number|
|
174
|
+
@signals[signal_number] ||= Signal.new()
|
175
|
+
@signals[signal_number].prefiltering = IO.binread(@filename, 80, offset+(signal_number*80))
|
216
176
|
end
|
217
177
|
end
|
218
178
|
|
219
|
-
|
220
|
-
|
179
|
+
# ns * 8 ascii : ns * nr of samples in each data record
|
180
|
+
def samples_in_data_records
|
181
|
+
offset = HEADER_OFFSET + ns * (16 + 80 + 8 + 8 + 8 + 8 + 8 + 80)
|
182
|
+
(0..ns-1).to_a.each do |signal_number|
|
183
|
+
@signals[signal_number] ||= Signal.new()
|
184
|
+
@signals[signal_number].samples_in_data_record = IO.binread(@filename, 8, offset+(signal_number*8))
|
185
|
+
end
|
221
186
|
end
|
222
187
|
|
223
|
-
|
224
|
-
|
188
|
+
# ns * 32 ascii : ns * reserved
|
189
|
+
def reserved_areas
|
190
|
+
offset = HEADER_OFFSET + ns * (16 + 80 + 8 + 8 + 8 + 8 + 8 + 80 + 8)
|
191
|
+
(0..ns-1).to_a.each do |signal_number|
|
192
|
+
@signals[signal_number] ||= Signal.new()
|
193
|
+
@signals[signal_number].reserved_area = IO.binread(@filename, 32, offset+(signal_number*32))
|
194
|
+
end
|
225
195
|
end
|
226
196
|
end
|
227
197
|
end
|
data/lib/edfize/signal.rb
CHANGED
@@ -1,49 +1,19 @@
|
|
1
1
|
module Edfize
|
2
2
|
class Signal
|
3
|
-
attr_accessor :label, :transducer_type, :physical_dimension,
|
4
|
-
:
|
5
|
-
:digital_minimum, :digital_maximum,
|
6
|
-
:prefiltering, :samples_per_data_record,
|
7
|
-
:reserved_area, :digital_values, :physical_values
|
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
|
-
}
|
3
|
+
attr_accessor :label, :transducer_type, :physical_dimension, :physical_minimum, :physical_maximum,
|
4
|
+
:digital_minimum, :digital_maximum, :prefiltering, :samples_in_data_record, :reserved_area
|
21
5
|
|
22
6
|
def initialize
|
23
|
-
@
|
24
|
-
@
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
def print_header
|
35
|
-
SIGNAL_CONFIG.each do |section, hash|
|
36
|
-
puts " #{hash[:name]}#{' '*(29 - hash[:name].size)}: " + self.send(section).to_s
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# Physical value (dimension PhysiDim) = (ASCIIvalue-DigiMin)*(PhysiMax-PhysiMin)/(DigiMax-DigiMin) + PhysiMin.
|
41
|
-
def calculate_physical_values!
|
42
|
-
@physical_values = @digital_values.collect{|sample| ( sample - @digital_minimum ) * ( @physical_maximum - @physical_minimum ) / ( @digital_maximum - @digital_minimum) + @physical_minimum }
|
43
|
-
end
|
44
|
-
|
45
|
-
def samples
|
46
|
-
@physical_values
|
7
|
+
@label = ''
|
8
|
+
@physical_dimension = ''
|
9
|
+
@transducer_type = ''
|
10
|
+
@physical_minimum = ''
|
11
|
+
@physical_maximum = ''
|
12
|
+
@digital_minimum = ''
|
13
|
+
@digital_maximum = ''
|
14
|
+
@prefiltering = ''
|
15
|
+
@samples_in_data_record = ''
|
16
|
+
@reserved_area = ''
|
47
17
|
end
|
48
18
|
|
49
19
|
end
|
data/lib/edfize/version.rb
CHANGED
data/lib/edfize.rb
CHANGED
@@ -1,16 +1,13 @@
|
|
1
1
|
require 'edfize/edf'
|
2
|
-
require 'edfize/tests'
|
3
2
|
require 'edfize/version'
|
4
3
|
|
5
|
-
require 'colorize'
|
6
|
-
|
7
4
|
module Edfize
|
8
5
|
def self.launch(argv)
|
9
6
|
case argv.first.to_s.scan(/\w/).first
|
10
7
|
when 'v'
|
11
8
|
version
|
12
9
|
when 'c', 't'
|
13
|
-
check
|
10
|
+
check
|
14
11
|
when 'r'
|
15
12
|
print_headers
|
16
13
|
else
|
@@ -20,7 +17,7 @@ module Edfize
|
|
20
17
|
|
21
18
|
def self.print_headers
|
22
19
|
puts "----------------"
|
23
|
-
|
20
|
+
edfs_in_current_directory.each do |edf_file_name|
|
24
21
|
edf = Edfize::Edf.new(edf_file_name)
|
25
22
|
edf.print_header
|
26
23
|
puts "----------------"
|
@@ -31,21 +28,9 @@ module Edfize
|
|
31
28
|
puts "Edfize #{Edfize::VERSION::STRING}"
|
32
29
|
end
|
33
30
|
|
34
|
-
def self.check
|
35
|
-
|
36
|
-
edf_count
|
37
|
-
test_count = 0
|
38
|
-
failure_count = 0
|
39
|
-
puts "Started\n"
|
40
|
-
edfs_in_current_directory_and_subdirectories.each do |edf_file_name|
|
41
|
-
edf = Edfize::Edf.new(edf_file_name)
|
42
|
-
runner = Edfize::Tests::Runner.new(edf, argv)
|
43
|
-
runner.run_tests
|
44
|
-
test_count += runner.tests_run
|
45
|
-
failure_count += runner.tests_failed
|
46
|
-
end
|
47
|
-
puts "\nFinished in #{Time.now - test_start_time}s"
|
48
|
-
puts "#{edf_count} EDF#{'s' unless edf_count == 1}, #{test_count} test#{'s' unless test_count == 1}, " + "#{failure_count} failure#{'s' unless failure_count == 1}".colorize( failure_count == 0 ? :green : :red )
|
31
|
+
def self.check
|
32
|
+
edf_count = edfs_in_current_directory.count
|
33
|
+
puts "Checking #{edf_count} EDF#{'s' unless edf_count == 1}"
|
49
34
|
end
|
50
35
|
|
51
36
|
def self.help
|
@@ -53,11 +38,9 @@ module Edfize
|
|
53
38
|
Usage: edfize COMMAND [ARGS]
|
54
39
|
|
55
40
|
The most common edfize commands are:
|
56
|
-
[
|
57
|
-
|
58
|
-
|
59
|
-
[r]un Print EDF header information
|
60
|
-
[h]elp Show edfize command documentation
|
41
|
+
[c]heck Check EDFs in current directory for errors
|
42
|
+
[t]est Same as [c]heck
|
43
|
+
[h]elp Show edfize command-line documentation
|
61
44
|
[v]ersion Returns the version of Edfize
|
62
45
|
|
63
46
|
Commands can be referenced by the first letter:
|
@@ -67,7 +50,7 @@ EOT
|
|
67
50
|
puts help_message
|
68
51
|
end
|
69
52
|
|
70
|
-
def self.
|
71
|
-
Dir.glob('
|
53
|
+
def self.edfs_in_current_directory
|
54
|
+
Dir.glob('*.edf')
|
72
55
|
end
|
73
56
|
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
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.pre
|
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-
|
11
|
+
date: 2014-04-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: colorize
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 0.7.2
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 0.7.2
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: bundler
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,12 +56,6 @@ files:
|
|
70
56
|
- lib/edfize.rb
|
71
57
|
- lib/edfize/edf.rb
|
72
58
|
- lib/edfize/signal.rb
|
73
|
-
- lib/edfize/tests.rb
|
74
|
-
- lib/edfize/tests/check_length.rb
|
75
|
-
- lib/edfize/tests/check_reserved_area.rb
|
76
|
-
- lib/edfize/tests/check_reserved_signal_areas.rb
|
77
|
-
- lib/edfize/tests/result.rb
|
78
|
-
- lib/edfize/tests/runner.rb
|
79
59
|
- lib/edfize/version.rb
|
80
60
|
homepage: https://github.com/sleepepi/edfize
|
81
61
|
licenses:
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module Edfize
|
2
|
-
module Tests
|
3
|
-
module CheckLength
|
4
|
-
# This test checks that the length calculated from the EDF header matches
|
5
|
-
# the total length of the file
|
6
|
-
def test_expected_length(runner)
|
7
|
-
result = Result.new
|
8
|
-
result.passes = (runner.edf.expected_edf_size == runner.edf.edf_size)
|
9
|
-
result.pass_fail = " #{result.passes ? 'PASS' : 'FAIL'}".colorize( result.passes ? :green : :red ) + " Expected File Size"
|
10
|
-
result.expected = " Expected : #{runner.edf.expected_edf_size} bytes"
|
11
|
-
result.actual = " Actual : #{runner.edf.edf_size} bytes"
|
12
|
-
result
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Edfize
|
2
|
-
module Tests
|
3
|
-
module CheckReservedArea
|
4
|
-
# This test checks that the reserved area in the header is blank
|
5
|
-
def test_reserved_area_blank(runner)
|
6
|
-
result = Result.new
|
7
|
-
result.passes = (runner.edf.reserved == ' ' * Edf::RESERVED_SIZE)
|
8
|
-
result.pass_fail = " #{result.passes ? 'PASS' : 'FAIL'}".colorize( result.passes ? :green : :red ) + " Reserved Area Blank"
|
9
|
-
result.expected = " Expected : #{(' ' * Edf::RESERVED_SIZE).inspect}"
|
10
|
-
result.actual = " Actual : #{runner.edf.reserved.to_s.inspect}"
|
11
|
-
result
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Edfize
|
2
|
-
module Tests
|
3
|
-
module CheckReservedSignalAreas
|
4
|
-
# This test checks that the reserved areas in the signal headers are blank
|
5
|
-
def test_reserved_signal_areas_blank(runner)
|
6
|
-
reserved_areas = runner.edf.signals.collect(&:reserved_area)
|
7
|
-
|
8
|
-
result = Result.new
|
9
|
-
result.passes = (reserved_areas.reject{|r| r.to_s.strip == ''}.count == 0)
|
10
|
-
result.pass_fail = " #{result.passes ? 'PASS' : 'FAIL'}".colorize( result.passes ? :green : :red ) + " Signal Reserved Area Blank"
|
11
|
-
result.expected = " Expected : #{[''] * runner.edf.signals.count}"
|
12
|
-
result.actual = " Actual : #{reserved_areas}"
|
13
|
-
result
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/lib/edfize/tests/result.rb
DELETED
data/lib/edfize/tests/runner.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
module Edfize
|
2
|
-
module Tests
|
3
|
-
class Runner
|
4
|
-
attr_reader :tests_run, :tests_failed, :edf, :verbose, :show_passing
|
5
|
-
|
6
|
-
TESTS = %w( expected_length reserved_area_blank reserved_signal_areas_blank )
|
7
|
-
|
8
|
-
def initialize(edf, argv)
|
9
|
-
@tests_run = 0
|
10
|
-
@tests_failed = 0
|
11
|
-
@edf = edf
|
12
|
-
@verbose = argv.include?('--quiet') ? false : true
|
13
|
-
@show_passing = argv.include?('--failing') ? false : true
|
14
|
-
end
|
15
|
-
|
16
|
-
def run_tests
|
17
|
-
results = []
|
18
|
-
|
19
|
-
TESTS.each do |test_name|
|
20
|
-
result = Edfize::Tests.send("test_#{test_name}", self)
|
21
|
-
@tests_failed += 1 unless result.passes
|
22
|
-
@tests_run += 1
|
23
|
-
results << result
|
24
|
-
end
|
25
|
-
|
26
|
-
puts "\n#{@edf.filename}" if results.reject{|r| r.passes}.count > 0 or @show_passing
|
27
|
-
results.each do |result|
|
28
|
-
print_result(result)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def print_result(result)
|
33
|
-
if self.show_passing or !result.passes
|
34
|
-
puts result.pass_fail
|
35
|
-
unless result.passes or not self.verbose
|
36
|
-
puts result.expected
|
37
|
-
puts result.actual
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
data/lib/edfize/tests.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'edfize/tests/result'
|
2
|
-
require 'edfize/tests/runner'
|
3
|
-
require 'edfize/tests/check_length'
|
4
|
-
require 'edfize/tests/check_reserved_area'
|
5
|
-
require 'edfize/tests/check_reserved_signal_areas'
|
6
|
-
|
7
|
-
module Edfize
|
8
|
-
module Tests
|
9
|
-
extend CheckLength
|
10
|
-
extend CheckReservedArea
|
11
|
-
extend CheckReservedSignalAreas
|
12
|
-
end
|
13
|
-
end
|