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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b13b0e9cec601a2943828f9244d10076e753da43
4
- data.tar.gz: cd90d88a3e5013561aa54bd5095a783e8e12f610
3
+ metadata.gz: 1f3601df4b0943ee9cc67d891b2b33b1fc642a00
4
+ data.tar.gz: 5da9b76426c0ca8a66faba6e86ebbc07bb3fe5b0
5
5
  SHA512:
6
- metadata.gz: ad56d3370783f45ddea0a4efd35d63a671a92f3142a8c37433ea03c46e3560974d9d0a5f0b35333c6fe2140daac2809746350b4df259cedcaffd2f9002927c9d
7
- data.tar.gz: 1eafd2e2412dcaa5922213cda04c24ab491a562539293800da3fdb3ae51a1f9581a327b53594e460f97e858352b1ba8887e24fee730a5a07f738cd6c4a7a9be5
6
+ metadata.gz: f6103759deab65b385a2e9df51f2ebc842e0a5c7f2dcb51325ecc74c8b20949a14995af44efd96c36dcbfba884f603d8f72685ce86fa730219f641b6e907e83b
7
+ data.tar.gz: 2ddc4c63af6a6056462defd4e7c29e3e48ff5a5c265bd6360b8333fbd257b1df80c6025445a1a5d0323a0b50e46aec1184641267cd148370eafe75005ece891a
@@ -31,5 +31,4 @@ 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", '~> 0.10.4'
35
34
  end
@@ -21,10 +21,15 @@ class DataField < FitObject
21
21
 
22
22
  attr_reader :raw, :valid
23
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])
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
- [f.field_def_num, DataField.new(io, f, definition.endian)]
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, :architecture, :global_msg_num, :num_fields, :field_definitions, :data_records, :local_num
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
- @num_fields = io.readbyte
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
@@ -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
- until (io.pos - 14) >= header.num_record_bytes
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
- d = DefinitionRecord.new(io, h.local_message_type)
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] if d.local_num != h.local_message_type
27
- d.data_records << DataRecord.new(io, d)
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
@@ -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
- h = Hash[d.map { |k, v| process_value(fields[k], v.raw) }]
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
- h
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
 
@@ -24,6 +24,10 @@ class RecordHeader < FitObject
24
24
  @header_type.zero? && @message_type.zero?
25
25
  end
26
26
 
27
+ def has_dev_defs?
28
+ @message_type_specific == 1
29
+ end
30
+
27
31
  def timestamp?
28
32
  @header_type == 1
29
33
  end
@@ -1,6 +1,8 @@
1
1
  module Unpack
2
2
  def readbytes(io, char, len)
3
- io.read(len).unpack(char).first
3
+ d = io.read(len)
4
+ # binding.pry if char == 'Z*'
5
+ d.unpack(char).first
4
6
  end
5
7
 
6
8
  def read_multiple(io, char, len, size)
@@ -1,3 +1,3 @@
1
1
  module Fitreader
2
- VERSION = '0.3.1'
2
+ VERSION = '0.3.2'
3
3
  end
@@ -5,7 +5,62 @@ class FitreaderTest < Minitest::Test
5
5
  refute_nil ::Fitreader::VERSION
6
6
  end
7
7
 
8
- def test_it_does_something_useful
9
- assert false
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
@@ -1,6 +1,24 @@
1
- require 'pry'
2
1
  require_relative '../lib/fitreader/fit'
2
+ require 'benchmark/ips'
3
3
 
4
- # f = File.open("../spec/2016-04-09-13-19-18.fit", "r")
5
- f = File.open("../spec/1471568492.fit", "r")
6
- fit = Fit.new f
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.1
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-07-09 00:00:00.000000000 Z
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
- - spec/1471568492.fit
104
- - spec/2016-04-09-13-19-18.fit
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
- - spec/1471568492.fit
134
- - spec/2016-04-09-13-19-18.fit
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