fit4ruby 0.0.6 → 0.0.7

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: 2cf519ee70f6b6337d1954ae6073ffa16e268953
4
- data.tar.gz: e823bcb9ea3857f7900e4e3e08a4bb97a057145a
3
+ metadata.gz: 012c8c74873a9ce1d703e72c75e14019d480db0f
4
+ data.tar.gz: 32137198f2a8a87dc473c7bdc0c3bdc978a9e31c
5
5
  SHA512:
6
- metadata.gz: a92c584b15630e0cdd8c0d18ceed90214f84413df8246776c15a69f3ed685768015e2f9be70f9dd62cda5f12fb459022203fe8d55f3d34d117f07914e4530272
7
- data.tar.gz: 3b11fec1a502086756ff02a3d206aa64350ce0be86245bc71bbaf816d402c20b0dd0917635996368331de0ddda211786042b93e6ead52e762a1005727ddd39f7
6
+ metadata.gz: b5c6653686b550a201b71ad021af82d79cc03b092fceda5441fd286c0a3e34ff2c7f2291b35aa5f8ed3c740060953a47b63ef1c2c895a8571cf09a0464566640
7
+ data.tar.gz: bb3b5778604e1f342163fd751c3e3beccb09c3aa8ddcebb5deb7274c1623e6d194408b54486f34837d80645aa471e5c2132236551d5bf4c8ff2fc1f3864f3a07
@@ -36,6 +36,7 @@ module Fit4Ruby
36
36
  # certain fields of the FitDataRecord.
37
37
  def initialize(field_values = {})
38
38
  super('activity')
39
+ @meta_field_units['total_gps_distance'] = 'm'
39
40
  @num_sessions = 0
40
41
 
41
42
  @file_id = FileId.new
@@ -59,7 +60,7 @@ module Fit4Ruby
59
60
  # Perform some basic logical checks on the object and all references sub
60
61
  # objects. Any errors will be reported via the Log object.
61
62
  def check
62
- unless @timestamp && @timestamp >= Time.parse('1990-01-01')
63
+ unless @timestamp && @timestamp >= Time.parse('1990-01-01T00:00:00+00:00')
63
64
  Log.error "Activity has no valid timestamp"
64
65
  end
65
66
  unless @total_timer_time
@@ -71,6 +72,12 @@ module Fit4Ruby
71
72
  "FIT file."
72
73
  end
73
74
  @sessions.each { |s| s.check(self) }
75
+ # Laps must have a consecutively growing message index.
76
+ @laps.each.with_index do |lap, index|
77
+ unless lap.message_index == index
78
+ Log.error "Lap #{index} has wrong message_index #{lap.message_index}"
79
+ end
80
+ end
74
81
  end
75
82
 
76
83
  # Convenience method that aggregates all the distances from the included
@@ -81,6 +88,48 @@ module Fit4Ruby
81
88
  d
82
89
  end
83
90
 
91
+ # Total distance convered by this activity purely computed by the GPS
92
+ # coordinates. This may differ from the distance computed by the device as
93
+ # it can be based on a purely calibrated footpod.
94
+ def total_gps_distance
95
+ timer_stops = []
96
+ # Generate a list of all timestamps where the timer was stopped.
97
+ @events.each do |e|
98
+ if e.event == 'timer' && e.event_type == 'stop_all'
99
+ timer_stops << e.timestamp
100
+ end
101
+ end
102
+
103
+ # The first record of a FIT file can already have a distance associated
104
+ # with it. The GPS location of the first record is not where the start
105
+ # button was pressed. This introduces a slight inaccurcy when computing
106
+ # the total distance purely on the GPS coordinates found in the records.
107
+ d = 0.0
108
+ last_lat = last_long = nil
109
+
110
+ # Iterate over all the records and accumlate the distances between the
111
+ # neiboring coordinates.
112
+ @records.each do |r|
113
+ if (lat = r.position_lat) && (long = r.position_long)
114
+ if last_lat && last_long
115
+ d += Fit4Ruby::GeoMath.distance(last_lat, last_long,
116
+ lat, long)
117
+ end
118
+ if timer_stops[0] == r.timestamp
119
+ # If a stop event was found for this record timestamp we clear the
120
+ # last_* values so that the distance covered while being stopped
121
+ # is not added to the total.
122
+ last_lat = last_long = nil
123
+ timer_stops.shift
124
+ else
125
+ last_lat = lat
126
+ last_long = long
127
+ end
128
+ end
129
+ end
130
+ d
131
+ end
132
+
84
133
  # Call this method to update the aggregated data fields stored in Lap and
85
134
  # Session objects.
86
135
  def aggregate
@@ -68,11 +68,11 @@ module Fit4Ruby
68
68
  end
69
69
 
70
70
  def time_to_fit_time(t)
71
- (t - Time.parse('1989-12-31')).to_i
71
+ (t - Time.parse('1989-12-31T00:00:00+00:00')).to_i
72
72
  end
73
73
 
74
74
  def fit_time_to_time(ft)
75
- Time.parse('1989-12-31') + ft.to_i
75
+ Time.parse('1989-12-31T00:00:00+00:00') + ft.to_i
76
76
  end
77
77
 
78
78
  end
@@ -67,6 +67,9 @@ module Fit4Ruby
67
67
  entry 5, 'critical'
68
68
 
69
69
  dict 'device_type'
70
+ entry 0, 'position' # Just a guess
71
+ entry 3, 'acceleration' # Just a guess
72
+ entry 4, 'barometric_pressure' # Just a guess
70
73
  entry 1, 'antfs'
71
74
  entry 11, 'bike_power'
72
75
  entry 12, 'environment_sensor_legacy'
@@ -159,8 +162,15 @@ module Fit4Ruby
159
162
  dict 'garmin_product'
160
163
  entry 8, 'hrm_run_single_byte_product_id'
161
164
  entry 1551, 'fenix'
165
+ # The Fenix3 is rumored to have a Mediatek MT3333 GPS chipset. Not sure if
166
+ # that would be a beter name.
167
+ entry 1620, 'fenix3_gps' # Just a guess
162
168
  entry 1623, 'fr620'
163
169
  entry 1632, 'fr220'
170
+ # The FR620 is rumored to have a MediaTek MT3339 GPS chipset while the
171
+ # FR920XT is rumored to have a MT3333. Not sure why they have the same ID
172
+ # in the FIT file for the GPS device.
173
+ entry 1689, 'fr620_fr920xt_gps' # Just a guess
164
174
  entry 1752, 'hrm_run'
165
175
  entry 1765, 'fr920xt'
166
176
  entry 1928, 'fr620_japan'
@@ -208,6 +218,10 @@ module Fit4Ruby
208
218
  entry 0x3FFF, 'mask'
209
219
  entry 0x8000, 'right'
210
220
 
221
+ dict 'length_type'
222
+ entry 0, 'idle'
223
+ entry 1, 'active'
224
+
211
225
  dict 'manufacturer'
212
226
  entry 1, 'garmin'
213
227
  entry 2, 'garmin_fr405_antfs'
@@ -26,6 +26,7 @@ module Fit4Ruby
26
26
  field 4, 'uint32', 'time_created', :type => 'date_time'
27
27
  field 5, 'uint16', 'number'
28
28
  field 6, 'uint16', 'undocumented_field_6'
29
+ field 7, 'uint32', 'undocumented_field_7'
29
30
 
30
31
  message 12, 'sport'
31
32
  field 0, 'enum', 'sport', :dict => 'sport'
@@ -253,13 +254,14 @@ module Fit4Ruby
253
254
  field 4, 'uint8', 'event_group'
254
255
  field 253, 'uint32', 'timestamp', :type => 'date_time'
255
256
 
256
- # Possibly the status of the sensors; Not documented in FIT SDK
257
- message 22, 'sensor_status'
258
- field 0, 'uint8', 'undocumented_field_0' # 1 - 5 or no value seen
259
- field 1, 'uint8', 'undocumented_field_1' # Always identical to field 0
260
- field 2, 'uint8', 'undocumented_field_2' # 1 - 5 or no value seen
261
- field 3, 'uint8', 'undocumented_field_3' # 1, 3, 5 or no value seen
262
- field 4, 'uint8', 'undocumented_field_4' # 1 - 3 or no value seen
257
+ # Possibly which device is used as metering source.
258
+ # Not documented in FIT SDK, so these are all guesses right now.
259
+ message 22, 'metering_devices'
260
+ field 0, 'uint8', 'speed'
261
+ field 1, 'uint8', 'distance'
262
+ field 2, 'uint8', 'cadence'
263
+ field 3, 'uint8', 'altitude'
264
+ field 4, 'uint8', 'heart_rate'
263
265
  field 5, 'enum', 'undocumented_field_5' # 0 or 3 seen
264
266
  field 6, 'uint8', 'undocumented_field_6' # First found in FR920XT
265
267
  field 14, 'uint8', 'undocumented_field_14' # First found in FR920XT
@@ -336,7 +338,7 @@ module Fit4Ruby
336
338
  field 0, 'enum', 'device_index'
337
339
  field 1, 'uint16', 'calories', :unit => 'kcal'
338
340
  field 2, 'uint32', 'distance', :scale => 100, :unit => 'm'
339
- field 5, 'enum', 'activity_type'
341
+ field 5, 'enum', 'activity_type', :dict => 'activity_type'
340
342
  alt_field 3, 'activity_type' do
341
343
  field :default, 'uint32', 'cycles', :scale => 2, :unit => 'cycles'
342
344
  field [ 'walking', 'running' ], 'uint32', 'steps', :unit => 'steps'
@@ -351,7 +353,7 @@ module Fit4Ruby
351
353
  field 11, 'uint16', 'local_timestamp'
352
354
  field 19, 'uint16', 'active_calories', :unit => 'kcal'
353
355
  field 24, 'byte', 'current_activity_type_intensity', :type => 'activity_intensity'
354
- field 26, 'uint16', 'timestamp16', :unit => 's'
356
+ field 26, 'uint16', 'timestamp_16', :unit => 's'
355
357
  field 29, 'uint16', 'duration_min', :unit => 'min'
356
358
  field 253, 'uint32', 'timestamp', :type => 'date_time'
357
359
 
@@ -383,6 +385,24 @@ module Fit4Ruby
383
385
  field 9, 'uint16', 'undocumented_field_9' # maybe activity measurement
384
386
  field 253, 'uint32', 'timestamp', :type => 'date_time'
385
387
 
388
+ message 101, 'length'
389
+ field 0, 'enum', 'event', :dict => 'event'
390
+ field 1, 'enum', 'event_type', :dict => 'event_type'
391
+ field 2, 'uint32', 'date_time', :type => 'date_time'
392
+ field 3, 'uint32', 'total_elapsed_time', :scale => 1000, :unit => 's'
393
+ field 4, 'uint32', 'total_timer_time', :scale => 1000, :unit => 's'
394
+ field 5, 'uint16', 'total_strokes', :unit => 'strokes'
395
+ field 6, 'uint16', 'avg_speed', :scale => 1000, :unit => 'm/s'
396
+ field 7, 'enum', 'swim_stroke', :dict => 'swim_stroke'
397
+ field 9, 'uint8', 'avg_swimming_cadence', :unit => 'strokes/min'
398
+ field 10, 'uint8', 'event_group'
399
+ field 11, 'uint16', 'total_calories', :unit => 'kcal'
400
+ field 12, 'enum', 'length_type', :dict => 'length_type'
401
+ field 18, 'uint16', 'player_score'
402
+ field 19, 'uint16', 'opponent_score'
403
+ field 253, 'uint32', 'timestamp', :type => 'date_time'
404
+ field 254, 'uint16', 'message_index'
405
+
386
406
  message 103, 'monitoring_info'
387
407
  field 0, 'uint32', 'local_time', :type => 'date_time'
388
408
  field 1, 'enum', 'activity_type', :array => true, :dict => 'activity_type'
@@ -45,6 +45,37 @@ module Fit4Ruby
45
45
  # Perform some basic logical checks on the object and all references sub
46
46
  # objects. Any errors will be reported via the Log object.
47
47
  def check
48
+ last_timestamp = ts_16_offset = nil
49
+
50
+ # The timestamp_16 is a 2 byte time stamp value that is used instead of
51
+ # the 4 byte timestamp field for monitoring records that have
52
+ # current_activity_type_intensity values with an activity type of 6. The
53
+ # value seems to be in seconds, but the 0 value reference does not seem
54
+ # to be included in the file. However, it can be approximated using the
55
+ # surrounding timestamp values.
56
+ @monitorings.each do |record|
57
+ if ts_16_offset
58
+ # We have already found the offset. Adjust all timestamps according
59
+ # to 'offset + timestamp_16'
60
+ if record.timestamp_16
61
+ record.timestamp = ts_16_offset + record.timestamp_16
62
+ end
63
+ else
64
+ # We are still looking for the offset.
65
+ if record.timestamp_16 && last_timestamp
66
+ # We have a previous timestamp and found the first record with a
67
+ # timestamp_16 value set. We assume that the timestamp of this
68
+ # record is one minute after the previously found timestamp.
69
+ # That's just a guess. Who knows what the Garmin engineers were
70
+ # thinking here?
71
+ ts_16_offset = last_timestamp + 60 - record.timestamp_16
72
+ record.timestamp = ts_16_offset + record.timestamp_16
73
+ else
74
+ # Just save the timestamp of the current record.
75
+ last_timestamp = record.timestamp
76
+ end
77
+ end
78
+ end
48
79
  end
49
80
 
50
81
  # Create a new FitDataRecord.
@@ -13,6 +13,7 @@
13
13
  require 'fit4ruby/Converters'
14
14
  require 'fit4ruby/FitDataRecord'
15
15
  require 'fit4ruby/RecordAggregator'
16
+ require 'fit4ruby/GeoMath'
16
17
 
17
18
  module Fit4Ruby
18
19
 
@@ -1,4 +1,4 @@
1
1
  module Fit4Ruby
2
2
  # The version number of the library.
3
- VERSION = '0.0.6'
3
+ VERSION = '0.0.7'
4
4
  end
data/spec/FitFile_spec.rb CHANGED
@@ -26,6 +26,7 @@ describe Fit4Ruby do
26
26
  a.new_device_info({ :device_index => 1, :manufacturer => 'development',
27
27
  :battery_status => 'ok' })
28
28
  ts = Time.now
29
+ laps = 0
29
30
  0.upto(a.total_timer_time / 60) do |mins|
30
31
  ts += 60
31
32
  a.new_record({
@@ -46,7 +47,8 @@ describe Fit4Ruby do
46
47
 
47
48
  if mins > 0 && mins % 5 == 0
48
49
  a.new_lap({ :timestamp => ts, :sport => 'running',
49
- :total_cycles => 195 })
50
+ :message_index => laps, :total_cycles => 195 })
51
+ laps += 1
50
52
  end
51
53
  end
52
54
  a.new_session({ :timestamp => ts, :sport => 'running' })
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: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Schlaeger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-11 00:00:00.000000000 Z
11
+ date: 2015-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bindata