fit4ruby 1.6.1 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
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