fitreader 0.2.4 → 0.3.0
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/.gitignore +1 -1
- data/Gemfile.lock +43 -0
- data/README.md +27 -38
- data/fitreader.gemspec +1 -3
- data/lib/fitreader.rb +3 -53
- data/lib/fitreader/data_field.rb +38 -0
- data/lib/fitreader/data_record.rb +14 -0
- data/lib/fitreader/definition_record.rb +28 -0
- data/lib/fitreader/field_definition.rb +8 -12
- data/lib/fitreader/file_header.rb +9 -14
- data/lib/fitreader/fit.rb +49 -0
- data/lib/fitreader/fit_object.rb +8 -0
- data/lib/fitreader/message.rb +82 -0
- data/lib/fitreader/record_header.rb +25 -26
- data/lib/fitreader/sdk/{constants.yml → enums.yml} +0 -81
- data/lib/fitreader/sdk/fields.yml +2476 -0
- data/lib/fitreader/sdk/messages.yml +82 -0
- data/lib/fitreader/sdk/sdk.rb +23 -0
- data/lib/fitreader/unpack.rb +41 -0
- data/lib/fitreader/version.rb +1 -1
- metadata +19 -24
- data/lib/fitreader/definition.rb +0 -34
- data/lib/fitreader/degraded_record.rb +0 -37
- data/lib/fitreader/errors.rb +0 -18
- data/lib/fitreader/field_data.rb +0 -58
- data/lib/fitreader/fitfile.rb +0 -63
- data/lib/fitreader/message_type.rb +0 -24
- data/lib/fitreader/record.rb +0 -170
- data/lib/fitreader/sdk/types.yml +0 -2551
- data/lib/fitreader/static.rb +0 -25
- data/schema +0 -37
- data/spec/fitreader_interfaces_spec.rb +0 -57
- data/spec/fitreader_readfile_spec.rb +0 -17
- data/spec/fitreader_static_spec.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa987e945eec8a037f1cd52e0d14ef91f26b72bd
|
4
|
+
data.tar.gz: c22121460e757cff8b7c20145bf06707af7cba47
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 972d02706d802da6d0965d2379dcae5e0e6326be5206bc55abbb27eb367be4c803faa5cdbc6d22482486baa767e8db69276c3eddbfe1fca89773291c19e24c02
|
7
|
+
data.tar.gz: 2857b1d11a428303e190b658be0770a24c0c78bf3a95a344e3cc0ab33e13c79249eab15988afd1109b64262186b102f38a4f41426fb3f43c9ef7439e337a1f7c
|
data/.gitignore
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
fitreader (0.3.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
coderay (1.1.1)
|
10
|
+
diff-lcs (1.3)
|
11
|
+
method_source (0.8.2)
|
12
|
+
pry (0.10.4)
|
13
|
+
coderay (~> 1.1.0)
|
14
|
+
method_source (~> 0.8.1)
|
15
|
+
slop (~> 3.4)
|
16
|
+
rake (10.5.0)
|
17
|
+
rspec (3.6.0)
|
18
|
+
rspec-core (~> 3.6.0)
|
19
|
+
rspec-expectations (~> 3.6.0)
|
20
|
+
rspec-mocks (~> 3.6.0)
|
21
|
+
rspec-core (3.6.0)
|
22
|
+
rspec-support (~> 3.6.0)
|
23
|
+
rspec-expectations (3.6.0)
|
24
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
25
|
+
rspec-support (~> 3.6.0)
|
26
|
+
rspec-mocks (3.6.0)
|
27
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
28
|
+
rspec-support (~> 3.6.0)
|
29
|
+
rspec-support (3.6.0)
|
30
|
+
slop (3.6.0)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
bundler (~> 1.12)
|
37
|
+
fitreader!
|
38
|
+
pry (~> 0.10.4)
|
39
|
+
rake (~> 10.0)
|
40
|
+
rspec (~> 3.5)
|
41
|
+
|
42
|
+
BUNDLED WITH
|
43
|
+
1.14.6
|
data/README.md
CHANGED
@@ -35,7 +35,7 @@ require 'fitreader'
|
|
35
35
|
to the class you wish to call it from. After that, it's a simple matter of calling
|
36
36
|
|
37
37
|
```ruby
|
38
|
-
|
38
|
+
fit_file = Fit.new(path_to_fit_file)
|
39
39
|
```
|
40
40
|
|
41
41
|
All of the interface and convenience functions can be found in lib/fitreader.rb.
|
@@ -43,64 +43,53 @@ All of the interface and convenience functions can be found in lib/fitreader.rb.
|
|
43
43
|
After reading a FIT file, the file header can be inspected by calling
|
44
44
|
|
45
45
|
```ruby
|
46
|
-
|
46
|
+
fit_file.header
|
47
47
|
```
|
48
48
|
|
49
49
|
A digest of the records found in the file is shown with
|
50
50
|
|
51
51
|
```ruby
|
52
|
-
|
52
|
+
fit_file.digest
|
53
53
|
```
|
54
54
|
|
55
|
-
which will return a list similar to the following, showing respectively the
|
55
|
+
which will return a list similar to the following, showing respectively the name of the record type (as defined in the FIT SDK), and the number of records parsed
|
56
56
|
|
57
57
|
```ruby
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
58
|
+
{:file_id=>1,
|
59
|
+
:file_creator=>1,
|
60
|
+
:device_settings=>1,
|
61
|
+
:user_profile=>1,
|
62
|
+
:sensor_info=>5,
|
63
|
+
:sport=>1,
|
64
|
+
:zones_target=>1,
|
65
|
+
:record=>4988,
|
66
|
+
:event=>120,
|
67
|
+
:device_info=>30,
|
68
|
+
:source=>43,
|
69
|
+
:segment_lap=>2,
|
70
|
+
:lap=>1,
|
71
|
+
:session=>1,
|
72
|
+
:activity=>1,
|
73
|
+
:battery_info=>20}
|
72
74
|
```
|
73
75
|
|
74
76
|
Armed with this information, we can call
|
75
77
|
|
76
78
|
```ruby
|
77
|
-
|
79
|
+
fit_file.type <name>
|
78
80
|
```
|
79
81
|
|
80
|
-
where
|
82
|
+
where <name> is the name supplied by the previous command, for example
|
81
83
|
|
82
84
|
```ruby
|
83
|
-
|
84
|
-
Fitreader.get_message_type :session
|
85
|
+
fit_file.type :session
|
85
86
|
```
|
86
87
|
|
87
|
-
will
|
88
|
+
will fetch the session record(s) in the form of a Message object. This object contains three fields: a name, a global_num (as defined by the FIT SDK), and an array of records.
|
88
89
|
|
89
|
-
The
|
90
|
-
|
91
|
-
The records array contains a list or Record objects. A Record object contains a fields array and an error_fields array. The fields array is a list of the actual data contined within this record, for example timestamp, or coordinated, etc. The particular FieldData object includes the name of the field (also found in the definition), along with the raw_value and the processed value which may differ in the case of, for example, a coordinate.
|
92
|
-
|
93
|
-
The error_fields array contains any fields defined in the FIT file itself that aren't defined within the SDK. Without the SKD we can most likely never know what the field represents or whether the raw value would need further processing to make sense to us. These are included mostly for debugging and future convenience sake.
|
94
|
-
|
95
|
-
The results of this function are probably a lot more data that we generally need, so a convenience function is included that distills the results of the previous call into a more efficient dataset
|
96
|
-
|
97
|
-
```ruby
|
98
|
-
Fitreader.record_values 20
|
99
|
-
Fitreader.record_values :record
|
100
|
-
```
|
101
|
-
|
102
|
-
This will return an array of hashes, one hash for each record of the specified type. The hashes contain only the field name and value, for example
|
90
|
+
The records array contains a list of hashes, with key-value pairs of field name (also according to the message-type definition) and value, for example timestamp, or coordinates, etc.
|
103
91
|
|
92
|
+
For example:
|
104
93
|
```ruby
|
105
94
|
{:timestamp=>2016-04-09 11:19:51 UTC,
|
106
95
|
:position_lat=>57.711100755259395,
|
@@ -116,7 +105,7 @@ This will return an array of hashes, one hash for each record of the specified t
|
|
116
105
|
|
117
106
|
Some things to watch out for:
|
118
107
|
|
119
|
-
- speed is recorded as m/s
|
108
|
+
- speed is recorded as m/s, rather than kph as one might expect.
|
120
109
|
|
121
110
|
## Development
|
122
111
|
|
data/fitreader.gemspec
CHANGED
@@ -31,7 +31,5 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_development_dependency "bundler", "~> 1.12"
|
32
32
|
spec.add_development_dependency "rake", "~> 10.0"
|
33
33
|
spec.add_development_dependency "rspec", "~> 3.5"
|
34
|
-
spec.add_development_dependency "pry"
|
35
|
-
|
36
|
-
# spec.add_dependency('bindata')
|
34
|
+
spec.add_development_dependency "pry", '~> 0.10.4'
|
37
35
|
end
|
data/lib/fitreader.rb
CHANGED
@@ -1,55 +1,5 @@
|
|
1
|
-
require 'fitreader/fitfile'
|
2
|
-
require 'fitreader/version'
|
3
|
-
require 'fitreader/static'
|
4
|
-
|
5
1
|
module Fitreader
|
6
|
-
def self.read(path)
|
7
|
-
@f = FitFile.new(path)
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.header
|
11
|
-
@f.header
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.available_records
|
15
|
-
@f.messages
|
16
|
-
.select { |_,v| !v.any? { |z| z.definition.name.nil? } }
|
17
|
-
.select { |_,v| !v.any? { |z| z.records.length.zero? } }
|
18
|
-
.collect { |x| [x[0], x[1].first.definition.name, x[1].collect(&:records).flatten.length] }
|
19
|
-
.sort
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.get_message_type(filter)
|
23
|
-
if filter.is_a?(Symbol)
|
24
|
-
res = @f.messages.find { |_,y| y.any? { |z| z.definition.name == filter } }
|
25
|
-
res[1] unless res.nil?
|
26
|
-
elsif filter.is_a?(Integer)
|
27
|
-
@f.messages[filter]
|
28
|
-
else
|
29
|
-
raise ArgumentError, 'needs a string or a symbol'
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.record_values(filter)
|
34
|
-
message = get_message_type filter
|
35
|
-
message.collect(&:record_values).flatten
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.error_messages
|
39
|
-
@f.messages.select { |_, v| !v.any? { |x| x.undefined_records.empty? } }
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.error_fields(filter)
|
43
|
-
message = get_message_type filter
|
44
|
-
message.collect(&:error_fields).flatten
|
45
|
-
end
|
46
|
-
|
47
|
-
# def self.filter_by_scope(filter)
|
48
|
-
# valid = Static.scope.include? filter
|
49
|
-
# unless valid
|
50
|
-
# @f.records.select { |x| x.type == filter }
|
51
|
-
# else
|
52
|
-
# puts "invalid scope, must be one of #{Static.scope}"
|
53
|
-
# end
|
54
|
-
# end
|
55
2
|
end
|
3
|
+
|
4
|
+
require 'fitreader/fit'
|
5
|
+
require 'fitreader/version'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class DataField < FitObject
|
2
|
+
TYPES = {
|
3
|
+
0 => { size: 1, unpack_type: 'C', endian: 0, invalid: 255 },
|
4
|
+
1 => { size: 1, unpack_type: 'c', endian: 0, invalid: 127 },
|
5
|
+
2 => { size: 1, unpack_type: 'C', endian: 0, invalid: 255 },
|
6
|
+
3 => { size: 2, unpack_type: { big: 's>', little: 's<' }, endian: 1, invalid: 32767 },
|
7
|
+
4 => { size: 2, unpack_type: { big: 'S>', little: 'S<' }, endian: 1, invalid: 65535 },
|
8
|
+
5 => { size: 4, unpack_type: { big: 'l>', little: 'l<' }, endian: 1, invalid: 2147483647 },
|
9
|
+
6 => { size: 4, unpack_type: { big: 'L>', little: 'L<' }, endian: 1, invalid: 4294967295 },
|
10
|
+
7 => { size: 1, unpack_type: 'Z*', endian: 0, invalid: 0 },
|
11
|
+
8 => { size: 4, unpack_type: { big: 'e', little: 'g' }, endian: 1, invalid: 4294967295 },
|
12
|
+
9 => { size: 8, unpack_type: { big: 'E', little: 'G' }, endian: 1, invalid: 18446744073709551615 },
|
13
|
+
10 => { size: 1, unpack_type: 'C', endian: 0, invalid: 0 },
|
14
|
+
11 => { size: 2, unpack_type: { big: 'S>', little: 'S<' }, endian: 1, invalid: 0 },
|
15
|
+
12 => { size: 4, unpack_type: { big: 'L>', little: 'L<' }, endian: 1, invalid: 0 },
|
16
|
+
13 => { size: 1, unpack_type: 'C', endian: 0, invalid: 0xFF },
|
17
|
+
14 => { size: 8, unpack_type: { big: 'q>', little: 'q<' }, endian: 1, invalid: 0x7FFFFFFFFFFFFFFF },
|
18
|
+
15 => { size: 8, unpack_type: { big: 'Q>', little: 'Q<' }, endian: 1, invalid: 0xFFFFFFFFFFFFFFFF },
|
19
|
+
16 => { size: 8, unpack_type: nil, endian: 1, invalid: 0x0000000000000000 }
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
attr_reader :raw, :valid
|
23
|
+
|
24
|
+
def initialize(io, d, arch)
|
25
|
+
base = TYPES[d.base_num]
|
26
|
+
char = d.endianness.zero? ? base[:unpack_type] : base[:unpack_type][arch]
|
27
|
+
@raw = read_multiple(io, char, d.size, base[:size])
|
28
|
+
@valid = check(@raw, base[:invalid])
|
29
|
+
end
|
30
|
+
|
31
|
+
def check(raw, invalid)
|
32
|
+
if raw.is_a? Array
|
33
|
+
raw.any? { |e| e != invalid }
|
34
|
+
else
|
35
|
+
raw != invalid
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class DataRecord < FitObject
|
2
|
+
attr_reader :fields, :global_num
|
3
|
+
|
4
|
+
def initialize(io, definition)
|
5
|
+
@global_num = definition.global_msg_num
|
6
|
+
@fields = Hash[definition.field_definitions.map do |f|
|
7
|
+
[f.field_def_num, DataField.new(io, f, definition.endian)]
|
8
|
+
end]
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid
|
12
|
+
@fields.select { |_, v| v.valid }
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'field_definition.rb'
|
2
|
+
|
3
|
+
class DefinitionRecord < FitObject
|
4
|
+
attr_reader :reserved, :architecture, :global_msg_num, :num_fields, :field_definitions, :data_records, :local_num
|
5
|
+
|
6
|
+
def initialize(io, local_num)
|
7
|
+
@local_num = local_num
|
8
|
+
@reserved = io.readbyte
|
9
|
+
@architecture = io.readbyte
|
10
|
+
char = @architecture.zero? ? 'v' : 'n'
|
11
|
+
@global_msg_num = readbytes(io, char, 2)
|
12
|
+
@num_fields = io.readbyte
|
13
|
+
@field_definitions = Array.new(num_fields) { FieldDefinition.new(io) }
|
14
|
+
@data_records = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def endian
|
18
|
+
@architecture.zero? ? :little : :big
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid
|
22
|
+
fd = Sdk.fields(@global_msg_num)
|
23
|
+
return if fd.nil?
|
24
|
+
@data_records.map do |d|
|
25
|
+
d.valid.select { |k, _| fd.keys.include? k }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,15 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
attr_accessor :def_num, :size, :base_num
|
1
|
+
class FieldDefinition < FitObject
|
2
|
+
attr_reader :field_def_num, :size, :endianness, :base_num
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
ENDIAN_ABILITY = 128
|
13
|
-
BASE_TYPE_NUM = 31
|
4
|
+
def initialize(io)
|
5
|
+
@field_def_num = io.readbyte
|
6
|
+
@size = io.readbyte
|
7
|
+
byte = io.readbyte
|
8
|
+
@endianness = read_bit(byte, 7)
|
9
|
+
@base_num = read_bits(byte, 4..0)
|
14
10
|
end
|
15
11
|
end
|
@@ -1,17 +1,12 @@
|
|
1
|
-
class FileHeader
|
2
|
-
attr_accessor :header_size, :protocol_version,
|
3
|
-
:profile_version, :num_records, :valid_file, :crc
|
1
|
+
class FileHeader < FitObject
|
2
|
+
attr_accessor :header_size, :protocol_version, :profile_version, :num_record_bytes, :valid_file, :crc
|
4
3
|
|
5
|
-
def initialize(
|
6
|
-
@header_size =
|
7
|
-
@protocol_version =
|
8
|
-
@profile_version =
|
9
|
-
@
|
10
|
-
@valid_file =
|
11
|
-
@crc =
|
12
|
-
end
|
13
|
-
|
14
|
-
def to_s
|
15
|
-
"size: #{header_size}, proto_v: #{protocol_version}, prof_v: #{profile_version}, records: #{num_records}"
|
4
|
+
def initialize(io)
|
5
|
+
@header_size = io.readbyte
|
6
|
+
@protocol_version = io.readbyte
|
7
|
+
@profile_version = readbytes(io, 'v', 2)
|
8
|
+
@num_record_bytes = readbytes(io, 'V', 4)
|
9
|
+
@valid_file = io.read(4) == '.FIT'
|
10
|
+
@crc = readbytes(io, 'v', 2)
|
16
11
|
end
|
17
12
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative 'unpack.rb'
|
2
|
+
require_relative 'fit_object.rb'
|
3
|
+
require_relative 'file_header.rb'
|
4
|
+
require_relative 'record_header.rb'
|
5
|
+
require_relative 'definition_record.rb'
|
6
|
+
require_relative 'data_field.rb'
|
7
|
+
require_relative 'data_record.rb'
|
8
|
+
require_relative 'message.rb'
|
9
|
+
require_relative 'sdk/sdk.rb'
|
10
|
+
|
11
|
+
class Fit
|
12
|
+
attr_reader :header, :messages
|
13
|
+
|
14
|
+
def initialize(io)
|
15
|
+
@header = FileHeader.new(io)
|
16
|
+
finished = []
|
17
|
+
begin
|
18
|
+
defs = {}
|
19
|
+
until io.pos >= header.num_record_bytes
|
20
|
+
h = RecordHeader.new(io)
|
21
|
+
if h.definition?
|
22
|
+
d = DefinitionRecord.new(io, h.local_message_type)
|
23
|
+
finished << defs[d.local_num] if defs.key? d.local_num
|
24
|
+
defs[d.local_num] = d
|
25
|
+
elsif h.data?
|
26
|
+
d = defs[h.local_message_type] if d.local_num != h.local_message_type
|
27
|
+
d.data_records << DataRecord.new(io, d)
|
28
|
+
else
|
29
|
+
# TODO implement timestamps
|
30
|
+
end
|
31
|
+
end
|
32
|
+
finished.push(*defs.values)
|
33
|
+
rescue
|
34
|
+
puts "error"
|
35
|
+
end
|
36
|
+
io.close
|
37
|
+
@messages = finished.group_by(&:global_msg_num)
|
38
|
+
.map { |x| Message.new x }
|
39
|
+
.reject { |x| x.data.nil? }
|
40
|
+
end
|
41
|
+
|
42
|
+
def digest
|
43
|
+
Hash[@messages.map { |x| [x.name, x.data.count] }]
|
44
|
+
end
|
45
|
+
|
46
|
+
def type(name)
|
47
|
+
messages.find { |x| x.name == name }
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
class Message
|
2
|
+
attr_accessor :global_num, :name, :data
|
3
|
+
|
4
|
+
def initialize(definitions)
|
5
|
+
@global_num = definitions[0]
|
6
|
+
@name = Sdk.message(@global_num)
|
7
|
+
return unless @name
|
8
|
+
|
9
|
+
fd = Sdk.fields(@global_num)
|
10
|
+
@data = definitions[1].map { |x| make_message(x, fd) }.flatten
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def make_message(definition, fields)
|
16
|
+
return if definition.valid.nil?
|
17
|
+
definition.valid.map do |d|
|
18
|
+
h = Hash[d.map { |k, v| process_value(fields[k], v.raw) }]
|
19
|
+
case @global_num
|
20
|
+
when 21
|
21
|
+
h = process_event(h)
|
22
|
+
when 0, 23
|
23
|
+
h = process_deviceinfo(h)
|
24
|
+
end
|
25
|
+
h
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def process_value(type, val)
|
30
|
+
if type[:type][0..3].to_sym == :enum
|
31
|
+
val = Sdk.enum(type[:type])[val]
|
32
|
+
elsif type[:type] == :date_time
|
33
|
+
t = Time.new(1989, 12, 31, 0, 0, 0, '+00:00').utc.to_i
|
34
|
+
Time.at(val + t).utc
|
35
|
+
elsif type[:type] == :local_date_time
|
36
|
+
t = Time.new(1989, 12, 31, 0, 0, 0, '+02:00').utc.to_i
|
37
|
+
val = Time.at(val + t)
|
38
|
+
elsif type[:type] == :coordinates
|
39
|
+
val *= (180.0 / 2**31)
|
40
|
+
end
|
41
|
+
|
42
|
+
unless type[:scale].zero?
|
43
|
+
if val.is_a? Array
|
44
|
+
val = val.map { |x| (x * 1.0) / type[:scale] }
|
45
|
+
else
|
46
|
+
val = (val * 1.0) / type[:scale]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
unless type[:offset].zero?
|
51
|
+
if val.is_a? Array
|
52
|
+
val.map { |x| x - type[:offset] }
|
53
|
+
else
|
54
|
+
val - type[:offset]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
[type[:name], val]
|
58
|
+
rescue => e
|
59
|
+
puts e
|
60
|
+
end
|
61
|
+
|
62
|
+
def process_event(h)
|
63
|
+
case h[:event]
|
64
|
+
when :rear_gear_change, :front_gear_change
|
65
|
+
h[:data] = h[:data].pack('V*').unpack('C*')
|
66
|
+
end
|
67
|
+
h
|
68
|
+
end
|
69
|
+
|
70
|
+
def process_deviceinfo(h)
|
71
|
+
case h[:source_type]
|
72
|
+
when :antplus
|
73
|
+
h[:device_type] = Sdk.enum(:antplus_device_type)[h[:value]]
|
74
|
+
end
|
75
|
+
|
76
|
+
case h[:manufacturer]
|
77
|
+
when :garmin, :dynastream, :dynastream_oem
|
78
|
+
h[:garmin_product] = Sdk.enum(:enum_garmin_product)[h[:garmin_product]]
|
79
|
+
end
|
80
|
+
h
|
81
|
+
end
|
82
|
+
end
|