fit4ruby 1.6.1 → 1.6.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: 02c5d5efa9834b239320385607392c18c215f60b
4
- data.tar.gz: 1a57d3f6d6e0e70f56c584d445aaa69c92cbd306
3
+ metadata.gz: 22fe9d584ae711b90ca8e963b254d959dd2e005c
4
+ data.tar.gz: 0b90e47c9f390ec90a8d5258638903222cd577a1
5
5
  SHA512:
6
- metadata.gz: a438a9c330043b253e8868490ffcd0dc972efa49064c9d5e3d8a16ae666590fea1d531d708658a8a5dbaf9015a084d4d668f909108efc0736910ef4239477ece
7
- data.tar.gz: 9636b7c9378345f4e62b5eb171b123e7fc1a0ec5c79e939164c5a20ab844e8befca0abee12a9d1875eca193d63ed1cbdb7f28b7f5495d1ef6c421d7de358df0e
6
+ metadata.gz: 075fdc211b229dab82d43e32738edc57f6926a8eeee5f7128ea3863cd37b819ff4c064a296e3536aac6698c91a1641d716d7a243fdb60eebefd1ff002c8be92c
7
+ data.tar.gz: 1e9ad8de227dfcf75e5ca60614583a249eac8be43d3876bc85dd23e93fdea441983c75448d28c8216ddd56d6b70045e41b0042ee2da826d0c2575864880140bb
data/fit4ruby.gemspec CHANGED
@@ -7,12 +7,12 @@ GEM_SPEC = Gem::Specification.new do |spec|
7
7
  spec.name = 'fit4ruby'
8
8
  spec.version = Fit4Ruby::VERSION
9
9
  spec.license = 'GNU GPL version 2'
10
- spec.summary = 'Library to read GARMIN FIT files.'
10
+ spec.summary = 'Library to read and write GARMIN FIT files.'
11
11
  spec.description = <<EOT
12
12
  This library can read and write FIT files and convert them into a Ruby data
13
13
  structure for easy processing. This library was written for Garmin devices
14
- like the FR620, Fenix 3 and Fenix 3 HR. Fit files from other devices may work
15
- as well but have not been tested.
14
+ like the FR620, Fenix 3, Fenix 3 HR, Fenix 5 (s and X). Fit files from other
15
+ devices may work as well but have not been tested.
16
16
  EOT
17
17
  spec.authors = [ 'Chris Schlaeger' ]
18
18
  spec.email = 'chris@linux.com'
@@ -12,6 +12,8 @@
12
12
 
13
13
  require 'fit4ruby/FitDataRecord'
14
14
  require 'fit4ruby/FileId'
15
+ require 'fit4ruby/FieldDescription'
16
+ require 'fit4ruby/DeveloperDataId'
15
17
  require 'fit4ruby/EPO_Data'
16
18
  require 'fit4ruby/FileCreator'
17
19
  require 'fit4ruby/DeviceInfo'
@@ -34,7 +36,7 @@ module Fit4Ruby
34
36
  # equivalents of the message record structures used in the FIT file.
35
37
  class Activity < FitDataRecord
36
38
 
37
- attr_accessor :file_id, :epo_data,
39
+ attr_accessor :file_id, :field_descriptions, :developer_data_ids, :epo_data,
38
40
  :file_creator, :device_infos, :sensor_settings, :data_sources,
39
41
  :user_profiles, :physiological_metrics,
40
42
  :sessions, :laps, :records, :hrv,
@@ -49,6 +51,8 @@ module Fit4Ruby
49
51
  @num_sessions = 0
50
52
 
51
53
  @file_id = FileId.new
54
+ @field_descriptions = []
55
+ @developer_data_ids = []
52
56
  @epo_data = nil
53
57
  @file_creator = FileCreator.new
54
58
  @device_infos = []
@@ -282,7 +286,9 @@ module Fit4Ruby
282
286
  @file_id.write(io, id_mapper)
283
287
  @file_creator.write(io, id_mapper)
284
288
 
285
- (@device_infos + @sensor_settings + @data_sources + @user_profiles +
289
+ (@field_descriptions + @developer_data_ids +
290
+ @device_infos + @sensor_settings +
291
+ @data_sources + @user_profiles +
286
292
  @physiological_metrics + @events +
287
293
  @sessions + @laps + @records + @heart_rate_zones +
288
294
  @personal_records).sort.each do |s|
@@ -300,6 +306,22 @@ module Fit4Ruby
300
306
  new_fit_data_record('file_id', field_values)
301
307
  end
302
308
 
309
+ # Add a new FieldDescription to the Activity.
310
+ # @param field_values [Hash] A Hash that provides initial values for
311
+ # certain fields of the FitDataRecord.
312
+ # @return [FieldDescription]
313
+ def new_field_description(field_values = {})
314
+ new_fit_data_record('field_description', field_values)
315
+ end
316
+
317
+ # Add a new DeveloperDataId to the Activity.
318
+ # @param field_values [Hash] A Hash that provides initial values for
319
+ # certain fields of the FitDataRecord.
320
+ # @return [DeveloperDataId]
321
+ def new_field_description(field_values = {})
322
+ new_fit_data_record('developer_data_id', field_values)
323
+ end
324
+
303
325
  # Add a new FileCreator to the Activity. It will replace any previously
304
326
  # added FileCreator object.
305
327
  # @param field_values [Hash] A Hash that provides initial values for
@@ -403,6 +425,8 @@ module Fit4Ruby
403
425
  def ==(a)
404
426
  super(a) && @file_id == a.file_id &&
405
427
  @file_creator == a.file_creator &&
428
+ @field_descriptions == a.field_descriptions &&
429
+ @developer_data_ids == a.developer_data_ids &&
406
430
  @device_infos == a.device_infos &&
407
431
  @sensor_settings == a.sensor_settings &&
408
432
  @data_sources == a.data_sources &&
@@ -423,6 +447,10 @@ module Fit4Ruby
423
447
  case record_type
424
448
  when 'file_id'
425
449
  @file_id = (record = FileId.new(field_values))
450
+ when 'field_description'
451
+ @field_descriptions << (record = FieldDescription.new(field_values))
452
+ when 'developer_data_id'
453
+ @developer_data_ids << (record = DeveloperDataId.new(field_values))
426
454
  when 'epo_data'
427
455
  @epo_data = (record = EPO_Data.new(field_values))
428
456
  when 'file_creator'
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby -w
2
+ # encoding: UTF-8
3
+ #
4
+ # = DeveloperDataId.rb -- Fit4Ruby - FIT file processing library for Ruby
5
+ #
6
+ # Copyright (c) 2017 by Chris Schlaeger <cs@taskjuggler.org>
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of version 2 of the GNU General Public License as
10
+ # published by the Free Software Foundation.
11
+ #
12
+
13
+ require 'fit4ruby/FitDataRecord'
14
+
15
+ module Fit4Ruby
16
+
17
+ # This class corresponds to the DeveloperDataId FIT message.
18
+ class DeveloperDataId < FitDataRecord
19
+
20
+ # Create a new DeveloperDataId object.
21
+ # @param field_values [Hash] Hash that provides initial values for certain
22
+ # fields.
23
+ def initialize(field_values = {})
24
+ super('developer_data_id')
25
+ set_field_values(field_values)
26
+ end
27
+
28
+ def create_global_definition
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby -w
2
+ # encoding: UTF-8
3
+ #
4
+ # = FieldDescription.rb -- Fit4Ruby - FIT file processing library for Ruby
5
+ #
6
+ # Copyright (c) 2017 by Chris Schlaeger <cs@taskjuggler.org>
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of version 2 of the GNU General Public License as
10
+ # published by the Free Software Foundation.
11
+ #
12
+
13
+ require 'fit4ruby/FitDataRecord'
14
+
15
+ module Fit4Ruby
16
+
17
+ # This class corresponds to the FieldDescription FIT message.
18
+ class FieldDescription < FitDataRecord
19
+
20
+ # Create a new FieldDescription object.
21
+ # @param field_values [Hash] Hash that provides initial values for certain
22
+ # fields.
23
+ def initialize(field_values = {})
24
+ super('field_description')
25
+ set_field_values(field_values)
26
+ end
27
+
28
+ def create_global_definition
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+
@@ -131,7 +131,7 @@ module Fit4Ruby
131
131
  # Create a BinData::Struct object to store the data record.
132
132
  fields = []
133
133
  global_fit_message.fields_by_number.each do |field_number, field|
134
- bin_data_type = FitDefinitionField.fit_type_to_bin_data(field.type)
134
+ bin_data_type = FitDefinitionFieldBase.fit_type_to_bin_data(field.type)
135
135
  fields << [ bin_data_type, field.name ]
136
136
  end
137
137
  bd = BinData::Struct.new(:endian => :little, :fields => fields)
@@ -146,7 +146,7 @@ module Fit4Ruby
146
146
  else
147
147
  # If we don't have a corresponding variable or the variable is nil
148
148
  # we write the 'undefined' value instead.
149
- value = FitDefinitionField.undefined_value(field.type)
149
+ value = FitDefinitionFieldBase.undefined_value(field.type)
150
150
  end
151
151
  bd[field.name] = value
152
152
  end
@@ -12,6 +12,7 @@
12
12
 
13
13
  require 'bindata'
14
14
  require 'fit4ruby/FitDefinitionField'
15
+ require 'fit4ruby/FitDeveloperDataFieldDefinition'
15
16
 
16
17
  module Fit4Ruby
17
18
 
@@ -24,6 +25,8 @@ module Fit4Ruby
24
25
  # required.
25
26
  class FitDefinition < BinData::Record
26
27
 
28
+ @@has_developer_data = false
29
+
27
30
  hide :reserved
28
31
 
29
32
  uint8 :reserved, :initial_value => 0
@@ -33,7 +36,14 @@ module Fit4Ruby
33
36
  uint16be :default
34
37
  end
35
38
  uint8 :field_count
36
- array :fields, :type => FitDefinitionField, :initial_length => :field_count
39
+ array :data_fields, :type => FitDefinitionField,
40
+ :initial_length => :field_count
41
+
42
+ uint8 :developer_fields_count, :onlyif => :has_developer_data?
43
+ array :developer_fields,
44
+ :type => FitDeveloperDataFieldDefinition,
45
+ :initial_length => :developer_fields_count,
46
+ :onlyif => :has_developer_data?
37
47
 
38
48
  def endian
39
49
  architecture.snapshot == 0 ? :little : :big
@@ -43,7 +53,22 @@ module Fit4Ruby
43
53
  if architecture.snapshot > 1
44
54
  Log.fatal "Illegal architecture value #{architecture.snapshot}"
45
55
  end
46
- fields.each { |f| f.check }
56
+ data_fields.each { |f| f.check }
57
+ end
58
+
59
+ def FitDefinition::read(io, entity, developer_data_flag)
60
+ @@has_developer_data = developer_data_flag != 0
61
+ super(io)
62
+ end
63
+
64
+ def FitDefinitionField::write(io)
65
+ # We don't support writing developer data fields yet.
66
+ @@has_developer_data = false
67
+ super(io)
68
+ end
69
+
70
+ def has_developer_data?
71
+ @@has_developer_data
47
72
  end
48
73
 
49
74
  def setup(fit_message_definition)
@@ -52,13 +77,12 @@ module Fit4Ruby
52
77
  fdf.field_definition_number = number
53
78
  fdf.set_type(f.type)
54
79
 
55
- fields << fdf
80
+ data_fields << fdf
56
81
  end
57
- self.field_count = fields.length
82
+ self.field_count = data_fields.length
58
83
  end
59
84
 
60
85
  end
61
86
 
62
-
63
87
  end
64
88
 
@@ -12,6 +12,7 @@
12
12
 
13
13
  require 'bindata'
14
14
  require 'time'
15
+ require 'fit4ruby/FitDefinitionFieldBase'
15
16
  require 'fit4ruby/Log'
16
17
  require 'fit4ruby/GlobalFitMessage'
17
18
 
@@ -24,23 +25,7 @@ module Fit4Ruby
24
25
  # the exact size of the binary records.
25
26
  class FitDefinitionField < BinData::Record
26
27
 
27
- @@TypeDefs = [
28
- # FIT Type, BinData type, undefined value, bytes
29
- [ 'enum', 'uint8', 0xFF, 1 ],
30
- [ 'sint8', 'int8', 0x7F, 1 ],
31
- [ 'uint8', 'uint8', 0xFF, 1 ],
32
- [ 'sint16', 'int16', 0x7FFF, 2 ],
33
- [ 'uint16', 'uint16', 0xFFFF, 2 ],
34
- [ 'sint32', 'int32', 0x7FFFFFFF, 4 ],
35
- [ 'uint32', 'uint32', 0xFFFFFFFF, 4 ],
36
- [ 'string', 'string', '', 0 ],
37
- [ 'float32', 'float', 0xFFFFFFFF, 4 ],
38
- [ 'float63', 'double', 0xFFFFFFFF, 4 ],
39
- [ 'uint8z', 'uint8', 0, 1 ],
40
- [ 'uint16z', 'uint16', 0, 2 ],
41
- [ 'uint32z', 'uint32', 0, 4 ],
42
- [ 'byte', 'uint8', 0xFF, 1 ]
43
- ]
28
+ include FitDefinitionFieldBase
44
29
 
45
30
  hide :reserved
46
31
 
@@ -50,18 +35,6 @@ module Fit4Ruby
50
35
  bit2 :reserved
51
36
  bit5 :base_type_number
52
37
 
53
- def self.fit_type_to_bin_data(fit_type)
54
- entry = @@TypeDefs.find { |e| e[0] == fit_type }
55
- raise "Unknown fit type #{fit_type}" unless entry
56
- entry[1]
57
- end
58
-
59
- def self.undefined_value(fit_type)
60
- entry = @@TypeDefs.find { |e| e[0] == fit_type }
61
- raise "Unknown fit type #{fit_type}" unless entry
62
- entry[2]
63
- end
64
-
65
38
  def init
66
39
  @global_message_number = parent.parent.global_message_number.snapshot
67
40
  @global_message_definition = GlobalFitMessages[@global_message_number]
@@ -72,7 +45,8 @@ module Fit4Ruby
72
45
  "choice_#{field_number}"
73
46
  @type = field.respond_to?('type') ? field.type : nil
74
47
 
75
- if @type && (td = @@TypeDefs[base_type_number]) && td[0] != @type
48
+ if @type && (td = @@TypeDefs[checked_base_type_number]) &&
49
+ td[0] != @type
76
50
  Log.warn "#{@global_message_number}:#{@name} must be of type " +
77
51
  "#{@type}, not #{td[0]}"
78
52
  end
@@ -129,51 +103,6 @@ module Fit4Ruby
129
103
  end
130
104
  end
131
105
 
132
- def set_type(fit_type)
133
- idx = @@TypeDefs.index { |x| x[0] == fit_type }
134
- raise "Unknown type #{fit_type}" unless idx
135
- self.base_type_number = idx
136
- self.byte_count = @@TypeDefs[idx][3]
137
- end
138
-
139
- def type(fit_type = false)
140
- check_fit_base_type
141
- @@TypeDefs[base_type_number.snapshot][fit_type ? 0 : 1]
142
- end
143
-
144
- def is_array?
145
- if total_bytes > base_type_bytes
146
- if total_bytes % base_type_bytes != 0
147
- Log.fatal "Total bytes (#{total_bytes}) must be multiple of " +
148
- "base type bytes (#{base_type_bytes})."
149
- end
150
- return true
151
- end
152
- false
153
- end
154
-
155
- def total_bytes
156
- self.byte_count.snapshot
157
- end
158
-
159
- def base_type_bytes
160
- check_fit_base_type
161
- @@TypeDefs[base_type_number.snapshot][3]
162
- end
163
-
164
- def undefined_value
165
- check_fit_base_type
166
- @@TypeDefs[base_type_number.snapshot][2]
167
- end
168
-
169
- private
170
-
171
- def check_fit_base_type
172
- if @@TypeDefs.length <= base_type_number.snapshot
173
- Log.fatal "Unknown FIT Base type #{base_type_number.snapshot}"
174
- end
175
- end
176
-
177
106
  end
178
107
 
179
108
  end
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env ruby -w
2
+ # encoding: UTF-8
3
+ #
4
+ # = FitDefinitionFieldBase.rb -- Fit4Ruby - FIT file processing library for Ruby
5
+ #
6
+ # Copyright (c) 2014, 2017 by Chris Schlaeger <cs@taskjuggler.org>
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of version 2 of the GNU General Public License as
10
+ # published by the Free Software Foundation.
11
+ #
12
+
13
+ module Fit4Ruby
14
+
15
+ module FitDefinitionFieldBase
16
+
17
+ @@TypeDefs = [
18
+ # FIT Type, BinData type, undefined value, bytes
19
+ [ 'enum', 'uint8', 0xFF, 1 ],
20
+ [ 'sint8', 'int8', 0x7F, 1 ],
21
+ [ 'uint8', 'uint8', 0xFF, 1 ],
22
+ [ 'sint16', 'int16', 0x7FFF, 2 ],
23
+ [ 'uint16', 'uint16', 0xFFFF, 2 ],
24
+ [ 'sint32', 'int32', 0x7FFFFFFF, 4 ],
25
+ [ 'uint32', 'uint32', 0xFFFFFFFF, 4 ],
26
+ [ 'string', 'string', '', 0 ],
27
+ [ 'float32', 'float', 0xFFFFFFFF, 4 ],
28
+ [ 'float63', 'double', 0xFFFFFFFF, 4 ],
29
+ [ 'uint8z', 'uint8', 0, 1 ],
30
+ [ 'uint16z', 'uint16', 0, 2 ],
31
+ [ 'uint32z', 'uint32', 0, 4 ],
32
+ [ 'byte', 'uint8', 0xFF, 1 ],
33
+ [ 'sint64', 'int64', 0x7FFFFFFFFFFFFFFF, 8 ],
34
+ [ 'uint64', 'uint64', 0xFFFFFFFFFFFFFFFF, 8 ],
35
+ [ 'uint64z', 'uint64', 0, 8 ]
36
+ ]
37
+
38
+ def FitDefinitionFieldBase::fit_type_to_bin_data(fit_type)
39
+ entry = @@TypeDefs.find { |e| e[0] == fit_type }
40
+ raise "Unknown fit type #{fit_type}" unless entry
41
+ entry[1]
42
+ end
43
+
44
+ def FitDefinitionFieldBase::undefined_value(fit_type)
45
+ entry = @@TypeDefs.find { |e| e[0] == fit_type }
46
+ raise "Unknown fit type #{fit_type}" unless entry
47
+ entry[2]
48
+ end
49
+
50
+ def set_type(fit_type)
51
+ idx = @@TypeDefs.index { |x| x[0] == fit_type }
52
+ raise "Unknown type #{fit_type}" unless idx
53
+ self.base_type_number = idx
54
+ self.byte_count = @@TypeDefs[idx][3]
55
+ end
56
+
57
+ def type(fit_type = false)
58
+ @@TypeDefs[checked_base_type_number][fit_type ? 0 : 1]
59
+ end
60
+
61
+ def is_array?
62
+ if total_bytes > base_type_bytes
63
+ if total_bytes % base_type_bytes != 0
64
+ Log.error "Total bytes (#{total_bytes}) must be multiple of " +
65
+ "base type bytes (#{base_type_bytes}) of type " +
66
+ "#{base_type_number.snapshot} in Global FIT " +
67
+ "Message #{name}."
68
+ end
69
+ return true
70
+ end
71
+ false
72
+ end
73
+
74
+ def total_bytes
75
+ self.byte_count.snapshot
76
+ end
77
+
78
+ def base_type_bytes
79
+ @@TypeDefs[checked_base_type_number][3]
80
+ end
81
+
82
+ def undefined_value
83
+ @@TypeDefs[checked_base_type_number][2]
84
+ end
85
+
86
+ private
87
+
88
+ def checked_base_type_number
89
+ if @@TypeDefs.length <= base_type_number.snapshot
90
+ Log.error "Unknown FIT Base type #{base_type_number.snapshot} in " +
91
+ "Global FIT Message #{name}"
92
+ return 0
93
+ end
94
+ base_type_number.snapshot
95
+ end
96
+
97
+ def check_fit_base_type
98
+ if @@TypeDefs.length <= base_type_number.snapshot
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby -w
2
+ # encoding: UTF-8
3
+ #
4
+ # = FitDeveloperDataFieldDefinition.rb -- Fit4Ruby - FIT file processing library for Ruby
5
+ #
6
+ # Copyright (c) 2017 by Chris Schlaeger <cs@taskjuggler.org>
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of version 2 of the GNU General Public License as
10
+ # published by the Free Software Foundation.
11
+ #
12
+
13
+ require 'bindata'
14
+ require 'fit4ruby/FitDefinitionFieldBase'
15
+
16
+ module Fit4Ruby
17
+
18
+ class FitDeveloperDataFieldDefinition < BinData::Record
19
+
20
+ include FitDefinitionFieldBase
21
+
22
+ uint8 :field_number
23
+ uint8 :size_in_bytes
24
+ uint8 :developer_data_index
25
+
26
+ def name
27
+ "developer_field_#{developer_data_index.snapshot}_" +
28
+ "#{field_number.snapshot}"
29
+ end
30
+
31
+ def bindata_type
32
+ fit_definition = parent.parent
33
+ if (entry = @@TypeDefs.find { |e| e[3] == size_in_bytes.snapshot })
34
+ entry[1]
35
+ else
36
+ 'uint8'
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -53,8 +53,8 @@ module Fit4Ruby
53
53
  obj = entity.new_fit_data_record(@name)
54
54
 
55
55
  # It's important to ensure that alternative fields processed after the
56
- # regular fields so that the decision field is already set.
57
- sorted_fields = @definition.fields.sort do |f1, f2|
56
+ # regular fields so that the decision field has already been already set.
57
+ sorted_fields = @definition.data_fields.sort do |f1, f2|
58
58
  f1alt = is_alt_field?(f1)
59
59
  f2alt = is_alt_field?(f2)
60
60
  f1alt == f2alt ?
@@ -89,6 +89,16 @@ module Fit4Ruby
89
89
  (field_def ? field_def : field).to_s(value))
90
90
  end
91
91
  end
92
+ #@definition.developer_fields.each do |field|
93
+ # $stderr.puts " ++ New Developer Field " +
94
+ # "#{field.field_number.snapshot} " +
95
+ # "Bytes: #{field.size_in_bytes.snapshot} " +
96
+ # "Developer ID: #{field.developer_data_index.snapshot}"
97
+ #end
98
+
99
+ #if @name == 'field_description'
100
+ # obj.create_global_definition
101
+ #end
92
102
  end
93
103
 
94
104
  private
@@ -131,7 +141,7 @@ module Fit4Ruby
131
141
 
132
142
  def produce(definition)
133
143
  fields = []
134
- definition.fields.each do |field|
144
+ definition.data_fields.each do |field|
135
145
  field_def = [ field.type, field.name ]
136
146
  if field.type == 'string'
137
147
  # Strings need special handling. We need to also include the length
@@ -146,6 +156,11 @@ module Fit4Ruby
146
156
  fields << field_def
147
157
  end
148
158
 
159
+ definition.developer_fields.each do |field|
160
+ field_def = [ field.bindata_type, field.name ]
161
+ fields << field_def
162
+ end
163
+
149
164
  BinData::Struct.new(:endian => definition.endian, :fields => fields)
150
165
  end
151
166
 
@@ -36,40 +36,39 @@ module Fit4Ruby
36
36
  def read(io, entity, filter, record_counters)
37
37
  header = FitRecordHeader.read(io)
38
38
 
39
- if !header.compressed?
40
- # process normal headers
41
- local_message_type = header.local_message_type.snapshot
42
- if header.message_type.snapshot == 1
43
- # process definition message
44
- definition = FitDefinition.read(io)
45
- @definitions[local_message_type] = FitMessageRecord.new(definition)
46
- else
47
- # process data message
48
- definition = @definitions[local_message_type]
49
- unless definition
50
- Log.fatal "Undefined local message type: #{local_message_type}"
51
- end
52
- if filter
53
- @number = @definitions[local_message_type].global_message_number
54
- index = (record_counters[@number] += 1)
39
+ if header.compressed?
40
+ # process compressed timestamp header
41
+ time_offset = header.time_offset.snapshot
42
+ end
43
+
44
+ local_message_type = header.local_message_type.snapshot
45
+ if header.normal? && header.message_type.snapshot == 1
46
+ # process definition message
47
+ definition = FitDefinition.read(io, entity,
48
+ header.developer_data_flag.snapshot)
49
+ @definitions[local_message_type] = FitMessageRecord.new(definition)
50
+ else
51
+ # process data message
52
+ definition = @definitions[local_message_type]
53
+ unless definition
54
+ Log.fatal "Undefined local message type: #{local_message_type}"
55
+ end
56
+ if filter
57
+ @number = @definitions[local_message_type].global_message_number
58
+ index = (record_counters[@number] += 1)
55
59
 
56
- # Check if we have a filter defined to collect raw dumps of the
57
- # data in the message records. The dump is collected in @fields
58
- # for later output.
59
- if (filter.record_numbers.nil? ||
60
- filter.record_numbers.include?(@number)) &&
61
- (filter.record_indexes.nil? ||
62
- filter.record_indexes.include?(index))
63
- @name = definition.name
64
- @fields = []
65
- end
60
+ # Check if we have a filter defined to collect raw dumps of the
61
+ # data in the message records. The dump is collected in @fields
62
+ # for later output.
63
+ if (filter.record_numbers.nil? ||
64
+ filter.record_numbers.include?(@number)) &&
65
+ (filter.record_indexes.nil? ||
66
+ filter.record_indexes.include?(index))
67
+ @name = definition.name
68
+ @fields = []
66
69
  end
67
- definition.read(io, entity, filter, @fields)
68
70
  end
69
- else
70
- # process compressed timestamp header
71
- time_offset = header.time_offset
72
- Log.fatal "Support for compressed headers not yet implemented"
71
+ definition.read(io, entity, filter, @fields)
73
72
  end
74
73
 
75
74
  self
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # = FitRecordHeader.rb -- Fit4Ruby - FIT file processing library for Ruby
5
5
  #
6
- # Copyright (c) 2014 by Chris Schlaeger <cs@taskjuggler.org>
6
+ # Copyright (c) 2014, 2017 by Chris Schlaeger <cs@taskjuggler.org>
7
7
  #
8
8
  # This program is free software; you can redistribute it and/or modify
9
9
  # it under the terms of version 2 of the GNU General Public License as
@@ -17,13 +17,13 @@ module Fit4Ruby
17
17
  bit1 :normal
18
18
 
19
19
  bit1 :message_type, :onlyif => :normal?
20
- bit2 :reserved, :onlyif => :normal?
21
20
 
21
+ bit1 :developer_data_flag, :onlyif => :normal?
22
+ bit1 :reserved, :onlyif => :normal?
22
23
  choice :local_message_type, :selection => :normal do
23
24
  bit4 0
24
25
  bit2 1
25
26
  end
26
-
27
27
  bit5 :time_offset, :onlyif => :compressed?
28
28
 
29
29
  def normal?
@@ -283,6 +283,25 @@ module Fit4Ruby
283
283
  entry 2, 'paused'
284
284
  entry 3, 'unknown'
285
285
 
286
+ dict 'fit_base_type'
287
+ entry 0, 'enum'
288
+ entry 1, 'sint8'
289
+ entry 2, 'uint8'
290
+ entry 7, 'string'
291
+ entry 10, 'uint8z'
292
+ entry 13, 'byte'
293
+ entry 131, 'sint16'
294
+ entry 132, 'uint16'
295
+ entry 133, 'sint32'
296
+ entry 134, 'uint32'
297
+ entry 136, 'float32'
298
+ entry 137, 'float64'
299
+ entry 139, 'uint16z'
300
+ entry 140, 'uint32z'
301
+ entry 142, 'sint64'
302
+ entry 143, 'uint64'
303
+ entry 144, 'uint64z'
304
+
286
305
  dict 'garmin_product'
287
306
  entry 8, 'hrm_run_single_byte_product_id'
288
307
  entry 1551, 'fenix'
@@ -86,7 +86,7 @@ module Fit4Ruby
86
86
  end
87
87
 
88
88
  def fit_to_native(value)
89
- return nil if value == FitDefinitionField.undefined_value(@type)
89
+ return nil if value == FitDefinitionFieldBase.undefined_value(@type)
90
90
 
91
91
  if @opts.include?(:dict) && (dict = GlobalFitDictionaries[@opts[:dict]])
92
92
  return dict.name(value) || "Undocumented value #{value}"
@@ -106,12 +106,12 @@ module Fit4Ruby
106
106
  end
107
107
 
108
108
  def native_to_fit(value)
109
- return FitDefinitionField.undefined_value(@type) if value.nil?
109
+ return FitDefinitionFieldBase.undefined_value(@type) if value.nil?
110
110
 
111
111
  if @opts.include?(:dict) && (dict = GlobalFitDictionaries[@opts[:dict]])
112
112
  unless (dv = dict.value_by_name(value))
113
113
  Log.error "Unknown value '#{value}' assigned to field #{@name}"
114
- return FitDefinitionField.undefined_value(@type)
114
+ return FitDefinitionFieldBase.undefined_value(@type)
115
115
  else
116
116
  return dv
117
117
  end
@@ -63,6 +63,7 @@ module Fit4Ruby
63
63
  field 53, 'enum', 'undocumented_field_53'
64
64
  field 54, 'enum', 'undocumented_field_54'
65
65
  field 56, 'enum', 'mounting_side'
66
+ field 55, 'enum', 'undocumented_field_55'
66
67
  field 58, 'uint16', 'autosync_min_steps'
67
68
  field 59, 'uint16', 'autosync_min_time'
68
69
  field 62, 'enum', 'undocumented_field_62'
@@ -98,6 +99,7 @@ module Fit4Ruby
98
99
  field 127, 'enum', 'undocumented_field_127'
99
100
  field 128, 'enum', 'undocumented_field_128'
100
101
  field 133, 'enum', 'undocumented_field_133'
102
+ field 138, 'enum', 'undocumented_field_138'
101
103
 
102
104
  message 3, 'user_profile'
103
105
  field 0, 'string', 'friendly_name'
@@ -115,7 +117,7 @@ module Fit4Ruby
115
117
  field 17, 'enum', 'activity_class'
116
118
  field 18, 'enum', 'position_setting', :dict => 'display_position'
117
119
  field 21, 'enum', 'temperature_setting', :dict => 'display_measure'
118
- field 24, 'uint8', 'undocumented_field_24'
120
+ field 24, 'uint8', 'birth_year'
119
121
  field 28, 'uint32', 'wake_time', :type => 'duration'
120
122
  field 29, 'uint32', 'sleep_time', :type => 'duration'
121
123
  field 30, 'enum', 'height_setting', :dict => 'display_measure'
@@ -199,6 +201,7 @@ module Fit4Ruby
199
201
  field 54, 'uint32', 'undocumented_field_54'
200
202
  field 55, 'enum', 'undocumented_field_55'
201
203
  field 57, 'enum', 'undocumented_field_57'
204
+ field 60, 'enum', 'undocumented_field_60'
202
205
  field 254, 'uint16', 'message_index'
203
206
 
204
207
  message 18, 'session'
@@ -302,6 +305,12 @@ module Fit4Ruby
302
305
  field 134, 'uint16', 'avg_stride_length', :scale => 10000, :unit => 'm' # guessed
303
306
  field 137, 'uint8', 'g_effect', :scale => 10
304
307
  field 138, 'uint8', 'undocumented_field_138'
308
+ field 151, 'uint16', 'undocumented_field_151'
309
+ field 152, 'uint32', 'undocumented_field_152'
310
+ field 157, 'uint16', 'undocumented_field_157'
311
+ field 158, 'uint16', 'undocumented_field_158'
312
+ field 153, 'enum', 'undocumented_field_153'
313
+ field 154, 'enum', 'undocumented_field_154'
305
314
  field 253, 'uint32', 'timestamp', :type => 'date_time'
306
315
  field 254, 'uint16', 'message_index'
307
316
 
@@ -390,6 +399,8 @@ module Fit4Ruby
390
399
  field 118, 'uint16', 'vertical_ratio', :scale => 100, :unit => '%' # guessed
391
400
  field 119, 'uint16', 'avg_gct_balance', :scale => 100, :unit => '%' # guessed
392
401
  field 120, 'uint16', 'avg_stride_length', :scale => 10000, :unit => 'm' # guessed
402
+ field 125, 'uint16', 'undocumented_field_125'
403
+ field 126, 'uint16', 'undocumented_field_126'
393
404
  field 253, 'uint32', 'timestamp', :type => 'date_time'
394
405
  field 254, 'uint16', 'message_index'
395
406
 
@@ -475,8 +486,8 @@ module Fit4Ruby
475
486
  # Possibly which device is used as metering source.
476
487
  # Not documented in FIT SDK, so the field names are all guesses right now.
477
488
  message 22, 'data_sources'
478
- field 0, 'uint8', 'distance'
479
- field 1, 'uint8', 'speed'
489
+ field 0, 'uint8', 'speed'
490
+ field 1, 'uint8', 'distance'
480
491
  field 2, 'uint8', 'cadence'
481
492
  field 3, 'uint8', 'elevation'
482
493
  field 4, 'uint8', 'heart_rate'
@@ -515,6 +526,7 @@ module Fit4Ruby
515
526
  field 25, 'enum', 'source_type', :dict => 'source_type'
516
527
  field 29, 'uint8', 'undocumented_field_29'
517
528
  field 30, 'uint8', 'undocumented_field_30'
529
+ field 31, 'uint32', 'undocumented_field_31'
518
530
  field 253, 'uint32', 'timestamp', :type => 'date_time'
519
531
 
520
532
  # This message was first seen on the Fenix3HR with firmware 3.0.
@@ -787,8 +799,32 @@ module Fit4Ruby
787
799
  field 56, 'uint16', 'undocumented_field_56'
788
800
  field 57, 'uint16', 'undocumented_field_57'
789
801
  field 58, 'enum', 'undocumented_field_58'
802
+ field 59, 'uint8', 'undocumented_field_59'
790
803
  field 254, 'uint16', 'message_index'
791
804
 
805
+ message 206, 'field_description'
806
+ field 0, 'uint8', 'developer_data_index'
807
+ field 1, 'uint8', 'field_definition_number'
808
+ field 2, 'uint8', 'fit_base_type_id'
809
+ field 3, 'string', 'field_name', :array => true
810
+ field 4, 'uint8', 'array'
811
+ field 5, 'string', 'components'
812
+ field 6, 'uint8', 'scale'
813
+ field 7, 'sint8', 'offset'
814
+ field 8, 'string', 'units', :array => true
815
+ field 9, 'string', 'bits'
816
+ field 10, 'string', 'accumulate'
817
+ field 13, 'uint16', 'fit_base_unit_id'
818
+ field 14, 'uint16', 'native_mesg_num'
819
+ field 15, 'uint8', 'native_field_num'
820
+
821
+ message 207, 'developer_data_id'
822
+ field 0, 'byte', 'developer_id', :array => true
823
+ field 1, 'byte', 'application_id', :array => true
824
+ field 2, 'uint16', 'manufacturer_id'
825
+ field 3, 'uint8', 'developer_data_index'
826
+ field 4, 'uint32', 'application_version'
827
+
792
828
  # Not part of the official ANT SDK doc.
793
829
  message 211, 'undocumented_211'
794
830
  field 0, 'uint8', 'undocumented_field_0'
@@ -811,6 +847,21 @@ module Fit4Ruby
811
847
  field 15, 'uint16', 'undocumented_field_15'
812
848
  field 253, 'uint32', 'timestamp', :type => 'date_time'
813
849
 
850
+ # Not part of the official ANT SDK doc.
851
+ message 227, 'stress'
852
+ field 0, 'sint16', 'undocumented_0'
853
+ field 1, 'uint32', 'undocumented_1'
854
+ field 2, 'sint8', 'level'
855
+
856
+ message 1024, 'undocumented_1024'
857
+ field 0, 'enum', 'undocumented_field_0'
858
+ field 2, 'enum', 'undocumented_field_2'
859
+ field 15, 'uint16', 'undocumented_field_15'
860
+ field 40, 'enum', 'undocumented_field_40'
861
+ field 44, 'enum', 'undocumented_field_44'
862
+ field 247, 'enum', 'undocumented_field_247'
863
+ field 255, 'enum', 'undocumented_field_255'
864
+
814
865
  # Not part of the official ANT SDK doc.
815
866
  message 233, 'undocumented_233'
816
867
  field 2, 'byte', 'undocumented_field_2'
@@ -1,4 +1,4 @@
1
1
  module Fit4Ruby
2
2
  # The version number of the library.
3
- VERSION = '1.6.1'
3
+ VERSION = '1.6.2'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fit4ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Schlaeger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-14 00:00:00.000000000 Z
11
+ date: 2017-10-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bindata
@@ -69,8 +69,8 @@ dependencies:
69
69
  description: |
70
70
  This library can read and write FIT files and convert them into a Ruby data
71
71
  structure for easy processing. This library was written for Garmin devices
72
- like the FR620, Fenix 3 and Fenix 3 HR. Fit files from other devices may work
73
- as well but have not been tested.
72
+ like the FR620, Fenix 3, Fenix 3 HR, Fenix 5 (s and X). Fit files from other
73
+ devices may work as well but have not been tested.
74
74
  email: chris@linux.com
75
75
  executables: []
76
76
  extensions: []
@@ -86,16 +86,20 @@ files:
86
86
  - lib/fit4ruby/Activity.rb
87
87
  - lib/fit4ruby/Converters.rb
88
88
  - lib/fit4ruby/DataSources.rb
89
+ - lib/fit4ruby/DeveloperDataId.rb
89
90
  - lib/fit4ruby/DeviceInfo.rb
90
91
  - lib/fit4ruby/DumpedField.rb
91
92
  - lib/fit4ruby/EPO_Data.rb
92
93
  - lib/fit4ruby/Event.rb
94
+ - lib/fit4ruby/FieldDescription.rb
93
95
  - lib/fit4ruby/FileCreator.rb
94
96
  - lib/fit4ruby/FileId.rb
95
97
  - lib/fit4ruby/FileNameCoder.rb
96
98
  - lib/fit4ruby/FitDataRecord.rb
97
99
  - lib/fit4ruby/FitDefinition.rb
98
100
  - lib/fit4ruby/FitDefinitionField.rb
101
+ - lib/fit4ruby/FitDefinitionFieldBase.rb
102
+ - lib/fit4ruby/FitDeveloperDataFieldDefinition.rb
99
103
  - lib/fit4ruby/FitFile.rb
100
104
  - lib/fit4ruby/FitFileEntity.rb
101
105
  - lib/fit4ruby/FitFilter.rb
@@ -155,7 +159,7 @@ rubyforge_project:
155
159
  rubygems_version: 2.2.5
156
160
  signing_key:
157
161
  specification_version: 4
158
- summary: Library to read GARMIN FIT files.
162
+ summary: Library to read and write GARMIN FIT files.
159
163
  test_files:
160
164
  - spec/FileNameCoder_spec.rb
161
165
  - spec/FitFile_spec.rb