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 +4 -4
- data/lib/fit4ruby/Activity.rb +50 -1
- data/lib/fit4ruby/Converters.rb +2 -2
- data/lib/fit4ruby/GlobalFitDictionaries.rb +14 -0
- data/lib/fit4ruby/GlobalFitMessages.rb +29 -9
- data/lib/fit4ruby/Monitoring_B.rb +31 -0
- data/lib/fit4ruby/Session.rb +1 -0
- data/lib/fit4ruby/version.rb +1 -1
- data/spec/FitFile_spec.rb +3 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 012c8c74873a9ce1d703e72c75e14019d480db0f
|
4
|
+
data.tar.gz: 32137198f2a8a87dc473c7bdc0c3bdc978a9e31c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b5c6653686b550a201b71ad021af82d79cc03b092fceda5441fd286c0a3e34ff2c7f2291b35aa5f8ed3c740060953a47b63ef1c2c895a8571cf09a0464566640
|
7
|
+
data.tar.gz: bb3b5778604e1f342163fd751c3e3beccb09c3aa8ddcebb5deb7274c1623e6d194408b54486f34837d80645aa471e5c2132236551d5bf4c8ff2fc1f3864f3a07
|
data/lib/fit4ruby/Activity.rb
CHANGED
@@ -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-
|
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
|
data/lib/fit4ruby/Converters.rb
CHANGED
@@ -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-
|
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-
|
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
|
257
|
-
|
258
|
-
|
259
|
-
field
|
260
|
-
field
|
261
|
-
field
|
262
|
-
field
|
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', '
|
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.
|
data/lib/fit4ruby/Session.rb
CHANGED
data/lib/fit4ruby/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2015-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bindata
|