fit4ruby 0.0.6 → 0.0.7

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: 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