fitreader 0.3.1 → 0.3.2
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/fitreader.gemspec +0 -1
- data/lib/fitreader/data_field.rb +9 -4
- data/lib/fitreader/data_record.rb +20 -1
- data/lib/fitreader/definition_record.rb +14 -4
- data/lib/fitreader/dev_field_definition.rb +10 -0
- data/lib/fitreader/fit.rb +32 -8
- data/lib/fitreader/message.rb +4 -2
- data/lib/fitreader/record_header.rb +4 -0
- data/lib/fitreader/unpack.rb +3 -1
- data/lib/fitreader/version.rb +1 -1
- data/{spec/1471568492.fit → test/files/working_garmin.fit} +0 -0
- data/test/files/working_wahoo.fit +0 -0
- data/test/files/working_wahoo_dev_fields.fit +0 -0
- data/test/fitreader_test.rb +57 -2
- data/test/manual.rb +22 -4
- metadata +9 -20
- data/spec/2016-04-09-13-19-18.fit +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f3601df4b0943ee9cc67d891b2b33b1fc642a00
|
4
|
+
data.tar.gz: 5da9b76426c0ca8a66faba6e86ebbc07bb3fe5b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6103759deab65b385a2e9df51f2ebc842e0a5c7f2dcb51325ecc74c8b20949a14995af44efd96c36dcbfba884f603d8f72685ce86fa730219f641b6e907e83b
|
7
|
+
data.tar.gz: 2ddc4c63af6a6056462defd4e7c29e3e48ff5a5c265bd6360b8333fbd257b1df80c6025445a1a5d0323a0b50e46aec1184641267cd148370eafe75005ece891a
|
data/fitreader.gemspec
CHANGED
data/lib/fitreader/data_field.rb
CHANGED
@@ -21,10 +21,15 @@ class DataField < FitObject
|
|
21
21
|
|
22
22
|
attr_reader :raw, :valid
|
23
23
|
|
24
|
-
def initialize(io,
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
def initialize(io, options)
|
25
|
+
base_num = options[:base_num]
|
26
|
+
size = options[:size]
|
27
|
+
arch = options[:arch]
|
28
|
+
|
29
|
+
base = TYPES[base_num]
|
30
|
+
char = base[:unpack_type]
|
31
|
+
char = char[arch] if char.is_a?(Hash)
|
32
|
+
@raw = read_multiple(io, char, size, base[:size])
|
28
33
|
@valid = check(@raw, base[:invalid])
|
29
34
|
end
|
30
35
|
|
@@ -4,11 +4,30 @@ class DataRecord < FitObject
|
|
4
4
|
def initialize(io, definition)
|
5
5
|
@global_num = definition.global_msg_num
|
6
6
|
@fields = Hash[definition.field_definitions.map do |f|
|
7
|
-
|
7
|
+
opts = {base_num: f.base_num,
|
8
|
+
size: f.size,
|
9
|
+
arch: definition.endian}
|
10
|
+
[f.field_def_num, DataField.new(io, opts)]
|
8
11
|
end]
|
12
|
+
if definition.dev_defs
|
13
|
+
@dev_fields = Hash[definition.dev_defs.map do |f|
|
14
|
+
opts = {base_num: f.field_def[:base_type_id].raw,
|
15
|
+
size: f.size,
|
16
|
+
arch: definition.endian}
|
17
|
+
[f.field_def[:field_name].raw.to_sym, DataField.new(io, opts)]
|
18
|
+
end]
|
19
|
+
end
|
9
20
|
end
|
10
21
|
|
11
22
|
def valid
|
12
23
|
@fields.select { |_, v| v.valid }
|
13
24
|
end
|
25
|
+
|
26
|
+
def dev_fields
|
27
|
+
if defined? @dev_fields
|
28
|
+
@dev_fields
|
29
|
+
else
|
30
|
+
Hash.new
|
31
|
+
end
|
32
|
+
end
|
14
33
|
end
|
@@ -1,16 +1,26 @@
|
|
1
1
|
require_relative 'field_definition.rb'
|
2
|
+
require_relative 'dev_field_definition.rb'
|
2
3
|
|
3
4
|
class DefinitionRecord < FitObject
|
4
|
-
attr_reader :reserved, :
|
5
|
+
attr_reader :reserved, :global_msg_num, :num_fields, :field_definitions, :data_records, :local_num, :dev_defs
|
5
6
|
|
6
|
-
def initialize(io, local_num)
|
7
|
+
def initialize(io, local_num, dev_field_defs = nil)
|
7
8
|
@local_num = local_num
|
9
|
+
|
10
|
+
# read record
|
8
11
|
@reserved = io.readbyte
|
9
12
|
@architecture = io.readbyte
|
10
13
|
char = @architecture.zero? ? 'v' : 'n'
|
11
14
|
@global_msg_num = readbytes(io, char, 2)
|
12
|
-
|
15
|
+
num_fields = io.readbyte
|
16
|
+
|
17
|
+
# read fields
|
13
18
|
@field_definitions = Array.new(num_fields) { FieldDefinition.new(io) }
|
19
|
+
|
20
|
+
unless dev_field_defs.nil?
|
21
|
+
num_fields = io.readbyte
|
22
|
+
@dev_defs = Array.new(num_fields) { DevFieldDefinition.new(io, dev_field_defs) }
|
23
|
+
end
|
14
24
|
@data_records = []
|
15
25
|
end
|
16
26
|
|
@@ -22,7 +32,7 @@ class DefinitionRecord < FitObject
|
|
22
32
|
fd = Sdk.fields(@global_msg_num)
|
23
33
|
return if fd.nil?
|
24
34
|
@data_records.map do |d|
|
25
|
-
d.valid.select { |k, _| fd.keys.include? k }
|
35
|
+
d.valid.select { |k, _| fd.keys.include? k }.merge(d.dev_fields)
|
26
36
|
end
|
27
37
|
end
|
28
38
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class DevFieldDefinition < FitObject
|
2
|
+
attr_reader :field_num, :size, :developer_data_index, :field_def
|
3
|
+
|
4
|
+
def initialize(io, field_defs)
|
5
|
+
@field_num = io.readbyte
|
6
|
+
@size = io.readbyte
|
7
|
+
@developer_data_index = io.readbyte
|
8
|
+
@field_def = field_defs[@developer_data_index]
|
9
|
+
end
|
10
|
+
end
|
data/lib/fitreader/fit.rb
CHANGED
@@ -7,6 +7,7 @@ require_relative 'data_field.rb'
|
|
7
7
|
require_relative 'data_record.rb'
|
8
8
|
require_relative 'message.rb'
|
9
9
|
require_relative 'sdk/sdk.rb'
|
10
|
+
require 'pry'
|
10
11
|
|
11
12
|
class Fit
|
12
13
|
attr_reader :header, :messages
|
@@ -16,27 +17,40 @@ class Fit
|
|
16
17
|
finished = []
|
17
18
|
begin
|
18
19
|
defs = {}
|
19
|
-
|
20
|
+
dev_field_defs = {}
|
21
|
+
# until (io.pos - 14) >= header.num_record_bytes
|
22
|
+
until ((@header.num_record_bytes + 14) - io.pos) == 0
|
20
23
|
h = RecordHeader.new(io)
|
21
24
|
if h.definition?
|
22
|
-
|
25
|
+
if h.has_dev_defs?
|
26
|
+
d = DefinitionRecord.new(io, h.local_message_type, dev_field_defs)
|
27
|
+
else
|
28
|
+
d = DefinitionRecord.new(io, h.local_message_type)
|
29
|
+
end
|
23
30
|
finished << defs[d.local_num] if defs.key? d.local_num
|
24
31
|
defs[d.local_num] = d
|
25
32
|
elsif h.data?
|
26
|
-
d = defs[h.local_message_type]
|
27
|
-
|
33
|
+
d = defs[h.local_message_type]
|
34
|
+
data_record = DataRecord.new(io, d)
|
35
|
+
if d.global_msg_num == 206
|
36
|
+
dev_field = make_developer_fields(data_record.fields)
|
37
|
+
dev_field_defs[dev_field[:dev_data_idx].raw] = dev_field
|
38
|
+
else
|
39
|
+
d.data_records << data_record
|
40
|
+
end
|
28
41
|
else
|
29
42
|
# TODO implement timestamps
|
30
43
|
end
|
31
44
|
end
|
32
45
|
finished.push(*defs.values)
|
46
|
+
io.close
|
47
|
+
@messages = finished.group_by(&:global_msg_num)
|
48
|
+
.map { |x| Message.new x }
|
49
|
+
.reject { |x| x.data.nil? }
|
33
50
|
rescue => e
|
51
|
+
binding.pry
|
34
52
|
puts "error: #{e}\n#{e.backtrace}"
|
35
53
|
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
54
|
end
|
41
55
|
|
42
56
|
def digest
|
@@ -46,4 +60,14 @@ class Fit
|
|
46
60
|
def type(name)
|
47
61
|
messages.find { |x| x.name == name }
|
48
62
|
end
|
63
|
+
|
64
|
+
def make_developer_fields(data_records)
|
65
|
+
lookup = {0 => :dev_data_idx, 1 => :field_def_num, 2 => :base_type_id, 3 => :field_name, 8 => :units}
|
66
|
+
map = {}
|
67
|
+
data_records.each do |k,v|
|
68
|
+
key = lookup[k]
|
69
|
+
map[key] = v
|
70
|
+
end
|
71
|
+
map
|
72
|
+
end
|
49
73
|
end
|
data/lib/fitreader/message.rb
CHANGED
@@ -15,14 +15,16 @@ class Message
|
|
15
15
|
def make_message(definition, fields)
|
16
16
|
return if definition.valid.nil?
|
17
17
|
definition.valid.map do |d|
|
18
|
-
|
18
|
+
sdk_fields = d.select { |k, v| fields.has_key?(k) }
|
19
|
+
h = Hash[sdk_fields.map { |k, v| process_value(fields[k], v.raw) }]
|
19
20
|
case @global_num
|
20
21
|
when 21
|
21
22
|
h = process_event(h)
|
22
23
|
when 0, 23
|
23
24
|
h = process_deviceinfo(h)
|
24
25
|
end
|
25
|
-
|
26
|
+
dev_fields = Hash[d.select { |k, v| k.is_a?(Symbol) }.map {|k,v| [k, v.raw]}]
|
27
|
+
h.merge(dev_fields)
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
data/lib/fitreader/unpack.rb
CHANGED
data/lib/fitreader/version.rb
CHANGED
File without changes
|
Binary file
|
Binary file
|
data/test/fitreader_test.rb
CHANGED
@@ -5,7 +5,62 @@ class FitreaderTest < Minitest::Test
|
|
5
5
|
refute_nil ::Fitreader::VERSION
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
|
8
|
+
def test_reads_a_garmin_file
|
9
|
+
path = "test/files/working_garmin.fit"
|
10
|
+
file = File.open(path, "r")
|
11
|
+
fit = Fit.new file
|
12
|
+
digest = {file_id: 1,
|
13
|
+
file_creator: 1,
|
14
|
+
device_settings: 1,
|
15
|
+
user_profile: 1,
|
16
|
+
sensor_info: 5,
|
17
|
+
sport: 1,
|
18
|
+
zones_target: 1,
|
19
|
+
record: 4988,
|
20
|
+
event: 120,
|
21
|
+
device_info: 30,
|
22
|
+
source: 43,
|
23
|
+
segment_lap: 2,
|
24
|
+
lap: 1,
|
25
|
+
session: 1,
|
26
|
+
activity: 1,
|
27
|
+
battery_info: 20}
|
28
|
+
assert_equal fit.digest, digest
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_reads_a_wahoo_file
|
32
|
+
path = "test/files/working_wahoo.fit"
|
33
|
+
file = File.open(path, "r")
|
34
|
+
fit = Fit.new file
|
35
|
+
digest = {file_id: 1,
|
36
|
+
event: 12,
|
37
|
+
device_info: 10,
|
38
|
+
sport: 1,
|
39
|
+
workout: 1,
|
40
|
+
record: 715,
|
41
|
+
mfg_range_min: 5,
|
42
|
+
lap: 1,
|
43
|
+
session: 1,
|
44
|
+
activity: 1}
|
45
|
+
assert_equal fit.digest, digest
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_reads_a_wahoo_file_with_dev_fields
|
49
|
+
path = "test/files/working_wahoo_dev_fields.fit"
|
50
|
+
file = File.open(path, "r")
|
51
|
+
fit = Fit.new file
|
52
|
+
digest = {file_id: 1,
|
53
|
+
developer_data_id: 1,
|
54
|
+
field_description: 1,
|
55
|
+
event: 288,
|
56
|
+
device_info: 139,
|
57
|
+
sport: 1,
|
58
|
+
workout: 1,
|
59
|
+
record: 5652,
|
60
|
+
mfg_range_min: 30,
|
61
|
+
lap: 1,
|
62
|
+
session: 1,
|
63
|
+
activity: 1}
|
64
|
+
assert_equal fit.digest, digest
|
10
65
|
end
|
11
66
|
end
|
data/test/manual.rb
CHANGED
@@ -1,6 +1,24 @@
|
|
1
|
-
require 'pry'
|
2
1
|
require_relative '../lib/fitreader/fit'
|
2
|
+
require 'benchmark/ips'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
p = "test/files/working_garmin.fit"
|
5
|
+
Benchmark.ips do |x|
|
6
|
+
# Configure the number of seconds used during
|
7
|
+
# the warmup phase (default 2) and calculation phase (default 5)
|
8
|
+
x.config(:time => 30, :warmup => 2)
|
9
|
+
|
10
|
+
# Typical mode, runs the block as many times as it can
|
11
|
+
# x.report("addition") { 1 + 2 }
|
12
|
+
|
13
|
+
# To reduce overhead, the number of iterations is passed in
|
14
|
+
# and the block must run the code the specific number of times.
|
15
|
+
# Used for when the workload is very small and any overhead
|
16
|
+
# introduces incorrectable errors.
|
17
|
+
x.report("single_run") do |times|
|
18
|
+
f = File.open(p, "r")
|
19
|
+
Fit.new f
|
20
|
+
end
|
21
|
+
|
22
|
+
# Compare the iterations per second of the various reports!
|
23
|
+
x.compare!
|
24
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fitreader
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Brodie
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,20 +52,6 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.5'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: pry
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 0.10.4
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 0.10.4
|
69
55
|
description:
|
70
56
|
email:
|
71
57
|
- richard@samsari.org
|
@@ -88,6 +74,7 @@ files:
|
|
88
74
|
- lib/fitreader/data_field.rb
|
89
75
|
- lib/fitreader/data_record.rb
|
90
76
|
- lib/fitreader/definition_record.rb
|
77
|
+
- lib/fitreader/dev_field_definition.rb
|
91
78
|
- lib/fitreader/field_definition.rb
|
92
79
|
- lib/fitreader/file_header.rb
|
93
80
|
- lib/fitreader/fit.rb
|
@@ -100,8 +87,9 @@ files:
|
|
100
87
|
- lib/fitreader/sdk/sdk.rb
|
101
88
|
- lib/fitreader/unpack.rb
|
102
89
|
- lib/fitreader/version.rb
|
103
|
-
-
|
104
|
-
-
|
90
|
+
- test/files/working_garmin.fit
|
91
|
+
- test/files/working_wahoo.fit
|
92
|
+
- test/files/working_wahoo_dev_fields.fit
|
105
93
|
- test/fitreader_test.rb
|
106
94
|
- test/manual.rb
|
107
95
|
- test/test_helper.rb
|
@@ -130,8 +118,9 @@ signing_key:
|
|
130
118
|
specification_version: 4
|
131
119
|
summary: Library for reading FIT files generated by Garmin devices.
|
132
120
|
test_files:
|
133
|
-
-
|
134
|
-
-
|
121
|
+
- test/files/working_garmin.fit
|
122
|
+
- test/files/working_wahoo.fit
|
123
|
+
- test/files/working_wahoo_dev_fields.fit
|
135
124
|
- test/fitreader_test.rb
|
136
125
|
- test/manual.rb
|
137
126
|
- test/test_helper.rb
|
Binary file
|