File Coverage

blib/lib/Geo/FIT.pm
Criterion Covered Total %
statement 606 998 60.7
branch 279 574 48.6
condition 29 78 37.1
subroutine 70 97 72.1
pod 32 83 38.5
total 1016 1830 55.5


line stmt bran cond sub pod time code
1             package Geo::FIT;
2 18     18   997439 use strict;
  18         36  
  18         1725  
3 18     18   121 use warnings;
  18         37  
  18         1896  
4              
5             our $VERSION = '1.13';
6              
7             =encoding utf-8
8              
9             =head1 NAME
10              
11             Geo::FIT - Decode Garmin FIT files
12              
13             =head1 SYNOPSIS
14              
15             use Geo::FIT;
16              
17             Create an instance, assign a FIT file to it, open it:
18              
19             my $fit = Geo::FIT->new();
20             $fit->file( $fname );
21             $fit->open or die $fit->error;
22              
23             Register a callback to get some info on where we've been and when:
24              
25             my $record_callback = sub {
26             my ($self, $descriptor, $values) = @_;
27              
28             my $time= $self->field_value( 'timestamp', $descriptor, $values );
29             my $lat = $self->field_value( 'position_lat', $descriptor, $values );
30             my $lon = $self->field_value( 'position_long', $descriptor, $values );
31              
32             print "Time was: ", join("\t", $time, $lat, $lon), "\n"
33             };
34              
35             $fit->data_message_callback_by_name('record', $record_callback ) or die $fit->error;
36              
37             my @header_things = $fit->fetch_header;
38              
39             1 while ( $fit->fetch );
40              
41             $fit->close;
42              
43             =head1 DESCRIPTION
44              
45             C is a Perl class to provide interfaces to decode Garmin FIT files (*.fit).
46              
47             The module also provides a script to read and print the contents of FIT files (L), a script to convert FIT files to TCX files (L), and a script to convert a locations file to GPX format (L).
48              
49             =cut
50              
51 18     18   128 use Carp qw/ croak /;
  18         43  
  18         1416  
52 18     18   10143 use FileHandle;
  18         219997  
  18         120  
53 18     18   19436 use POSIX qw(BUFSIZ);
  18         165689  
  18         140  
54 18     18   45850 use Time::Local;
  18         57268  
  18         1547  
55 18     18   164 use Scalar::Util qw(blessed looks_like_number);
  18         34  
  18         2697  
56              
57             my $uint64_invalid;
58             BEGIN {
59 18     18   71 eval { $uint64_invalid = unpack('Q', pack('a', -1)) };
  18         73  
60 18 50       76 unless (defined $uint64_invalid) {
61 18         29791 require Math::BigInt;
62 18         1006764 import Math::BigInt
63             }
64             }
65              
66             require Exporter;
67             our @ISA = qw(Exporter);
68             our @EXPORT = qw(
69             FIT_ENUM
70             FIT_SINT8
71             FIT_UINT8
72             FIT_SINT16
73             FIT_UINT16
74             FIT_SINT32
75             FIT_UINT32
76             FIT_SINT64
77             FIT_UINT64
78             FIT_STRING
79             FIT_FLOAT32
80             FIT_FLOAT64
81             FIT_UINT8Z
82             FIT_UINT16Z
83             FIT_UINT32Z
84             FIT_UINT64Z
85             FIT_BYTE
86             FIT_BASE_TYPE_MAX
87             FIT_HEADER_LENGTH
88             );
89              
90             sub FIT_ENUM() {0;}
91             sub FIT_SINT8() {1;}
92             sub FIT_UINT8() {2;}
93             sub FIT_SINT16() {3;}
94             sub FIT_UINT16() {4;}
95             sub FIT_SINT32() {5;}
96             sub FIT_UINT32() {6;}
97             sub FIT_STRING() {7;}
98             sub FIT_FLOAT32() {8;}
99             sub FIT_FLOAT64() {9;}
100             sub FIT_UINT8Z() {10;}
101             sub FIT_UINT16Z() {11;}
102             sub FIT_UINT32Z() {12;}
103             sub FIT_BYTE() {13;}
104             sub FIT_SINT64() {14;}
105             sub FIT_UINT64() {15;}
106             sub FIT_UINT64Z() {16;}
107             sub FIT_BASE_TYPE_MAX() {FIT_UINT64Z;}
108              
109             my ($rechd_offset_compressed_timestamp_header, $rechd_mask_compressed_timestamp_header,
110             $rechd_offset_cth_local_message_type, $rechd_length_cth_local_message_type,
111             $rechd_mask_cth_local_message_type, $rechd_length_cth_timestamp, $rechd_mask_cth_timestamp,
112             $rechd_offset_definition_message, $rechd_mask_definition_message, $rechd_offset_devdata_message,
113             $rechd_mask_devdata_message, $rechd_length_local_message_type, $rechd_mask_local_message_type,
114             $cthd_offset_local_message_type, $cthd_length_local_message_type, $cthd_mask_local_message_type,
115             $cthd_length_time_offset, $cthd_mask_time_offset
116             );
117              
118             $rechd_offset_compressed_timestamp_header = 7;
119             $rechd_mask_compressed_timestamp_header = 1 << $rechd_offset_compressed_timestamp_header;
120             $rechd_offset_cth_local_message_type = 5;
121             $rechd_length_cth_local_message_type = 2;
122             $rechd_mask_cth_local_message_type = ((1 << $rechd_length_cth_local_message_type) - 1) << $rechd_offset_cth_local_message_type;
123             $rechd_length_cth_timestamp = $rechd_offset_cth_local_message_type;
124             $rechd_mask_cth_timestamp = (1 << $rechd_length_cth_timestamp) - 1;
125             $rechd_offset_definition_message = 6;
126             $rechd_mask_definition_message = 1 << $rechd_offset_definition_message;
127             $rechd_offset_devdata_message = 5;
128             $rechd_mask_devdata_message = 1 << $rechd_offset_devdata_message;
129             $rechd_length_local_message_type = 4;
130             $rechd_mask_local_message_type = (1 << $rechd_length_local_message_type) - 1;
131             $cthd_offset_local_message_type = 5;
132             $cthd_length_local_message_type = 2;
133             $cthd_mask_local_message_type = (1 << $cthd_length_local_message_type) - 1;
134             $cthd_length_time_offset = 5;
135             $cthd_mask_time_offset = (1 << $cthd_length_time_offset) - 1;
136              
137             my ($defmsg_min_template, $defmsg_min_length);
138             $defmsg_min_template = 'C C C S C';
139             $defmsg_min_length = length(pack($defmsg_min_template));
140              
141             my ($deffld_template, $deffld_length, $deffld_mask_endian_p, $deffld_mask_type);
142             $deffld_template = 'C C C';
143             $deffld_length = length(pack($deffld_template));
144             $deffld_mask_endian_p = 1 << 7;
145             $deffld_mask_type = (1 << 5) - 1;
146              
147             my ($devdata_min_template, $devdata_min_length, $devdata_deffld_template, $devdata_deffld_length);
148             $devdata_min_template = 'C';
149             $devdata_min_length = length(pack($devdata_min_template));
150             $devdata_deffld_template = 'C C C';
151             $devdata_deffld_length = length(pack($deffld_template));
152              
153             my @invalid = (0xFF) x ($deffld_mask_type + 1);
154             $invalid[FIT_SINT8] = 0x7F;
155             $invalid[FIT_SINT16] = 0x7FFF;
156             $invalid[FIT_UINT16] = 0xFFFF;
157             $invalid[FIT_SINT32] = 0x7FFFFFFF;
158             $invalid[FIT_UINT32] = 0xFFFFFFFF;
159             $invalid[FIT_STRING] = $invalid[FIT_UINT8Z] = $invalid[FIT_UINT16Z] = $invalid[FIT_UINT32Z] = $invalid[FIT_UINT64Z] = 0;
160             #$invalid[FIT_FLOAT32] = NaN;
161             #$invalid[FIT_FLOAT64] = NaN;
162             $invalid[FIT_FLOAT32] = unpack('f', pack('V', 0xFFFFFFFF));
163             $invalid[FIT_FLOAT64] = unpack('d', pack('V V', 0xFFFFFFFF, 0xFFFFFFFF));
164              
165             my ($big_int_base32, $sint64_2c_mask, $sint64_2c_base, $sint64_2c_sign);
166              
167             if (defined $uint64_invalid) {
168             $invalid[FIT_UINT64] = $uint64_invalid;
169             $invalid[FIT_SINT64] = eval '0x7FFFFFFFFFFFFFFF';
170             } else {
171             $invalid[FIT_UINT64] = Math::BigInt->new('0xFFFFFFFFFFFFFFFF');
172             $invalid[FIT_SINT64] = Math::BigInt->new('0x7FFFFFFFFFFFFFFF');
173             $big_int_base32 = Math::BigInt->new('0x100000000');
174             $sint64_2c_mask = Math::BigInt->new('0xFFFFFFFFFFFFFFFF');
175             $sint64_2c_base = Math::BigInt->new('0x10000000000000000');
176             $sint64_2c_sign = Math::BigInt->new('0x1000000000000000');
177             }
178              
179             sub packfilter_uint64_big_endian {
180 0     0 0 0 my @res = $_[0]->bdiv($big_int_base32);
181 0         0 @res;
182             }
183              
184             sub packfilter_uint64_little_endian {
185 0     0 0 0 my @res = $_[0]->bdiv($big_int_base32);
186 0         0 @res[1, 0];
187             }
188              
189             my $my_endian = unpack('L', pack('N', 1)) == 1 ? 1 : 0;
190              
191             *packfilter_uint64 = $my_endian ? \&packfilter_uint64_big_endian : \&packfilter_uint64_little_endian;
192              
193             sub unpackfilter_uint64_big_endian {
194 0     0 0 0 my ($hi, $lo) = @_;
195 0         0 Math::BigInt->new($hi)->blsft(32)->badd($lo);
196             }
197              
198             sub unpackfilter_uint64_little_endian {
199 0     0 0 0 &unpackfilter_uint64_big_endian(@_[1, 0]);
200             }
201              
202             *unpackfilter_uint64 = $my_endian ? \&unpackfilter_uint64_big_endian : \&unpackfilter_uint64_little_endian;
203              
204             sub packfilter_sint64_big_endian {
205 0 0   0 0 0 if ($_[0]->bcmp(0) < 0) {
206 0         0 &packfilter_uint64_big_endian($sint64_2c_mask->band($sint64_2c_base->badd($_[0])));
207             } else {
208 0         0 &packfilter_uint64_big_endian($_[0]);
209             }
210             }
211              
212             sub packfilter_sint64_little_endian {
213 0 0   0 0 0 if ($_[0]->bcmp(0) < 0) {
214 0         0 &packfilter_uint64_little_endian($sint64_2c_mask->band($sint64_2c_base->badd($_[0])));
215             } else {
216 0         0 &packfilter_uint64_little_endian($_[0]);
217             }
218             }
219              
220             *packfilter_sint64 = $my_endian ? \&packfilter_sint64_big_endian : \&packfilter_sint64_little_endian;
221              
222             sub unpackfilter_sint64_big_endian {
223 0     0 0 0 my ($hi, $lo) = @_;
224 0         0 my $n = Math::BigInt->new($hi)->blsft(32)->badd($lo)->band($sint64_2c_mask);
225              
226 0 0       0 if ($n->band($sint64_2c_sign)->bcmp(0) == 0) {
227 0         0 $n;
228             } else {
229 0         0 $n->bsub($sint64_2c_base);
230             }
231             }
232              
233             sub unpackfilter_sint64_little_endian {
234 0     0 0 0 &unpackfilter_sint64_big_endian(@_[1, 0]);
235             }
236              
237             *unpackfilter_sint64 = $my_endian ? \&unpackfilter_sint64_big_endian : \&unpackfilter_sint64_little_endian;
238              
239             sub invalid {
240 0     0 1 0 my ($self, $type) = @_;
241 0         0 $invalid[$type & $deffld_mask_type];
242             }
243              
244             my @size = (1) x ($deffld_mask_type + 1);
245             $size[FIT_SINT16] = $size[FIT_UINT16] = $size[FIT_UINT16Z] = 2;
246             $size[FIT_SINT32] = $size[FIT_UINT32] = $size[FIT_UINT32Z] = $size[FIT_FLOAT32] = 4;
247             $size[FIT_FLOAT64] = $size[FIT_SINT64] = $size[FIT_UINT64] = $size[FIT_UINT64Z] = 8;
248              
249             my (@template, @packfactor, @packfilter, @unpackfilter);
250             @template = ('C') x ($deffld_mask_type + 1);
251             @packfactor = (1) x ($deffld_mask_type + 1);
252             @packfilter = (undef) x ($deffld_mask_type + 1);
253             @unpackfilter = (undef) x ($deffld_mask_type + 1);
254              
255             $template[FIT_SINT8] = 'c';
256             $template[FIT_SINT16] = 's';
257             $template[FIT_UINT16] = $template[FIT_UINT16Z] = 'S';
258             $template[FIT_SINT32] = 'l';
259             $template[FIT_UINT32] = $template[FIT_UINT32Z] = 'L';
260             $template[FIT_FLOAT32] = 'f';
261             $template[FIT_FLOAT64] = 'd';
262              
263             if (defined $uint64_invalid) {
264             $template[FIT_SINT64] = 'q';
265             $template[FIT_UINT64] = $template[FIT_UINT64Z] = 'Q';
266             } else {
267             $template[FIT_SINT64] = $template[FIT_UINT64] = $template[FIT_UINT64Z] = 'L';
268             $packfactor[FIT_SINT64] = $packfactor[FIT_UINT64] = $packfactor[FIT_UINT64Z] = 2;
269             $packfilter[FIT_SINT64] = \&packfilter_sint64;
270             $unpackfilter[FIT_SINT64] = \&unpackfilter_sint64;
271             $packfilter[FIT_UINT64] = $packfilter[FIT_UINT64Z] = \&packfilter_uint64;
272             $unpackfilter[FIT_UINT64] = $unpackfilter[FIT_UINT64Z] = \&unpackfilter_uint64;
273             }
274              
275             my %named_type = (
276             'file' => +{
277             '_base_type' => FIT_ENUM,
278             'device' => 1,
279             'settings' => 2,
280             'sport' => 3,
281             'activity' => 4,
282             'workout' => 5,
283             'course' => 6,
284             'schedules' => 7,
285             'weight' => 9,
286             'totals' => 10,
287             'goals' => 11,
288             'blood_pressure' => 14,
289             'monitoring_a' => 15,
290             'activity_summary' => 20,
291             'monitoring_daily' => 28,
292             'monitoring_b' => 32,
293             'segment' => 34,
294             'segment_list' => 35,
295             'exd_configuration' => 40,
296             'mfg_range_min' => 0xF7,
297             'mfg_range_max' => 0xFE,
298             },
299              
300             'mesg_num' => +{
301             '_base_type' => FIT_UINT16,
302             'file_id' => 0,
303             'capabilities' => 1,
304             'device_settings' => 2,
305             'user_profile' => 3,
306             'hrm_profile' => 4,
307             'sdm_profile' => 5,
308             'bike_profile' => 6,
309             'zones_target' => 7,
310             'hr_zone' => 8,
311             'power_zone' => 9,
312             'met_zone' => 10,
313             'sport' => 12,
314             'goal' => 15,
315             'session' => 18,
316             'lap' => 19,
317             'record' => 20,
318             'event' => 21,
319             'source' => 22, # undocumented
320             'device_info' => 23,
321             'workout' => 26,
322             'workout_step' => 27,
323             'schedule' => 28,
324             'location' => 29, # undocumented
325             'weight_scale' => 30,
326             'course' => 31,
327             'course_point' => 32,
328             'totals' => 33,
329             'activity' => 34,
330             'software' => 35,
331             'file_capabilities' => 37,
332             'mesg_capabilities' => 38,
333             'field_capabilities' => 39,
334             'file_creator' => 49,
335             'blood_pressure' => 51,
336             'speed_zone' => 53,
337             'monitoring' => 55,
338             'training_file' => 72,
339             'hrv' => 78,
340             'ant_rx' => 80,
341             'ant_tx' => 81,
342             'ant_channel_id' => 82,
343             'length' => 101,
344             'monitoring_info' => 103,
345             'battery' => 104, # undocumented
346             'pad' => 105,
347             'slave_device' => 106,
348             'connectivity' => 127,
349             'weather_conditions' => 128,
350             'weather_alert' => 129,
351             'cadence_zone' => 131,
352             'hr' => 132,
353             'segment_lap' => 142,
354             'memo_glob' => 145,
355             'sensor' => 147, # undocumented
356             'segment_id' => 148,
357             'segment_leaderboard_entry' => 149,
358             'segment_point' => 150,
359             'segment_file' => 151,
360             'workout_session' => 158,
361             'watchface_settings' => 159,
362             'gps_metadata' => 160,
363             'camera_event' => 161,
364             'timestamp_correlation' => 162,
365             'gyroscope_data' => 164,
366             'accelerometer_data' => 165,
367             'three_d_sensor_calibration' => 167,
368             'video_frame' => 169,
369             'obdii_data' => 174,
370             'nmea_sentence' => 177,
371             'aviation_attitude' => 178,
372             'video' => 184,
373             'video_title' => 185,
374             'video_description' => 186,
375             'video_clip' => 187,
376             'ohr_settings' => 188,
377             'exd_screen_configuration' => 200,
378             'exd_data_field_configuration' => 201,
379             'exd_data_concept_configuration' => 202,
380             'field_description' => 206,
381             'developer_data_id' => 207,
382             'magnetometer_data' => 208,
383             'barometer_data' => 209,
384             'one_d_sensor_calibration' => 210,
385             'monitoring_hr_data' => 211,
386             'time_in_zone' => 216,
387             'set' => 225,
388             'stress_level' => 227,
389             'max_met_data' => 229,
390             'dive_settings' => 258,
391             'dive_gas' => 259,
392             'dive_alarm' => 262,
393             'exercise_title' => 264,
394             'dive_summary' => 268,
395             'spo2_data' => 269,
396             'sleep_level' => 275,
397             'jump' => 285,
398             'aad_accel_features' => 289,
399             'beat_intervals' => 290,
400             'respiration_rate' => 297,
401             'hsa_accelerometer_data' => 302,
402             'hsa_step_data' => 304,
403             'hsa_spo2_data' => 305,
404             'hsa_stress_data' => 306,
405             'hsa_respiration_data' => 307,
406             'hsa_heart_rate_data' => 308,
407             'split' => 312,
408             'split_summary' => 313,
409             'hsa_body_battery_data' => 314,
410             'hsa_event' => 315,
411             'climb_pro' => 317,
412             'tank_update' => 319,
413             'tank_summary' => 323,
414             'sleep_assessment' => 346,
415             'hrv_status_summary' => 370,
416             'hrv_value' => 371,
417             'raw_bbi' => 372,
418             'device_aux_battery_info' => 375,
419             'hsa_gyroscope_data' => 376,
420             'chrono_shot_session' => 387,
421             'chrono_shot_data' => 388,
422             'hsa_configuration_data' => 389,
423             'dive_apnea_alarm' => 393,
424             'skin_temp_overnight' => 398,
425             'hsa_wrist_temperature_data' => 409, # Message number for the HSA wrist temperature data message
426             'mfg_range_min' => 0xFF00, # 0xFF00 - 0xFFFE reserved for manufacturer specific messages
427             'mfg_range_max' => 0xFFFE,
428             },
429              
430             'checksum' => +{
431             '_base_type' => FIT_UINT8,
432             'clear' => 0,
433             'ok' => 1,
434             },
435              
436             'file_flags' => +{
437             '_base_type' => FIT_UINT8Z,
438             '_mask' => 1,
439             'read' => 0x02,
440             'write' => 0x04,
441             'erase' => 0x08,
442             },
443              
444             'mesg_count' => +{
445             '_base_type' => FIT_ENUM,
446             'num_per_file' => 0,
447             'max_per_file' => 1,
448             'max_per_file_type' => 2,
449             },
450              
451             'date_time' => +{
452             '_base_type' => FIT_UINT32,
453             'min' => 0x10000000,
454             '_min' => 0x10000000,
455             '_out_of_range' => 'seconds from device power on',
456             '_offset' => -timegm(0, 0, 0, 31, 11, 1989), # 1989-12-31 00:00:00 UTC
457             },
458              
459             'local_date_time' => +{ # same as above, but in local time zone
460             '_base_type' => FIT_UINT32,
461             'min' => 0x10000000,
462             },
463              
464             'message_index' => +{
465             '_base_type' => FIT_UINT16,
466             '_mask' => 1,
467             'selected' => 0x8000,
468             'reserved' => 0x7000,
469             'mask' => 0x0FFF,
470             },
471              
472             'device_index' => +{ # dynamically created, as devices are added
473             '_base_type' => FIT_UINT8,
474             'creator' => 0, # creator of the file is always device index 0
475             'device1' => 1, # local, v6.00, garmin prod. edge520 (2067)
476             'device2' => 2, # local, v3.00, garmin prod. 1619
477             'heart_rate' => 3, # antplus
478             'speed' => 4, # antplus
479             'cadence' => 5, # antplus
480             'device6' => 6, # antplus power?
481             },
482              
483             'gender' => +{
484             '_base_type' => FIT_ENUM,
485             'female' => 0,
486             'male' => 1,
487             },
488              
489             'language' => +{
490             '_base_type' => FIT_ENUM,
491             'english' => 0,
492             'french' => 1,
493             'italian' => 2,
494             'german' => 3,
495             'spanish' => 4,
496             'croatian' => 5,
497             'czech' => 6,
498             'danish' => 7,
499             'dutch' => 8,
500             'finnish' => 9,
501             'greek' => 10,
502             'hungarian' => 11,
503             'norwegian' => 12,
504             'polish' => 13,
505             'portuguese' => 14,
506             'slovakian' => 15,
507             'slovenian' => 16,
508             'swedish' => 17,
509             'russian' => 18,
510             'turkish' => 19,
511             'latvian' => 20,
512             'ukrainian' => 21,
513             'arabic' => 22,
514             'farsi' => 23,
515             'bulgarian' => 24,
516             'romanian' => 25,
517             'chinese' => 26,
518             'japanese' => 27,
519             'korean' => 28,
520             'taiwanese' => 29,
521             'thai' => 30,
522             'hebrew' => 31,
523             'brazilian_portuguese' => 32,
524             'indonesian' => 33,
525             'malaysian' => 34,
526             'vietnamese' => 35,
527             'burmese' => 36,
528             'mongolian' => 37,
529             'custom' => 254,
530             },
531              
532             'language_bits_0' => +{
533             '_base_type' => FIT_UINT8Z,
534             'english' => 0x01,
535             'french' => 0x02,
536             'italian' => 0x04,
537             'german' => 0x08,
538             'spanish' => 0x10,
539             'croatian' => 0x20,
540             'czech' => 0x40,
541             'danish' => 0x80,
542             },
543              
544             'language_bits_1' => +{
545             '_base_type' => FIT_UINT8Z,
546             'dutch' => 0x01,
547             'finnish' => 0x02,
548             'greek' => 0x04,
549             'hungarian' => 0x08,
550             'norwegian' => 0x10,
551             'polish' => 0x20,
552             'portuguese' => 0x40,
553             'slovakian' => 0x80,
554             },
555              
556             'language_bits_2' => +{
557             '_base_type' => FIT_UINT8Z,
558             'slovenian' => 0x01,
559             'swedish' => 0x02,
560             'russian' => 0x04,
561             'turkish' => 0x08,
562             'latvian' => 0x10,
563             'ukrainian' => 0x20,
564             'arabic' => 0x40,
565             'farsi' => 0x80,
566             },
567              
568             'language_bits_3' => +{
569             '_base_type' => FIT_UINT8Z,
570             'bulgarian' => 0x01,
571             'romanian' => 0x02,
572             'chinese' => 0x04,
573             'japanese' => 0x08,
574             'korean' => 0x10,
575             'taiwanese' => 0x20,
576             'thai' => 0x40,
577             'hebrew' => 0x80,
578             },
579              
580             'language_bits_4' => +{
581             '_base_type' => FIT_UINT8Z,
582             'brazilian_portuguese' => 0x01,
583             'indonesian' => 0x02,
584             'malaysian' => 0x04,
585             'vietnamese' => 0x08,
586             'burmese' => 0x10,
587             'mongolian' => 0x20,
588             },
589              
590             'time_zone' => +{
591             '_base_type' => FIT_ENUM,
592             'almaty' => 0,
593             'bangkok' => 1,
594             'bombay' => 2,
595             'brasilia' => 3,
596             'cairo' => 4,
597             'cape_verde_is' => 5,
598             'darwin' => 6,
599             'eniwetok' => 7,
600             'fiji' => 8,
601             'hong_kong' => 9,
602             'islamabad' => 10,
603             'kabul' => 11,
604             'magadan' => 12,
605             'mid_atlantic' => 13,
606             'moscow' => 14,
607             'muscat' => 15,
608             'newfoundland' => 16,
609             'samoa' => 17,
610             'sydney' => 18,
611             'tehran' => 19,
612             'tokyo' => 20,
613             'us_alaska' => 21,
614             'us_atlantic' => 22,
615             'us_central' => 23,
616             'us_eastern' => 24,
617             'us_hawaii' => 25,
618             'us_mountain' => 26,
619             'us_pacific' => 27,
620             'other' => 28,
621             'auckland' => 29,
622             'kathmandu' => 30,
623             'europe_western_wet' => 31,
624             'europe_central_cet' => 32,
625             'europe_eastern_eet' => 33,
626             'jakarta' => 34,
627             'perth' => 35,
628             'adelaide' => 36,
629             'brisbane' => 37,
630             'tasmania' => 38,
631             'iceland' => 39,
632             'amsterdam' => 40,
633             'athens' => 41,
634             'barcelona' => 42,
635             'berlin' => 43,
636             'brussels' => 44,
637             'budapest' => 45,
638             'copenhagen' => 46,
639             'dublin' => 47,
640             'helsinki' => 48,
641             'lisbon' => 49,
642             'london' => 50,
643             'madrid' => 51,
644             'munich' => 52,
645             'oslo' => 53,
646             'paris' => 54,
647             'prague' => 55,
648             'reykjavik' => 56,
649             'rome' => 57,
650             'stockholm' => 58,
651             'vienna' => 59,
652             'warsaw' => 60,
653             'zurich' => 61,
654             'quebec' => 62,
655             'ontario' => 63,
656             'manitoba' => 64,
657             'saskatchewan' => 65,
658             'alberta' => 66,
659             'british_columbia' => 67,
660             'boise' => 68,
661             'boston' => 69,
662             'chicago' => 70,
663             'dallas' => 71,
664             'denver' => 72,
665             'kansas_city' => 73,
666             'las_vegas' => 74,
667             'los_angeles' => 75,
668             'miami' => 76,
669             'minneapolis' => 77,
670             'new_york' => 78,
671             'new_orleans' => 79,
672             'phoenix' => 80,
673             'santa_fe' => 81,
674             'seattle' => 82,
675             'washington_dc' => 83,
676             'us_arizona' => 84,
677             'chita' => 85,
678             'ekaterinburg' => 86,
679             'irkutsk' => 87,
680             'kaliningrad' => 88,
681             'krasnoyarsk' => 89,
682             'novosibirsk' => 90,
683             'petropavlovsk_kamchatskiy' => 91,
684             'samara' => 92,
685             'vladivostok' => 93,
686             'mexico_central' => 94,
687             'mexico_mountain' => 95,
688             'mexico_pacific' => 96,
689             'cape_town' => 97,
690             'winkhoek' => 98,
691             'lagos' => 99,
692             'riyahd' => 100,
693             'venezuela' => 101,
694             'australia_lh' => 102,
695             'santiago' => 103,
696             'manual' => 253,
697             'automatic' => 254,
698             },
699              
700             'display_measure' => +{
701             '_base_type' => FIT_ENUM,
702             'metric' => 0,
703             'statute' => 1,
704             'nautical' => 2,
705             },
706              
707             'display_heart' => +{
708             '_base_type' => FIT_ENUM,
709             'bpm' => 0,
710             'max' => 1,
711             'reserve' => 2,
712             },
713              
714             'display_power' => +{
715             '_base_type' => FIT_ENUM,
716             'watts' => 0,
717             'percent_ftp' => 1,
718             },
719              
720             'display_position' => +{
721             '_base_type' => FIT_ENUM,
722             'degree' => 0,
723             'degree_minute' => 1,
724             'degree_minute_second' => 2,
725             'austrian_grid' => 3,
726             'british_grid' => 4,
727             'dutch_grid' => 5,
728             'hungarian_grid' => 6,
729             'finnish_grid' => 7,
730             'german_grid' => 8,
731             'icelandic_grid' => 9,
732             'indonesian_equatorial' => 10,
733             'indonesian_irian' => 11,
734             'indonesian_southern' => 12,
735             'india_zone_0' => 13,
736             'india_zone_IA' => 14,
737             'india_zone_IB' => 15,
738             'india_zone_IIA' => 16,
739             'india_zone_IIB' => 17,
740             'india_zone_IIIA' => 18,
741             'india_zone_IIIB' => 19,
742             'india_zone_IVA' => 20,
743             'india_zone_IVB' => 21,
744             'irish_transverse' => 22,
745             'irish_grid' => 23,
746             'loran' => 24,
747             'maidenhead_grid' => 25,
748             'mgrs_grid' => 26,
749             'new_zealand_grid' => 27,
750             'new_zealand_transverse' => 28,
751             'qatar_grid' => 29,
752             'modified_swedish_grid' => 30,
753             'swedish_grid' => 31,
754             'south_african_grid' => 32,
755             'swiss_grid' => 33,
756             'taiwan_grid' => 34,
757             'united_states_grid' => 35,
758             'utm_ups_grid' => 36,
759             'west_malayan' => 37,
760             'borneo_rso' => 38,
761             'estonian_grid' => 39,
762             'latvian_grid' => 40,
763             'swedish_ref_99_grid' => 41,
764             },
765              
766             'switch' => +{
767             '_base_type' => FIT_ENUM,
768             'off' => 0,
769             'on' => 1,
770             'auto' => 2,
771             },
772              
773             'sport' => +{
774             '_base_type' => FIT_ENUM,
775             'generic' => 0,
776             'running' => 1,
777             'cycling' => 2,
778             'transition' => 3, # multisport transition
779             'fitness_equipment' => 4,
780             'swimming' => 5,
781             'basketball' => 6,
782             'soccer' => 7,
783             'tennis' => 8,
784             'american_football' => 9,
785             'training' => 10,
786             'walking' => 11,
787             'cross_country_skiing' => 12,
788             'alpine_skiing' => 13,
789             'snowboarding' => 14,
790             'rowing' => 15,
791             'mountaineering' => 16,
792             'hiking' => 17,
793             'multisport' => 18,
794             'paddling' => 19,
795             'flying' => 20,
796             'e_biking' => 21,
797             'motorcycling' => 22,
798             'boating' => 23,
799             'driving' => 24,
800             'golf' => 25,
801             'hang_gliding' => 26,
802             'horseback_riding' => 27,
803             'hunting' => 28,
804             'fishing' => 29,
805             'inline_skating' => 30,
806             'rock_climbing' => 31,
807             'sailing' => 32,
808             'ice_skating' => 33,
809             'sky_diving' => 34,
810             'snowshoeing' => 35,
811             'snowmobiling' => 36,
812             'stand_up_paddleboarding' => 37,
813             'surfing' => 38,
814             'wakeboarding' => 39,
815             'water_skiing' => 40,
816             'kayaking' => 41,
817             'rafting' => 42,
818             'windsurfing' => 43,
819             'kitesurfing' => 44,
820             'tactical' => 45,
821             'jumpmaster' => 46,
822             'boxing' => 47,
823             'floor_climbing' => 48,
824             'baseball' => 49,
825             'diving' => 53,
826             'hiit' => 62,
827             'racket' => 64,
828             'wheelchair_push_walk' => 65,
829             'wheelchair_push_run' => 66,
830             'meditation' => 67,
831             'disc_golf' => 69,
832             'cricket' => 71,
833             'rugby' => 72,
834             'hockey' => 73,
835             'lacrosse' => 74,
836             'volleyball' => 75,
837             'water_tubing' => 76,
838             'wakesurfing' => 77,
839             'mixed_martial_arts' => 80,
840             'snorkeling' => 82,
841             'dance' => 83,
842             'jump_rope' => 84,
843             'all' => 254, # All is for goals only to include all sports.
844             },
845              
846             'sport_bits_0' => +{
847             '_base_type' => FIT_UINT8Z,
848             'generic' => 0x01,
849             'running' => 0x02,
850             'cycling' => 0x04,
851             'transition' => 0x08,
852             'fitness_equipment' => 0x10,
853             'swimming' => 0x20,
854             'basketball' => 0x40,
855             'soccer' => 0x80,
856             },
857              
858             'sport_bits_1' => +{
859             '_base_type' => FIT_UINT8Z,
860             'tennis' => 0x01,
861             'american_football' => 0x02,
862             'training' => 0x04,
863             'walking' => 0x08,
864             'cross_country_skiing' => 0x10,
865             'alpine_skiing' => 0x20,
866             'snowboarding' => 0x40,
867             'rowing' => 0x80,
868             },
869              
870             'sport_bits_2' => +{
871             '_base_type' => FIT_UINT8Z,
872             'mountaineering' => 0x01,
873             'hiking' => 0x02,
874             'multisport' => 0x04,
875             'paddling' => 0x08,
876             'flying' => 0x10,
877             'e_biking' => 0x20,
878             'motorcycling' => 0x40,
879             'boating' => 0x80,
880             },
881              
882             'sport_bits_3' => +{
883             '_base_type' => FIT_UINT8Z,
884             'driving' => 0x01,
885             'golf' => 0x02,
886             'hang_gliding' => 0x04,
887             'horseback_riding' => 0x08,
888             'hunting' => 0x10,
889             'fishing' => 0x20,
890             'inline_skating' => 0x40,
891             'rock_climbing' => 0x80,
892             },
893              
894             'sport_bits_4' => +{
895             '_base_type' => FIT_UINT8Z,
896             'sailing' => 0x01,
897             'ice_skating' => 0x02,
898             'sky_diving' => 0x04,
899             'snowshoeing' => 0x08,
900             'snowmobiling' => 0x10,
901             'stand_up_paddleboarding' => 0x20,
902             'surfing' => 0x40,
903             'wakeboarding' => 0x80,
904             },
905              
906             'sport_bits_5' => +{
907             '_base_type' => FIT_UINT8Z,
908             'water_skiing' => 0x01,
909             'kayaking' => 0x02,
910             'rafting' => 0x04,
911             'windsurfing' => 0x08,
912             'kitesurfing' => 0x10,
913             'tactical' => 0x20,
914             'jumpmaster' => 0x40,
915             'boxing' => 0x80,
916             },
917              
918             'sport_bits_6' => +{
919             '_base_type' => FIT_UINT8Z,
920             'floor_climbing' => 0x01,
921             },
922              
923             'sub_sport' => +{
924             '_base_type' => FIT_ENUM,
925             'generic' => 0,
926             'treadmill' => 1, # run/fitness equipment
927             'street' => 2, # run
928             'trail' => 3, # run
929             'track' => 4, # run
930             'spin' => 5, # cycling
931             'indoor_cycling' => 6, # cycling/fitness equipment
932             'road' => 7, # cycling
933             'mountain' => 8, # cycling
934             'downhill' => 9, # cycling
935             'recumbent' => 10, # cycling
936             'cyclocross' => 11, # cycling
937             'hand_cycling' => 12, # cycling
938             'track_cycling' => 13, # cycling
939             'indoor_rowing' => 14, # fitness equipment
940             'elliptical' => 15, # fitness equipment
941             'stair_climbing' => 16, # fitness equipment
942             'lap_swimming' => 17, # swimming
943             'open_water' => 18, # swimming
944             'flexibility_training' => 19, # training
945             'strength_training' => 20, # training
946             'warm_up' => 21, # tennis
947             'match' => 22, # tennis
948             'exercise' => 23, # tennis
949             'challenge' => 24,
950             'indoor_skiing' => 25, # fitness equipment
951             'cardio_training' => 26, # training
952             'indoor_walking' => 27, # walking/fitness equipment
953             'e_bike_fitness' => 28, # e-biking
954             'bmx' => 29, # cycling
955             'casual_walking' => 30, # walking
956             'speed_walking' => 31, # walking
957             'bike_to_run_transition' => 32, # transition
958             'run_to_bike_transition' => 33, # transition
959             'swim_to_bike_transition' => 34, # transition
960             'atv' => 35, # motorcycling
961             'motocross' => 36, # motorcycling
962             'backcountry' => 37, # alpine skiing/snowboarding
963             'resort' => 38, # alpine skiing/snowboarding
964             'rc_drone' => 39, # flying
965             'wingsuit' => 40, # flying
966             'whitewater' => 41, # kayaking/rafting
967             'skate_skiing' => 42, # cross country skiing
968             'yoga' => 43, # training
969             'pilates' => 44, # fitness equipment
970             'indoor_running' => 45, # run/fitness equipment
971             'gravel_cycling' => 46, # cycling
972             'e_bike_mountain' => 47, # cycling
973             'commuting' => 48, # cycling
974             'mixed_surface' => 49, # cycling
975             'navigate' => 50,
976             'track_me' => 51,
977             'map' => 52,
978             'single_gas_diving' => 53, # Diving
979             'multi_gas_diving' => 54, # Diving
980             'gauge_diving' => 55, # Diving
981             'apnea_diving' => 56, # Diving
982             'apnea_hunting' => 57, # Diving
983             'virtual_activity' => 58,
984             'obstacle' => 59, # Used for events where participants run, crawl through mud, climb over walls, etc.
985             'breathing' => 62,
986             'sail_race' => 65, # Sailing
987             'ultra' => 67, # Ultramarathon
988             'indoor_climbing' => 68, # Climbing
989             'bouldering' => 69, # Climbing
990             'hiit' => 70, # High Intensity Interval Training
991             'amrap' => 73, # HIIT
992             'emom' => 74, # HIIT
993             'tabata' => 75, # HIIT
994             'pickleball' => 84, # Racket
995             'padel' => 85, # Racket
996             'indoor_wheelchair_walk' => 86,
997             'indoor_wheelchair_run' => 87,
998             'indoor_hand_cycling' => 88,
999             'squash' => 94,
1000             'badminton' => 95,
1001             'racquetball' => 96,
1002             'table_tennis' => 97,
1003             'fly_canopy' => 110, # Flying
1004             'fly_paraglide' => 111, # Flying
1005             'fly_paramotor' => 112, # Flying
1006             'fly_pressurized' => 113, # Flying
1007             'fly_navigate' => 114, # Flying
1008             'fly_timer' => 115, # Flying
1009             'fly_altimeter' => 116, # Flying
1010             'fly_wx' => 117, # Flying
1011             'fly_vfr' => 118, # Flying
1012             'fly_ifr' => 119, # Flying
1013             'all' => 254,
1014             },
1015              
1016             'sport_event' => +{
1017             '_base_type' => FIT_ENUM,
1018             'uncategorized' => 0,
1019             'geocaching' => 1,
1020             'fitness' => 2,
1021             'recreation' => 3,
1022             'race' => 4,
1023             'special_event' => 5,
1024             'training' => 6,
1025             'transportation' => 7,
1026             'touring' => 8,
1027             },
1028              
1029             'activity' => +{
1030             '_base_type' => FIT_ENUM,
1031             'manual' => 0,
1032             'auto_multi_sport' => 1,
1033             },
1034              
1035             'intensity' => +{
1036             '_base_type' => FIT_ENUM,
1037             'active' => 0,
1038             'rest' => 1,
1039             'warmup' => 2,
1040             'cooldown' => 3,
1041             },
1042              
1043             'session_trigger' => +{
1044             '_base_type' => FIT_ENUM,
1045             'activity_end' => 0,
1046             'manual' => 1,
1047             'auto_multi_sport' => 2,
1048             'fitness_equipment' => 3,
1049             },
1050              
1051             'autolap_trigger' => +{
1052             '_base_type' => FIT_ENUM,
1053             'time' => 0,
1054             'distance' => 1,
1055             'position_start' => 2,
1056             'position_lap' => 3,
1057             'position_waypoint' => 4,
1058             'position_marked' => 5,
1059             'off' => 6,
1060             },
1061              
1062             'lap_trigger' => +{
1063             '_base_type' => FIT_ENUM,
1064             'manual' => 0,
1065             'time' => 1,
1066             'distance' => 2,
1067             'position_start' => 3,
1068             'position_lap' => 4,
1069             'position_waypoint' => 5,
1070             'position_marked' => 6,
1071             'session_end' => 7,
1072             'fitness_equipment' => 8,
1073             },
1074              
1075             'time_mode' => +{
1076             '_base_type' => FIT_ENUM,
1077             'hour12' => 0,
1078             'hour24' => 1,
1079             'military' => 2,
1080             'hour_12_with_seconds' => 3,
1081             'hour_24_with_seconds' => 4,
1082             'utc' => 5,
1083             },
1084              
1085             'backlight_mode' => +{
1086             '_base_type' => FIT_ENUM,
1087             'off' => 0,
1088             'manual' => 1,
1089             'key_and_messages' => 2,
1090             'auto_brightness' => 3,
1091             'smart_notifications' => 4,
1092             'key_and_messages_night' => 5,
1093             'key_and_messages_and_smart_notifications' => 6,
1094             },
1095              
1096             'date_mode' => +{
1097             '_base_type' => FIT_ENUM,
1098             'day_month' => 0,
1099             'month_day' => 1,
1100             },
1101              
1102             'backlight_timeout' => +{
1103             '_base_type' => FIT_UINT8,
1104             'infinite' => 0,
1105             },
1106              
1107             'event' => +{
1108             '_base_type' => FIT_ENUM,
1109             'timer' => 0,
1110             'workout' => 3,
1111             'workout_step' => 4,
1112             'power_down' => 5,
1113             'power_up' => 6,
1114             'off_course' => 7,
1115             'session' => 8,
1116             'lap' => 9,
1117             'course_point' => 10,
1118             'battery' => 11,
1119             'virtual_partner_pace' => 12,
1120             'hr_high_alert' => 13,
1121             'hr_low_alert' => 14,
1122             'speed_high_alert' => 15,
1123             'speed_low_alert' => 16,
1124             'cad_high_alert' => 17,
1125             'cad_low_alert' => 18,
1126             'power_high_alert' => 19,
1127             'power_low_alert' => 20,
1128             'recovery_hr' => 21,
1129             'battery_low' => 22,
1130             'time_duration_alert' => 23,
1131             'distance_duration_alert' => 24,
1132             'calorie_duration_alert' => 25,
1133             'activity' => 26,
1134             'fitness_equipment' => 27,
1135             'length' => 28,
1136             'user_marker' => 32,
1137             'sport_point' => 33,
1138             'calibration' => 36,
1139             'front_gear_change' => 42,
1140             'rear_gear_change' => 43,
1141             'rider_position_change' => 44,
1142             'elev_high_alert' => 45,
1143             'elev_low_alert' => 46,
1144             'comm_timeout' => 47,
1145             'auto_activity_detect' => 54, # marker
1146             'dive_alert' => 56, # marker
1147             'dive_gas_switched' => 57, # marker
1148             'tank_pressure_reserve' => 71, # marker
1149             'tank_pressure_critical' => 72, # marker
1150             'tank_lost' => 73, # marker
1151             'radar_threat_alert' => 75, # start/stop/marker
1152             'tank_battery_low' => 76, # marker
1153             'tank_pod_connected' => 81, # marker - tank pod has connected
1154             'tank_pod_disconnected' => 82, # marker - tank pod has lost connection
1155             },
1156              
1157             'event_type' => +{
1158             '_base_type' => FIT_ENUM,
1159             'start' => 0,
1160             'stop' => 1,
1161             'consecutive_depreciated' => 2,
1162             'marker' => 3,
1163             'stop_all' => 4,
1164             'begin_depreciated' => 5,
1165             'end_depreciated' => 6,
1166             'end_all_depreciated' => 7,
1167             'stop_disable' => 8,
1168             'stop_disable_all' => 9,
1169             },
1170              
1171             'timer_trigger' => +{
1172             '_base_type' => FIT_ENUM,
1173             'manual' => 0,
1174             'auto' => 1,
1175             'fitness_equipment' => 2,
1176             },
1177              
1178             'fitness_equipment_state' => +{
1179             '_base_type' => FIT_ENUM,
1180             'ready' => 0,
1181             'in_use' => 1,
1182             'paused' => 2,
1183             'unknown' => 3,
1184             },
1185              
1186             'tone' => +{
1187             '_base_type' => FIT_ENUM,
1188             'off' => 0,
1189             'tone' => 1,
1190             'vibrate' => 2,
1191             'tone_and_vibrate' => 3,
1192             },
1193             'autoscroll' => +{
1194             '_base_type' => FIT_ENUM,
1195             'none' => 0,
1196             'slow' => 1,
1197             'medium' => 2,
1198             'fast' => 3,
1199             },
1200              
1201             'activity_class' => +{
1202             '_base_type' => FIT_ENUM,
1203             '_mask' => 1,
1204             'level' => 0x7f,
1205             'level_max' => 100,
1206             'athlete' => 0x80,
1207             },
1208              
1209             'hr_zone_calc' => +{
1210             '_base_type' => FIT_ENUM,
1211             'custom' => 0,
1212             'percent_max_hr' => 1,
1213             'percent_hrr' => 2,
1214             'percent_lthr' => 3,
1215             },
1216              
1217             'pwr_zone_calc' => +{
1218             '_base_type' => FIT_ENUM,
1219             'custom' => 0,
1220             'percent_ftp' => 1,
1221             },
1222              
1223             'wkt_step_duration' => +{
1224             '_base_type' => FIT_ENUM,
1225             'time' => 0,
1226             'distance' => 1,
1227             'hr_less_than' => 2,
1228             'hr_greater_than' => 3,
1229             'calories' => 4,
1230             'open' => 5,
1231             'repeat_until_steps_cmplt' => 6,
1232             'repeat_until_time' => 7,
1233             'repeat_until_distance' => 8,
1234             'repeat_until_calories' => 9,
1235             'repeat_until_hr_less_than' => 10,
1236             'repeat_until_hr_greater_than' => 11,
1237             'repeat_until_power_less_than' => 12,
1238             'repeat_until_power_greater_than' => 13,
1239             'power_less_than' => 14,
1240             'power_greater_than' => 15,
1241             'training_peaks_tss' => 16,
1242             'repeat_until_power_last_lap_less_than' => 17,
1243             'repeat_until_max_power_last_lap_less_than' => 18,
1244             'power_3s_less_than' => 19,
1245             'power_10s_less_than' => 20,
1246             'power_30s_less_than' => 21,
1247             'power_3s_greater_than' => 22,
1248             'power_10s_greater_than' => 23,
1249             'power_30s_greater_than' => 24,
1250             'power_lap_less_than' => 25,
1251             'power_lap_greater_than' => 26,
1252             'repeat_until_training_peaks_tss' => 27,
1253             'repetition_time' => 28,
1254             'reps' => 29,
1255             'time_only' => 31,
1256             },
1257              
1258             'wkt_step_target' => +{
1259             '_base_type' => FIT_ENUM,
1260             'speed' => 0,
1261             'heart_rate' => 1,
1262             'open' => 2,
1263             'cadence' => 3,
1264             'power' => 4,
1265             'grade' => 5,
1266             'resistance' => 6,
1267             'power_3s' => 7,
1268             'power_10s' => 8,
1269             'power_30s' => 9,
1270             'power_lap' => 10,
1271             'swim_stroke' => 11,
1272             'speed_lap' => 12,
1273             'heart_rate_lap' => 13,
1274             },
1275              
1276             'goal' => +{
1277             '_base_type' => FIT_ENUM,
1278             'time' => 0,
1279             'distance' => 1,
1280             'calories' => 2,
1281             'frequency' => 3,
1282             'steps' => 4,
1283             'ascent' => 5,
1284             'active_minutes' => 6,
1285             },
1286              
1287             'goal_recurrence' => +{
1288             '_base_type' => FIT_ENUM,
1289             'off' => 0,
1290             'daily' => 1,
1291             'weekly' => 2,
1292             'monthly' => 3,
1293             'yearly' => 4,
1294             'custom' => 5,
1295             },
1296              
1297             'goal_source' => +{
1298             '_base_type' => FIT_ENUM,
1299             'auto' => 0,
1300             'community' => 1,
1301             'user' => 2,
1302             },
1303              
1304             'schedule' => +{
1305             '_base_type' => FIT_ENUM,
1306             'workout' => 0,
1307             'course' => 1,
1308             },
1309              
1310             'course_point' => +{
1311             '_base_type' => FIT_ENUM,
1312             'generic' => 0,
1313             'summit' => 1,
1314             'valley' => 2,
1315             'water' => 3,
1316             'food' => 4,
1317             'danger' => 5,
1318             'left' => 6,
1319             'right' => 7,
1320             'straight' => 8,
1321             'first_aid' => 9,
1322             'fourth_category' => 10,
1323             'third_category' => 11,
1324             'second_category' => 12,
1325             'first_category' => 13,
1326             'hors_category' => 14,
1327             'sprint' => 15,
1328             'left_fork' => 16,
1329             'right_fork' => 17,
1330             'middle_fork' => 18,
1331             'slight_left' => 19,
1332             'sharp_left' => 20,
1333             'slight_right' => 21,
1334             'sharp_right' => 22,
1335             'u_turn' => 23,
1336             'segment_start' => 24,
1337             'segment_end' => 25,
1338             },
1339              
1340             'manufacturer' => +{
1341             '_base_type' => FIT_UINT16,
1342             'garmin' => 1,
1343             'garmin_fr405_antfs' => 2,
1344             'zephyr' => 3,
1345             'dayton' => 4,
1346             'idt' => 5,
1347             'srm' => 6,
1348             'quarq' => 7,
1349             'ibike' => 8,
1350             'saris' => 9,
1351             'spark_hk' => 10,
1352             'tanita' => 11,
1353             'echowell' => 12,
1354             'dynastream_oem' => 13,
1355             'nautilus' => 14,
1356             'dynastream' => 15,
1357             'timex' => 16,
1358             'metrigear' => 17,
1359             'xelic' => 18,
1360             'beurer' => 19,
1361             'cardiosport' => 20,
1362             'a_and_d' => 21,
1363             'hmm' => 22,
1364             'suunto' => 23,
1365             'thita_elektronik' => 24,
1366             'gpulse' => 25,
1367             'clean_mobile' => 26,
1368             'pedal_brain' => 27,
1369             'peaksware' => 28,
1370             'saxonar' => 29,
1371             'lemond_fitness' => 30,
1372             'dexcom' => 31,
1373             'wahoo_fitness' => 32,
1374             'octane_fitness' => 33,
1375             'archinoetics' => 34,
1376             'the_hurt_box' => 35,
1377             'citizen_systems' => 36,
1378             'magellan' => 37,
1379             'osynce' => 38,
1380             'holux' => 39,
1381             'concept2' => 40,
1382             'one_giant_leap' => 42,
1383             'ace_sensor' => 43,
1384             'brim_brothers' => 44,
1385             'xplova' => 45,
1386             'perception_digital' => 46,
1387             'bf1systems' => 47,
1388             'pioneer' => 48,
1389             'spantec' => 49,
1390             'metalogics' => 50,
1391             '4iiiis' => 51,
1392             'seiko_epson' => 52,
1393             'seiko_epson_oem' => 53,
1394             'ifor_powell' => 54,
1395             'maxwell_guider' => 55,
1396             'star_trac' => 56,
1397             'breakaway' => 57,
1398             'alatech_technology_ltd' => 58,
1399             'mio_technology_europe' => 59,
1400             'rotor' => 60,
1401             'geonaute' => 61,
1402             'id_bike' => 62,
1403             'specialized' => 63,
1404             'wtek' => 64,
1405             'physical_enterprises' => 65,
1406             'north_pole_engineering' => 66,
1407             'bkool' => 67,
1408             'cateye' => 68,
1409             'stages_cycling' => 69,
1410             'sigmasport' => 70,
1411             'tomtom' => 71,
1412             'peripedal' => 72,
1413             'wattbike' => 73,
1414             'moxy' => 76,
1415             'ciclosport' => 77,
1416             'powerbahn' => 78,
1417             'acorn_projects_aps' => 79,
1418             'lifebeam' => 80,
1419             'bontrager' => 81,
1420             'wellgo' => 82,
1421             'scosche' => 83,
1422             'magura' => 84,
1423             'woodway' => 85,
1424             'elite' => 86,
1425             'nielsen_kellerman' => 87,
1426             'dk_city' => 88,
1427             'tacx' => 89,
1428             'direction_technology' => 90,
1429             'magtonic' => 91,
1430             '1partcarbon' => 92,
1431             'inside_ride_technologies' => 93,
1432             'sound_of_motion' => 94,
1433             'stryd' => 95,
1434             'icg' => 96,
1435             'mipulse' => 97,
1436             'bsx_athletics' => 98,
1437             'look' => 99,
1438             'campagnolo_srl' => 100,
1439             'body_bike_smart' => 101,
1440             'praxisworks' => 102,
1441             'limits_technology' => 103,
1442             'topaction_technology' => 104,
1443             'cosinuss' => 105,
1444             'fitcare' => 106,
1445             'magene' => 107,
1446             'giant_manufacturing_co' => 108,
1447             'tigrasport' => 109,
1448             'salutron' => 110,
1449             'technogym' => 111,
1450             'bryton_sensors' => 112,
1451             'latitude_limited' => 113,
1452             'soaring_technology' => 114,
1453             'igpsport' => 115,
1454             'thinkrider' => 116,
1455             'gopher_sport' => 117,
1456             'waterrower' => 118,
1457             'orangetheory' => 119,
1458             'inpeak' => 120,
1459             'kinetic' => 121,
1460             'johnson_health_tech' => 122,
1461             'polar_electro' => 123,
1462             'seesense' => 124,
1463             'nci_technology' => 125,
1464             'iqsquare' => 126,
1465             'leomo' => 127,
1466             'ifit_com' => 128,
1467             'coros_byte' => 129,
1468             'versa_design' => 130,
1469             'chileaf' => 131,
1470             'cycplus' => 132,
1471             'gravaa_byte' => 133,
1472             'sigeyi' => 134,
1473             'coospo' => 135,
1474             'geoid' => 136,
1475             'bosch' => 137,
1476             'kyto' => 138,
1477             'kinetic_sports' => 139,
1478             'decathlon_byte' => 140,
1479             'tq_systems' => 141,
1480             'tag_heuer' => 142,
1481             'keiser_fitness' => 143,
1482             'zwift_byte' => 144,
1483             'porsche_ep' => 145,
1484             'blackbird' => 146,
1485             'meilan_byte' => 147,
1486             'ezon' => 148,
1487             'laisi' => 149,
1488             'myzone' => 150,
1489             'development' => 255,
1490             'healthandlife' => 257,
1491             'lezyne' => 258,
1492             'scribe_labs' => 259,
1493             'zwift' => 260,
1494             'watteam' => 261,
1495             'recon' => 262,
1496             'favero_electronics' => 263,
1497             'dynovelo' => 264,
1498             'strava' => 265,
1499             'precor' => 266,
1500             'bryton' => 267,
1501             'sram' => 268,
1502             'navman' => 269,
1503             'cobi' => 270,
1504             'spivi' => 271,
1505             'mio_magellan' => 272,
1506             'evesports' => 273,
1507             'sensitivus_gauge' => 274,
1508             'podoon' => 275,
1509             'life_time_fitness' => 276,
1510             'falco_e_motors' => 277,
1511             'minoura' => 278,
1512             'cycliq' => 279,
1513             'luxottica' => 280,
1514             'trainer_road' => 281,
1515             'the_sufferfest' => 282,
1516             'fullspeedahead' => 283,
1517             'virtualtraining' => 284,
1518             'feedbacksports' => 285,
1519             'omata' => 286,
1520             'vdo' => 287,
1521             'magneticdays' => 288,
1522             'hammerhead' => 289,
1523             'kinetic_by_kurt' => 290,
1524             'shapelog' => 291,
1525             'dabuziduo' => 292,
1526             'jetblack' => 293,
1527             'coros' => 294,
1528             'virtugo' => 295,
1529             'velosense' => 296,
1530             'cycligentinc' => 297,
1531             'trailforks' => 298,
1532             'mahle_ebikemotion' => 299,
1533             'nurvv' => 300,
1534             'microprogram' => 301,
1535             'zone5cloud' => 302,
1536             'greenteg' => 303,
1537             'yamaha_motors' => 304,
1538             'whoop' => 305,
1539             'gravaa' => 306,
1540             'onelap' => 307,
1541             'monark_exercise' => 308,
1542             'form' => 309,
1543             'decathlon' => 310,
1544             'syncros' => 311,
1545             'heatup' => 312,
1546             'cannondale' => 313,
1547             'true_fitness' => 314,
1548             'RGT_cycling' => 315,
1549             'vasa' => 316,
1550             'race_republic' => 317,
1551             'fazua' => 318,
1552             'oreka_training' => 319,
1553             'lsec' => 320, # Lishun Electric & Communication
1554             'lululemon_studio' => 321,
1555             'shanyue' => 322,
1556             'spinning_mda' => 323,
1557             'hilldating' => 324,
1558             'aero_sensor' => 325,
1559             'nike' => 326,
1560             'magicshine' => 327,
1561             'ictrainer' => 328,
1562             'absolute_cycling' => 329,
1563             'actigraphcorp' => 5759,
1564             },
1565              
1566             'garmin_product' => +{
1567             '_base_type' => FIT_UINT16,
1568             'hrm1' => 1,
1569             'axh01' => 2, # AXH01 HRM chipset
1570             'axb01' => 3,
1571             'axb02' => 4,
1572             'hrm2ss' => 5,
1573             'dsi_alf02' => 6,
1574             'hrm3ss' => 7,
1575             'hrm_run_single_byte_product_id' => 8, # hrm_run model for HRM ANT+ messaging
1576             'bsm' => 9, # BSM model for ANT+ messaging
1577             'bcm' => 10, # BCM model for ANT+ messaging
1578             'axs01' => 11, # AXS01 HRM Bike Chipset model for ANT+ messaging
1579             'hrm_tri_single_byte_product_id' => 12, # hrm_tri model for HRM ANT+ messaging
1580             'hrm4_run_single_byte_product_id' => 13, # hrm4 run model for HRM ANT+ messaging
1581             'fr225_single_byte_product_id' => 14, # fr225 model for HRM ANT+ messaging
1582             'gen3_bsm_single_byte_product_id' => 15, # gen3_bsm model for Bike Speed ANT+ messaging
1583             'gen3_bcm_single_byte_product_id' => 16, # gen3_bcm model for Bike Cadence ANT+ messaging
1584             'hrm_fit_single_byte_product_id' => 22,
1585             'OHR' => 255, # Garmin Wearable Optical Heart Rate Sensor for ANT+ HR Profile Broadcasting
1586             'fr301_china' => 473,
1587             'fr301_japan' => 474,
1588             'fr301_korea' => 475,
1589             'fr301_taiwan' => 494,
1590             'fr405' => 717, # Forerunner 405
1591             'fr50' => 782, # Forerunner 50
1592             'fr405_japan' => 987,
1593             'fr60' => 988, # Forerunner 60
1594             'dsi_alf01' => 1011,
1595             'fr310xt' => 1018, # Forerunner 310
1596             'edge500' => 1036,
1597             'fr110' => 1124, # Forerunner 110
1598             'edge800' => 1169,
1599             'edge500_taiwan' => 1199,
1600             'edge500_japan' => 1213,
1601             'chirp' => 1253,
1602             'fr110_japan' => 1274,
1603             'edge200' => 1325,
1604             'fr910xt' => 1328,
1605             'edge800_taiwan' => 1333,
1606             'edge800_japan' => 1334,
1607             'alf04' => 1341,
1608             'fr610' => 1345,
1609             'fr210_japan' => 1360,
1610             'vector_ss' => 1380,
1611             'vector_cp' => 1381,
1612             'edge800_china' => 1386,
1613             'edge500_china' => 1387,
1614             'approach_g10' => 1405,
1615             'fr610_japan' => 1410,
1616             'edge500_korea' => 1422,
1617             'fr70' => 1436,
1618             'fr310xt_4t' => 1446,
1619             'amx' => 1461,
1620             'fr10' => 1482,
1621             'edge800_korea' => 1497,
1622             'swim' => 1499,
1623             'fr910xt_china' => 1537,
1624             'fenix' => 1551,
1625             'edge200_taiwan' => 1555,
1626             'edge510' => 1561,
1627             'edge810' => 1567,
1628             'tempe' => 1570,
1629             'fr910xt_japan' => 1600,
1630             'fr620' => 1623,
1631             'fr220' => 1632,
1632             'fr910xt_korea' => 1664,
1633             'fr10_japan' => 1688,
1634             'edge810_japan' => 1721,
1635             'virb_elite' => 1735,
1636             'edge_touring' => 1736, # Also Edge Touring Plus
1637             'edge510_japan' => 1742,
1638             'hrm_tri' => 1743, # Also HRM-Swim
1639             'hrm_run' => 1752,
1640             'fr920xt' => 1765,
1641             'edge510_asia' => 1821,
1642             'edge810_china' => 1822,
1643             'edge810_taiwan' => 1823,
1644             'edge1000' => 1836,
1645             'vivo_fit' => 1837,
1646             'virb_remote' => 1853,
1647             'vivo_ki' => 1885,
1648             'fr15' => 1903,
1649             'vivo_active' => 1907,
1650             'edge510_korea' => 1918,
1651             'fr620_japan' => 1928,
1652             'fr620_china' => 1929,
1653             'fr220_japan' => 1930,
1654             'fr220_china' => 1931,
1655             'approach_s6' => 1936,
1656             'vivo_smart' => 1956,
1657             'fenix2' => 1967,
1658             'epix' => 1988,
1659             'fenix3' => 2050,
1660             'edge1000_taiwan' => 2052,
1661             'edge1000_japan' => 2053,
1662             'fr15_japan' => 2061,
1663             'edge520' => 2067,
1664             'edge1000_china' => 2070,
1665             'fr620_russia' => 2072,
1666             'fr220_russia' => 2073,
1667             'vector_s' => 2079,
1668             'edge1000_korea' => 2100,
1669             'fr920xt_taiwan' => 2130,
1670             'fr920xt_china' => 2131,
1671             'fr920xt_japan' => 2132,
1672             'virbx' => 2134,
1673             'vivo_smart_apac' => 2135,
1674             'etrex_touch' => 2140,
1675             'edge25' => 2147,
1676             'fr25' => 2148,
1677             'vivo_fit2' => 2150,
1678             'fr225' => 2153,
1679             'fr630' => 2156,
1680             'fr230' => 2157,
1681             'fr735xt' => 2158,
1682             'vivo_active_apac' => 2160,
1683             'vector_2' => 2161,
1684             'vector_2s' => 2162,
1685             'virbxe' => 2172,
1686             'fr620_taiwan' => 2173,
1687             'fr220_taiwan' => 2174,
1688             'truswing' => 2175,
1689             'd2airvenu' => 2187,
1690             'fenix3_china' => 2188,
1691             'fenix3_twn' => 2189,
1692             'varia_headlight' => 2192,
1693             'varia_taillight_old' => 2193,
1694             'edge_explore_1000' => 2204,
1695             'fr225_asia' => 2219,
1696             'varia_radar_taillight' => 2225,
1697             'varia_radar_display' => 2226,
1698             'edge20' => 2238,
1699             'edge520_asia' => 2260,
1700             'edge520_japan' => 2261,
1701             'd2_bravo' => 2262,
1702             'approach_s20' => 2266,
1703             'vivo_smart2' => 2271,
1704             'edge1000_thai' => 2274,
1705             'varia_remote' => 2276,
1706             'edge25_asia' => 2288,
1707             'edge25_jpn' => 2289,
1708             'edge20_asia' => 2290,
1709             'approach_x40' => 2292,
1710             'fenix3_japan' => 2293,
1711             'vivo_smart_emea' => 2294,
1712             'fr630_asia' => 2310,
1713             'fr630_jpn' => 2311,
1714             'fr230_jpn' => 2313,
1715             'hrm4_run' => 2327,
1716             'epix_japan' => 2332,
1717             'vivo_active_hr' => 2337,
1718             'vivo_smart_gps_hr' => 2347,
1719             'vivo_smart_hr' => 2348,
1720             'vivo_smart_hr_asia' => 2361,
1721             'vivo_smart_gps_hr_asia' => 2362,
1722             'vivo_move' => 2368,
1723             'varia_taillight' => 2379,
1724             'fr235_asia' => 2396,
1725             'fr235_japan' => 2397,
1726             'varia_vision' => 2398,
1727             'vivo_fit3' => 2406,
1728             'fenix3_korea' => 2407,
1729             'fenix3_sea' => 2408,
1730             'fenix3_hr' => 2413,
1731             'virb_ultra_30' => 2417,
1732             'index_smart_scale' => 2429,
1733             'fr235' => 2431,
1734             'fenix3_chronos' => 2432,
1735             'oregon7xx' => 2441,
1736             'rino7xx' => 2444,
1737             'epix_korea' => 2457,
1738             'fenix3_hr_chn' => 2473,
1739             'fenix3_hr_twn' => 2474,
1740             'fenix3_hr_jpn' => 2475,
1741             'fenix3_hr_sea' => 2476,
1742             'fenix3_hr_kor' => 2477,
1743             'nautix' => 2496,
1744             'vivo_active_hr_apac' => 2497,
1745             'fr35' => 2503,
1746             'oregon7xx_ww' => 2512,
1747             'edge_820' => 2530,
1748             'edge_explore_820' => 2531,
1749             'fr735xt_apac' => 2533,
1750             'fr735xt_japan' => 2534,
1751             'fenix5s' => 2544,
1752             'd2_bravo_titanium' => 2547,
1753             'varia_ut800' => 2567, # Varia UT 800 SW
1754             'running_dynamics_pod' => 2593,
1755             'edge_820_china' => 2599,
1756             'edge_820_japan' => 2600,
1757             'fenix5x' => 2604,
1758             'vivo_fit_jr' => 2606,
1759             'vivo_smart3' => 2622,
1760             'vivo_sport' => 2623,
1761             'edge_820_taiwan' => 2628,
1762             'edge_820_korea' => 2629,
1763             'edge_820_sea' => 2630,
1764             'fr35_hebrew' => 2650,
1765             'approach_s60' => 2656,
1766             'fr35_apac' => 2667,
1767             'fr35_japan' => 2668,
1768             'fenix3_chronos_asia' => 2675,
1769             'virb_360' => 2687,
1770             'fr935' => 2691,
1771             'fenix5' => 2697,
1772             'vivoactive3' => 2700,
1773             'fr235_china_nfc' => 2733,
1774             'foretrex_601_701' => 2769,
1775             'vivo_move_hr' => 2772,
1776             'edge_1030' => 2713,
1777             'fr35_sea' => 2727,
1778             'vector_3' => 2787,
1779             'fenix5_asia' => 2796,
1780             'fenix5s_asia' => 2797,
1781             'fenix5x_asia' => 2798,
1782             'approach_z80' => 2806,
1783             'fr35_korea' => 2814,
1784             'd2charlie' => 2819,
1785             'vivo_smart3_apac' => 2831,
1786             'vivo_sport_apac' => 2832,
1787             'fr935_asia' => 2833,
1788             'descent' => 2859,
1789             'vivo_fit4' => 2878,
1790             'fr645' => 2886,
1791             'fr645m' => 2888,
1792             'fr30' => 2891,
1793             'fenix5s_plus' => 2900,
1794             'Edge_130' => 2909,
1795             'edge_1030_asia' => 2924,
1796             'vivosmart_4' => 2927,
1797             'vivo_move_hr_asia' => 2945,
1798             'approach_x10' => 2962,
1799             'fr30_asia' => 2977,
1800             'vivoactive3m_w' => 2988,
1801             'fr645_asia' => 3003,
1802             'fr645m_asia' => 3004,
1803             'edge_explore' => 3011,
1804             'gpsmap66' => 3028,
1805             'approach_s10' => 3049,
1806             'vivoactive3m_l' => 3066,
1807             'approach_g80' => 3085,
1808             'edge_130_asia' => 3092,
1809             'edge_1030_bontrager' => 3095,
1810             'fenix5_plus' => 3110,
1811             'fenix5x_plus' => 3111,
1812             'edge_520_plus' => 3112,
1813             'fr945' => 3113,
1814             'edge_530' => 3121,
1815             'edge_830' => 3122,
1816             'instinct_esports' => 3126,
1817             'fenix5s_plus_apac' => 3134,
1818             'fenix5x_plus_apac' => 3135,
1819             'edge_520_plus_apac' => 3142,
1820             'descent_t1' => 3143,
1821             'fr235l_asia' => 3144,
1822             'fr245_asia' => 3145,
1823             'vivo_active3m_apac' => 3163,
1824             'gen3_bsm' => 3192, # gen3 bike speed sensor
1825             'gen3_bcm' => 3193, # gen3 bike cadence sensor
1826             'vivo_smart4_asia' => 3218,
1827             'vivoactive4_small' => 3224,
1828             'vivoactive4_large' => 3225,
1829             'venu' => 3226,
1830             'marq_driver' => 3246,
1831             'marq_aviator' => 3247,
1832             'marq_captain' => 3248,
1833             'marq_commander' => 3249,
1834             'marq_expedition' => 3250,
1835             'marq_athlete' => 3251,
1836             'descent_mk2' => 3258,
1837             'gpsmap66i' => 3284,
1838             'fenix6S_sport' => 3287,
1839             'fenix6S' => 3288,
1840             'fenix6_sport' => 3289,
1841             'fenix6' => 3290,
1842             'fenix6x' => 3291,
1843             'hrm_dual' => 3299, # HRM-Dual
1844             'hrm_pro' => 3300, # HRM-Pro
1845             'vivo_move3_premium' => 3308,
1846             'approach_s40' => 3314,
1847             'fr245m_asia' => 3321,
1848             'edge_530_apac' => 3349,
1849             'edge_830_apac' => 3350,
1850             'vivo_move3' => 3378,
1851             'vivo_active4_small_asia' => 3387,
1852             'vivo_active4_large_asia' => 3388,
1853             'vivo_active4_oled_asia' => 3389,
1854             'swim2' => 3405,
1855             'marq_driver_asia' => 3420,
1856             'marq_aviator_asia' => 3421,
1857             'vivo_move3_asia' => 3422,
1858             'fr945_asia' => 3441,
1859             'vivo_active3t_chn' => 3446,
1860             'marq_captain_asia' => 3448,
1861             'marq_commander_asia' => 3449,
1862             'marq_expedition_asia' => 3450,
1863             'marq_athlete_asia' => 3451,
1864             'instinct_solar' => 3466,
1865             'fr45_asia' => 3469,
1866             'vivoactive3_daimler' => 3473,
1867             'legacy_rey' => 3498,
1868             'legacy_darth_vader' => 3499,
1869             'legacy_captain_marvel' => 3500,
1870             'legacy_first_avenger' => 3501,
1871             'fenix6s_sport_asia' => 3512,
1872             'fenix6s_asia' => 3513,
1873             'fenix6_sport_asia' => 3514,
1874             'fenix6_asia' => 3515,
1875             'fenix6x_asia' => 3516,
1876             'legacy_captain_marvel_asia' => 3535,
1877             'legacy_first_avenger_asia' => 3536,
1878             'legacy_rey_asia' => 3537,
1879             'legacy_darth_vader_asia' => 3538,
1880             'descent_mk2s' => 3542,
1881             'edge_130_plus' => 3558,
1882             'edge_1030_plus' => 3570,
1883             'rally_200' => 3578, # Rally 100/200 Power Meter Series
1884             'fr745' => 3589,
1885             'venusq' => 3600,
1886             'lily' => 3615,
1887             'marq_adventurer' => 3624,
1888             'enduro' => 3638,
1889             'swim2_apac' => 3639,
1890             'marq_adventurer_asia' => 3648,
1891             'fr945_lte' => 3652,
1892             'descent_mk2_asia' => 3702, # Mk2 and Mk2i
1893             'venu2' => 3703,
1894             'venu2s' => 3704,
1895             'venu_daimler_asia' => 3737,
1896             'marq_golfer' => 3739,
1897             'venu_daimler' => 3740,
1898             'fr745_asia' => 3794,
1899             'varia_rct715' => 3808,
1900             'lily_asia' => 3809,
1901             'edge_1030_plus_asia' => 3812,
1902             'edge_130_plus_asia' => 3813,
1903             'approach_s12' => 3823,
1904             'enduro_asia' => 3872,
1905             'venusq_asia' => 3837,
1906             'edge_1040' => 3843,
1907             'marq_golfer_asia' => 3850,
1908             'venu2_plus' => 3851,
1909             'gnss' => 3865,
1910             'fr55' => 3869,
1911             'instinct_2' => 3888,
1912             'fenix7s' => 3905,
1913             'fenix7' => 3906,
1914             'fenix7x' => 3907,
1915             'fenix7s_apac' => 3908,
1916             'fenix7_apac' => 3909,
1917             'fenix7x_apac' => 3910,
1918             'approach_g12' => 3927,
1919             'descent_mk2s_asia' => 3930,
1920             'approach_s42' => 3934,
1921             'epix_gen2' => 3943,
1922             'epix_gen2_apac' => 3944,
1923             'venu2s_asia' => 3949,
1924             'venu2_asia' => 3950,
1925             'fr945_lte_asia' => 3978,
1926             'vivo_move_sport' => 3982,
1927             'vivomove_trend' => 3983,
1928             'approach_S12_asia' => 3986,
1929             'fr255_music' => 3990,
1930             'fr255_small_music' => 3991,
1931             'fr255' => 3992,
1932             'fr255_small' => 3993,
1933             'approach_g12_asia' => 4001,
1934             'approach_s42_asia' => 4002,
1935             'descent_g1' => 4005,
1936             'venu2_plus_asia' => 4017,
1937             'fr955' => 4024,
1938             'fr55_asia' => 4033,
1939             'edge_540' => 4061,
1940             'edge_840' => 4062,
1941             'vivosmart_5' => 4063,
1942             'instinct_2_asia' => 4071,
1943             'marq_gen2' => 4105, # Adventurer, Athlete, Captain, Golfer
1944             'venusq2' => 4115,
1945             'venusq2music' => 4116,
1946             'marq_gen2_aviator' => 4124,
1947             'd2_air_x10' => 4125,
1948             'hrm_pro_plus' => 4130,
1949             'descent_g1_asia' => 4132,
1950             'tactix7' => 4135,
1951             'instinct_crossover' => 4155,
1952             'edge_explore2' => 4169,
1953             'descent_mk3' => 4222,
1954             'descent_mk3i' => 4223,
1955             'approach_s70' => 4233,
1956             'fr265_large' => 4257,
1957             'fr265_small' => 4258,
1958             'venu3' => 4260,
1959             'venu3s' => 4261,
1960             'tacx_neo_smart' => 4265, # Neo Smart, Tacx
1961             'tacx_neo2_smart' => 4266, # Neo 2 Smart, Tacx
1962             'tacx_neo2_t_smart' => 4267, # Neo 2T Smart, Tacx
1963             'tacx_neo_smart_bike' => 4268, # Neo Smart Bike, Tacx
1964             'tacx_satori_smart' => 4269, # Satori Smart, Tacx
1965             'tacx_flow_smart' => 4270, # Flow Smart, Tacx
1966             'tacx_vortex_smart' => 4271, # Vortex Smart, Tacx
1967             'tacx_bushido_smart' => 4272, # Bushido Smart, Tacx
1968             'tacx_genius_smart' => 4273, # Genius Smart, Tacx
1969             'tacx_flux_flux_s_smart' => 4274, # Flux/Flux S Smart, Tacx
1970             'tacx_flux2_smart' => 4275, # Flux 2 Smart, Tacx
1971             'tacx_magnum' => 4276, # Magnum, Tacx
1972             'edge_1040_asia' => 4305,
1973             'epix_gen2_pro_42' => 4312,
1974             'epix_gen2_pro_47' => 4313,
1975             'epix_gen2_pro_51' => 4314,
1976             'fr965' => 4315,
1977             'enduro2' => 4341,
1978             'fenix7s_pro_solar' => 4374,
1979             'fenix7_pro_solar' => 4375,
1980             'fenix7x_pro_solar' => 4376,
1981             'lily2' => 4380,
1982             'instinct_2x' => 4394,
1983             'vivoactive5' => 4426,
1984             'fr165' => 4432,
1985             'fr165_music' => 4433,
1986             'descent_t2' => 4442,
1987             'hrm_fit' => 4446,
1988             'marq_gen2_commander' => 4472,
1989             'd2_mach1_pro' => 4556,
1990             'sdm4' => 10007, # SDM4 footpod
1991             'edge_remote' => 10014,
1992             'tacx_training_app_win' => 20533,
1993             'tacx_training_app_mac' => 20534,
1994             'tacx_training_app_mac_catalyst' => 20565,
1995             'training_center' => 20119,
1996             'tacx_training_app_android' => 30045,
1997             'tacx_training_app_ios' => 30046,
1998             'tacx_training_app_legacy' => 30047,
1999             'connectiq_simulator' => 65531,
2000             'android_antplus_plugin' => 65532,
2001             'connect' => 65534, # Garmin Connect website
2002             },
2003              
2004             'device_type' => +{
2005             '_moved_to' => 'antplus_device_type',
2006             },
2007              
2008             'antplus_device_type' => +{
2009             '_base_type' => FIT_UINT8,
2010             'antfs' => 1,
2011             'bike_power' => 11,
2012             'environment_sensor_legacy' => 12,
2013             'multi_sport_speed_distance' => 15,
2014             'control' => 16,
2015             'fitness_equipment' => 17,
2016             'blood_pressure' => 18,
2017             'geocache_node' => 19,
2018             'light_electric_vehicle' => 20,
2019             'env_sensor' => 25,
2020             'racquet' => 26,
2021             'control_hub' => 27,
2022             'muscle_oxygen' => 31,
2023             'shifting' => 34,
2024             'bike_light_main' => 35,
2025             'bike_light_shared' => 36,
2026             'exd' => 38,
2027             'bike_radar' => 40,
2028             'bike_aero' => 46,
2029             'weight_scale' => 119,
2030             'heart_rate' => 120,
2031             'bike_speed_cadence' => 121,
2032             'bike_cadence' => 122,
2033             'bike_speed' => 123,
2034             'stride_speed_distance' => 124,
2035             },
2036              
2037             'ant_network' => +{
2038             '_base_type' => FIT_ENUM,
2039             'public' => 0,
2040             'antplus' => 1,
2041             'antfs' => 2,
2042             'private' => 3,
2043             },
2044              
2045             'workout_capabilities' => +{
2046             '_base_type' => FIT_UINT32Z,
2047             '_mask' => 1,
2048             'interval' => 0x00000001,
2049             'custom' => 0x00000002,
2050             'fitness_equipment' => 0x00000004,
2051             'firstbeat' => 0x00000008,
2052             'new_leaf' => 0x00000010,
2053             'tcx' => 0x00000020,
2054             'speed' => 0x00000080,
2055             'heart_rate' => 0x00000100,
2056             'distance' => 0x00000200,
2057             'cadence' => 0x00000400,
2058             'power' => 0x00000800,
2059             'grade' => 0x00001000,
2060             'resistance' => 0x00002000,
2061             'protected' => 0x00004000,
2062             },
2063              
2064             'battery_status' => +{
2065             '_base_type' => FIT_UINT8,
2066             'new' => 1,
2067             'good' => 2,
2068             'ok' => 3,
2069             'low' => 4,
2070             'critical' => 5,
2071             'charging' => 6,
2072             'unknown' => 7,
2073             },
2074              
2075             'hr_type' => +{
2076             '_base_type' => FIT_ENUM,
2077             'normal' => 0,
2078             'irregular' => 1,
2079             },
2080              
2081             'course_capabilities' => +{
2082             '_base_type' => FIT_UINT32Z,
2083             '_mask' => 1,
2084             'processed' => 0x00000001,
2085             'valid' => 0x00000002,
2086             'time' => 0x00000004,
2087             'distance' => 0x00000008,
2088             'position' => 0x00000010,
2089             'heart_rate' => 0x00000020,
2090             'power' => 0x00000040,
2091             'cadence' => 0x00000080,
2092             'training' => 0x00000100,
2093             'navigation' => 0x00000200,
2094             'bikeway' => 0x00000400,
2095             'aviation'=> 0x00001000, # Denote course files to be used as flight plans
2096             },
2097              
2098             'weight' => +{
2099             '_base_type' => FIT_UINT16,
2100             'calculating' => 0xFFFE,
2101             },
2102              
2103             'workout_hr' => +{
2104             '_base_type' => FIT_UINT32,
2105             'bpm_offset' => 100,
2106             },
2107              
2108             'workout_power' => +{
2109             '_base_type' => FIT_UINT32,
2110             'watts_offset' => 1000,
2111             },
2112              
2113             'bp_status' => +{
2114             '_base_type' => FIT_ENUM,
2115             'no_error' => 0,
2116             'error_incomplete_data' => 1,
2117             'error_no_measurement' => 2,
2118             'error_data_out_of_range' => 3,
2119             'error_irregular_heart_rate' => 4,
2120             },
2121              
2122             'user_local_id' => +{
2123             '_base_type' => FIT_UINT16,
2124             'local_min' => 0x0001,
2125             'local_max' => 0x000F,
2126             'stationary_min' => 0x0010,
2127             'stationary_max' => 0x00FF,
2128             'portable_min' => 0x0100,
2129             'portable_max' => 0xFFFE,
2130             },
2131              
2132             'swim_stroke' => +{
2133             '_base_type' => FIT_ENUM,
2134             'freestyle' => 0,
2135             'backstroke' => 1,
2136             'breaststroke' => 2,
2137             'butterfly' => 3,
2138             'drill' => 4,
2139             'mixed' => 5,
2140             'im' => 6,
2141             },
2142              
2143             'activity_type' => +{
2144             '_base_type' => FIT_ENUM,
2145             'generic' => 0,
2146             'running' => 1,
2147             'cycling' => 2,
2148             'transition' => 3,
2149             'fitness_equipment' => 4,
2150             'swimming' => 5,
2151             'walking' => 6,
2152             'sedentary' => 8,
2153             'all' => 254,
2154             },
2155              
2156             'activity_subtype' => +{
2157             '_base_type' => FIT_ENUM,
2158             'generic' => 0,
2159             'treadmill' => 1, # run
2160             'street' => 2, # run
2161             'trail' => 3, # run
2162             'track' => 4, # run
2163             'spin' => 5, # cycling
2164             'indoor_cycling' => 6, # cycling
2165             'road' => 7, # cycling
2166             'mountain' => 8, # cycling
2167             'downhill' => 9, # cycling
2168             'recumbent' => 10, # cycling
2169             'cyclocross' => 11, # cycling
2170             'hand_cycling' => 12, # cycling
2171             'track_cycling' => 13, # cycling
2172             'indoor_rowing' => 14, # fitness equipment
2173             'elliptical' => 15, # fitness equipment
2174             'stair_climbing' => 16, # fitness equipment
2175             'lap_swimming' => 17, # swimming
2176             'open_water' => 18, # swimming
2177             'all' => 254,
2178             },
2179              
2180             'activity_level' => +{
2181             '_base_type' => FIT_ENUM,
2182             'low' => 0,
2183             'medium' => 1,
2184             'high' => 2,
2185             },
2186              
2187             'side' => +{
2188             '_base_type' => FIT_ENUM,
2189             'right' => 0,
2190             'left' => 1,
2191             },
2192              
2193             'left_right_balance' => +{
2194             '_base_type' => FIT_UINT8,
2195             'mask' => 0x7F,
2196             'right' => 0x80,
2197             },
2198              
2199             'left_right_balance_100' => +{
2200             '_base_type' => FIT_UINT16,
2201             'mask' => 0x3FFF,
2202             'right' => 0x8000,
2203             },
2204              
2205             'length_type' => +{
2206             '_base_type' => FIT_ENUM,
2207             'idle' => 0,
2208             'active' => 1,
2209             },
2210              
2211             'day_of_week' => +{
2212             '_base_type' => FIT_ENUM,
2213             'sunday' => 0,
2214             'monday' => 1,
2215             'tuesday' => 2,
2216             'wednesday' => 3,
2217             'thursday' => 4,
2218             'friday' => 5,
2219             'saturday' => 6,
2220             },
2221              
2222             'connectivity_capabilities' => +{
2223             '_base_type' => FIT_UINT32Z,
2224             'bluetooth' => 0x00000001,
2225             'bluetooth_le' => 0x00000002,
2226             'ant' => 0x00000004,
2227             'activity_upload' => 0x00000008,
2228             'course_download' => 0x00000010,
2229             'workout_download' => 0x00000020,
2230             'live_track' => 0x00000040,
2231             'weather_conditions' => 0x00000080,
2232             'weather_alerts' => 0x00000100,
2233             'gps_ephemeris_download' => 0x00000200,
2234             'explicit_archive' => 0x00000400,
2235             'setup_incomplete' => 0x00000800,
2236             'continue_sync_after_software_update' => 0x00001000,
2237             'connect_iq_app_download' => 0x00002000,
2238             'golf_course_download' => 0x00004000,
2239             'device_initiates_sync' => 0x00008000,
2240             'connect_iq_watch_app_download' => 0x00010000,
2241             'connect_iq_widget_download' => 0x00020000,
2242             'connect_iq_watch_face_download' => 0x00040000,
2243             'connect_iq_data_field_download' => 0x00080000,
2244             'connect_iq_app_managment' => 0x00100000,
2245             'swing_sensor' => 0x00200000,
2246             'swing_sensor_remote' => 0x00400000,
2247             'incident_detection' => 0x00800000,
2248             'audio_prompts' => 0x01000000,
2249             'wifi_verification' => 0x02000000,
2250             'true_up' => 0x04000000,
2251             'find_my_watch' => 0x08000000,
2252             'remote_manual_sync' => 0x10000000,
2253             'live_track_auto_start' => 0x20000000,
2254             'live_track_messaging' => 0x40000000,
2255             'instant_input' => 0x80000000, # Device supports instant input feature
2256             },
2257              
2258             'weather_report' => +{
2259             '_base_type' => FIT_ENUM,
2260             'current' => 0,
2261             # 'forecast' => 1, # deprecated, use hourly_forecast instead
2262             'hourly_forecast' => 1,
2263             'daily_forecast' => 2,
2264             },
2265              
2266             'weather_status' => +{
2267             '_base_type' => FIT_ENUM,
2268             'clear' => 0,
2269             'partly_cloudy' => 1,
2270             'mostly_cloudy' => 2,
2271             'rain' => 3,
2272             'snow' => 4,
2273             'windy' => 5,
2274             'thunderstorms' => 6,
2275             'wintry_mix' => 7,
2276             'fog' => 8,
2277             'hazy' => 11,
2278             'hail' => 12,
2279             'scattered_showers' => 13,
2280             'scattered_thunderstorms' => 14,
2281             'unknown_precipitation' => 15,
2282             'light_rain' => 16,
2283             'heavy_rain' => 17,
2284             'light_snow' => 18,
2285             'heavy_snow' => 19,
2286             'light_rain_snow' => 20,
2287             'heavy_rain_snow' => 21,
2288             'cloudy' => 22,
2289             },
2290              
2291             'weather_severity' => +{
2292             '_base_type' => FIT_ENUM,
2293             'unknown' => 0,
2294             'warning' => 1,
2295             'watch' => 2,
2296             'advisory' => 3,
2297             'statement' => 4,
2298             },
2299              
2300             'weather_severe_type' => +{
2301             '_base_type' => FIT_ENUM,
2302             'unspecified' => 0,
2303             'tornado' => 1,
2304             'tsunami' => 2,
2305             'hurricane' => 3,
2306             'extreme_wind' => 4,
2307             'typhoon' => 5,
2308             'inland_hurricane' => 6,
2309             'hurricane_force_wind' => 7,
2310             'waterspout' => 8,
2311             'severe_thunderstorm' => 9,
2312             'wreckhouse_winds' => 10,
2313             'les_suetes_wind' => 11,
2314             'avalanche' => 12,
2315             'flash_flood' => 13,
2316             'tropical_storm' => 14,
2317             'inland_tropical_storm' => 15,
2318             'blizzard' => 16,
2319             'ice_storm' => 17,
2320             'freezing_rain' => 18,
2321             'debris_flow' => 19,
2322             'flash_freeze' => 20,
2323             'dust_storm' => 21,
2324             'high_wind' => 22,
2325             'winter_storm' => 23,
2326             'heavy_freezing_spray' => 24,
2327             'extreme_cold' => 25,
2328             'wind_chill' => 26,
2329             'cold_wave' => 27,
2330             'heavy_snow_alert' => 28,
2331             'lake_effect_blowing_snow' => 29,
2332             'snow_squall' => 30,
2333             'lake_effect_snow' => 31,
2334             'winter_weather' => 32,
2335             'sleet' => 33,
2336             'snowfall' => 34,
2337             'snow_and_blowing_snow' => 35,
2338             'blowing_snow' => 36,
2339             'snow_alert' => 37,
2340             'arctic_outflow' => 38,
2341             'freezing_drizzle' => 39,
2342             'storm' => 40,
2343             'storm_surge' => 41,
2344             'rainfall' => 42,
2345             'areal_flood' => 43,
2346             'coastal_flood' => 44,
2347             'lakeshore_flood' => 45,
2348             'excessive_heat' => 46,
2349             'heat' => 47,
2350             'weather' => 48,
2351             'high_heat_and_humidity' => 49,
2352             'humidex_and_health' => 50,
2353             'humidex' => 51,
2354             'gale' => 52,
2355             'freezing_spray' => 53,
2356             'special_marine' => 54,
2357             'squall' => 55,
2358             'strong_wind' => 56,
2359             'lake_wind' => 57,
2360             'marine_weather' => 58,
2361             'wind' => 59,
2362             'small_craft_hazardous_seas' => 60,
2363             'hazardous_seas' => 61,
2364             'small_craft' => 62,
2365             'small_craft_winds' => 63,
2366             'small_craft_rough_bar' => 64,
2367             'high_water_level' => 65,
2368             'ashfall' => 66,
2369             'freezing_fog' => 67,
2370             'dense_fog' => 68,
2371             'dense_smoke' => 69,
2372             'blowing_dust' => 70,
2373             'hard_freeze' => 71,
2374             'freeze' => 72,
2375             'frost' => 73,
2376             'fire_weather' => 74,
2377             'flood' => 75,
2378             'rip_tide' => 76,
2379             'high_surf' => 77,
2380             'smog' => 78,
2381             'air_quality' => 79,
2382             'brisk_wind' => 80,
2383             'air_stagnation' => 81,
2384             'low_water' => 82,
2385             'hydrological' => 83,
2386             'special_weather' => 84,
2387             },
2388              
2389             'time_into_day' => +{ # since 00:00:00 UTC
2390             '_base_type' => FIT_UINT32,
2391             },
2392              
2393             'localtime_into_day' => +{ # same as above, but in local time zone
2394             '_base_type' => FIT_UINT32,
2395             },
2396              
2397             'stroke_type' => +{
2398             '_base_type' => FIT_ENUM,
2399             'no_event' => 0,
2400             'other' => 1,
2401             'serve' => 2,
2402             'forehand' => 3,
2403             'backhand' => 4,
2404             'smash' => 5,
2405             },
2406              
2407             'body_location' => +{
2408             '_base_type' => FIT_ENUM,
2409             'left_leg' => 0,
2410             'left_calf' => 1,
2411             'left_shin' => 2,
2412             'left_hamstring' => 3,
2413             'left_quad' => 4,
2414             'left_glute' => 5,
2415             'right_leg' => 6,
2416             'right_calf' => 7,
2417             'right_shin' => 8,
2418             'right_hamstring' => 9,
2419             'right_quad' => 10,
2420             'right_glute' => 11,
2421             'torso_back' => 12,
2422             'left_lower_back' => 13,
2423             'left_upper_back' => 14,
2424             'right_lower_back' => 15,
2425             'right_upper_back' => 16,
2426             'torso_front' => 17,
2427             'left_abdomen' => 18,
2428             'left_chest' => 19,
2429             'right_abdomen' => 20,
2430             'right_chest' => 21,
2431             'left_arm' => 22,
2432             'left_shoulder' => 23,
2433             'left_bicep' => 24,
2434             'left_tricep' => 25,
2435             'left_brachioradialis' => 26,
2436             'left_forearm_extensors' => 27,
2437             'right_arm' => 28,
2438             'right_shoulder' => 29,
2439             'right_bicep' => 30,
2440             'right_tricep' => 31,
2441             'right_brachioradialis' => 32,
2442             'right_forearm_extensors' => 33,
2443             'neck' => 34,
2444             'throat' => 35,
2445             'waist_mid_back' => 36,
2446             'waist_front' => 37,
2447             'waist_left' => 38,
2448             'waist_right' => 39,
2449             },
2450              
2451             'segment_lap_status' => +{
2452             '_base_type' => FIT_ENUM,
2453             'end' => 0,
2454             'fail' => 1,
2455             },
2456              
2457             'segment_leaderboard_type' => +{
2458             '_base_type' => FIT_ENUM,
2459             'overall' => 0,
2460             'personal_best' => 1,
2461             'connections' => 2,
2462             'group' => 3,
2463             'challenger' => 4,
2464             'kom' => 5,
2465             'qom' => 6,
2466             'pr' => 7,
2467             'goal' => 8,
2468             'carrot' => 9,
2469             'club_leader' => 10,
2470             'rival' => 11,
2471             'last' => 12,
2472             'recent_best' => 13,
2473             'course_record' => 14,
2474             },
2475              
2476             'segment_delete_status' => +{
2477             '_base_type' => FIT_ENUM,
2478             'do_not_delete' => 0,
2479             'delete_one' => 1,
2480             'delete_all' => 2,
2481             },
2482              
2483             'segment_selection_type' => +{
2484             '_base_type' => FIT_ENUM,
2485             'starred' => 0,
2486             'suggested' => 1,
2487             },
2488              
2489             'source_type' => +{
2490             '_base_type' => FIT_ENUM,
2491             'ant' => 0,
2492             'antplus' => 1,
2493             'bluetooth' => 2,
2494             'bluetooth_low_energy' => 3,
2495             'wifi' => 4,
2496             'local' => 5,
2497             },
2498              
2499             'local_device_type' => +{
2500             '_base_type' => FIT_UINT8,
2501             'gps' => 0, # Onboard gps receiver
2502             'glonass' => 1, # Onboard glonass receiver
2503             'gps_glonass' => 2, # Onboard gps glonass receiver
2504             'accelerometer' => 3, # Onboard sensor
2505             'barometer' => 4, # Onboard sensor
2506             'temperature' => 5, # Onboard sensor
2507             'whr' => 10, # Onboard wrist HR sensor
2508             'sensor_hub' => 12, # Onboard software package
2509             },
2510              
2511             'ble_device_type' => +{
2512             '_base_type' => FIT_ENUM,
2513             'connected_gps' => 0, # GPS that is provided over a proprietary bluetooth service
2514             'heart_rate' => 1,
2515             'bike_power' => 2,
2516             'bike_speed_cadence' => 3,
2517             'bike_speed' => 4,
2518             'bike_cadence' => 5,
2519             'footpod' => 6,
2520             'bike_trainer' => 7, # Indoor-Bike FTMS protocol
2521             },
2522              
2523             'ant_channel_id' => +{
2524             '_base_type' => FIT_UINT32Z,
2525             'ant_extended_device_number_upper_nibble' => 0xF0000000,
2526             'ant_transmission_type_lower_nibble' => 0x0F000000,
2527             'ant_device_type' => 0x00FF0000,
2528             'ant_device_number' => 0x0000FFFF,
2529             },
2530              
2531             'display_orientation' => +{
2532             '_base_type' => FIT_ENUM,
2533             'auto' => 0,
2534             'portrait' => 1,
2535             'landscape' => 2,
2536             'portrait_flipped' => 3,
2537             'landscape_flipped' => 4,
2538             },
2539              
2540             'workout_equipment' => +{
2541             '_base_type' => FIT_ENUM,
2542             'none' => 0,
2543             'swim_fins' => 1,
2544             'swim_kickboard' => 2,
2545             'swim_paddles' => 3,
2546             'swim_pull_buoy' => 4,
2547             'swim_snorkel' => 5,
2548             },
2549             'watchface_mode' => +{
2550             '_base_type' => FIT_ENUM,
2551             'digital' => 0,
2552             'analog' => 1,
2553             'connect_iq' => 2,
2554             'disabled' => 3,
2555             },
2556              
2557             'digital_watchface_layout' => +{
2558             '_base_type' => FIT_ENUM,
2559             'traditional' => 0,
2560             'modern' => 1,
2561             'bold' => 2,
2562             },
2563              
2564             'analog_watchface_layout' => +{
2565             '_base_type' => FIT_ENUM,
2566             'minimal' => 0,
2567             'traditional' => 1,
2568             'modern' => 2,
2569             },
2570              
2571             'rider_position_type' => +{
2572             '_base_type' => FIT_ENUM,
2573             'seated' => 0,
2574             'standing' => 1,
2575             'transition_to_seated' => 2,
2576             'transition_to_standing' => 3,
2577             },
2578              
2579             'power_phase_type' => +{
2580             '_base_type' => FIT_ENUM,
2581             'power_phase_start_angle' => 0,
2582             'power_phase_end_angle' => 1,
2583             'power_phase_arc_length' => 2,
2584             'power_phase_center' => 3,
2585             },
2586              
2587             'camera_event_type' => +{
2588             '_base_type' => FIT_ENUM,
2589             'video_start' => 0,
2590             'video_split' => 1,
2591             'video_end' => 2,
2592             'photo_taken' => 3,
2593             'video_second_stream_start' => 4,
2594             'video_second_stream_split' => 5,
2595             'video_second_stream_end' => 6,
2596             'video_split_start' => 7,
2597             'video_second_stream_split_start' => 8,
2598             'video_pause' => 11,
2599             'video_second_stream_pause' => 12,
2600             'video_resume' => 13,
2601             'video_second_stream_resume' => 14,
2602             },
2603              
2604             'sensor_type' => +{
2605             '_base_type' => FIT_ENUM,
2606             'accelerometer' => 0,
2607             'gyroscope' => 1,
2608             'compass' => 2, # Magnetometer
2609             'barometer' => 3,
2610             },
2611              
2612             'bike_light_network_config_type' => +{
2613             '_base_type' => FIT_ENUM,
2614             'auto' => 0,
2615             'individual' => 4,
2616             'high_visibility' => 5,
2617             'trail' => 6,
2618             },
2619              
2620             'comm_timeout_type' => +{
2621             '_base_type' => FIT_UINT16,
2622             'wildcard_pairing_timeout' => 0,
2623             'pairing_timeout' => 1,
2624             'connection_lost' => 2,
2625             'connection_timeout' => 3,
2626             },
2627              
2628             'camera_orientation_type' => +{
2629             '_base_type' => FIT_ENUM,
2630             'camera_orientation_0' => 0,
2631             'camera_orientation_90' => 1,
2632             'camera_orientation_180' => 2,
2633             'camera_orientation_270' => 3,
2634             },
2635              
2636             'attitude_stage' => +{
2637             '_base_type' => FIT_ENUM,
2638             'failed' => 0,
2639             'aligning' => 1,
2640             'degraded' => 2,
2641             'valid' => 3,
2642             },
2643              
2644             'attitude_validity' => +{
2645             '_base_type' => FIT_UINT16,
2646             'track_angle_heading_valid' => 0x0001,
2647             'pitch_valid' => 0x0002,
2648             'roll_valid' => 0x0004,
2649             'lateral_body_accel_valid' => 0x0008,
2650             'normal_body_accel_valid' => 0x0010,
2651             'turn_rate_valid' => 0x0020,
2652             'hw_fail' => 0x0040,
2653             'mag_invalid' => 0x0080,
2654             'no_gps' => 0x0100,
2655             'gps_invalid' => 0x0200,
2656             'solution_coasting' => 0x0400,
2657             'true_track_angle' => 0x0800,
2658             'magnetic_heading' => 0x1000,
2659             },
2660              
2661             'auto_sync_frequency' => +{
2662             '_base_type' => FIT_ENUM,
2663             'never' => 0,
2664             'occasionally' => 1,
2665             'frequent' => 2,
2666             'once_a_day' => 3,
2667             'remote' => 4,
2668             },
2669              
2670             'exd_layout' => +{
2671             '_base_type' => FIT_ENUM,
2672             'full_screen' => 0,
2673             'half_vertical' => 1,
2674             'half_horizontal' => 2,
2675             'half_vertical_right_split' => 3,
2676             'half_horizontal_bottom_split' => 4,
2677             'full_quarter_split' => 5,
2678             'half_vertical_left_split' => 6,
2679             'half_horizontal_top_split' => 7,
2680             'dynamic' => 8, # The EXD may display the configured concepts in any layout it sees fit.
2681             },
2682              
2683             'exd_display_type' => +{
2684             '_base_type' => FIT_ENUM,
2685             'numerical' => 0,
2686             'simple' => 1,
2687             'graph' => 2,
2688             'bar' => 3,
2689             'circle_graph' => 4,
2690             'virtual_partner' => 5,
2691             'balance' => 6,
2692             'string_list' => 7,
2693             'string' => 8,
2694             'simple_dynamic_icon' => 9,
2695             'gauge' => 10,
2696             },
2697              
2698             'exd_data_units' => +{
2699             '_base_type' => FIT_ENUM,
2700             'no_units' => 0,
2701             'laps' => 1,
2702             'miles_per_hour' => 2,
2703             'kilometers_per_hour' => 3,
2704             'feet_per_hour' => 4,
2705             'meters_per_hour' => 5,
2706             'degrees_celsius' => 6,
2707             'degrees_farenheit' => 7,
2708             'zone' => 8,
2709             'gear' => 9,
2710             'rpm' => 10,
2711             'bpm' => 11,
2712             'degrees' => 12,
2713             'millimeters' => 13,
2714             'meters' => 14,
2715             'kilometers' => 15,
2716             'feet' => 16,
2717             'yards' => 17,
2718             'kilofeet' => 18,
2719             'miles' => 19,
2720             'time' => 20,
2721             'enum_turn_type' => 21,
2722             'percent' => 22,
2723             'watts' => 23,
2724             'watts_per_kilogram' => 24,
2725             'enum_battery_status' => 25,
2726             'enum_bike_light_beam_angle_mode' => 26,
2727             'enum_bike_light_battery_status' => 27,
2728             'enum_bike_light_network_config_type' => 28,
2729             'lights' => 29,
2730             'seconds' => 30,
2731             'minutes' => 31,
2732             'hours' => 32,
2733             'calories' => 33,
2734             'kilojoules' => 34,
2735             'milliseconds' => 35,
2736             'second_per_mile' => 36,
2737             'second_per_kilometer' => 37,
2738             'centimeter' => 38,
2739             'enum_course_point' => 39,
2740             'bradians' => 40,
2741             'enum_sport' => 41,
2742             'inches_hg' => 42,
2743             'mm_hg' => 43,
2744             'mbars' => 44,
2745             'hecto_pascals' => 45,
2746             'feet_per_min' => 46,
2747             'meters_per_min' => 47,
2748             'meters_per_sec' => 48,
2749             'eight_cardinal' => 49,
2750             },
2751              
2752             'exd_qualifiers' => +{
2753             '_base_type' => FIT_ENUM,
2754             'no_qualifier' => 0,
2755             'instantaneous' => 1,
2756             'average' => 2,
2757             'lap' => 3,
2758             'maximum' => 4,
2759             'maximum_average' => 5,
2760             'maximum_lap' => 6,
2761             'last_lap' => 7,
2762             'average_lap' => 8,
2763             'to_destination' => 9,
2764             'to_go' => 10,
2765             'to_next' => 11,
2766             'next_course_point' => 12,
2767             'total' => 13,
2768             'three_second_average' => 14,
2769             'ten_second_average' => 15,
2770             'thirty_second_average' => 16,
2771             'percent_maximum' => 17,
2772             'percent_maximum_average' => 18,
2773             'lap_percent_maximum' => 19,
2774             'elapsed' => 20,
2775             'sunrise' => 21,
2776             'sunset' => 22,
2777             'compared_to_virtual_partner' => 23,
2778             'maximum_24h' => 24,
2779             'minimum_24h' => 25,
2780             'minimum' => 26,
2781             'first' => 27,
2782             'second' => 28,
2783             'third' => 29,
2784             'shifter' => 30,
2785             'last_sport' => 31,
2786             'moving' => 32,
2787             'stopped' => 33,
2788             'estimated_total' => 34,
2789             'zone_9' => 242,
2790             'zone_8' => 243,
2791             'zone_7' => 244,
2792             'zone_6' => 245,
2793             'zone_5' => 246,
2794             'zone_4' => 247,
2795             'zone_3' => 248,
2796             'zone_2' => 249,
2797             'zone_1' => 250,
2798             },
2799              
2800             'exd_descriptors' => +{
2801             '_base_type' => FIT_ENUM,
2802             'bike_light_battery_status' => 0,
2803             'beam_angle_status' => 1,
2804             'batery_level' => 2,
2805             'light_network_mode' => 3,
2806             'number_lights_connected' => 4,
2807             'cadence' => 5,
2808             'distance' => 6,
2809             'estimated_time_of_arrival' => 7,
2810             'heading' => 8,
2811             'time' => 9,
2812             'battery_level' => 10,
2813             'trainer_resistance' => 11,
2814             'trainer_target_power' => 12,
2815             'time_seated' => 13,
2816             'time_standing' => 14,
2817             'elevation' => 15,
2818             'grade' => 16,
2819             'ascent' => 17,
2820             'descent' => 18,
2821             'vertical_speed' => 19,
2822             'di2_battery_level' => 20,
2823             'front_gear' => 21,
2824             'rear_gear' => 22,
2825             'gear_ratio' => 23,
2826             'heart_rate' => 24,
2827             'heart_rate_zone' => 25,
2828             'time_in_heart_rate_zone' => 26,
2829             'heart_rate_reserve' => 27,
2830             'calories' => 28,
2831             'gps_accuracy' => 29,
2832             'gps_signal_strength' => 30,
2833             'temperature' => 31,
2834             'time_of_day' => 32,
2835             'balance' => 33,
2836             'pedal_smoothness' => 34,
2837             'power' => 35,
2838             'functional_threshold_power' => 36,
2839             'intensity_factor' => 37,
2840             'work' => 38,
2841             'power_ratio' => 39,
2842             'normalized_power' => 40,
2843             'training_stress_score' => 41,
2844             'time_on_zone' => 42,
2845             'speed' => 43,
2846             'laps' => 44,
2847             'reps' => 45,
2848             'workout_step' => 46,
2849             'course_distance' => 47,
2850             'navigation_distance' => 48,
2851             'course_estimated_time_of_arrival' => 49,
2852             'navigation_estimated_time_of_arrival' => 50,
2853             'course_time' => 51,
2854             'navigation_time' => 52,
2855             'course_heading' => 53,
2856             'navigation_heading' => 54,
2857             'power_zone' => 55,
2858             'torque_effectiveness' => 56,
2859             'timer_time' => 57,
2860             'power_weight_ratio' => 58,
2861             'left_platform_center_offset' => 59,
2862             'right_platform_center_offset' => 60,
2863             'left_power_phase_start_angle' => 61,
2864             'right_power_phase_start_angle' => 62,
2865             'left_power_phase_finish_angle' => 63,
2866             'right_power_phase_finish_angle' => 64,
2867             'gears' => 65,
2868             'pace' => 66,
2869             'training_effect' => 67,
2870             'vertical_oscillation' => 68,
2871             'vertical_ratio' => 69,
2872             'ground_contact_time' => 70,
2873             'left_ground_contact_time_balance' => 71,
2874             'right_ground_contact_time_balance' => 72,
2875             'stride_length' => 73,
2876             'running_cadence' => 74,
2877             'performance_condition' => 75,
2878             'course_type' => 76,
2879             'time_in_power_zone' => 77,
2880             'navigation_turn' => 78,
2881             'course_location' => 79,
2882             'navigation_location' => 80,
2883             'compass' => 81,
2884             'gear_combo' => 82,
2885             'muscle_oxygen' => 83,
2886             'icon' => 84,
2887             'compass_heading' => 85,
2888             'gps_heading' => 86,
2889             'gps_elevation' => 87,
2890             'anaerobic_training_effect' => 88,
2891             'course' => 89,
2892             'off_course' => 90,
2893             'glide_ratio' => 91,
2894             'vertical_distance' => 92,
2895             'vmg' => 93,
2896             'ambient_pressure' => 94,
2897             'pressure' => 95,
2898             'vam' => 96,
2899             },
2900              
2901             'auto_activity_detect' => +{
2902             '_base_type' => FIT_UINT32,
2903             'none' => 0x00000000,
2904             'running' => 0x00000001,
2905             'cycling' => 0x00000002,
2906             'swimming' => 0x00000004,
2907             'walking' => 0x00000008,
2908             'elliptical' => 0x00000020,
2909             'sedentary' => 0x00000400,
2910             },
2911              
2912             'supported_exd_screen_layouts' => +{
2913             '_base_type' => FIT_UINT32Z,
2914             'full_screen' => 0x00000001,
2915             'half_vertical' => 0x00000002,
2916             'half_horizontal' => 0x00000004,
2917             'half_vertical_right_split' => 0x00000008,
2918             'half_horizontal_bottom_split' => 0x00000010,
2919             'full_quarter_split' => 0x00000020,
2920             'half_vertical_left_split' => 0x00000040,
2921             'half_horizontal_top_split' => 0x00000080,
2922             },
2923              
2924             'fit_base_type' => +{
2925             '_base_type' => FIT_UINT8,
2926             'enum' => 0,
2927             'sint8' => 1,
2928             'uint8' => 2,
2929             'sint16' => 131,
2930             'uint16' => 132,
2931             'sint32' => 133,
2932             'uint32' => 134,
2933             'string' => 7,
2934             'float32' => 136,
2935             'float64' => 137,
2936             'uint8z' => 10,
2937             'uint16z' => 139,
2938             'uint32z' => 140,
2939             'byte' => 13,
2940             'sint64' => 142,
2941             'uint64' => 143,
2942             'uint64z' => 144,
2943             },
2944              
2945             'turn_type' => +{
2946             '_base_type' => FIT_ENUM,
2947             'arriving_idx' => 0,
2948             'arriving_left_idx' => 1,
2949             'arriving_right_idx' => 2,
2950             'arriving_via_idx' => 3,
2951             'arriving_via_left_idx' => 4,
2952             'arriving_via_right_idx' => 5,
2953             'bear_keep_left_idx' => 6,
2954             'bear_keep_right_idx' => 7,
2955             'continue_idx' => 8,
2956             'exit_left_idx' => 9,
2957             'exit_right_idx' => 10,
2958             'ferry_idx' => 11,
2959             'roundabout_45_idx' => 12,
2960             'roundabout_90_idx' => 13,
2961             'roundabout_135_idx' => 14,
2962             'roundabout_180_idx' => 15,
2963             'roundabout_225_idx' => 16,
2964             'roundabout_270_idx' => 17,
2965             'roundabout_315_idx' => 18,
2966             'roundabout_360_idx' => 19,
2967             'roundabout_neg_45_idx' => 20,
2968             'roundabout_neg_90_idx' => 21,
2969             'roundabout_neg_135_idx' => 22,
2970             'roundabout_neg_180_idx' => 23,
2971             'roundabout_neg_225_idx' => 24,
2972             'roundabout_neg_270_idx' => 25,
2973             'roundabout_neg_315_idx' => 26,
2974             'roundabout_neg_360_idx' => 27,
2975             'roundabout_generic_idx' => 28,
2976             'roundabout_neg_generic_idx' => 29,
2977             'sharp_turn_left_idx' => 30,
2978             'sharp_turn_right_idx' => 31,
2979             'turn_left_idx' => 32,
2980             'turn_right_idx' => 33,
2981             'uturn_left_idx' => 34,
2982             'uturn_right_idx' => 35,
2983             'icon_inv_idx' => 36,
2984             'icon_idx_cnt' => 37,
2985             },
2986              
2987             'bike_light_beam_angle_mode' => +{
2988             '_base_type' => FIT_ENUM,
2989             'manual' => 0,
2990             'auto' => 1,
2991             },
2992              
2993             'fit_base_unit' => +{
2994             '_base_type' => FIT_UINT16,
2995             'other' => 0,
2996             'kilogram' => 1,
2997             'pound' => 2,
2998             },
2999              
3000             'set_type' => +{
3001             '_base_type' => FIT_UINT8,
3002             'rest' => 0,
3003             'active' => 1,
3004             },
3005              
3006             'max_met_category' => +{
3007             '_base_type' => FIT_ENUM,
3008             'generic' => 0,
3009             'cycling' => 1,
3010             },
3011              
3012             'exercise_category' => +{
3013             '_base_type' => FIT_UINT16,
3014             'bench_press' => 0,
3015             'calf_raise' => 1,
3016             'cardio' => 2,
3017             'carry' => 3,
3018             'chop' => 4,
3019             'core' => 5,
3020             'crunch' => 6,
3021             'curl' => 7,
3022             'deadlift' => 8,
3023             'flye' => 9,
3024             'hip_raise' => 10,
3025             'hip_stability' => 11,
3026             'hip_swing' => 12,
3027             'hyperextension' => 13,
3028             'lateral_raise' => 14,
3029             'leg_curl' => 15,
3030             'leg_raise' => 16,
3031             'lunge' => 17,
3032             'olympic_lift' => 18,
3033             'plank' => 19,
3034             'plyo' => 20,
3035             'pull_up' => 21,
3036             'push_up' => 22,
3037             'row' => 23,
3038             'shoulder_press' => 24,
3039             'shoulder_stability' => 25,
3040             'shrug' => 26,
3041             'sit_up' => 27,
3042             'squat' => 28,
3043             'total_body' => 29,
3044             'triceps_extension' => 30,
3045             'warm_up' => 31,
3046             'run' => 32,
3047             'unknown' => 65534,
3048             },
3049              
3050             'bench_press_exercise_name' => +{
3051             '_base_type' => FIT_UINT16,
3052             'alternating_dumbbell_chest_press_on_swiss_ball' => 0,
3053             'barbell_bench_press' => 1,
3054             'barbell_board_bench_press' => 2,
3055             'barbell_floor_press' => 3,
3056             'close_grip_barbell_bench_press' => 4,
3057             'decline_dumbbell_bench_press' => 5,
3058             'dumbbell_bench_press' => 6,
3059             'dumbbell_floor_press' => 7,
3060             'incline_barbell_bench_press' => 8,
3061             'incline_dumbbell_bench_press' => 9,
3062             'incline_smith_machine_bench_press' => 10,
3063             'isometric_barbell_bench_press' => 11,
3064             'kettlebell_chest_press' => 12,
3065             'neutral_grip_dumbbell_bench_press' => 13,
3066             'neutral_grip_dumbbell_incline_bench_press' => 14,
3067             'one_arm_floor_press' => 15,
3068             'weighted_one_arm_floor_press' => 16,
3069             'partial_lockout' => 17,
3070             'reverse_grip_barbell_bench_press' => 18,
3071             'reverse_grip_incline_bench_press' => 19,
3072             'single_arm_cable_chest_press' => 20,
3073             'single_arm_dumbbell_bench_press' => 21,
3074             'smith_machine_bench_press' => 22,
3075             'swiss_ball_dumbbell_chest_press' => 23,
3076             'triple_stop_barbell_bench_press' => 24,
3077             'wide_grip_barbell_bench_press' => 25,
3078             'alternating_dumbbell_chest_press' => 26,
3079             },
3080              
3081             'calf_raise_exercise_name' => +{
3082             '_base_type' => FIT_UINT16,
3083             '3_way_calf_raise' => 0,
3084             '3_way_weighted_calf_raise' => 1,
3085             '3_way_single_leg_calf_raise' => 2,
3086             '3_way_weighted_single_leg_calf_raise' => 3,
3087             'donkey_calf_raise' => 4,
3088             'weighted_donkey_calf_raise' => 5,
3089             'seated_calf_raise' => 6,
3090             'weighted_seated_calf_raise' => 7,
3091             'seated_dumbbell_toe_raise' => 8,
3092             'single_leg_bent_knee_calf_raise' => 9,
3093             'weighted_single_leg_bent_knee_calf_raise' => 10,
3094             'single_leg_decline_push_up' => 11,
3095             'single_leg_donkey_calf_raise' => 12,
3096             'weighted_single_leg_donkey_calf_raise' => 13,
3097             'single_leg_hip_raise_with_knee_hold' => 14,
3098             'single_leg_standing_calf_raise' => 15,
3099             'single_leg_standing_dumbbell_calf_raise' => 16,
3100             'standing_barbell_calf_raise' => 17,
3101             'standing_calf_raise' => 18,
3102             'weighted_standing_calf_raise' => 19,
3103             'standing_dumbbell_calf_raise' => 20,
3104             },
3105              
3106             'cardio_exercise_name' => +{
3107             '_base_type' => FIT_UINT16,
3108             'bob_and_weave_circle' => 0,
3109             'weighted_bob_and_weave_circle' => 1,
3110             'cardio_core_crawl' => 2,
3111             'weighted_cardio_core_crawl' => 3,
3112             'double_under' => 4,
3113             'weighted_double_under' => 5,
3114             'jump_rope' => 6,
3115             'weighted_jump_rope' => 7,
3116             'jump_rope_crossover' => 8,
3117             'weighted_jump_rope_crossover' => 9,
3118             'jump_rope_jog' => 10,
3119             'weighted_jump_rope_jog' => 11,
3120             'jumping_jacks' => 12,
3121             'weighted_jumping_jacks' => 13,
3122             'ski_moguls' => 14,
3123             'weighted_ski_moguls' => 15,
3124             'split_jacks' => 16,
3125             'weighted_split_jacks' => 17,
3126             'squat_jacks' => 18,
3127             'weighted_squat_jacks' => 19,
3128             'triple_under' => 20,
3129             'weighted_triple_under' => 21,
3130             },
3131              
3132             'carry_exercise_name' => +{
3133             '_base_type' => FIT_UINT16,
3134             'bar_holds' => 0,
3135             'farmers_walk' => 1,
3136             'farmers_walk_on_toes' => 2,
3137             'hex_dumbbell_hold' => 3,
3138             'overhead_carry' => 4,
3139             },
3140              
3141             'chop_exercise_name' => +{
3142             '_base_type' => FIT_UINT16,
3143             'cable_pull_through' => 0,
3144             'cable_rotational_lift' => 1,
3145             'cable_woodchop' => 2,
3146             'cross_chop_to_knee' => 3,
3147             'weighted_cross_chop_to_knee' => 4,
3148             'dumbbell_chop' => 5,
3149             'half_kneeling_rotation' => 6,
3150             'weighted_half_kneeling_rotation' => 7,
3151             'half_kneeling_rotational_chop' => 8,
3152             'half_kneeling_rotational_reverse_chop' => 9,
3153             'half_kneeling_stability_chop' => 10,
3154             'half_kneeling_stability_reverse_chop' => 11,
3155             'kneeling_rotational_chop' => 12,
3156             'kneeling_rotational_reverse_chop' => 13,
3157             'kneeling_stability_chop' => 14,
3158             'kneeling_woodchopper' => 15,
3159             'medicine_ball_wood_chops' => 16,
3160             'power_squat_chops' => 17,
3161             'weighted_power_squat_chops' => 18,
3162             'standing_rotational_chop' => 19,
3163             'standing_split_rotational_chop' => 20,
3164             'standing_split_rotational_reverse_chop' => 21,
3165             'standing_stability_reverse_chop' => 22,
3166             },
3167              
3168             'core_exercise_name' => +{
3169             '_base_type' => FIT_UINT16,
3170             'abs_jabs' => 0,
3171             'weighted_abs_jabs' => 1,
3172             'alternating_plate_reach' => 2,
3173             'barbell_rollout' => 3,
3174             'weighted_barbell_rollout' => 4,
3175             'body_bar_oblique_twist' => 5,
3176             'cable_core_press' => 6,
3177             'cable_side_bend' => 7,
3178             'side_bend' => 8,
3179             'weighted_side_bend' => 9,
3180             'crescent_circle' => 10,
3181             'weighted_crescent_circle' => 11,
3182             'cycling_russian_twist' => 12,
3183             'weighted_cycling_russian_twist' => 13,
3184             'elevated_feet_russian_twist' => 14,
3185             'weighted_elevated_feet_russian_twist' => 15,
3186             'half_turkish_get_up' => 16,
3187             'kettlebell_windmill' => 17,
3188             'kneeling_ab_wheel' => 18,
3189             'weighted_kneeling_ab_wheel' => 19,
3190             'modified_front_lever' => 20,
3191             'open_knee_tucks' => 21,
3192             'weighted_open_knee_tucks' => 22,
3193             'side_abs_leg_lift' => 23,
3194             'weighted_side_abs_leg_lift' => 24,
3195             'swiss_ball_jackknife' => 25,
3196             'weighted_swiss_ball_jackknife' => 26,
3197             'swiss_ball_pike' => 27,
3198             'weighted_swiss_ball_pike' => 28,
3199             'swiss_ball_rollout' => 29,
3200             'weighted_swiss_ball_rollout' => 30,
3201             'triangle_hip_press' => 31,
3202             'weighted_triangle_hip_press' => 32,
3203             'trx_suspended_jackknife' => 33,
3204             'weighted_trx_suspended_jackknife' => 34,
3205             'u_boat' => 35,
3206             'weighted_u_boat' => 36,
3207             'windmill_switches' => 37,
3208             'weighted_windmill_switches' => 38,
3209             'alternating_slide_out' => 39,
3210             'weighted_alternating_slide_out' => 40,
3211             'ghd_back_extensions' => 41,
3212             'weighted_ghd_back_extensions' => 42,
3213             'overhead_walk' => 43,
3214             'inchworm' => 44,
3215             'weighted_modified_front_lever' => 45,
3216             'russian_twist' => 46,
3217             'abdominal_leg_rotations' => 47, # Deprecated do not use
3218             'arm_and_leg_extension_on_knees' => 48,
3219             'bicycle' => 49,
3220             'bicep_curl_with_leg_extension' => 50,
3221             'cat_cow' => 51,
3222             'corkscrew' => 52,
3223             'criss_cross' => 53,
3224             'criss_cross_with_ball' => 54, # Deprecated do not use
3225             'double_leg_stretch' => 55,
3226             'knee_folds' => 56,
3227             'lower_lift' => 57,
3228             'neck_pull' => 58,
3229             'pelvic_clocks' => 59,
3230             'roll_over' => 60,
3231             'roll_up' => 61,
3232             'rolling' => 62,
3233             'rowing_1' => 63,
3234             'rowing_2' => 64,
3235             'scissors' => 65,
3236             'single_leg_circles' => 66,
3237             'single_leg_stretch' => 67,
3238             'snake_twist_1_and_2' => 68, # Deprecated do not use
3239             'swan' => 69,
3240             'swimming' => 70,
3241             'teaser' => 71,
3242             'the_hundred' => 72,
3243             },
3244              
3245             'crunch_exercise_name' => +{
3246             '_base_type' => FIT_UINT16,
3247             'bicycle_crunch' => 0,
3248             'cable_crunch' => 1,
3249             'circular_arm_crunch' => 2,
3250             'crossed_arms_crunch' => 3,
3251             'weighted_crossed_arms_crunch' => 4,
3252             'cross_leg_reverse_crunch' => 5,
3253             'weighted_cross_leg_reverse_crunch' => 6,
3254             'crunch_chop' => 7,
3255             'weighted_crunch_chop' => 8,
3256             'double_crunch' => 9,
3257             'weighted_double_crunch' => 10,
3258             'elbow_to_knee_crunch' => 11,
3259             'weighted_elbow_to_knee_crunch' => 12,
3260             'flutter_kicks' => 13,
3261             'weighted_flutter_kicks' => 14,
3262             'foam_roller_reverse_crunch_on_bench' => 15,
3263             'weighted_foam_roller_reverse_crunch_on_bench' => 16,
3264             'foam_roller_reverse_crunch_with_dumbbell' => 17,
3265             'foam_roller_reverse_crunch_with_medicine_ball' => 18,
3266             'frog_press' => 19,
3267             'hanging_knee_raise_oblique_crunch' => 20,
3268             'weighted_hanging_knee_raise_oblique_crunch' => 21,
3269             'hip_crossover' => 22,
3270             'weighted_hip_crossover' => 23,
3271             'hollow_rock' => 24,
3272             'weighted_hollow_rock' => 25,
3273             'incline_reverse_crunch' => 26,
3274             'weighted_incline_reverse_crunch' => 27,
3275             'kneeling_cable_crunch' => 28,
3276             'kneeling_cross_crunch' => 29,
3277             'weighted_kneeling_cross_crunch' => 30,
3278             'kneeling_oblique_cable_crunch' => 31,
3279             'knees_to_elbow' => 32,
3280             'leg_extensions' => 33,
3281             'weighted_leg_extensions' => 34,
3282             'leg_levers' => 35,
3283             'mcgill_curl_up' => 36,
3284             'weighted_mcgill_curl_up' => 37,
3285             'modified_pilates_roll_up_with_ball' => 38,
3286             'weighted_modified_pilates_roll_up_with_ball' => 39,
3287             'pilates_crunch' => 40,
3288             'weighted_pilates_crunch' => 41,
3289             'pilates_roll_up_with_ball' => 42,
3290             'weighted_pilates_roll_up_with_ball' => 43,
3291             'raised_legs_crunch' => 44,
3292             'weighted_raised_legs_crunch' => 45,
3293             'reverse_crunch' => 46,
3294             'weighted_reverse_crunch' => 47,
3295             'reverse_crunch_on_a_bench' => 48,
3296             'weighted_reverse_crunch_on_a_bench' => 49,
3297             'reverse_curl_and_lift' => 50,
3298             'weighted_reverse_curl_and_lift' => 51,
3299             'rotational_lift' => 52,
3300             'weighted_rotational_lift' => 53,
3301             'seated_alternating_reverse_crunch' => 54,
3302             'weighted_seated_alternating_reverse_crunch' => 55,
3303             'seated_leg_u' => 56,
3304             'weighted_seated_leg_u' => 57,
3305             'side_to_side_crunch_and_weave' => 58,
3306             'weighted_side_to_side_crunch_and_weave' => 59,
3307             'single_leg_reverse_crunch' => 60,
3308             'weighted_single_leg_reverse_crunch' => 61,
3309             'skater_crunch_cross' => 62,
3310             'weighted_skater_crunch_cross' => 63,
3311             'standing_cable_crunch' => 64,
3312             'standing_side_crunch' => 65,
3313             'step_climb' => 66,
3314             'weighted_step_climb' => 67,
3315             'swiss_ball_crunch' => 68,
3316             'swiss_ball_reverse_crunch' => 69,
3317             'weighted_swiss_ball_reverse_crunch' => 70,
3318             'swiss_ball_russian_twist' => 71,
3319             'weighted_swiss_ball_russian_twist' => 72,
3320             'swiss_ball_side_crunch' => 73,
3321             'weighted_swiss_ball_side_crunch' => 74,
3322             'thoracic_crunches_on_foam_roller' => 75,
3323             'weighted_thoracic_crunches_on_foam_roller' => 76,
3324             'triceps_crunch' => 77,
3325             'weighted_bicycle_crunch' => 78,
3326             'weighted_crunch' => 79,
3327             'weighted_swiss_ball_crunch' => 80,
3328             'toes_to_bar' => 81,
3329             'weighted_toes_to_bar' => 82,
3330             'crunch' => 83,
3331             'straight_leg_crunch_with_ball' => 84,
3332             },
3333              
3334             'curl_exercise_name' => +{
3335             '_base_type' => FIT_UINT16,
3336             'alternating_dumbbell_biceps_curl' => 0,
3337             'alternating_dumbbell_biceps_curl_on_swiss_ball' => 1,
3338             'alternating_incline_dumbbell_biceps_curl' => 2,
3339             'barbell_biceps_curl' => 3,
3340             'barbell_reverse_wrist_curl' => 4,
3341             'barbell_wrist_curl' => 5,
3342             'behind_the_back_barbell_reverse_wrist_curl' => 6,
3343             'behind_the_back_one_arm_cable_curl' => 7,
3344             'cable_biceps_curl' => 8,
3345             'cable_hammer_curl' => 9,
3346             'cheating_barbell_biceps_curl' => 10,
3347             'close_grip_ez_bar_biceps_curl' => 11,
3348             'cross_body_dumbbell_hammer_curl' => 12,
3349             'dead_hang_biceps_curl' => 13,
3350             'decline_hammer_curl' => 14,
3351             'dumbbell_biceps_curl_with_static_hold' => 15,
3352             'dumbbell_hammer_curl' => 16,
3353             'dumbbell_reverse_wrist_curl' => 17,
3354             'dumbbell_wrist_curl' => 18,
3355             'ez_bar_preacher_curl' => 19,
3356             'forward_bend_biceps_curl' => 20,
3357             'hammer_curl_to_press' => 21,
3358             'incline_dumbbell_biceps_curl' => 22,
3359             'incline_offset_thumb_dumbbell_curl' => 23,
3360             'kettlebell_biceps_curl' => 24,
3361             'lying_concentration_cable_curl' => 25,
3362             'one_arm_preacher_curl' => 26,
3363             'plate_pinch_curl' => 27,
3364             'preacher_curl_with_cable' => 28,
3365             'reverse_ez_bar_curl' => 29,
3366             'reverse_grip_wrist_curl' => 30,
3367             'reverse_grip_barbell_biceps_curl' => 31,
3368             'seated_alternating_dumbbell_biceps_curl' => 32,
3369             'seated_dumbbell_biceps_curl' => 33,
3370             'seated_reverse_dumbbell_curl' => 34,
3371             'split_stance_offset_pinky_dumbbell_curl' => 35,
3372             'standing_alternating_dumbbell_curls' => 36,
3373             'standing_dumbbell_biceps_curl' => 37,
3374             'standing_ez_bar_biceps_curl' => 38,
3375             'static_curl' => 39,
3376             'swiss_ball_dumbbell_overhead_triceps_extension' => 40,
3377             'swiss_ball_ez_bar_preacher_curl' => 41,
3378             'twisting_standing_dumbbell_biceps_curl' => 42,
3379             'wide_grip_ez_bar_biceps_curl' => 43,
3380             },
3381              
3382             'deadlift_exercise_name' => +{
3383             '_base_type' => FIT_UINT16,
3384             'barbell_deadlift' => 0,
3385             'barbell_straight_leg_deadlift' => 1,
3386             'dumbbell_deadlift' => 2,
3387             'dumbbell_single_leg_deadlift_to_row' => 3,
3388             'dumbbell_straight_leg_deadlift' => 4,
3389             'kettlebell_floor_to_shelf' => 5,
3390             'one_arm_one_leg_deadlift' => 6,
3391             'rack_pull' => 7,
3392             'rotational_dumbbell_straight_leg_deadlift' => 8,
3393             'single_arm_deadlift' => 9,
3394             'single_leg_barbell_deadlift' => 10,
3395             'single_leg_barbell_straight_leg_deadlift' => 11,
3396             'single_leg_deadlift_with_barbell' => 12,
3397             'single_leg_rdl_circuit' => 13,
3398             'single_leg_romanian_deadlift_with_dumbbell' => 14,
3399             'sumo_deadlift' => 15,
3400             'sumo_deadlift_high_pull' => 16,
3401             'trap_bar_deadlift' => 17,
3402             'wide_grip_barbell_deadlift' => 18,
3403             },
3404              
3405             'flye_exercise_name' => +{
3406             '_base_type' => FIT_UINT16,
3407             'cable_crossover' => 0,
3408             'decline_dumbbell_flye' => 1,
3409             'dumbbell_flye' => 2,
3410             'incline_dumbbell_flye' => 3,
3411             'kettlebell_flye' => 4,
3412             'kneeling_rear_flye' => 5,
3413             'single_arm_standing_cable_reverse_flye' => 6,
3414             'swiss_ball_dumbbell_flye' => 7,
3415             'arm_rotations' => 8,
3416             'hug_a_tree' => 9,
3417             },
3418              
3419             'hip_raise_exercise_name' => +{
3420             '_base_type' => FIT_UINT16,
3421             'barbell_hip_thrust_on_floor' => 0,
3422             'barbell_hip_thrust_with_bench' => 1,
3423             'bent_knee_swiss_ball_reverse_hip_raise' => 2,
3424             'weighted_bent_knee_swiss_ball_reverse_hip_raise' => 3,
3425             'bridge_with_leg_extension' => 4,
3426             'weighted_bridge_with_leg_extension' => 5,
3427             'clam_bridge' => 6,
3428             'front_kick_tabletop' => 7,
3429             'weighted_front_kick_tabletop' => 8,
3430             'hip_extension_and_cross' => 9,
3431             'weighted_hip_extension_and_cross' => 10,
3432             'hip_raise' => 11,
3433             'weighted_hip_raise' => 12,
3434             'hip_raise_with_feet_on_swiss_ball' => 13,
3435             'weighted_hip_raise_with_feet_on_swiss_ball' => 14,
3436             'hip_raise_with_head_on_bosu_ball' => 15,
3437             'weighted_hip_raise_with_head_on_bosu_ball' => 16,
3438             'hip_raise_with_head_on_swiss_ball' => 17,
3439             'weighted_hip_raise_with_head_on_swiss_ball' => 18,
3440             'hip_raise_with_knee_squeeze' => 19,
3441             'weighted_hip_raise_with_knee_squeeze' => 20,
3442             'incline_rear_leg_extension' => 21,
3443             'weighted_incline_rear_leg_extension' => 22,
3444             'kettlebell_swing' => 23,
3445             'marching_hip_raise' => 24,
3446             'weighted_marching_hip_raise' => 25,
3447             'marching_hip_raise_with_feet_on_a_swiss_ball' => 26,
3448             'weighted_marching_hip_raise_with_feet_on_a_swiss_ball' => 27,
3449             'reverse_hip_raise' => 28,
3450             'weighted_reverse_hip_raise' => 29,
3451             'single_leg_hip_raise' => 30,
3452             'weighted_single_leg_hip_raise' => 31,
3453             'single_leg_hip_raise_with_foot_on_bench' => 32,
3454             'weighted_single_leg_hip_raise_with_foot_on_bench' => 33,
3455             'single_leg_hip_raise_with_foot_on_bosu_ball' => 34,
3456             'weighted_single_leg_hip_raise_with_foot_on_bosu_ball' => 35,
3457             'single_leg_hip_raise_with_foot_on_foam_roller' => 36,
3458             'weighted_single_leg_hip_raise_with_foot_on_foam_roller' => 37,
3459             'single_leg_hip_raise_with_foot_on_medicine_ball' => 38,
3460             'weighted_single_leg_hip_raise_with_foot_on_medicine_ball' => 39,
3461             'single_leg_hip_raise_with_head_on_bosu_ball' => 40,
3462             'weighted_single_leg_hip_raise_with_head_on_bosu_ball' => 41,
3463             'weighted_clam_bridge' => 42,
3464             'single_leg_swiss_ball_hip_raise_and_leg_curl' => 43,
3465             'clams' => 44,
3466             'inner_thigh_circles' => 45, # Deprecated do not use
3467             'inner_thigh_side_lift' => 46, # Deprecated do not use
3468             'leg_circles' => 47,
3469             'leg_lift' => 48,
3470             'leg_lift_in_external_rotation' => 49,
3471             },
3472              
3473             'hip_stability_exercise_name' => +{
3474             '_base_type' => FIT_UINT16,
3475             'band_side_lying_leg_raise' => 0,
3476             'dead_bug' => 1,
3477             'weighted_dead_bug' => 2,
3478             'external_hip_raise' => 3,
3479             'weighted_external_hip_raise' => 4,
3480             'fire_hydrant_kicks' => 5,
3481             'weighted_fire_hydrant_kicks' => 6,
3482             'hip_circles' => 7,
3483             'weighted_hip_circles' => 8,
3484             'inner_thigh_lift' => 9,
3485             'weighted_inner_thigh_lift' => 10,
3486             'lateral_walks_with_band_at_ankles' => 11,
3487             'pretzel_side_kick' => 12,
3488             'weighted_pretzel_side_kick' => 13,
3489             'prone_hip_internal_rotation' => 14,
3490             'weighted_prone_hip_internal_rotation' => 15,
3491             'quadruped' => 16,
3492             'quadruped_hip_extension' => 17,
3493             'weighted_quadruped_hip_extension' => 18,
3494             'quadruped_with_leg_lift' => 19,
3495             'weighted_quadruped_with_leg_lift' => 20,
3496             'side_lying_leg_raise' => 21,
3497             'weighted_side_lying_leg_raise' => 22,
3498             'sliding_hip_adduction' => 23,
3499             'weighted_sliding_hip_adduction' => 24,
3500             'standing_adduction' => 25,
3501             'weighted_standing_adduction' => 26,
3502             'standing_cable_hip_abduction' => 27,
3503             'standing_hip_abduction' => 28,
3504             'weighted_standing_hip_abduction' => 29,
3505             'standing_rear_leg_raise' => 30,
3506             'weighted_standing_rear_leg_raise' => 31,
3507             'supine_hip_internal_rotation' => 32,
3508             'weighted_supine_hip_internal_rotation' => 33,
3509             },
3510              
3511             'hip_swing_exercise_name' => +{
3512             '_base_type' => FIT_UINT16,
3513             'single_arm_kettlebell_swing' => 0,
3514             'single_arm_dumbbell_swing' => 1,
3515             'step_out_swing' => 2,
3516             },
3517              
3518             'hyperextension_exercise_name' => +{
3519             '_base_type' => FIT_UINT16,
3520             'back_extension_with_opposite_arm_and_leg_reach' => 0,
3521             'weighted_back_extension_with_opposite_arm_and_leg_reach' => 1,
3522             'base_rotations' => 2,
3523             'weighted_base_rotations' => 3,
3524             'bent_knee_reverse_hyperextension' => 4,
3525             'weighted_bent_knee_reverse_hyperextension' => 5,
3526             'hollow_hold_and_roll' => 6,
3527             'weighted_hollow_hold_and_roll' => 7,
3528             'kicks' => 8,
3529             'weighted_kicks' => 9,
3530             'knee_raises' => 10,
3531             'weighted_knee_raises' => 11,
3532             'kneeling_superman' => 12,
3533             'weighted_kneeling_superman' => 13,
3534             'lat_pull_down_with_row' => 14,
3535             'medicine_ball_deadlift_to_reach' => 15,
3536             'one_arm_one_leg_row' => 16,
3537             'one_arm_row_with_band' => 17,
3538             'overhead_lunge_with_medicine_ball' => 18,
3539             'plank_knee_tucks' => 19,
3540             'weighted_plank_knee_tucks' => 20,
3541             'side_step' => 21,
3542             'weighted_side_step' => 22,
3543             'single_leg_back_extension' => 23,
3544             'weighted_single_leg_back_extension' => 24,
3545             'spine_extension' => 25,
3546             'weighted_spine_extension' => 26,
3547             'static_back_extension' => 27,
3548             'weighted_static_back_extension' => 28,
3549             'superman_from_floor' => 29,
3550             'weighted_superman_from_floor' => 30,
3551             'swiss_ball_back_extension' => 31,
3552             'weighted_swiss_ball_back_extension' => 32,
3553             'swiss_ball_hyperextension' => 33,
3554             'weighted_swiss_ball_hyperextension' => 34,
3555             'swiss_ball_opposite_arm_and_leg_lift' => 35,
3556             'weighted_swiss_ball_opposite_arm_and_leg_lift' => 36,
3557             'superman_on_swiss_ball' => 37,
3558             'cobra' => 38,
3559             'supine_floor_barre' => 39, # Deprecated do not use
3560             },
3561              
3562             'lateral_raise_exercise_name' => +{
3563             '_base_type' => FIT_UINT16,
3564             '45_degree_cable_external_rotation' => 0,
3565             'alternating_lateral_raise_with_static_hold' => 1,
3566             'bar_muscle_up' => 2,
3567             'bent_over_lateral_raise' => 3,
3568             'cable_diagonal_raise' => 4,
3569             'cable_front_raise' => 5,
3570             'calorie_row' => 6,
3571             'combo_shoulder_raise' => 7,
3572             'dumbbell_diagonal_raise' => 8,
3573             'dumbbell_v_raise' => 9,
3574             'front_raise' => 10,
3575             'leaning_dumbbell_lateral_raise' => 11,
3576             'lying_dumbbell_raise' => 12,
3577             'muscle_up' => 13,
3578             'one_arm_cable_lateral_raise' => 14,
3579             'overhand_grip_rear_lateral_raise' => 15,
3580             'plate_raises' => 16,
3581             'ring_dip' => 17,
3582             'weighted_ring_dip' => 18,
3583             'ring_muscle_up' => 19,
3584             'weighted_ring_muscle_up' => 20,
3585             'rope_climb' => 21,
3586             'weighted_rope_climb' => 22,
3587             'scaption' => 23,
3588             'seated_lateral_raise' => 24,
3589             'seated_rear_lateral_raise' => 25,
3590             'side_lying_lateral_raise' => 26,
3591             'standing_lift' => 27,
3592             'suspended_row' => 28,
3593             'underhand_grip_rear_lateral_raise' => 29,
3594             'wall_slide' => 30,
3595             'weighted_wall_slide' => 31,
3596             'arm_circles' => 32,
3597             'shaving_the_head' => 33,
3598             },
3599              
3600             'leg_curl_exercise_name' => +{
3601             '_base_type' => FIT_UINT16,
3602             'leg_curl' => 0,
3603             'weighted_leg_curl' => 1,
3604             'good_morning' => 2,
3605             'seated_barbell_good_morning' => 3,
3606             'single_leg_barbell_good_morning' => 4,
3607             'single_leg_sliding_leg_curl' => 5,
3608             'sliding_leg_curl' => 6,
3609             'split_barbell_good_morning' => 7,
3610             'split_stance_extension' => 8,
3611             'staggered_stance_good_morning' => 9,
3612             'swiss_ball_hip_raise_and_leg_curl' => 10,
3613             'zercher_good_morning' => 11,
3614             },
3615              
3616             'leg_raise_exercise_name' => +{
3617             '_base_type' => FIT_UINT16,
3618             'hanging_knee_raise' => 0,
3619             'hanging_leg_raise' => 1,
3620             'weighted_hanging_leg_raise' => 2,
3621             'hanging_single_leg_raise' => 3,
3622             'weighted_hanging_single_leg_raise' => 4,
3623             'kettlebell_leg_raises' => 5,
3624             'leg_lowering_drill' => 6,
3625             'weighted_leg_lowering_drill' => 7,
3626             'lying_straight_leg_raise' => 8,
3627             'weighted_lying_straight_leg_raise' => 9,
3628             'medicine_ball_leg_drops' => 10,
3629             'quadruped_leg_raise' => 11,
3630             'weighted_quadruped_leg_raise' => 12,
3631             'reverse_leg_raise' => 13,
3632             'weighted_reverse_leg_raise' => 14,
3633             'reverse_leg_raise_on_swiss_ball' => 15,
3634             'weighted_reverse_leg_raise_on_swiss_ball' => 16,
3635             'single_leg_lowering_drill' => 17,
3636             'weighted_single_leg_lowering_drill' => 18,
3637             'weighted_hanging_knee_raise' => 19,
3638             'lateral_stepover' => 20,
3639             'weighted_lateral_stepover' => 21,
3640             },
3641              
3642             'lunge_exercise_name' => +{
3643             '_base_type' => FIT_UINT16,
3644             'overhead_lunge' => 0,
3645             'lunge_matrix' => 1,
3646             'weighted_lunge_matrix' => 2,
3647             'alternating_barbell_forward_lunge' => 3,
3648             'alternating_dumbbell_lunge_with_reach' => 4,
3649             'back_foot_elevated_dumbbell_split_squat' => 5,
3650             'barbell_box_lunge' => 6,
3651             'barbell_bulgarian_split_squat' => 7,
3652             'barbell_crossover_lunge' => 8,
3653             'barbell_front_split_squat' => 9,
3654             'barbell_lunge' => 10,
3655             'barbell_reverse_lunge' => 11,
3656             'barbell_side_lunge' => 12,
3657             'barbell_split_squat' => 13,
3658             'core_control_rear_lunge' => 14,
3659             'diagonal_lunge' => 15,
3660             'drop_lunge' => 16,
3661             'dumbbell_box_lunge' => 17,
3662             'dumbbell_bulgarian_split_squat' => 18,
3663             'dumbbell_crossover_lunge' => 19,
3664             'dumbbell_diagonal_lunge' => 20,
3665             'dumbbell_lunge' => 21,
3666             'dumbbell_lunge_and_rotation' => 22,
3667             'dumbbell_overhead_bulgarian_split_squat' => 23,
3668             'dumbbell_reverse_lunge_to_high_knee_and_press' => 24,
3669             'dumbbell_side_lunge' => 25,
3670             'elevated_front_foot_barbell_split_squat' => 26,
3671             'front_foot_elevated_dumbbell_split_squat' => 27,
3672             'gunslinger_lunge' => 28,
3673             'lawnmower_lunge' => 29,
3674             'low_lunge_with_isometric_adduction' => 30,
3675             'low_side_to_side_lunge' => 31,
3676             'lunge' => 32,
3677             'weighted_lunge' => 33,
3678             'lunge_with_arm_reach' => 34,
3679             'lunge_with_diagonal_reach' => 35,
3680             'lunge_with_side_bend' => 36,
3681             'offset_dumbbell_lunge' => 37,
3682             'offset_dumbbell_reverse_lunge' => 38,
3683             'overhead_bulgarian_split_squat' => 39,
3684             'overhead_dumbbell_reverse_lunge' => 40,
3685             'overhead_dumbbell_split_squat' => 41,
3686             'overhead_lunge_with_rotation' => 42,
3687             'reverse_barbell_box_lunge' => 43,
3688             'reverse_box_lunge' => 44,
3689             'reverse_dumbbell_box_lunge' => 45,
3690             'reverse_dumbbell_crossover_lunge' => 46,
3691             'reverse_dumbbell_diagonal_lunge' => 47,
3692             'reverse_lunge_with_reach_back' => 48,
3693             'weighted_reverse_lunge_with_reach_back' => 49,
3694             'reverse_lunge_with_twist_and_overhead_reach' => 50,
3695             'weighted_reverse_lunge_with_twist_and_overhead_reach' => 51,
3696             'reverse_sliding_box_lunge' => 52,
3697             'weighted_reverse_sliding_box_lunge' => 53,
3698             'reverse_sliding_lunge' => 54,
3699             'weighted_reverse_sliding_lunge' => 55,
3700             'runners_lunge_to_balance' => 56,
3701             'weighted_runners_lunge_to_balance' => 57,
3702             'shifting_side_lunge' => 58,
3703             'side_and_crossover_lunge' => 59,
3704             'weighted_side_and_crossover_lunge' => 60,
3705             'side_lunge' => 61,
3706             'weighted_side_lunge' => 62,
3707             'side_lunge_and_press' => 63,
3708             'side_lunge_jump_off' => 64,
3709             'side_lunge_sweep' => 65,
3710             'weighted_side_lunge_sweep' => 66,
3711             'side_lunge_to_crossover_tap' => 67,
3712             'weighted_side_lunge_to_crossover_tap' => 68,
3713             'side_to_side_lunge_chops' => 69,
3714             'weighted_side_to_side_lunge_chops' => 70,
3715             'siff_jump_lunge' => 71,
3716             'weighted_siff_jump_lunge' => 72,
3717             'single_arm_reverse_lunge_and_press' => 73,
3718             'sliding_lateral_lunge' => 74,
3719             'weighted_sliding_lateral_lunge' => 75,
3720             'walking_barbell_lunge' => 76,
3721             'walking_dumbbell_lunge' => 77,
3722             'walking_lunge' => 78,
3723             'weighted_walking_lunge' => 79,
3724             'wide_grip_overhead_barbell_split_squat' => 80,
3725             },
3726              
3727             'olympic_lift_exercise_name' => +{
3728             '_base_type' => FIT_UINT16,
3729             'barbell_hang_power_clean' => 0,
3730             'barbell_hang_squat_clean' => 1,
3731             'barbell_power_clean' => 2,
3732             'barbell_power_snatch' => 3,
3733             'barbell_squat_clean' => 4,
3734             'clean_and_jerk' => 5,
3735             'barbell_hang_power_snatch' => 6,
3736             'barbell_hang_pull' => 7,
3737             'barbell_high_pull' => 8,
3738             'barbell_snatch' => 9,
3739             'barbell_split_jerk' => 10,
3740             'clean' => 11,
3741             'dumbbell_clean' => 12,
3742             'dumbbell_hang_pull' => 13,
3743             'one_hand_dumbbell_split_snatch' => 14,
3744             'push_jerk' => 15,
3745             'single_arm_dumbbell_snatch' => 16,
3746             'single_arm_hang_snatch' => 17,
3747             'single_arm_kettlebell_snatch' => 18,
3748             'split_jerk' => 19,
3749             'squat_clean_and_jerk' => 20,
3750             },
3751              
3752             'plank_exercise_name' => +{
3753             '_base_type' => FIT_UINT16,
3754             '45_degree_plank' => 0,
3755             'weighted_45_degree_plank' => 1,
3756             '90_degree_static_hold' => 2,
3757             'weighted_90_degree_static_hold' => 3,
3758             'bear_crawl' => 4,
3759             'weighted_bear_crawl' => 5,
3760             'cross_body_mountain_climber' => 6,
3761             'weighted_cross_body_mountain_climber' => 7,
3762             'elbow_plank_pike_jacks' => 8,
3763             'weighted_elbow_plank_pike_jacks' => 9,
3764             'elevated_feet_plank' => 10,
3765             'weighted_elevated_feet_plank' => 11,
3766             'elevator_abs' => 12,
3767             'weighted_elevator_abs' => 13,
3768             'extended_plank' => 14,
3769             'weighted_extended_plank' => 15,
3770             'full_plank_passe_twist' => 16,
3771             'weighted_full_plank_passe_twist' => 17,
3772             'inching_elbow_plank' => 18,
3773             'weighted_inching_elbow_plank' => 19,
3774             'inchworm_to_side_plank' => 20,
3775             'weighted_inchworm_to_side_plank' => 21,
3776             'kneeling_plank' => 22,
3777             'weighted_kneeling_plank' => 23,
3778             'kneeling_side_plank_with_leg_lift' => 24,
3779             'weighted_kneeling_side_plank_with_leg_lift' => 25,
3780             'lateral_roll' => 26,
3781             'weighted_lateral_roll' => 27,
3782             'lying_reverse_plank' => 28,
3783             'weighted_lying_reverse_plank' => 29,
3784             'medicine_ball_mountain_climber' => 30,
3785             'weighted_medicine_ball_mountain_climber' => 31,
3786             'modified_mountain_climber_and_extension' => 32,
3787             'weighted_modified_mountain_climber_and_extension' => 33,
3788             'mountain_climber' => 34,
3789             'weighted_mountain_climber' => 35,
3790             'mountain_climber_on_sliding_discs' => 36,
3791             'weighted_mountain_climber_on_sliding_discs' => 37,
3792             'mountain_climber_with_feet_on_bosu_ball' => 38,
3793             'weighted_mountain_climber_with_feet_on_bosu_ball' => 39,
3794             'mountain_climber_with_hands_on_bench' => 40,
3795             'mountain_climber_with_hands_on_swiss_ball' => 41,
3796             'weighted_mountain_climber_with_hands_on_swiss_ball' => 42,
3797             'plank' => 43,
3798             'plank_jacks_with_feet_on_sliding_discs' => 44,
3799             'weighted_plank_jacks_with_feet_on_sliding_discs' => 45,
3800             'plank_knee_twist' => 46,
3801             'weighted_plank_knee_twist' => 47,
3802             'plank_pike_jumps' => 48,
3803             'weighted_plank_pike_jumps' => 49,
3804             'plank_pikes' => 50,
3805             'weighted_plank_pikes' => 51,
3806             'plank_to_stand_up' => 52,
3807             'weighted_plank_to_stand_up' => 53,
3808             'plank_with_arm_raise' => 54,
3809             'weighted_plank_with_arm_raise' => 55,
3810             'plank_with_knee_to_elbow' => 56,
3811             'weighted_plank_with_knee_to_elbow' => 57,
3812             'plank_with_oblique_crunch' => 58,
3813             'weighted_plank_with_oblique_crunch' => 59,
3814             'plyometric_side_plank' => 60,
3815             'weighted_plyometric_side_plank' => 61,
3816             'rolling_side_plank' => 62,
3817             'weighted_rolling_side_plank' => 63,
3818             'side_kick_plank' => 64,
3819             'weighted_side_kick_plank' => 65,
3820             'side_plank' => 66,
3821             'weighted_side_plank' => 67,
3822             'side_plank_and_row' => 68,
3823             'weighted_side_plank_and_row' => 69,
3824             'side_plank_lift' => 70,
3825             'weighted_side_plank_lift' => 71,
3826             'side_plank_with_elbow_on_bosu_ball' => 72,
3827             'weighted_side_plank_with_elbow_on_bosu_ball' => 73,
3828             'side_plank_with_feet_on_bench' => 74,
3829             'weighted_side_plank_with_feet_on_bench' => 75,
3830             'side_plank_with_knee_circle' => 76,
3831             'weighted_side_plank_with_knee_circle' => 77,
3832             'side_plank_with_knee_tuck' => 78,
3833             'weighted_side_plank_with_knee_tuck' => 79,
3834             'side_plank_with_leg_lift' => 80,
3835             'weighted_side_plank_with_leg_lift' => 81,
3836             'side_plank_with_reach_under' => 82,
3837             'weighted_side_plank_with_reach_under' => 83,
3838             'single_leg_elevated_feet_plank' => 84,
3839             'weighted_single_leg_elevated_feet_plank' => 85,
3840             'single_leg_flex_and_extend' => 86,
3841             'weighted_single_leg_flex_and_extend' => 87,
3842             'single_leg_side_plank' => 88,
3843             'weighted_single_leg_side_plank' => 89,
3844             'spiderman_plank' => 90,
3845             'weighted_spiderman_plank' => 91,
3846             'straight_arm_plank' => 92,
3847             'weighted_straight_arm_plank' => 93,
3848             'straight_arm_plank_with_shoulder_touch' => 94,
3849             'weighted_straight_arm_plank_with_shoulder_touch' => 95,
3850             'swiss_ball_plank' => 96,
3851             'weighted_swiss_ball_plank' => 97,
3852             'swiss_ball_plank_leg_lift' => 98,
3853             'weighted_swiss_ball_plank_leg_lift' => 99,
3854             'swiss_ball_plank_leg_lift_and_hold' => 100,
3855             'swiss_ball_plank_with_feet_on_bench' => 101,
3856             'weighted_swiss_ball_plank_with_feet_on_bench' => 102,
3857             'swiss_ball_prone_jackknife' => 103,
3858             'weighted_swiss_ball_prone_jackknife' => 104,
3859             'swiss_ball_side_plank' => 105,
3860             'weighted_swiss_ball_side_plank' => 106,
3861             'three_way_plank' => 107,
3862             'weighted_three_way_plank' => 108,
3863             'towel_plank_and_knee_in' => 109,
3864             'weighted_towel_plank_and_knee_in' => 110,
3865             't_stabilization' => 111,
3866             'weighted_t_stabilization' => 112,
3867             'turkish_get_up_to_side_plank' => 113,
3868             'weighted_turkish_get_up_to_side_plank' => 114,
3869             'two_point_plank' => 115,
3870             'weighted_two_point_plank' => 116,
3871             'weighted_plank' => 117,
3872             'wide_stance_plank_with_diagonal_arm_lift' => 118,
3873             'weighted_wide_stance_plank_with_diagonal_arm_lift' => 119,
3874             'wide_stance_plank_with_diagonal_leg_lift' => 120,
3875             'weighted_wide_stance_plank_with_diagonal_leg_lift' => 121,
3876             'wide_stance_plank_with_leg_lift' => 122,
3877             'weighted_wide_stance_plank_with_leg_lift' => 123,
3878             'wide_stance_plank_with_opposite_arm_and_leg_lift' => 124,
3879             'weighted_mountain_climber_with_hands_on_bench' => 125,
3880             'weighted_swiss_ball_plank_leg_lift_and_hold' => 126,
3881             'weighted_wide_stance_plank_with_opposite_arm_and_leg_lift' => 127,
3882             'plank_with_feet_on_swiss_ball' => 128,
3883             'side_plank_to_plank_with_reach_under' => 129,
3884             'bridge_with_glute_lower_lift' => 130,
3885             'bridge_one_leg_bridge' => 131,
3886             'plank_with_arm_variations' => 132,
3887             'plank_with_leg_lift' => 133,
3888             'reverse_plank_with_leg_pull' => 134,
3889             },
3890              
3891             'plyo_exercise_name' => +{
3892             '_base_type' => FIT_UINT16,
3893             'alternating_jump_lunge' => 0,
3894             'weighted_alternating_jump_lunge' => 1,
3895             'barbell_jump_squat' => 2,
3896             'body_weight_jump_squat' => 3,
3897             'weighted_jump_squat' => 4,
3898             'cross_knee_strike' => 5,
3899             'weighted_cross_knee_strike' => 6,
3900             'depth_jump' => 7,
3901             'weighted_depth_jump' => 8,
3902             'dumbbell_jump_squat' => 9,
3903             'dumbbell_split_jump' => 10,
3904             'front_knee_strike' => 11,
3905             'weighted_front_knee_strike' => 12,
3906             'high_box_jump' => 13,
3907             'weighted_high_box_jump' => 14,
3908             'isometric_explosive_body_weight_jump_squat' => 15,
3909             'weighted_isometric_explosive_jump_squat' => 16,
3910             'lateral_leap_and_hop' => 17,
3911             'weighted_lateral_leap_and_hop' => 18,
3912             'lateral_plyo_squats' => 19,
3913             'weighted_lateral_plyo_squats' => 20,
3914             'lateral_slide' => 21,
3915             'weighted_lateral_slide' => 22,
3916             'medicine_ball_overhead_throws' => 23,
3917             'medicine_ball_side_throw' => 24,
3918             'medicine_ball_slam' => 25,
3919             'side_to_side_medicine_ball_throws' => 26,
3920             'side_to_side_shuffle_jump' => 27,
3921             'weighted_side_to_side_shuffle_jump' => 28,
3922             'squat_jump_onto_box' => 29,
3923             'weighted_squat_jump_onto_box' => 30,
3924             'squat_jumps_in_and_out' => 31,
3925             'weighted_squat_jumps_in_and_out' => 32,
3926             },
3927              
3928             'pull_up_exercise_name' => +{
3929             '_base_type' => FIT_UINT16,
3930             'banded_pull_ups' => 0,
3931             '30_degree_lat_pulldown' => 1,
3932             'band_assisted_chin_up' => 2,
3933             'close_grip_chin_up' => 3,
3934             'weighted_close_grip_chin_up' => 4,
3935             'close_grip_lat_pulldown' => 5,
3936             'crossover_chin_up' => 6,
3937             'weighted_crossover_chin_up' => 7,
3938             'ez_bar_pullover' => 8,
3939             'hanging_hurdle' => 9,
3940             'weighted_hanging_hurdle' => 10,
3941             'kneeling_lat_pulldown' => 11,
3942             'kneeling_underhand_grip_lat_pulldown' => 12,
3943             'lat_pulldown' => 13,
3944             'mixed_grip_chin_up' => 14,
3945             'weighted_mixed_grip_chin_up' => 15,
3946             'mixed_grip_pull_up' => 16,
3947             'weighted_mixed_grip_pull_up' => 17,
3948             'reverse_grip_pulldown' => 18,
3949             'standing_cable_pullover' => 19,
3950             'straight_arm_pulldown' => 20,
3951             'swiss_ball_ez_bar_pullover' => 21,
3952             'towel_pull_up' => 22,
3953             'weighted_towel_pull_up' => 23,
3954             'weighted_pull_up' => 24,
3955             'wide_grip_lat_pulldown' => 25,
3956             'wide_grip_pull_up' => 26,
3957             'weighted_wide_grip_pull_up' => 27,
3958             'burpee_pull_up' => 28,
3959             'weighted_burpee_pull_up' => 29,
3960             'jumping_pull_ups' => 30,
3961             'weighted_jumping_pull_ups' => 31,
3962             'kipping_pull_up' => 32,
3963             'weighted_kipping_pull_up' => 33,
3964             'l_pull_up' => 34,
3965             'weighted_l_pull_up' => 35,
3966             'suspended_chin_up' => 36,
3967             'weighted_suspended_chin_up' => 37,
3968             'pull_up' => 38,
3969             },
3970              
3971             'push_up_exercise_name' => +{
3972             '_base_type' => FIT_UINT16,
3973             'chest_press_with_band' => 0,
3974             'alternating_staggered_push_up' => 1,
3975             'weighted_alternating_staggered_push_up' => 2,
3976             'alternating_hands_medicine_ball_push_up' => 3,
3977             'weighted_alternating_hands_medicine_ball_push_up' => 4,
3978             'bosu_ball_push_up' => 5,
3979             'weighted_bosu_ball_push_up' => 6,
3980             'clapping_push_up' => 7,
3981             'weighted_clapping_push_up' => 8,
3982             'close_grip_medicine_ball_push_up' => 9,
3983             'weighted_close_grip_medicine_ball_push_up' => 10,
3984             'close_hands_push_up' => 11,
3985             'weighted_close_hands_push_up' => 12,
3986             'decline_push_up' => 13,
3987             'weighted_decline_push_up' => 14,
3988             'diamond_push_up' => 15,
3989             'weighted_diamond_push_up' => 16,
3990             'explosive_crossover_push_up' => 17,
3991             'weighted_explosive_crossover_push_up' => 18,
3992             'explosive_push_up' => 19,
3993             'weighted_explosive_push_up' => 20,
3994             'feet_elevated_side_to_side_push_up' => 21,
3995             'weighted_feet_elevated_side_to_side_push_up' => 22,
3996             'hand_release_push_up' => 23,
3997             'weighted_hand_release_push_up' => 24,
3998             'handstand_push_up' => 25,
3999             'weighted_handstand_push_up' => 26,
4000             'incline_push_up' => 27,
4001             'weighted_incline_push_up' => 28,
4002             'isometric_explosive_push_up' => 29,
4003             'weighted_isometric_explosive_push_up' => 30,
4004             'judo_push_up' => 31,
4005             'weighted_judo_push_up' => 32,
4006             'kneeling_push_up' => 33,
4007             'weighted_kneeling_push_up' => 34,
4008             'medicine_ball_chest_pass' => 35,
4009             'medicine_ball_push_up' => 36,
4010             'weighted_medicine_ball_push_up' => 37,
4011             'one_arm_push_up' => 38,
4012             'weighted_one_arm_push_up' => 39,
4013             'weighted_push_up' => 40,
4014             'push_up_and_row' => 41,
4015             'weighted_push_up_and_row' => 42,
4016             'push_up_plus' => 43,
4017             'weighted_push_up_plus' => 44,
4018             'push_up_with_feet_on_swiss_ball' => 45,
4019             'weighted_push_up_with_feet_on_swiss_ball' => 46,
4020             'push_up_with_one_hand_on_medicine_ball' => 47,
4021             'weighted_push_up_with_one_hand_on_medicine_ball' => 48,
4022             'shoulder_push_up' => 49,
4023             'weighted_shoulder_push_up' => 50,
4024             'single_arm_medicine_ball_push_up' => 51,
4025             'weighted_single_arm_medicine_ball_push_up' => 52,
4026             'spiderman_push_up' => 53,
4027             'weighted_spiderman_push_up' => 54,
4028             'stacked_feet_push_up' => 55,
4029             'weighted_stacked_feet_push_up' => 56,
4030             'staggered_hands_push_up' => 57,
4031             'weighted_staggered_hands_push_up' => 58,
4032             'suspended_push_up' => 59,
4033             'weighted_suspended_push_up' => 60,
4034             'swiss_ball_push_up' => 61,
4035             'weighted_swiss_ball_push_up' => 62,
4036             'swiss_ball_push_up_plus' => 63,
4037             'weighted_swiss_ball_push_up_plus' => 64,
4038             't_push_up' => 65,
4039             'weighted_t_push_up' => 66,
4040             'triple_stop_push_up' => 67,
4041             'weighted_triple_stop_push_up' => 68,
4042             'wide_hands_push_up' => 69,
4043             'weighted_wide_hands_push_up' => 70,
4044             'parallette_handstand_push_up' => 71,
4045             'weighted_parallette_handstand_push_up' => 72,
4046             'ring_handstand_push_up' => 73,
4047             'weighted_ring_handstand_push_up' => 74,
4048             'ring_push_up' => 75,
4049             'weighted_ring_push_up' => 76,
4050             'push_up' => 77,
4051             'pilates_pushup' => 78,
4052             },
4053              
4054             'row_exercise_name' => +{
4055             '_base_type' => FIT_UINT16,
4056             'barbell_straight_leg_deadlift_to_row' => 0,
4057             'cable_row_standing' => 1,
4058             'dumbbell_row' => 2,
4059             'elevated_feet_inverted_row' => 3,
4060             'weighted_elevated_feet_inverted_row' => 4,
4061             'face_pull' => 5,
4062             'face_pull_with_external_rotation' => 6,
4063             'inverted_row_with_feet_on_swiss_ball' => 7,
4064             'weighted_inverted_row_with_feet_on_swiss_ball' => 8,
4065             'kettlebell_row' => 9,
4066             'modified_inverted_row' => 10,
4067             'weighted_modified_inverted_row' => 11,
4068             'neutral_grip_alternating_dumbbell_row' => 12,
4069             'one_arm_bent_over_row' => 13,
4070             'one_legged_dumbbell_row' => 14,
4071             'renegade_row' => 15,
4072             'reverse_grip_barbell_row' => 16,
4073             'rope_handle_cable_row' => 17,
4074             'seated_cable_row' => 18,
4075             'seated_dumbbell_row' => 19,
4076             'single_arm_cable_row' => 20,
4077             'single_arm_cable_row_and_rotation' => 21,
4078             'single_arm_inverted_row' => 22,
4079             'weighted_single_arm_inverted_row' => 23,
4080             'single_arm_neutral_grip_dumbbell_row' => 24,
4081             'single_arm_neutral_grip_dumbbell_row_and_rotation' => 25,
4082             'suspended_inverted_row' => 26,
4083             'weighted_suspended_inverted_row' => 27,
4084             't_bar_row' => 28,
4085             'towel_grip_inverted_row' => 29,
4086             'weighted_towel_grip_inverted_row' => 30,
4087             'underhand_grip_cable_row' => 31,
4088             'v_grip_cable_row' => 32,
4089             'wide_grip_seated_cable_row' => 33,
4090             },
4091              
4092             'shoulder_press_exercise_name' => +{
4093             '_base_type' => FIT_UINT16,
4094             'alternating_dumbbell_shoulder_press' => 0,
4095             'arnold_press' => 1,
4096             'barbell_front_squat_to_push_press' => 2,
4097             'barbell_push_press' => 3,
4098             'barbell_shoulder_press' => 4,
4099             'dead_curl_press' => 5,
4100             'dumbbell_alternating_shoulder_press_and_twist' => 6,
4101             'dumbbell_hammer_curl_to_lunge_to_press' => 7,
4102             'dumbbell_push_press' => 8,
4103             'floor_inverted_shoulder_press' => 9,
4104             'weighted_floor_inverted_shoulder_press' => 10,
4105             'inverted_shoulder_press' => 11,
4106             'weighted_inverted_shoulder_press' => 12,
4107             'one_arm_push_press' => 13,
4108             'overhead_barbell_press' => 14,
4109             'overhead_dumbbell_press' => 15,
4110             'seated_barbell_shoulder_press' => 16,
4111             'seated_dumbbell_shoulder_press' => 17,
4112             'single_arm_dumbbell_shoulder_press' => 18,
4113             'single_arm_step_up_and_press' => 19,
4114             'smith_machine_overhead_press' => 20,
4115             'split_stance_hammer_curl_to_press' => 21,
4116             'swiss_ball_dumbbell_shoulder_press' => 22,
4117             'weight_plate_front_raise' => 23,
4118             },
4119              
4120             'shoulder_stability_exercise_name' => +{
4121             '_base_type' => FIT_UINT16,
4122             '90_degree_cable_external_rotation' => 0,
4123             'band_external_rotation' => 1,
4124             'band_internal_rotation' => 2,
4125             'bent_arm_lateral_raise_and_external_rotation' => 3,
4126             'cable_external_rotation' => 4,
4127             'dumbbell_face_pull_with_external_rotation' => 5,
4128             'floor_i_raise' => 6,
4129             'weighted_floor_i_raise' => 7,
4130             'floor_t_raise' => 8,
4131             'weighted_floor_t_raise' => 9,
4132             'floor_y_raise' => 10,
4133             'weighted_floor_y_raise' => 11,
4134             'incline_i_raise' => 12,
4135             'weighted_incline_i_raise' => 13,
4136             'incline_l_raise' => 14,
4137             'weighted_incline_l_raise' => 15,
4138             'incline_t_raise' => 16,
4139             'weighted_incline_t_raise' => 17,
4140             'incline_w_raise' => 18,
4141             'weighted_incline_w_raise' => 19,
4142             'incline_y_raise' => 20,
4143             'weighted_incline_y_raise' => 21,
4144             'lying_external_rotation' => 22,
4145             'seated_dumbbell_external_rotation' => 23,
4146             'standing_l_raise' => 24,
4147             'swiss_ball_i_raise' => 25,
4148             'weighted_swiss_ball_i_raise' => 26,
4149             'swiss_ball_t_raise' => 27,
4150             'weighted_swiss_ball_t_raise' => 28,
4151             'swiss_ball_w_raise' => 29,
4152             'weighted_swiss_ball_w_raise' => 30,
4153             'swiss_ball_y_raise' => 31,
4154             'weighted_swiss_ball_y_raise' => 32,
4155             },
4156              
4157             'shrug_exercise_name' => +{
4158             '_base_type' => FIT_UINT16,
4159             'barbell_jump_shrug' => 0,
4160             'barbell_shrug' => 1,
4161             'barbell_upright_row' => 2,
4162             'behind_the_back_smith_machine_shrug' => 3,
4163             'dumbbell_jump_shrug' => 4,
4164             'dumbbell_shrug' => 5,
4165             'dumbbell_upright_row' => 6,
4166             'incline_dumbbell_shrug' => 7,
4167             'overhead_barbell_shrug' => 8,
4168             'overhead_dumbbell_shrug' => 9,
4169             'scaption_and_shrug' => 10,
4170             'scapular_retraction' => 11,
4171             'serratus_chair_shrug' => 12,
4172             'weighted_serratus_chair_shrug' => 13,
4173             'serratus_shrug' => 14,
4174             'weighted_serratus_shrug' => 15,
4175             'wide_grip_jump_shrug' => 16,
4176             },
4177              
4178             'sit_up_exercise_name' => +{
4179             '_base_type' => FIT_UINT16,
4180             'alternating_sit_up' => 0,
4181             'weighted_alternating_sit_up' => 1,
4182             'bent_knee_v_up' => 2,
4183             'weighted_bent_knee_v_up' => 3,
4184             'butterfly_sit_up' => 4,
4185             'weighted_butterfly_situp' => 5,
4186             'cross_punch_roll_up' => 6,
4187             'weighted_cross_punch_roll_up' => 7,
4188             'crossed_arms_sit_up' => 8,
4189             'weighted_crossed_arms_sit_up' => 9,
4190             'get_up_sit_up' => 10,
4191             'weighted_get_up_sit_up' => 11,
4192             'hovering_sit_up' => 12,
4193             'weighted_hovering_sit_up' => 13,
4194             'kettlebell_sit_up' => 14,
4195             'medicine_ball_alternating_v_up' => 15,
4196             'medicine_ball_sit_up' => 16,
4197             'medicine_ball_v_up' => 17,
4198             'modified_sit_up' => 18,
4199             'negative_sit_up' => 19,
4200             'one_arm_full_sit_up' => 20,
4201             'reclining_circle' => 21,
4202             'weighted_reclining_circle' => 22,
4203             'reverse_curl_up' => 23,
4204             'weighted_reverse_curl_up' => 24,
4205             'single_leg_swiss_ball_jackknife' => 25,
4206             'weighted_single_leg_swiss_ball_jackknife' => 26,
4207             'the_teaser' => 27,
4208             'the_teaser_weighted' => 28,
4209             'three_part_roll_down' => 29,
4210             'weighted_three_part_roll_down' => 30,
4211             'v_up' => 31,
4212             'weighted_v_up' => 32,
4213             'weighted_russian_twist_on_swiss_ball' => 33,
4214             'weighted_sit_up' => 34,
4215             'x_abs' => 35,
4216             'weighted_x_abs' => 36,
4217             'sit_up' => 37,
4218             },
4219              
4220             'squat_exercise_name' => +{
4221             '_base_type' => FIT_UINT16,
4222             'leg_press' => 0,
4223             'back_squat_with_body_bar' => 1,
4224             'back_squats' => 2,
4225             'weighted_back_squats' => 3,
4226             'balancing_squat' => 4,
4227             'weighted_balancing_squat' => 5,
4228             'barbell_back_squat' => 6,
4229             'barbell_box_squat' => 7,
4230             'barbell_front_squat' => 8,
4231             'barbell_hack_squat' => 9,
4232             'barbell_hang_squat_snatch' => 10,
4233             'barbell_lateral_step_up' => 11,
4234             'barbell_quarter_squat' => 12,
4235             'barbell_siff_squat' => 13,
4236             'barbell_squat_snatch' => 14,
4237             'barbell_squat_with_heels_raised' => 15,
4238             'barbell_stepover' => 16,
4239             'barbell_step_up' => 17,
4240             'bench_squat_with_rotational_chop' => 18,
4241             'weighted_bench_squat_with_rotational_chop' => 19,
4242             'body_weight_wall_squat' => 20,
4243             'weighted_wall_squat' => 21,
4244             'box_step_squat' => 22,
4245             'weighted_box_step_squat' => 23,
4246             'braced_squat' => 24,
4247             'crossed_arm_barbell_front_squat' => 25,
4248             'crossover_dumbbell_step_up' => 26,
4249             'dumbbell_front_squat' => 27,
4250             'dumbbell_split_squat' => 28,
4251             'dumbbell_squat' => 29,
4252             'dumbbell_squat_clean' => 30,
4253             'dumbbell_stepover' => 31,
4254             'dumbbell_step_up' => 32,
4255             'elevated_single_leg_squat' => 33,
4256             'weighted_elevated_single_leg_squat' => 34,
4257             'figure_four_squats' => 35,
4258             'weighted_figure_four_squats' => 36,
4259             'goblet_squat' => 37,
4260             'kettlebell_squat' => 38,
4261             'kettlebell_swing_overhead' => 39,
4262             'kettlebell_swing_with_flip_to_squat' => 40,
4263             'lateral_dumbbell_step_up' => 41,
4264             'one_legged_squat' => 42,
4265             'overhead_dumbbell_squat' => 43,
4266             'overhead_squat' => 44,
4267             'partial_single_leg_squat' => 45,
4268             'weighted_partial_single_leg_squat' => 46,
4269             'pistol_squat' => 47,
4270             'weighted_pistol_squat' => 48,
4271             'plie_slides' => 49,
4272             'weighted_plie_slides' => 50,
4273             'plie_squat' => 51,
4274             'weighted_plie_squat' => 52,
4275             'prisoner_squat' => 53,
4276             'weighted_prisoner_squat' => 54,
4277             'single_leg_bench_get_up' => 55,
4278             'weighted_single_leg_bench_get_up' => 56,
4279             'single_leg_bench_squat' => 57,
4280             'weighted_single_leg_bench_squat' => 58,
4281             'single_leg_squat_on_swiss_ball' => 59,
4282             'weighted_single_leg_squat_on_swiss_ball' => 60,
4283             'squat' => 61,
4284             'weighted_squat' => 62,
4285             'squats_with_band' => 63,
4286             'staggered_squat' => 64,
4287             'weighted_staggered_squat' => 65,
4288             'step_up' => 66,
4289             'weighted_step_up' => 67,
4290             'suitcase_squats' => 68,
4291             'sumo_squat' => 69,
4292             'sumo_squat_slide_in' => 70,
4293             'weighted_sumo_squat_slide_in' => 71,
4294             'sumo_squat_to_high_pull' => 72,
4295             'sumo_squat_to_stand' => 73,
4296             'weighted_sumo_squat_to_stand' => 74,
4297             'sumo_squat_with_rotation' => 75,
4298             'weighted_sumo_squat_with_rotation' => 76,
4299             'swiss_ball_body_weight_wall_squat' => 77,
4300             'weighted_swiss_ball_wall_squat' => 78,
4301             'thrusters' => 79,
4302             'uneven_squat' => 80,
4303             'weighted_uneven_squat' => 81,
4304             'waist_slimming_squat' => 82,
4305             'wall_ball' => 83,
4306             'wide_stance_barbell_squat' => 84,
4307             'wide_stance_goblet_squat' => 85,
4308             'zercher_squat' => 86,
4309             'kbs_overhead' => 87, # Deprecated do not use
4310             'squat_and_side_kick' => 88,
4311             'squat_jumps_in_n_out' => 89,
4312             'pilates_plie_squats_parallel_turned_out_flat_and_heels' => 90,
4313             'releve_straight_leg_and_knee_bent_with_one_leg_variation' => 91,
4314             },
4315              
4316             'total_body_exercise_name' => +{
4317             '_base_type' => FIT_UINT16,
4318             'burpee' => 0,
4319             'weighted_burpee' => 1,
4320             'burpee_box_jump' => 2,
4321             'weighted_burpee_box_jump' => 3,
4322             'high_pull_burpee' => 4,
4323             'man_makers' => 5,
4324             'one_arm_burpee' => 6,
4325             'squat_thrusts' => 7,
4326             'weighted_squat_thrusts' => 8,
4327             'squat_plank_push_up' => 9,
4328             'weighted_squat_plank_push_up' => 10,
4329             'standing_t_rotation_balance' => 11,
4330             'weighted_standing_t_rotation_balance' => 12,
4331             },
4332              
4333             'triceps_extension_exercise_name' => +{
4334             '_base_type' => FIT_UINT16,
4335             'bench_dip' => 0,
4336             'weighted_bench_dip' => 1,
4337             'body_weight_dip' => 2,
4338             'cable_kickback' => 3,
4339             'cable_lying_triceps_extension' => 4,
4340             'cable_overhead_triceps_extension' => 5,
4341             'dumbbell_kickback' => 6,
4342             'dumbbell_lying_triceps_extension' => 7,
4343             'ez_bar_overhead_triceps_extension' => 8,
4344             'incline_dip' => 9,
4345             'weighted_incline_dip' => 10,
4346             'incline_ez_bar_lying_triceps_extension' => 11,
4347             'lying_dumbbell_pullover_to_extension' => 12,
4348             'lying_ez_bar_triceps_extension' => 13,
4349             'lying_triceps_extension_to_close_grip_bench_press' => 14,
4350             'overhead_dumbbell_triceps_extension' => 15,
4351             'reclining_triceps_press' => 16,
4352             'reverse_grip_pressdown' => 17,
4353             'reverse_grip_triceps_pressdown' => 18,
4354             'rope_pressdown' => 19,
4355             'seated_barbell_overhead_triceps_extension' => 20,
4356             'seated_dumbbell_overhead_triceps_extension' => 21,
4357             'seated_ez_bar_overhead_triceps_extension' => 22,
4358             'seated_single_arm_overhead_dumbbell_extension' => 23,
4359             'single_arm_dumbbell_overhead_triceps_extension' => 24,
4360             'single_dumbbell_seated_overhead_triceps_extension' => 25,
4361             'single_leg_bench_dip_and_kick' => 26,
4362             'weighted_single_leg_bench_dip_and_kick' => 27,
4363             'single_leg_dip' => 28,
4364             'weighted_single_leg_dip' => 29,
4365             'static_lying_triceps_extension' => 30,
4366             'suspended_dip' => 31,
4367             'weighted_suspended_dip' => 32,
4368             'swiss_ball_dumbbell_lying_triceps_extension' => 33,
4369             'swiss_ball_ez_bar_lying_triceps_extension' => 34,
4370             'swiss_ball_ez_bar_overhead_triceps_extension' => 35,
4371             'tabletop_dip' => 36,
4372             'weighted_tabletop_dip' => 37,
4373             'triceps_extension_on_floor' => 38,
4374             'triceps_pressdown' => 39,
4375             'weighted_dip' => 40,
4376             },
4377              
4378             'warm_up_exercise_name' => +{
4379             '_base_type' => FIT_UINT16,
4380             'quadruped_rocking' => 0,
4381             'neck_tilts' => 1,
4382             'ankle_circles' => 2,
4383             'ankle_dorsiflexion_with_band' => 3,
4384             'ankle_internal_rotation' => 4,
4385             'arm_circles' => 5,
4386             'bent_over_reach_to_sky' => 6,
4387             'cat_camel' => 7,
4388             'elbow_to_foot_lunge' => 8,
4389             'forward_and_backward_leg_swings' => 9,
4390             'groiners' => 10,
4391             'inverted_hamstring_stretch' => 11,
4392             'lateral_duck_under' => 12,
4393             'neck_rotations' => 13,
4394             'opposite_arm_and_leg_balance' => 14,
4395             'reach_roll_and_lift' => 15,
4396             'scorpion' => 16, # Deprecated do not use
4397             'shoulder_circles' => 17,
4398             'side_to_side_leg_swings' => 18,
4399             'sleeper_stretch' => 19,
4400             'slide_out' => 20,
4401             'swiss_ball_hip_crossover' => 21,
4402             'swiss_ball_reach_roll_and_lift' => 22,
4403             'swiss_ball_windshield_wipers' => 23,
4404             'thoracic_rotation' => 24,
4405             'walking_high_kicks' => 25,
4406             'walking_high_knees' => 26,
4407             'walking_knee_hugs' => 27,
4408             'walking_leg_cradles' => 28,
4409             'walkout' => 29,
4410             'walkout_from_push_up_position' => 30,
4411             },
4412              
4413             'run_exercise_name' => +{
4414             '_base_type' => FIT_UINT16,
4415             'run' => 0,
4416             'walk' => 1,
4417             'jog' => 2,
4418             'sprint' => 3,
4419             },
4420              
4421             'water_type' => +{
4422             '_base_type' => FIT_ENUM,
4423             'fresh' => 0,
4424             'salt' => 1,
4425             'en13319' => 2,
4426             'custom' => 3,
4427             },
4428              
4429             'tissue_model_type' => +{
4430             '_base_type' => FIT_ENUM,
4431             'zhl_16c' => 0, # Buhlmann's decompression algorithm, version C
4432             },
4433              
4434             'dive_gas_status' => +{
4435             '_base_type' => FIT_ENUM,
4436             'disabled' => 0,
4437             'enabled' => 1,
4438             'backup_only' => 2,
4439             },
4440              
4441             'dive_alert' => +{
4442             '_base_type' => FIT_ENUM,
4443             'ndl_reached' => 0,
4444             'gas_switch_prompted' => 1,
4445             'near_surface' => 2,
4446             'approaching_ndl' => 3,
4447             'po2_warn' => 4,
4448             'po2_crit_high' => 5,
4449             'po2_crit_low' => 6,
4450             'time_alert' => 7,
4451             'depth_alert' => 8,
4452             'deco_ceiling_broken' => 9,
4453             'deco_complete' => 10,
4454             'safety_stop_broken' => 11,
4455             'safety_stop_complete' => 12,
4456             'cns_warning' => 13,
4457             'cns_critical' => 14,
4458             'otu_warning' => 15,
4459             'otu_critical' => 16,
4460             'ascent_critical' => 17,
4461             'alert_dismissed_by_key' => 18,
4462             'alert_dismissed_by_timeout' => 19,
4463             'battery_low' => 20,
4464             'battery_critical' => 21,
4465             'safety_stop_started' => 22,
4466             'approaching_first_deco_stop' => 23,
4467             'setpoint_switch_auto_low' => 24,
4468             'setpoint_switch_auto_high' => 25,
4469             'setpoint_switch_manual_low' => 26,
4470             'setpoint_switch_manual_high' => 27,
4471             'auto_setpoint_switch_ignored' => 28,
4472             'switched_to_open_circuit' => 29,
4473             'switched_to_closed_circuit' => 30,
4474             'tank_battery_low' => 32,
4475             'po2_ccr_dil_low' => 33, # ccr diluent has low po2
4476             'deco_stop_cleared' => 34, # a deco stop has been cleared
4477             'apnea_neutral_buoyancy' => 35, # Target Depth Apnea Alarm triggered
4478             'apnea_target_depth' => 36, # Neutral Buoyance Apnea Alarm triggered
4479             'apnea_surface' => 37, # Surface Apnea Alarm triggered
4480             'apnea_high_speed' => 38, # High Speed Apnea Alarm triggered
4481             'apnea_low_speed' => 39, # Low Speed Apnea Alarm triggered
4482             },
4483              
4484             'dive_alarm_type' => +{
4485             '_base_type' => FIT_ENUM,
4486             'depth' => 0,
4487             'time' => 1,
4488             'speed' => 2, # Alarm when a certain ascent or descent rate is exceeded
4489             },
4490              
4491             'dive_backlight_mode' => +{
4492             '_base_type' => FIT_ENUM,
4493             'at_depth' => 0,
4494             'always_on' => 1,
4495             },
4496              
4497             'sleep_level' => +{
4498             '_base_type' => FIT_ENUM,
4499             'unmeasurable' => 0,
4500             'awake' => 1,
4501             'light' => 2,
4502             'deep' => 3,
4503             'rem' => 4,
4504             },
4505              
4506             'spo2_measurement_type' => +{
4507             '_base_type' => FIT_ENUM,
4508             'off_wrist' => 0,
4509             'spot_check' => 1,
4510             'continuous_check' => 2,
4511             'periodic' => 3,
4512             },
4513              
4514             'ccr_setpoint_switch_mode' => +{
4515             '_base_type' => FIT_ENUM,
4516             'manual' => 0, # User switches setpoints manually
4517             'automatic' => 1, # Switch automatically based on depth
4518             },
4519              
4520             'dive_gas_mode' => +{
4521             '_base_type' => FIT_ENUM,
4522             'open_circuit' => 0,
4523             'closed_circuit_diluent' => 1,
4524             },
4525              
4526             'projectile_type' => +{
4527             '_base_type' => FIT_ENUM,
4528             'arrow' => 0, # Arrow projectile type
4529             'rifle_cartridge' => 1, # Rifle cartridge projectile type
4530             'pistol_cartridge' => 2, # Pistol cartridge projectile type
4531             'shotshell' => 3, # Shotshell projectile type
4532             'air_rifle_pellet' => 4, # Air rifle pellet projectile type
4533             'other' => 5, # Other projectile type
4534             },
4535              
4536             'favero_product' => +{
4537             '_base_type' => FIT_UINT16,
4538             'assioma_uno' => 10,
4539             'assioma_duo' => 12,
4540             },
4541              
4542             'split_type' => +{
4543             '_base_type' => FIT_ENUM,
4544             'ascent_split' => 1,
4545             'descent_split' => 2,
4546             'interval_active' => 3,
4547             'interval_rest' => 4,
4548             'interval_warmup' => 5,
4549             'interval_cooldown' => 6,
4550             'interval_recovery' => 7,
4551             'interval_other' => 8,
4552             'climb_active' => 9,
4553             'climb_rest' => 10,
4554             'surf_active' => 11,
4555             'run_active' => 12,
4556             'run_rest' => 13,
4557             'workout_round' => 14,
4558             'rwd_run' => 17, # run/walk detection running
4559             'rwd_walk' => 18, # run/walk detection walking
4560             'windsurf_active' => 21,
4561             'rwd_stand' => 22, # run/walk detection standing
4562             'transition' => 23, # Marks the time going from ascent_split to descent_split/used in backcountry ski
4563             'ski_lift_split' => 28,
4564             'ski_run_split' => 29,
4565             },
4566              
4567             'climb_pro_event' => +{
4568             '_base_type' => FIT_ENUM,
4569             'approach' => 0,
4570             'start' => 1,
4571             'complete' => 2,
4572             },
4573              
4574             'gas_consumption_rate_type' => +{
4575             '_base_type' => FIT_ENUM,
4576             'pressure_sac' => 0, # Pressure-based Surface Air Consumption
4577             'volume_sac' => 1, # Volumetric Surface Air Consumption
4578             'rmv' => 2, # Respiratory Minute Volume
4579             },
4580              
4581             'tap_sensitivity' => +{
4582             '_base_type' => FIT_ENUM,
4583             'high' => 0,
4584             'medium' => 1,
4585             'low' => 2,
4586             },
4587              
4588             'radar_threat_level_type' => +{
4589             '_base_type' => FIT_ENUM,
4590             'threat_unknown' => 0,
4591             'threat_none' => 1,
4592             'threat_approaching' => 2,
4593             'threat_approaching_fast' => 3,
4594             },
4595              
4596             'max_met_speed_source' => +{
4597             '_base_type' => FIT_ENUM,
4598             'onboard_gps' => 0,
4599             'connected_gps' => 1,
4600             'cadence' => 2,
4601             },
4602              
4603             'max_met_heart_rate_source' => +{
4604             '_base_type' => FIT_ENUM,
4605             'whr' => 0, # Wrist Heart Rate Monitor
4606             'hrm' => 1, # Chest Strap Heart Rate Monitor
4607             },
4608              
4609             'hrv_status' => +{
4610             '_base_type' => FIT_ENUM,
4611             'none' => 0,
4612             'poor' => 1,
4613             'low' => 2,
4614             'unbalanced' => 3,
4615             'balanced' => 4,
4616             },
4617              
4618             'no_fly_time_mode' => +{
4619             '_base_type' => FIT_ENUM,
4620             'standard' => 0, # Standard Diver Alert Network no-fly guidance
4621             'flat_24_hours' => 1, # Flat 24 hour no-fly guidance
4622             },
4623              
4624             );
4625              
4626             for my $typenam (keys %named_type) { # if a type was _moved_to, copy the new href to $named_type{$typenam}
4627             my $typedesc = $named_type{$typenam};
4628             if (defined $typedesc->{_moved_to} and $typedesc->{_moved_to} ne '') {
4629             if ($typenam ne 'device_type') { $DB::single=1 } # want to know if it applies to others
4630             my $to = $named_type{$typedesc->{_moved_to}};
4631             $named_type{$typenam} = {%$to} if ref $to eq 'HASH'
4632             }
4633             }
4634             while (my ($typenam, $typedesc) = each %named_type) {
4635             # copy and flip key/value pairs of all hrefs in %named_type (except _base_type, _mask, …)
4636             for my $name (grep {!/^_/} keys %$typedesc) {
4637             $typedesc->{$typedesc->{$name}} = $name
4638             }
4639             }
4640              
4641             my %msgtype_by_name = (
4642             'file_id' => +{ # begins === Common messages === section
4643             0 => +{'name' => 'type', 'type_name' => 'file'},
4644             1 => +{'name' => 'manufacturer', 'type_name' => 'manufacturer'},
4645              
4646             2 => +{
4647             'name' => 'product',
4648              
4649             'switch' => +{
4650             '_by' => 'manufacturer',
4651             'garmin' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4652             'dynastream' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4653             'dynastream_oem' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4654             'favero_electronics' => +{'name' => 'favero_product', 'type_name' => 'favero_product'},
4655             'tacx' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4656             },
4657             },
4658              
4659             3 => +{'name' => 'serial_number'},
4660             4 => +{'name' => 'time_created', 'type_name' => 'date_time'},
4661             5 => +{'name' => 'number'},
4662             7 => +{'name' => 'unknown7'}, # unknown UINT32
4663             8 => +{'name' => 'product_name'},
4664             },
4665              
4666             'file_creator' => +{
4667             0 => +{'name' => 'software_version'},
4668             1 => +{'name' => 'hardware_version'},
4669             },
4670              
4671             'timestamp_correlation' => +{
4672             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
4673             0 => +{'name' => 'fractional_timestamp', 'scale' => 32768, 'unit' => 's'},
4674             1 => +{'name' => 'system_timestamp', 'type_name' => 'date_time'},
4675             2 => +{'name' => 'fractional_system_timestamp', 'scale' => 32768, 'unit' => 's'},
4676             3 => +{'name' => 'local_timestamp', 'type_name' => 'local_date_time'},
4677             4 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
4678             5 => +{'name' => 'system_timestamp_ms', 'unit' => 'ms'},
4679             },
4680              
4681             'software' => +{ # begins === Device file messages === section
4682             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4683             3 => +{'name' => 'version', 'scale' => 100},
4684             5 => +{'name' => 'part_number'},
4685             },
4686              
4687             'slave_device' => +{
4688             0 => +{'name' => 'manufacturer', 'type_name' => 'manufacturer'},
4689              
4690             1 => +{
4691             'name' => 'product',
4692              
4693             'switch' => +{
4694             '_by' => 'manufacturer',
4695             'garmin' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4696             'dynastream' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4697             'dynastream_oem' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4698             'favero_electronics' => +{'name' => 'favero_product', 'type_name' => 'favero_product'},
4699             'tacx' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4700             },
4701             },
4702             },
4703              
4704             'capabilities' => +{
4705             0 => +{'name' => 'languages'},
4706             1 => +{'name' => 'sports', 'type_name' => 'sport_bits_0'},
4707             21 => +{'name' => 'workouts_supported', 'type_name' => 'workout_capabilities'},
4708             22 => +{'name' => 'unknown22'}, # unknown ENUM
4709             23 => +{'name' => 'connectivity_supported', 'type_name' => 'connectivity_capabilities'},
4710             24 => +{'name' => 'unknown24'}, # unknown ENUM
4711             25 => +{'name' => 'unknown25'}, # unknown UINT32Z
4712             },
4713              
4714             'file_capabilities' => +{
4715             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4716             0 => +{'name' => 'type', 'type_name' => 'file'},
4717             1 => +{'name' => 'flags', 'type_name' => 'file_flags'},
4718             2 => +{'name' => 'directory'},
4719             3 => +{'name' => 'max_count'},
4720             4 => +{'name' => 'max_size', 'unit' => 'bytes'},
4721             },
4722              
4723             'mesg_capabilities' => +{
4724             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4725             0 => +{'name' => 'file', 'type_name' => 'file'},
4726             1 => +{'name' => 'mesg_num', 'type_name' => 'mesg_num'},
4727             2 => +{'name' => 'count_type', 'type_name' => 'mesg_count'},
4728              
4729             3 => +{
4730             'name' => 'count',
4731              
4732             'switch' => +{
4733             '_by' => 'count_type',
4734             'num_per_file' => +{'name' => 'num_per_file'},
4735             'max_per_file' => +{'name' => 'max_per_file'},
4736             'max_per_file_type' => +{'name' => 'max_per_file_type'},
4737             },
4738             },
4739             },
4740              
4741             'field_capabilities' => +{
4742             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4743             0 => +{'name' => 'file', 'type_name' => 'file'},
4744             1 => +{'name' => 'mesg_num', 'type_name' => 'mesg_num'},
4745             2 => +{'name' => 'field_num'},
4746             3 => +{'name' => 'count'},
4747             4 => +{'name' => 'bits'}, # not present? PATJOL: I don't see this one in the profile
4748             },
4749              
4750             'device_settings' => +{ # begins === Settings file messages === section
4751             0 => +{'name' => 'active_time_zone'},
4752             1 => +{'name' => 'utc_offset'},
4753             2 => +{'name' => 'time_offset', 'unit' => 's'},
4754             3 => +{'name' => 'unknown3'}, # unknown ENUM
4755             4 => +{'name' => 'time_mode', 'type_name' => 'time_mode'},
4756             5 => +{'name' => 'time_zone_offset', 'scale' => 4, 'unit' => 'hr'},
4757             10 => +{'name' => 'unknown10'}, # unknown ENUM
4758             11 => +{'name' => 'unknown11'}, # unknown ENUM
4759             12 => +{'name' => 'backlight_mode', 'type_name' => 'backlight_mode'},
4760             13 => +{'name' => 'unknown13'}, # unknown UINT8
4761             14 => +{'name' => 'unknown14'}, # unknown UINT8
4762             15 => +{'name' => 'unknown15'}, # unknown UINT8
4763             16 => +{'name' => 'unknown16'}, # unknown ENUM
4764             17 => +{'name' => 'unknown17'}, # unknown ENUM
4765             18 => +{'name' => 'unknown18'}, # unknown ENUM
4766             21 => +{'name' => 'unknown21'}, # unknown ENUM
4767             22 => +{'name' => 'unknown22'}, # unknown ENUM
4768             26 => +{'name' => 'unknown26'}, # unknown ENUM
4769             27 => +{'name' => 'unknown27'}, # unknown ENUM
4770             29 => +{'name' => 'unknown29'}, # unknown ENUM
4771             33 => +{'name' => 'unknown33'}, # unknown UNIT8
4772             36 => +{'name' => 'activity_tracker_enabled', 'type_name' => 'bool'},
4773             38 => +{'name' => 'unknown38'}, # unknown ENUM
4774             39 => +{'name' => 'clock_time', 'type_name' => 'date_time'},
4775             40 => +{'name' => 'pages_enabled'},
4776             41 => +{'name' => 'unknown41'}, # unknown ENUM
4777             46 => +{'name' => 'move_alert_enabled', 'type_name' => 'bool'},
4778             47 => +{'name' => 'date_mode', 'type_name' => 'date_mode'},
4779             48 => +{'name' => 'unknown48'}, # unknown ENUM
4780             49 => +{'name' => 'unknown49'}, # unknown UINT16
4781             52 => +{'name' => 'unknown52'}, # unknown ENUM
4782             53 => +{'name' => 'unknown53'}, # unknown ENUM
4783             54 => +{'name' => 'unknown54'}, # unknown ENUM
4784             55 => +{'name' => 'display_orientation', 'type_name' => 'display_orientation'},
4785             56 => +{'name' => 'mounting_side', 'type_name' => 'side'},
4786             57 => +{'name' => 'default_page'},
4787             58 => +{'name' => 'autosync_min_steps', 'unit' => 'steps'},
4788             59 => +{'name' => 'autosync_min_time', 'unit' => 'minutes'},
4789             75 => +{'name' => 'unknown75'}, # unknown ENUM
4790             80 => +{'name' => 'lactate_threshold_autodetect_enabled', 'type_name' => 'bool'},
4791             85 => +{'name' => 'unknown85'}, # unknown ENUM
4792             86 => +{'name' => 'ble_auto_upload_enabled', 'type_name' => 'bool'},
4793             89 => +{'name' => 'auto_sync_frequency', 'type_name' => 'auto_sync_frequency'},
4794             90 => +{'name' => 'auto_activity_detect', 'type_name' => 'auto_activity_detect'},
4795             94 => +{'name' => 'number_of_screens'},
4796             95 => +{'name' => 'smart_notification_display_orientation', 'type_name' => 'display_orientation'},
4797             97 => +{'name' => 'unknown97'}, # unknown UINT8Z
4798             98 => +{'name' => 'unknown98'}, # unknown ENUM
4799             103 => +{'name' => 'unknown103'}, # unknown ENUM
4800             134 => +{'name' => 'tap_interface', 'type_name' => 'switch'},
4801             174 => +{'name' => 'tap_sensitivity', 'type_name' => 'tap_sensitivity'},
4802             },
4803              
4804             'user_profile' => +{
4805             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4806             0 => +{'name' => 'friendly_name'}, # Used for Morning Report greeting
4807             1 => +{'name' => 'gender', 'type_name' => 'gender'},
4808             2 => +{'name' => 'age', 'unit' => 'years'},
4809             3 => +{'name' => 'height', scale => 100, 'unit' => 'm'},
4810             4 => +{'name' => 'weight', scale => 10, 'unit' => 'kg'},
4811             5 => +{'name' => 'language', 'type_name' => 'language'},
4812             6 => +{'name' => 'elev_setting', 'type_name' => 'display_measure'},
4813             7 => +{'name' => 'weight_setting', 'type_name' => 'display_measure'},
4814             8 => +{'name' => 'resting_heart_rate', 'unit' => 'bpm'},
4815             9 => +{'name' => 'default_max_running_heart_rate', 'unit' => 'bpm'},
4816             10 => +{'name' => 'default_max_biking_heart_rate', 'unit' => 'bpm'},
4817             11 => +{'name' => 'default_max_heart_rate', 'unit' => 'bpm'},
4818             12 => +{'name' => 'hr_setting', 'type_name' => 'display_heart'},
4819             13 => +{'name' => 'speed_setting', 'type_name' => 'display_measure'},
4820             14 => +{'name' => 'dist_setting', 'type_name' => 'display_measure'},
4821             16 => +{'name' => 'power_setting', 'type_name' => 'display_power'},
4822             17 => +{'name' => 'activity_class', 'type_name' => 'activity_class'},
4823             18 => +{'name' => 'position_setting', 'type_name' => 'display_position'},
4824             21 => +{'name' => 'temperature_setting', 'type_name' => 'display_measure'},
4825             22 => +{'name' => 'local_id', 'type_name' => 'user_local_id'},
4826             23 => +{'name' => 'global_id'},
4827             24 => +{'name' => 'unknown24'}, # unknown UINT8
4828             28 => +{'name' => 'wake_time', 'type_name' => 'localtime_into_day'},
4829             29 => +{'name' => 'sleep_time', 'type_name' => 'localtime_into_day'},
4830             30 => +{'name' => 'height_setting', 'type_name' => 'display_measure'},
4831             31 => +{'name' => 'user_running_step_length', scale => 1000, 'unit' => 'm'},
4832             32 => +{'name' => 'user_walking_step_length', scale => 1000, 'unit' => 'm'},
4833             33 => +{'name' => 'unknown33'}, # unknown UINT16
4834             34 => +{'name' => 'unknown34'}, # unknown UINT16
4835             35 => +{'name' => 'unknown35'}, # unknown UINT32
4836             36 => +{'name' => 'unknown36'}, # unknown UINT8
4837             38 => +{'name' => 'unknown38'}, # unknown UINT16
4838             40 => +{'name' => 'unknown40'}, # unknown FLOAT32
4839             42 => +{'name' => 'unknown42'}, # unknown UINT32
4840             47 => +{'name' => 'depth_setting', 'type_name' => 'display_measure'},
4841             49 => +{'name' => 'dive_count'},
4842             },
4843              
4844             'hrm_profile' => +{
4845             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4846             0 => +{'name' => 'enabled', 'type_name' => 'bool'},
4847             1 => +{'name' => 'hrm_ant_id'},
4848             2 => +{'name' => 'log_hrv', 'type_name' => 'bool'},
4849             3 => +{'name' => 'hrm_ant_id_trans_type'},
4850             },
4851              
4852             'sdm_profile' => +{
4853             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4854             0 => +{'name' => 'enabled', 'type_name' => 'bool'},
4855             1 => +{'name' => 'sdm_ant_id'},
4856             2 => +{'name' => 'sdm_cal_factor', 'scale' => 10, 'unit' => '%'},
4857             3 => +{'name' => 'odometer', 'scale' => 100, 'unit' => 'm'},
4858             4 => +{'name' => 'speed_source', 'type_name' => 'bool'},
4859             5 => +{'name' => 'sdm_ant_id_trans_type'},
4860             7 => +{'name' => 'odometer_rollover'},
4861             },
4862              
4863             'bike_profile' => +{
4864             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4865             0 => +{'name' => 'name'},
4866             1 => +{'name' => 'sport', 'type_name' => 'sport'},
4867             2 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
4868             3 => +{'name' => 'odometer', 'scale' => 100, 'unit' => 'm'},
4869             4 => +{'name' => 'bike_spd_ant_id'},
4870             5 => +{'name' => 'bike_cad_ant_id'},
4871             6 => +{'name' => 'bike_spdcad_ant_id'},
4872             7 => +{'name' => 'bike_power_ant_id'},
4873             8 => +{'name' => 'custom_wheelsize', 'scale' => 1000, 'unit' => 'm'},
4874             9 => +{'name' => 'auto_wheelsize', 'scale' => 1000, 'unit' => 'm'},
4875             10 => +{'name' => 'bike_weight', 'scale' => 10, 'unit' => 'kg'},
4876             11 => +{'name' => 'power_cal_factor', 'scale' => 10, 'unit' => '%'},
4877             12 => +{'name' => 'auto_wheel_cal', 'type_name' => 'bool'},
4878             13 => +{'name' => 'auto_power_zero', 'type_name' => 'bool'},
4879             14 => +{'name' => 'id'},
4880             15 => +{'name' => 'spd_enabled', 'type_name' => 'bool'},
4881             16 => +{'name' => 'cad_enabled', 'type_name' => 'bool'},
4882             17 => +{'name' => 'spdcad_enabled', 'type_name' => 'bool'},
4883             18 => +{'name' => 'power_enabled', 'type_name' => 'bool'},
4884             19 => +{'name' => 'crank_length', 'scale' => 2, 'offset' => -110, 'unit' => 'mm'},
4885             20 => +{'name' => 'enabled', 'type_name' => 'bool'},
4886             21 => +{'name' => 'bike_spd_ant_id_trans_type'},
4887             22 => +{'name' => 'bike_cad_ant_id_trans_type'},
4888             23 => +{'name' => 'bike_spdcad_ant_id_trans_type'},
4889             24 => +{'name' => 'bike_power_ant_id_trans_type'},
4890             35 => +{'name' => 'unknown35'}, # unknown UINT8 (array[3])
4891             36 => +{'name' => 'unknown36'}, # unknown ENUM
4892             37 => +{'name' => 'odometer_rollover'},
4893             38 => +{'name' => 'front_gear_num'},
4894             39 => +{'name' => 'front_gear'},
4895             40 => +{'name' => 'rear_gear_num'},
4896             41 => +{'name' => 'rear_gear'},
4897             44 => +{'name' => 'shimano_di2_enabled'},
4898             },
4899              
4900             'connectivity' => +{
4901             0 => +{'name' => 'bluetooth_enabled', 'type_name' => 'bool'},
4902             1 => +{'name' => 'bluetooth_le_enabled', 'type_name' => 'bool'},
4903             2 => +{'name' => 'ant_enabled', 'type_name' => 'bool'},
4904             3 => +{'name' => 'name'},
4905             4 => +{'name' => 'live_tracking_enabled', 'type_name' => 'bool'},
4906             5 => +{'name' => 'weather_conditions_enabled', 'type_name' => 'bool'},
4907             6 => +{'name' => 'weather_alerts_enabled', 'type_name' => 'bool'},
4908             7 => +{'name' => 'auto_activity_upload_enabled', 'type_name' => 'bool'},
4909             8 => +{'name' => 'course_download_enabled', 'type_name' => 'bool'},
4910             9 => +{'name' => 'workout_download_enabled', 'type_name' => 'bool'},
4911             10 => +{'name' => 'gps_ephemeris_download_enabled', 'type_name' => 'bool'},
4912             11 => +{'name' => 'incident_detection_enabled', 'type_name' => 'bool'},
4913             12 => +{'name' => 'grouptrack_enabled', 'type_name' => 'bool'},
4914             },
4915              
4916             'watchface_settings' => +{
4917             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4918             0 => +{'name' => 'mode', 'type_name' => 'watchface_mode'},
4919              
4920             1 => +{
4921             'name' => 'layout',
4922              
4923             'switch' => +{
4924             '_by' => 'mode',
4925             'digital' => +{'name' => 'digital_layout', 'type_name' => 'digital_watchface_layout'},
4926             'analog' => +{'name' => 'analog_layout', 'type_name' => 'analog_watchface_layout'},
4927             },
4928             },
4929             },
4930              
4931             'ohr_settings' => +{
4932             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
4933             0 => +{'name' => 'enabled', 'type_name' => 'switch'},
4934             },
4935              
4936             'time_in_zone' => +{
4937             253 => +{ 'name' => 'timestamp', 'type_name' => 'date_time' },
4938             0 => +{ 'name' => 'reference_mesg', 'type_name' => 'mesg_num' },
4939             1 => +{ 'name' => 'reference_index', 'type_name' => 'message_index' },
4940             2 => +{ 'name' => 'time_in_hr_zone', 'scale' => 1000 },
4941             3 => +{ 'name' => 'time_in_speed_zone', 'scale' => 1000 },
4942             4 => +{ 'name' => 'time_in_cadence_zone', 'scale' => 1000 },
4943             5 => +{ 'name' => 'time_in_power_zone', 'scale' => 1000 },
4944             6 => +{ 'name' => 'hr_zone_high_boundary', 'unit' => 'bpm' },
4945             7 => +{ 'name' => 'speed_zone_high_boundary', 'unit' => 'm/s', 'scale' => 1000 },
4946             8 => +{ 'name' => 'cadence_zone_high_boundary', 'unit' => 'rpm' },
4947             9 => +{ 'name' => 'power_zone_high_boundary', 'unit' => 'watts' },
4948             10 => +{ 'name' => 'hr_calc_type', 'type_name' => 'hr_zone_calc' },
4949             11 => +{ 'name' => 'max_heart_rate' },
4950             12 => +{ 'name' => 'resting_heart_rate' },
4951             13 => +{ 'name' => 'threshold_heart_rate' },
4952             14 => +{ 'name' => 'pwr_calc_type', 'type_name' => 'pwr_zone_calc' },
4953             15 => +{ 'name' => 'functional_threshold_power' },
4954             },
4955              
4956             'zones_target' => +{ # begins === Sport settings file messages === section
4957             1 => +{'name' => 'max_heart_rate', 'unit' => 'bpm'},
4958             2 => +{'name' => 'threshold_heart_rate', 'unit' => 'bpm'},
4959             3 => +{'name' => 'functional_threshold_power', 'unit' => 'watts'},
4960             5 => +{'name' => 'hr_calc_type', 'type_name' => 'hr_zone_calc'},
4961             7 => +{'name' => 'pwr_calc_type', 'type_name' => 'power_zone_calc'},
4962             8 => +{'name' => 'unknown8'}, # unknown UINT16
4963             9 => +{'name' => 'unknown9'}, # unknown ENUM
4964             10 => +{'name' => 'unknown10'}, # unknown ENUM
4965             11 => +{'name' => 'unknown11'}, # unknown ENUM
4966             12 => +{'name' => 'unknown12'}, # unknown ENUM
4967             13 => +{'name' => 'unknown13'}, # unknown ENUM
4968             },
4969              
4970             'sport' => +{
4971             0 => +{'name' => 'sport', 'type_name' => 'sport'},
4972             1 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
4973             3 => +{'name' => 'name'},
4974             4 => +{'name' => 'unknown4'}, # unknown UINT16
4975             5 => +{'name' => 'unknown5'}, # unknown ENUM
4976             6 => +{'name' => 'unknown6'}, # unknown ENUM
4977             7 => +{'name' => 'unknown7'}, # unknown UINT8
4978             8 => +{'name' => 'unknown8'}, # unknown UINT8
4979             9 => +{'name' => 'unknown9'}, # unknown UINT8
4980             10 => +{'name' => 'unknown10'}, # unknown UINT8 (array[3])
4981             12 => +{'name' => 'unknown12'}, # unknown UINT8
4982             },
4983              
4984             'hr_zone' => +{
4985             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4986             1 => +{'name' => 'high_bpm', 'unit' => 'bpm'},
4987             2 => +{'name' => 'name'},
4988             },
4989              
4990             'speed_zone' => +{
4991             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4992             0 => +{'name' => 'high_value', 'scale' => 1000, 'unit' => 'm/s'},
4993             1 => +{'name' => 'name'},
4994             },
4995              
4996             'cadence_zone' => +{
4997             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4998             0 => +{'name' => 'high_value', 'unit' => 'rpm'},
4999             1 => +{'name' => 'name'},
5000             },
5001              
5002             'power_zone' => +{
5003             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5004             1 => +{'name' => 'high_value', 'unit' => 'watts'},
5005             2 => +{'name' => 'name'},
5006             },
5007              
5008             'met_zone' => +{
5009             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5010             1 => +{'name' => 'high_bpm', 'unit' => 'bpm'},
5011             2 => +{'name' => 'calories', 'scale' => 10, 'unit' => 'kcal/min'},
5012             3 => +{'name' => 'fat_calories', 'scale' => 10, 'unit' => 'kcal/min'},
5013             },
5014              
5015             'dive_settings' => +{
5016             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5017             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5018             0 => +{'name' => 'name'},
5019             1 => +{'name' => 'model', 'type_name' => 'tissue_model_type'},
5020             2 => +{'name' => 'gf_low', 'unit' => '%'},
5021             3 => +{'name' => 'gf_high', 'unit' => '%'},
5022             4 => +{'name' => 'water_type', 'type_name' => 'water_type'},
5023             5 => +{'name' => 'water_density', 'unit' => 'kg/m^3'},
5024             6 => +{'name' => 'po2_warn', 'scale' => 100, 'unit' => '%'},
5025             7 => +{'name' => 'po2_critical', 'scale' => 100, 'unit' => '%'},
5026             8 => +{'name' => 'po2_deco', 'scale' => 100, 'unit' => '%'},
5027             9 => +{'name' => 'safety_stop_enabled', 'type_name' => 'bool'},
5028             10 => +{'name' => 'bottom_depth'},
5029             11 => +{'name' => 'bottom_time'},
5030             12 => +{'name' => 'apnea_countdown_enabled', 'type_name' => 'bool'},
5031             13 => +{'name' => 'apnea_countdown_time'},
5032             14 => +{'name' => 'backlight_mode', 'type_name' => 'dive_backlight_mode'},
5033             15 => +{'name' => 'backlight_brightness'},
5034             16 => +{'name' => 'backlight_timeout', 'type_name' => 'backlight_timeout'},
5035             17 => +{'name' => 'repeat_dive_interval', 'unit' => 's'},
5036             18 => +{'name' => 'safety_stop_time', 'unit' => 's'},
5037             19 => +{'name' => 'heart_rate_source_type', 'type_name' => 'source_type'},
5038             20 => +{
5039             'name' => 'heart_rate_source',
5040             'switch' => +{
5041             '_by' => 'heart_rate_source_type',
5042             'antplus' => +{'name' => 'heart_rate_antplus_device_type', 'type_name' => 'antplus_device_type'},
5043             'local' => +{'name' => 'heart_rate_local_device_type', 'type_name' => 'local_device_type'},
5044             },
5045             },
5046             21 => +{ 'name' => 'travel_gas', 'type_name' => 'message_index' }, # Index of travel dive_gas message
5047             22 => +{ 'name' => 'ccr_low_setpoint_switch_mode', 'type_name' => 'ccr_setpoint_switch_mode' }, # If low PO2 should be switched to automatically
5048             23 => +{ 'name' => 'ccr_low_setpoint', 'scale' => 100 }, # Target PO2 when using low setpoint
5049             24 => +{ 'name' => 'ccr_low_setpoint_depth', 'unit' => 'm', 'scale' => 1000 }, # Depth to switch to low setpoint in automatic mode
5050             25 => +{ 'name' => 'ccr_high_setpoint_switch_mode', 'type' => 'ccr_setpoint_switch_mode' }, # If high PO2 should be switched to automatically
5051             26 => +{ 'name' => 'ccr_high_setpoint', 'unit' => '%', 'scale' => 100 }, # Target PO2 when using high setpoint
5052             27 => +{ 'name' => 'ccr_high_setpoint_depth', 'unit' => 'm', 'scale' => 1000 }, # Depth to switch to high setpoint in automatic mode
5053             29 => +{ 'name' => 'gas_consumption_display', 'type_name' => 'gas_consumption_rate_type' }, # Type of gas consumption rate to display. Some values are only valid if tank volume is known.
5054             30 => +{ 'name' => 'up_key_enabled', 'type_name' => 'bool' }, # Indicates whether the up key is enabled during dives
5055             35 => +{ 'name' => 'dive_sounds', 'type_name' => 'tone' }, # Sounds and vibration enabled or disabled in-dive
5056             36 => +{ 'name' => 'last_stop_multiple', 'scale' => 10 }, # Usually 1.0/1.5/2.0 representing 3/4.5/6m or 10/15/20ft
5057             37 => +{ 'name' => 'no_fly_time_mode', 'type_name' => 'no_fly_time_mode' }, # Indicates which guidelines to use for no-fly surface interval.
5058             },
5059              
5060             'dive_alarm' => +{
5061             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5062             0 => +{'name' => 'depth', 'scale' => 1000, 'unit' => 'm'},
5063             1 => +{'name' => 'time', 'unit' => 's'},
5064             2 => +{'name' => 'enabled', 'type_name' => 'bool'},
5065             3 => +{'name' => 'alarm_type', 'type_name' => 'dive_alarm_type'},
5066             4 => +{'name' => 'sound', 'type_name' => 'tone'},
5067             5 => +{'name' => 'dive_types', 'type_name' => 'sub_sport'},
5068             6 => +{ 'name' => 'id' }, # Alarm ID
5069             7 => +{ 'name' => 'popup_enabled', 'type_name' => 'bool' }, # Show a visible pop-up for this alarm
5070             8 => +{ 'name' => 'trigger_on_descent', 'type_name' => 'bool' }, # Trigger the alarm on descent
5071             9 => +{ 'name' => 'trigger_on_ascent', 'type_name' => 'bool' }, # Trigger the alarm on ascent
5072             10 => +{ 'name' => 'repeating', 'type_name' => 'bool' }, # Repeat alarm each time threshold is crossed?
5073             11 => +{ 'name' => 'speed', 'scale' => 1000 }, # Ascent/descent rate (mps) setting for speed type alarms
5074             },
5075              
5076             'dive_apnea_alarm' => +{
5077             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5078             0 => +{ 'name' => 'depth', 'unit' => 'm', 'scale' => 1000 }, # Depth setting (m) for depth type alarms
5079             1 => +{ 'name' => 'time', 'unit' => 's' }, # Time setting (s) for time type alarms
5080             2 => +{ 'name' => 'enabled', 'type_name' => 'bool' }, # Enablement flag
5081             3 => +{ 'name' => 'alarm_type', 'type_name' => 'dive_alarm_type' }, # Alarm type setting
5082             4 => +{ 'name' => 'sound', 'type_name' => 'tone' }, # Tone and Vibe setting for the alarm.
5083             5 => +{ 'name' => 'dive_types', 'type_name' => 'sub_sport' }, # Dive types the alarm will trigger on
5084             6 => +{ 'name' => 'id' }, # Alarm ID
5085             7 => +{ 'name' => 'popup_enabled', 'type_name' => 'bool' }, # Show a visible pop-up for this alarm
5086             8 => +{ 'name' => 'trigger_on_descent', 'type_name' => 'bool' }, # Trigger the alarm on descent
5087             9 => +{ 'name' => 'trigger_on_ascent', 'type_name' => 'bool' }, # Trigger the alarm on ascent
5088             10 => +{ 'name' => 'repeating', 'type_name' => 'bool' }, # Repeat alarm each time threshold is crossed?
5089             11 => +{ 'name' => 'speed', 'unit' => 'mps', 'scale' => 1000 }, # Ascent/descent rate (mps) setting for speed type alarms
5090             },
5091              
5092             'dive_gas' => +{
5093             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5094             0 => +{'name' => 'helium_content', 'unit' => '%'},
5095             1 => +{'name' => 'oxygen_content', 'unit' => '%'},
5096             2 => +{'name' => 'status', 'type_name' => 'dive_gas_status'},
5097             3 => +{'name' => 'mode', 'type_name' => 'dive_gas_mode'},
5098             },
5099              
5100             'goal' => +{ # begins === Goals file messages === section
5101             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5102             0 => +{'name' => 'sport', 'type_name' => 'sport'},
5103             1 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
5104             2 => +{'name' => 'start_date', 'type_name' => 'date_time'},
5105             3 => +{'name' => 'end_date', 'type_name' => 'date_time'},
5106             4 => +{'name' => 'type', 'type_name' => 'goal'},
5107             5 => +{'name' => 'value'},
5108             6 => +{'name' => 'repeat', 'type_name' => 'bool'},
5109             7 => +{'name' => 'target_value'},
5110             8 => +{'name' => 'recurrence', 'type_name' => 'goal_recurrence'},
5111             9 => +{'name' => 'recurrence_value'},
5112             10 => +{'name' => 'enabled', 'type_name' => 'bool'},
5113             11 => +{'name' => 'source', 'type_name' => 'goal_source'},
5114             },
5115              
5116             'activity' => +{ # begins === Activity file messages === section
5117             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5118             0 => +{'name' => 'total_timer_time', 'scale' => 1000, 'unit' => 's'},
5119             1 => +{'name' => 'num_sessions'},
5120             2 => +{'name' => 'type', 'type_name' => 'activity'},
5121             3 => +{'name' => 'event', 'type_name' => 'event'},
5122             4 => +{'name' => 'event_type', 'type_name' => 'event_type'},
5123             5 => +{'name' => 'local_timestamp', 'type_name' => 'local_date_time'},
5124             6 => +{'name' => 'event_group'},
5125             },
5126              
5127             'session' => +{
5128             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5129             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5130             0 => +{'name' => 'event', 'type_name' => 'event'},
5131             1 => +{'name' => 'event_type', 'type_name' => 'event_type'},
5132             2 => +{'name' => 'start_time', 'type_name' => 'date_time'},
5133             3 => +{'name' => 'start_position_lat', 'unit' => 'semicircles'},
5134             4 => +{'name' => 'start_position_long', 'unit' => 'semicircles'},
5135             5 => +{'name' => 'sport', 'type_name' => 'sport'},
5136             6 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
5137             7 => +{'name' => 'total_elapsed_time', 'scale' => 1000, 'unit' => 's'},
5138             8 => +{'name' => 'total_timer_time', 'scale' => 1000, 'unit' => 's'},
5139             9 => +{'name' => 'total_distance', 'scale' => 100, 'unit' => 'm'},
5140              
5141             10 => +{
5142             'name' => 'total_cycles', 'unit' => 'cycles',
5143              
5144             'switch' => +{
5145             '_by' => 'sport',
5146             'walking' => +{'name' => 'total_steps', 'unit' => 'steps'},
5147             'running' => +{'name' => 'total_strides', 'unit' => 'strides'},
5148             'swimming' => +{'name' => 'total_strokes', 'unit' => 'strokes'},
5149             },
5150             },
5151              
5152             11 => +{'name' => 'total_calories', 'unit' => 'kcal'},
5153             13 => +{'name' => 'total_fat_calories', 'unit' => 'kcal'},
5154             14 => +{'name' => 'avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
5155             15 => +{'name' => 'max_speed', 'scale' => 1000, 'unit' => 'm/s'},
5156             16 => +{'name' => 'avg_heart_rate', 'unit' => 'bpm'},
5157             17 => +{'name' => 'max_heart_rate', 'unit' => 'bpm'},
5158              
5159             18 => +{
5160             'name' => 'avg_cadence', 'unit' => 'rpm',
5161              
5162             'switch' => +{
5163             '_by' => 'sport',
5164             'walking' => +{'name' => 'avg_walking_cadence', 'unit' => 'steps/min'},
5165             'running' => +{'name' => 'avg_running_cadence', 'unit' => 'strides/min'},
5166             'swimming' => +{'name' => 'avg_swimming_cadence', 'unit' => 'strokes/min'},
5167             },
5168             },
5169              
5170             19 => +{
5171             'name' => 'max_cadence', 'unit' => 'rpm',
5172              
5173             'switch' => +{
5174             '_by' => 'sport',
5175             'walking' => +{'name' => 'max_walking_cadence', 'unit' => 'steps/min'},
5176             'running' => +{'name' => 'max_running_cadence', 'unit' => 'strides/min'},
5177             'swimming' => +{'name' => 'max_swimming_cadence', 'unit' => 'strokes/min'},
5178             },
5179             },
5180              
5181             20 => +{'name' => 'avg_power', 'unit' => 'watts'},
5182             21 => +{'name' => 'max_power', 'unit' => 'watts'},
5183             22 => +{'name' => 'total_ascent', 'unit' => 'm'},
5184             23 => +{'name' => 'total_descent', 'unit' => 'm'},
5185             24 => +{'name' => 'total_training_effect', 'scale' => 10},
5186             25 => +{'name' => 'first_lap_index'},
5187             26 => +{'name' => 'num_laps'},
5188             27 => +{'name' => 'event_group'},
5189             28 => +{'name' => 'trigger', 'type_name' => 'session_trigger'},
5190             29 => +{'name' => 'nec_lat', 'unit' => 'semicircles'},
5191             30 => +{'name' => 'nec_long', 'unit' => 'semicircles'},
5192             31 => +{'name' => 'swc_lat', 'unit' => 'semicircles'},
5193             32 => +{'name' => 'swc_long', 'unit' => 'semicircles'},
5194             34 => +{'name' => 'normalized_power', 'unit' => 'watts'},
5195             35 => +{'name' => 'training_stress_score', 'scale' => 10, 'unit' => 'tss'},
5196             36 => +{'name' => 'intensity_factor', 'scale' => 1000, 'unit' => 'if'},
5197             37 => +{'name' => 'left_right_balance', 'type_name' => 'left_right_balance_100'},
5198             38 => +{'name' => 'end_position_lat', 'unit' => 'semicircles'},
5199             39 => +{'name' => 'end_position_long', 'unit' => 'semicircles'},
5200             41 => +{'name' => 'avg_stroke_count', 'scale' => 10, 'unit' => 'strokes/lap'},
5201             42 => +{'name' => 'avg_stroke_distance', 'scale' => 100, 'unit' => 'm'},
5202             43 => +{'name' => 'swim_stroke', 'type_name' => 'swim_stroke'},
5203             44 => +{'name' => 'pool_length', 'scale' => 100, 'unit' => 'm'},
5204             45 => +{'name' => 'threshold_power', 'unit' => 'watts'},
5205             46 => +{'name' => 'pool_length_unit', 'type_name' => 'display_measure'},
5206             47 => +{'name' => 'num_active_lengths', 'unit' => 'lengths'},
5207             48 => +{'name' => 'total_work', 'unit' => 'J'},
5208             49 => +{'name' => 'avg_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5209             50 => +{'name' => 'max_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5210             51 => +{'name' => 'gps_accuracy', 'unit' => 'm'},
5211             52 => +{'name' => 'avg_grade', 'scale' => 100, 'unit' => '%'},
5212             53 => +{'name' => 'avg_pos_grade', 'scale' => 100, 'unit' => '%'},
5213             54 => +{'name' => 'avg_neg_grade', 'scale' => 100, 'unit' => '%'},
5214             55 => +{'name' => 'max_pos_grade', 'scale' => 100, 'unit' => '%'},
5215             56 => +{'name' => 'max_neg_grade', 'scale' => 100, 'unit' => '%'},
5216             57 => +{'name' => 'avg_temperature', 'unit' => 'deg.C'},
5217             58 => +{'name' => 'max_temperature', 'unit' => 'deg.C'},
5218             59 => +{'name' => 'total_moving_time', 'scale' => 1000, 'unit' => 's'},
5219             60 => +{'name' => 'avg_pos_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5220             61 => +{'name' => 'avg_neg_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5221             62 => +{'name' => 'max_pos_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5222             63 => +{'name' => 'max_neg_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5223             64 => +{'name' => 'min_heart_rate', 'unit' => 'bpm'},
5224             65 => +{'name' => 'time_in_hr_zone', 'scale' => 1000, 'unit' => 's'},
5225             66 => +{'name' => 'time_in_speed_zone', 'scale' => 1000, 'unit' => 's'},
5226             67 => +{'name' => 'time_in_cadence_zone', 'scale' => 1000, 'unit' => 's'},
5227             68 => +{'name' => 'time_in_power_zone', 'scale' => 1000, 'unit' => 's'},
5228             69 => +{'name' => 'avg_lap_time', 'scale' => 1000, 'unit' => 's'},
5229             70 => +{'name' => 'best_lap_index'},
5230             71 => +{'name' => 'min_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5231             78 => +{'name' => 'unknown78'}, # unknown UINT32
5232             81 => +{'name' => 'unknown81'}, # unknown ENUM
5233             82 => +{'name' => 'player_score'},
5234             83 => +{'name' => 'opponent_score'},
5235             84 => +{'name' => 'opponent_name'},
5236             85 => +{'name' => 'stroke_count', 'unit' => 'counts'},
5237             86 => +{'name' => 'zone_count', 'unit' => 'counts'},
5238             87 => +{'name' => 'max_ball_speed', 'scale' => 100, 'unit' => 'm/s'},
5239             88 => +{'name' => 'avg_ball_speed', 'scale' => 100, 'unit' => 'm/s'},
5240             89 => +{'name' => 'avg_vertical_oscillation', 'scale' => 10, 'unit' => 'mm'},
5241             90 => +{'name' => 'avg_stance_time_percent', 'scale' => 100, 'unit' => '%'},
5242             91 => +{'name' => 'avg_stance_time', 'scale' => 10, 'unit' => 'ms'},
5243             92 => +{'name' => 'avg_fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
5244             93 => +{'name' => 'max_fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
5245             94 => +{'name' => 'total_fractional_cycles', 'scale' => 128, 'unit' => 'cycles'},
5246             95 => +{'name' => 'avg_total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5247             96 => +{'name' => 'min_total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5248             97 => +{'name' => 'max_total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5249             98 => +{'name' => 'avg_saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5250             99 => +{'name' => 'min_saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5251             100 => +{'name' => 'max_saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5252             101 => +{'name' => 'avg_left_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5253             102 => +{'name' => 'avg_right_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5254             103 => +{'name' => 'avg_left_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5255             104 => +{'name' => 'avg_right_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5256             105 => +{'name' => 'avg_combined_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5257             106 => +{'name' => 'unknown106'}, # unknown UINT16
5258             107 => +{'name' => 'unknown107'}, # unknown UINT16
5259             108 => +{'name' => 'unknown108'}, # unknown UINT16
5260             109 => +{'name' => 'unknown109'}, # unknown UINT8
5261             110 => +{'name' => 'sport_profile_name'}, # Sport name from associated sport mesg
5262             111 => +{'name' => 'sport_index'},
5263             112 => +{'name' => 'time_standing', 'scale' => 1000, 'unit' => 's'},
5264             113 => +{'name' => 'stand_count'},
5265             114 => +{'name' => 'avg_left_pco', 'unit' => 'mm'},
5266             115 => +{'name' => 'avg_right_pco', 'unit' => 'mm'},
5267             116 => +{'name' => 'avg_left_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5268             117 => +{'name' => 'avg_left_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5269             118 => +{'name' => 'avg_right_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5270             119 => +{'name' => 'avg_right_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5271             120 => +{'name' => 'avg_power_position', 'unit' => 'watts'},
5272             121 => +{'name' => 'max_power_position', 'unit' => 'watts'},
5273             122 => +{'name' => 'avg_cadence_position', 'unit' => 'rpm'},
5274             123 => +{'name' => 'max_cadence_position', 'unit' => 'rpm'},
5275             124 => +{'name' => 'enhanced_avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
5276             125 => +{'name' => 'enhanced_max_speed', 'scale' => 1000, 'unit' => 'm/s'},
5277             126 => +{'name' => 'enhanced_avg_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5278             127 => +{'name' => 'enhanced_min_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5279             128 => +{'name' => 'enhanced_max_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5280             129 => +{'name' => 'avg_lev_motor_power', 'unit' => 'watts'},
5281             130 => +{'name' => 'max_lev_motor_power', 'unit' => 'watts'},
5282             131 => +{'name' => 'lev_battery_consumption', 'scale' => 2, 'unit' => '%'},
5283             132 => +{'name' => 'avg_vertical_ratio', 'scale' => 100, 'unit' => '%'},
5284             133 => +{'name' => 'avg_stance_time_balance', 'scale' => 100, 'unit' => '%'},
5285             134 => +{'name' => 'avg_step_length', 'scale' => 10, 'unit' => 'mm'},
5286             137 => +{'name' => 'total_anaerobic_training_effect', 'scale' => 10},
5287             139 => +{'name' => 'avg_vam', 'scale' => 1000, 'unit' => 'm/s'},
5288             140 => +{ 'name' => 'avg_depth', 'unit' => 'm', 'scale' => 1000 }, # 0 if above water
5289             141 => +{ 'name' => 'max_depth', 'unit' => 'm', 'scale' => 1000 }, # 0 if above water
5290             142 => +{ 'name' => 'surface_interval', 'unit' => 's' }, # Time since end of last dive
5291             143 => +{ 'name' => 'start_cns', 'unit' => '%' },
5292             144 => +{ 'name' => 'end_cns', 'unit' => '%' },
5293             145 => +{ 'name' => 'start_n2', 'unit' => '%' },
5294             146 => +{ 'name' => 'end_n2', 'unit' => '%' },
5295             147 => +{ 'name' => 'avg_respiration_rate' },
5296             148 => +{ 'name' => 'max_respiration_rate' },
5297             149 => +{ 'name' => 'min_respiration_rate' },
5298             150 => +{ 'name' => 'min_temperature', 'unit' => 'deg.C' },
5299             155 => +{ 'name' => 'o2_toxicity', 'unit' => 'OTUs' },
5300             156 => +{ 'name' => 'dive_number' },
5301             168 => +{ 'name' => 'training_load_peak', 'scale' => 65536 },
5302             169 => +{ 'name' => 'enhanced_avg_respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 },
5303             170 => +{ 'name' => 'enhanced_max_respiration_rate', 'unit' => 'breaths/min' },
5304             180 => +{ 'name' => 'enhanced_min_respiration_rate', 'scale' => 100 },
5305             181 => +{'name' => 'total_grit', 'unit' => 'kGrit'},
5306             182 => +{'name' => 'total_flow', 'unit' => 'Flow'},
5307             183 => +{'name' => 'jump_count'},
5308             186 => +{'name' => 'avg_grit', 'unit' => 'kGrit'},
5309             187 => +{'name' => 'avg_flow', 'unit' => 'Flow'},
5310             192 => +{'name' => 'workout_feel'}, # A 0-100 scale representing how a user felt while performing a workout. Low values are considered feeling bad, while high values are good.
5311             193 => +{'name' => 'workout_rpe'}, # Common Borg CR10 / 0-10 RPE scale, multiplied 10x.. Aggregate score for all workouts in a single session.
5312             194 => +{'name' => 'avg_spo2', 'unit' => 'percent'}, # Average SPO2 for the monitoring session
5313             195 => +{'name' => 'avg_stress', 'unit' => 'percent'}, # Average stress for the monitoring session
5314             197 => +{'name' => 'sdrr_hrv', 'unit' => 'mS'}, # Standard deviation of R-R interval (SDRR) - Heart rate variability measure most useful for wellness users.
5315             198 => +{'name' => 'rmssd_hrv', 'unit' => 'mS'}, # Root mean square successive difference (RMSSD) - Heart rate variability measure most useful for athletes
5316             199 => +{'name' => 'total_fractional_ascent', 'unit' => 'm'},
5317             200 => +{'name' => 'total_fractional_descent', 'unit' => 'm'},
5318             208 => +{'name' => 'avg_core_temperature', 'unit' => 'deg.C'},
5319             209 => +{'name' => 'min_core_temperature', 'unit' => 'deg.C'},
5320             210 => +{'name' => 'max_core_temperature', 'unit' => 'deg.C'},
5321             },
5322              
5323             'lap' => +{
5324             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5325             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5326             0 => +{'name' => 'event', 'type_name' => 'event'},
5327             1 => +{'name' => 'event_type', 'type_name' => 'event_type'},
5328             2 => +{'name' => 'start_time', 'type_name' => 'date_time'},
5329             3 => +{'name' => 'start_position_lat', 'unit' => 'semicircles'},
5330             4 => +{'name' => 'start_position_long', 'unit' => 'semicircles'},
5331             5 => +{'name' => 'end_position_lat', 'unit' => 'semicircles'},
5332             6 => +{'name' => 'end_position_long', 'unit' => 'semicircles'},
5333             7 => +{'name' => 'total_elapsed_time', 'scale' => 1000, 'unit' => 's'},
5334             8 => +{'name' => 'total_timer_time', 'scale' => 1000, 'unit' => 's'},
5335             9 => +{'name' => 'total_distance', 'scale' => 100, 'unit' => 'm'},
5336              
5337             10 => +{
5338             'name' => 'total_cycles', 'unit' => 'cycles',
5339              
5340             'switch' => +{
5341             '_by' => 'sport',
5342             'walking' => +{'name' => 'total_steps', 'unit' => 'steps'},
5343             'running' => +{'name' => 'total_strides', 'unit' => 'strides'},
5344             'swimming' => +{'name' => 'total_strokes', 'unit' => 'strokes'},
5345             },
5346             },
5347              
5348             11 => +{'name' => 'total_calories', 'unit' => 'kcal'},
5349             12 => +{'name' => 'total_fat_calories', 'unit' => 'kcal'},
5350             13 => +{'name' => 'avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
5351             14 => +{'name' => 'max_speed', 'scale' => 1000, 'unit' => 'm/s'},
5352             15 => +{'name' => 'avg_heart_rate', 'unit' => 'bpm'},
5353             16 => +{'name' => 'max_heart_rate', 'unit' => 'bpm'},
5354              
5355             17 => +{
5356             'name' => 'avg_cadence', 'unit' => 'rpm',
5357              
5358             'switch' => +{
5359             '_by' => 'sport',
5360             'walking' => +{'name' => 'avg_walking_cadence', 'unit' => 'steps/min'},
5361             'running' => +{'name' => 'avg_running_cadence', 'unit' => 'strides/min'},
5362             'swimming' => +{'name' => 'avg_swimming_cadence', 'unit' => 'strokes/min'},
5363             },
5364             },
5365              
5366             18 => +{
5367             'name' => 'max_cadence', 'unit' => 'rpm',
5368              
5369             'switch' => +{
5370             '_by' => 'sport',
5371             'walking' => +{'name' => 'max_walking_cadence', 'unit' => 'steps/min'},
5372             'running' => +{'name' => 'max_running_cadence', 'unit' => 'strides/min'},
5373             'swimming' => +{'name' => 'max_swimming_cadence', 'unit' => 'strokes/min'},
5374             },
5375             },
5376              
5377             19 => +{'name' => 'avg_power', 'unit' => 'watts'},
5378             20 => +{'name' => 'max_power', 'unit' => 'watts'},
5379             21 => +{'name' => 'total_ascent', 'unit' => 'm'},
5380             22 => +{'name' => 'total_descent', 'unit' => 'm'},
5381             23 => +{'name' => 'intensity', 'type_name' => 'intensity'},
5382             24 => +{'name' => 'lap_trigger', 'type_name' => 'lap_trigger'},
5383             25 => +{'name' => 'sport', 'type_name' => 'sport'},
5384             26 => +{'name' => 'event_group'},
5385             27 => +{'name' => 'nec_lat', 'unit' => 'semicircles'}, # not present?
5386             28 => +{'name' => 'nec_long', 'unit' => 'semicircles'}, # not present?
5387             29 => +{'name' => 'swc_lat', 'unit' => 'semicircles'}, # not present?
5388             30 => +{'name' => 'swc_long', 'unit' => 'semicircles'}, # not present?
5389             32 => +{'name' => 'num_lengths', 'unit' => 'lengths'},
5390             33 => +{'name' => 'normalized_power', 'unit' => 'watts'},
5391             34 => +{'name' => 'left_right_balance', 'type_name' => 'left_right_balance_100'},
5392             35 => +{'name' => 'first_length_index'},
5393             37 => +{'name' => 'avg_stroke_distance', 'scale' => 100, 'unit' => 'm'},
5394             38 => +{'name' => 'swim_stroke', 'type_name' => 'swim_stroke'},
5395             39 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
5396             40 => +{'name' => 'num_active_lengths', 'unit' => 'lengths'},
5397             41 => +{'name' => 'total_work', 'unit' => 'J'},
5398             42 => +{'name' => 'avg_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5399             43 => +{'name' => 'max_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5400             44 => +{'name' => 'gps_accuracy', 'unit' => 'm'},
5401             45 => +{'name' => 'avg_grade', 'scale' => 100, 'unit' => '%'},
5402             46 => +{'name' => 'avg_pos_grade', 'scale' => 100, 'unit' => '%'},
5403             47 => +{'name' => 'avg_neg_grade', 'scale' => 100, 'unit' => '%'},
5404             48 => +{'name' => 'max_pos_grade', 'scale' => 100, 'unit' => '%'},
5405             49 => +{'name' => 'max_neg_grade', 'scale' => 100, 'unit' => '%'},
5406             50 => +{'name' => 'avg_temperature', 'unit' => 'deg.C'},
5407             51 => +{'name' => 'max_temperature', 'unit' => 'deg.C'},
5408             52 => +{'name' => 'total_moving_time', 'scale' => 1000, 'unit' => 's'},
5409             53 => +{'name' => 'avg_pos_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5410             54 => +{'name' => 'avg_neg_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5411             55 => +{'name' => 'max_pos_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5412             56 => +{'name' => 'max_neg_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5413             57 => +{'name' => 'time_in_hr_zone', 'scale' => 1000, 'unit' => 's'},
5414             58 => +{'name' => 'time_in_speed_zone', 'scale' => 1000, 'unit' => 's'},
5415             59 => +{'name' => 'time_in_cadence_zone', 'scale' => 1000, 'unit' => 's'},
5416             60 => +{'name' => 'time_in_power_zone', 'scale' => 1000, 'unit' => 's'},
5417             61 => +{'name' => 'repetition_num'},
5418             62 => +{'name' => 'min_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5419             63 => +{'name' => 'min_heart_rate', 'unit' => 'bpm'},
5420             70 => +{'name' => 'unknown70'}, # unknown UINT32
5421             71 => +{'name' => 'wkt_step_index', 'type_name' => 'message_index'},
5422             72 => +{'name' => 'unknown72'}, # unknown ENUM
5423             74 => +{'name' => 'opponent_score'},
5424             75 => +{'name' => 'stroke_count', 'unit' => 'counts'},
5425             76 => +{'name' => 'zone_count', 'unit' => 'counts'},
5426             77 => +{'name' => 'avg_vertical_oscillation', 'scale' => 10, 'unit' => 'mm'},
5427             78 => +{'name' => 'avg_stance_time_percent', 'scale' => 100, 'unit' => '%'},
5428             79 => +{'name' => 'avg_stance_time', 'scale' => 10, 'unit' => 'ms'},
5429             80 => +{'name' => 'avg_fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
5430             81 => +{'name' => 'max_fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
5431             82 => +{'name' => 'total_fractional_cycles', 'scale' => 128, 'unit' => 'cycles'},
5432             83 => +{'name' => 'player_score'},
5433             84 => +{'name' => 'avg_total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5434             85 => +{'name' => 'min_total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5435             86 => +{'name' => 'max_total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5436             87 => +{'name' => 'avg_saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5437             88 => +{'name' => 'min_saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5438             89 => +{'name' => 'max_saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5439             91 => +{'name' => 'avg_left_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5440             92 => +{'name' => 'avg_right_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5441             93 => +{'name' => 'avg_left_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5442             94 => +{'name' => 'avg_right_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5443             95 => +{'name' => 'avg_combined_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5444             96 => +{'name' => 'unknown96'}, # unknown UINT16
5445             97 => +{'name' => 'unknown97'}, # unknown UINT16
5446             98 => +{'name' => 'time_standing', 'scale' => 1000, 'unit' => 's'},
5447             99 => +{'name' => 'stand_count'},
5448             100 => +{'name' => 'avg_left_pco', 'unit' => 'mm'},
5449             101 => +{'name' => 'avg_right_pco', 'unit' => 'mm'},
5450             102 => +{'name' => 'avg_left_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5451             103 => +{'name' => 'avg_left_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5452             104 => +{'name' => 'avg_right_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5453             105 => +{'name' => 'avg_right_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5454             106 => +{'name' => 'avg_power_position', 'unit' => 'watts'},
5455             107 => +{'name' => 'max_power_position', 'unit' => 'watts'},
5456             108 => +{'name' => 'avg_cadence_position', 'unit' => 'rpm'},
5457             109 => +{'name' => 'max_cadence_position', 'unit' => 'rpm'},
5458             110 => +{'name' => 'enhanced_avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
5459             111 => +{'name' => 'enhanced_max_speed', 'scale' => 1000, 'unit' => 'm/s'},
5460             112 => +{'name' => 'enhanced_avg_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5461             113 => +{'name' => 'enhanced_min_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5462             114 => +{'name' => 'enhanced_max_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5463             115 => +{'name' => 'avg_lev_motor_power', 'unit' => 'watts'},
5464             116 => +{'name' => 'max_lev_motor_power', 'unit' => 'watts'},
5465             117 => +{'name' => 'lev_battery_consumption', 'scale' => 2, 'unit' => '%'},
5466             118 => +{'name' => 'avg_vertical_ratio', 'scale' => 100, 'unit' => '%'},
5467             119 => +{'name' => 'avg_stance_time_balance', 'scale' => 100, 'unit' => '%'},
5468             120 => +{'name' => 'avg_step_length', 'scale' => 10, 'unit' => 'mm'},
5469             121 => +{'name' => 'avg_vam', 'scale' => 1000, 'unit' => 'm/s'},
5470             122 => +{ 'name' => 'avg_depth', 'unit' => 'm', 'scale' => 1000 }, # 0 if above water
5471             123 => +{ 'name' => 'max_depth', 'unit' => 'm', 'scale' => 1000 }, # 0 if above water
5472             124 => +{ 'name' => 'min_temperature', 'unit' => 'deg.C' },
5473             136 => +{ 'name' => 'enhanced_avg_respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 },
5474             137 => +{ 'name' => 'enhanced_max_respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 },
5475             147 => +{ 'name' => 'avg_respiration_rate' },
5476             148 => +{ 'name' => 'max_respiration_rate' },
5477             149 => +{'name' => 'total_grit', 'unit' => 'kGrit'},
5478             150 => +{'name' => 'total_flow', 'unit' => 'Flow'},
5479             151 => +{'name' => 'jump_count'},
5480             153 => +{'name' => 'avg_grit', 'unit' => 'kGrit'},
5481             154 => +{'name' => 'avg_flow', 'unit' => 'Flow'},
5482             156 => +{'name' => 'total_fractional_ascent', 'unit' => 'm'},
5483             157 => +{'name' => 'total_fractional_descent', 'unit' => 'm'},
5484             158 => +{'name' => 'avg_core_temperature', 'unit' => 'deg.C'},
5485             159 => +{'name' => 'min_core_temperature', 'unit' => 'deg.C'},
5486             160 => +{'name' => 'max_core_temperature', 'unit' => 'deg.C'},
5487             },
5488              
5489             'length' => +{
5490             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5491             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5492             0 => +{'name' => 'event', 'type_name' => 'event'},
5493             1 => +{'name' => 'event_type', 'type_name' => 'event_type'},
5494             2 => +{'name' => 'start_time', 'type_name' => 'date_time'},
5495             3 => +{'name' => 'total_elapsed_time', 'scale' => 1000, 'unit' => 's'},
5496             4 => +{'name' => 'total_timer_time', 'scale' => 1000, 'unit' => 's'},
5497             5 => +{'name' => 'total_strokes', 'unit' => 'strokes'},
5498             6 => +{'name' => 'avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
5499             7 => +{'name' => 'swim_stroke', 'type_name' => 'swim_stroke'},
5500             9 => +{'name' => 'avg_swimming_cadence', 'unit' => 'strokes/min'},
5501             10 => +{'name' => 'event_group'},
5502             11 => +{'name' => 'total_calories', 'unit' => 'kcal'},
5503             12 => +{'name' => 'length_type', 'type_name' => 'length_type'},
5504             18 => +{'name' => 'player_score'},
5505             19 => +{'name' => 'opponent_score'},
5506             20 => +{'name' => 'stroke_count', 'unit' => 'counts'},
5507             21 => +{'name' => 'zone_count', 'unit' => 'counts'},
5508             22 => +{ 'name' => 'enhanced_avg_respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 },
5509             23 => +{ 'name' => 'enhanced_max_respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 },
5510             24 => +{ 'name' => 'avg_respiration_rate' },
5511             25 => +{ 'name' => 'max_respiration_rate' },
5512             },
5513              
5514             'record' => +{
5515             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5516             0 => +{'name' => 'position_lat', 'unit' => 'semicircles'},
5517             1 => +{'name' => 'position_long', 'unit' => 'semicircles'},
5518             2 => +{'name' => 'altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5519             3 => +{'name' => 'heart_rate', 'unit' => 'bpm'},
5520             4 => +{'name' => 'cadence', 'unit' => 'rpm'},
5521             5 => +{'name' => 'distance', 'scale' => 100, 'unit' => 'm'},
5522             6 => +{'name' => 'speed', 'scale' => 1000, 'unit' => 'm/s'},
5523             7 => +{'name' => 'power', 'unit' => 'watts'},
5524             8 => +{'name' => 'compressed_speed_distance'}, # complex decoding!
5525             9 => +{'name' => 'grade', 'scale' => 100, 'unit' => '%'},
5526             10 => +{'name' => 'resistance'},
5527             11 => +{'name' => 'time_from_course', 'scale' => 1000, 'unit' => 's'},
5528             12 => +{'name' => 'cycle_length', 'scale' => 100, 'unit' => 'm'},
5529             13 => +{'name' => 'temperature', 'unit' => 'deg.C'},
5530             17 => +{'name' => 'speed_1s', 'scale' => 16, 'unit' => 'm/s'},
5531             18 => +{'name' => 'cycles', 'unit' => 'cycles'},
5532             19 => +{'name' => 'total_cycles', 'unit' => 'cycles'},
5533             28 => +{'name' => 'compressed_accumulated_power', 'unit' => 'watts'},
5534             29 => +{'name' => 'accumulated_power', 'unit' => 'watts'},
5535             30 => +{'name' => 'left_right_balance', 'type_name' => 'left_right_balance'},
5536             31 => +{'name' => 'gps_accuracy', 'unit' => 'm'},
5537             32 => +{'name' => 'vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5538             33 => +{'name' => 'calories', 'unit' => 'kcal'},
5539             39 => +{'name' => 'vertical_oscillation', 'scale' => 10, 'unit' => 'mm'},
5540             40 => +{'name' => 'stance_time_percent', 'scale' => 100, 'unit' => '%'},
5541             41 => +{'name' => 'stance_time', 'scale' => 10, 'unit' => 'ms'},
5542             42 => +{'name' => 'activity_type', 'type_name' => 'activity_type'},
5543             43 => +{'name' => 'left_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5544             44 => +{'name' => 'right_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5545             45 => +{'name' => 'left_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5546             46 => +{'name' => 'right_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5547             47 => +{'name' => 'combined_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5548             48 => +{'name' => 'time128', 'scale' => 128, 'unit' => 's'},
5549             49 => +{'name' => 'stroke_type', 'type_name' => 'stroke_type'},
5550             50 => +{'name' => 'zone'},
5551             51 => +{'name' => 'ball_speed', 'scale' => 100, 'unit' => 'm/s'},
5552             52 => +{'name' => 'cadence256', 'scale' => 256, 'unit' => 'rpm'},
5553             53 => +{'name' => 'fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
5554             54 => +{'name' => 'total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5555             55 => +{'name' => 'total_hemoglobin_conc_min', 'scale' => 100, 'unit' => 'g/dL'},
5556             56 => +{'name' => 'total_hemoglobin_conc_max', 'scale' => 100, 'unit' => 'g/dL'},
5557             57 => +{'name' => 'saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5558             58 => +{'name' => 'saturated_hemoglobin_percent_min', 'scale' => 10, 'unit' => '%'},
5559             59 => +{'name' => 'saturated_hemoglobin_percent_max', 'scale' => 10, 'unit' => '%'},
5560             61 => +{'name' => 'unknown61'}, # unknown UINT16
5561             62 => +{'name' => 'device_index', 'type_name' => 'device_index'},
5562             66 => +{'name' => 'unknown66'}, # unknown SINT16
5563             67 => +{'name' => 'left_pco', 'unit' => 'mm'},
5564             68 => +{'name' => 'right_pco', 'unit' => 'mm'},
5565             69 => +{'name' => 'left_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5566             70 => +{'name' => 'left_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5567             71 => +{'name' => 'right_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5568             72 => +{'name' => 'right_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5569             73 => +{'name' => 'enhanced_speed', 'scale' => 1000, 'unit' => 'm/s'},
5570             78 => +{'name' => 'enhanced_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5571             81 => +{'name' => 'battery_soc', 'scale' => 2, 'unit' => '%'},
5572             82 => +{'name' => 'motor_power', 'unit' => 'watts'},
5573             83 => +{'name' => 'vertical_ratio', 'scale' => 100, 'unit' => '%'},
5574             84 => +{'name' => 'stance_time_balance', 'scale' => 100, 'unit' => '%'},
5575             85 => +{'name' => 'step_length', 'scale' => 10, 'unit' => 'mm'},
5576             87 => +{'name' => 'cycle_length16', 'scale' => 100, 'unit' => 'm'}, # Supports larger cycle sizes needed for paddlesports. Max cycle size: 655.35
5577             91 => +{'name' => 'absolute_pressure', 'unit' => 'Pa'},
5578             92 => +{'name' => 'depth', 'scale' => 1000, 'unit' => 'm'},
5579             93 => +{'name' => 'next_stop_depth', 'scale' => 1000, 'unit' => 'm'},
5580             94 => +{'name' => 'next_stop_time', 'unit' => 's'},
5581             95 => +{'name' => 'time_to_surface', 'unit' => 's'},
5582             96 => +{'name' => 'ndl_time', 'unit' => 's'},
5583             97 => +{'name' => 'cns_load', 'unit' => '%'},
5584             98 => +{'name' => 'n2_load', 'unit' => '%'},
5585             99 => +{ 'name' => 'respiration_rate' },
5586             108 => +{ 'name' => 'enhanced_respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 },
5587             114 => +{'name' => 'grit'},
5588             115 => +{'name' => 'flow'},
5589             116 => +{'name' => 'current_stress', 'scale' => 100}, # Current Stress value
5590             117 => +{'name' => 'ebike_travel_range', 'unit' => 'km'},
5591             118 => +{'name' => 'ebike_battery_level', 'unit' => '%'},
5592             119 => +{'name' => 'ebike_assist_mode'},
5593             120 => +{'name' => 'ebike_assist_level_percent', 'unit' => '%'},
5594             123 => +{ 'name' => 'air_time_remaining' },
5595             124 => +{ 'name' => 'pressure_sac', 'unit' => 'bar/min', 'scale' => 100 }, # Pressure-based surface air consumption
5596             125 => +{ 'name' => 'volume_sac', 'unit' => 'l/min', 'scale' => 100 }, # Volumetric surface air consumption
5597             126 => +{ 'name' => 'rmv', 'unit' => 'l/min', 'scale' => 100 }, # Respiratory minute volume
5598             127 => +{ 'name' => 'ascent_rate', 'unit' => 'm/s', 'scale' => 1000 },
5599             129 => +{ 'name' => 'po2', 'unit' => 'percent', 'scale' => 100 }, # Current partial pressure of oxygen
5600             139 => +{'name' => 'core_temperature', 'scale' => 100, 'unit' => 'deg.C'},
5601             },
5602              
5603             'event' => +{
5604             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5605             0 => +{'name' => 'event', 'type_name' => 'event'},
5606             1 => +{'name' => 'event_type', 'type_name' => 'event_type'},
5607             2 => +{'name' => 'data16'}, # no switch?
5608              
5609             3 => +{
5610             'name' => 'data',
5611              
5612             'switch' => +{
5613             '_by' => 'event',
5614             'timer' => +{'name' => 'timer_trigger', 'type_name' => 'timer_trigger'},
5615             'course_point' => +{'name' => 'course_point_index', 'type_name' => 'message_index'},
5616             'battery' => +{'name' => 'battery_level', 'scale' => 1000, 'unit' => 'V'},
5617             'virtual_partner_pace' => +{'name' => 'virtual_partner_speed', 'scale' => 1000, 'unit' => 'm/s'},
5618             'hr_high_alert' => +{'name' => 'hr_high_alert', 'unit' => 'bpm'},
5619             'hr_low_alert' => +{'name' => 'hr_low_alert', 'unit' => 'bpm'},
5620             'speed_high_alert' => +{'name' => 'speed_high_alert', 'scale' => 1000, 'unit' => 'm/s'},
5621             'speed_low_alert' => +{'name' => 'speed_low_alert', 'scale' => 1000, 'unit' => 'm/s'},
5622             'cad_high_alert' => +{'name' => 'cad_high_alert', 'unit' => 'rpm'},
5623             'cad_low_alert' => +{'name' => 'cad_low_alert', 'unit' => 'rpm'},
5624             'power_high_alert' => +{'name' => 'power_high_alert', 'unit' => 'watts'},
5625             'power_low_alert' => +{'name' => 'power_low_alert', 'unit' => 'watts'},
5626             'time_duration_alert' => +{'name' => 'time_duration_alert', 'scale' => 1000, 'unit' => 's'},
5627             'distance_duration_alert' => +{'name' => 'distance_duration_alert', 'scale' => 100, 'unit' => 'm'},
5628             'calorie_duration_alert' => +{'name' => 'calorie_duration_alert', 'unit' => 'kcal'},
5629             # why is this key not the same as the name?
5630             'fitness_equipment' => +{'name' => 'fitness_equipment_state', 'type_name' => 'fitness_equipment_state'},
5631             'sport_point' => +{'name' => 'sport_point', 'scale' => 11}, # complex decoding!
5632             'front_gear_change' => +{'name' => 'gear_change_data', 'scale' => 1111}, # complex decoding!
5633             'rear_gear_change' => +{'name' => 'gear_change_data', 'scale' => 1111}, # complex decoding!
5634             'rider_position_change' => +{'name' => 'rider_position', 'type_name' => 'rider_position_type'},
5635             'comm_timeout' => +{'name' => 'comm_timeout', 'type_name' => 'comm_timeout_type'},
5636             'dive_alert' => +{ 'name' => 'dive_alert', 'type_name' => 'dive_alert' },
5637             'auto_activity_detect' => +{'name' => 'auto_activity_detect_duration', 'unit' => 'min'},
5638             'radar_threat_alert' => +{'name' => 'radar_threat_alert'},
5639             },
5640             },
5641              
5642             4 => +{'name' => 'event_group'},
5643             7 => +{'name' => 'score'},
5644             8 => +{'name' => 'opponent_score'},
5645             9 => +{'name' => 'front_gear_num'},
5646             10 => +{'name' => 'front_gear'},
5647             11 => +{'name' => 'rear_gear_num'},
5648             12 => +{'name' => 'rear_gear'},
5649             13 => +{'name' => 'device_index', 'type_name' => 'device_index'},
5650             14 => +{'name' => 'activity_type', 'type_name' => 'activity_type'}, # Activity Type associated with an auto_activity_detect event
5651             15 => +{
5652             'name' => 'start_timestamp', # Timestamp of when the event started
5653              
5654             'switch' => +{
5655             '_by' => 'event',
5656             'auto_activity_detect' => +{'name' => 'auto_activity_detect_start_timestamp', 'type_name' => 'date_time', 'unit' => 's'},
5657             },
5658             },
5659             21 => +{'name' => 'radar_threat_level_max', 'type_name' => 'radar_threat_level_type'},
5660             22 => +{'name' => 'radar_threat_count'},
5661             23 => +{'name' => 'radar_threat_avg_approach_speed', unit => 'm/s'},
5662             24 => +{'name' => 'radar_threat_max_approach_speed', unit => 'm/s'},
5663             },
5664              
5665             'device_info' => +{
5666             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5667             0 => +{'name' => 'device_index', 'type_name' => 'device_index'},
5668              
5669             1 => +{
5670             'name' => 'device_type',
5671              
5672             'switch' => +{
5673             '_by' => 'source_type',
5674             'bluetooth_low_energy' => +{ 'name' => 'ble_device_type', 'type_name' => 'ble_device_type' },
5675             'antplus' => +{'name' => 'antplus_device_type', 'type_name' => 'antplus_device_type'},
5676             'ant' => +{'name' => 'ant_device_type'},
5677             'local' => +{'name' => 'local_device_type', 'type_name' => 'local_device_type'},
5678             },
5679             },
5680              
5681             2 => +{'name' => 'manufacturer', 'type_name' => 'manufacturer'},
5682             3 => +{'name' => 'serial_number'},
5683              
5684             4 => +{
5685             'name' => 'product',
5686              
5687             'switch' => +{
5688             '_by' => 'manufacturer',
5689             'garmin' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5690             'dynastream' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5691             'dynastream_oem' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5692             'favero_electronics' => +{'name' => 'favero_product', 'type_name' => 'favero_product'},
5693             'tacx' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5694             },
5695             },
5696              
5697             5 => +{'name' => 'software_version', 'scale' => 100},
5698             6 => +{'name' => 'hardware_version'},
5699             7 => +{'name' => 'cum_operating_time', 'unit' => 's'},
5700             8 => +{'name' => 'unknown8'}, # unknown UINT32
5701             9 => +{'name' => 'unknown9'}, # unknown UINT8
5702             10 => +{'name' => 'battery_voltage', 'scale' => 256, 'unit' => 'V'},
5703             11 => +{'name' => 'battery_status', 'type_name' => 'battery_status'},
5704             15 => +{'name' => 'unknown15'}, # unknown UINT32
5705             16 => +{'name' => 'unknown16'}, # unknown UINT32
5706             18 => +{'name' => 'sensor_position', 'type_name' => 'body_location'},
5707             19 => +{'name' => 'descriptor'},
5708             20 => +{'name' => 'ant_transmission_type'},
5709             21 => +{'name' => 'ant_device_number'},
5710             22 => +{'name' => 'ant_network', 'type_name' => 'ant_network'},
5711             24 => +{'name' => 'unknown24'}, # unknown UINT32Z
5712             25 => +{'name' => 'source_type', 'type_name' => 'source_type'},
5713             27 => +{'name' => 'product_name'},
5714             32 => +{'name' => 'battery_level', 'unit' => '%'},
5715             },
5716              
5717             'device_aux_battery_info' => +{
5718             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5719             0 => +{'name' => 'device_index', 'type_name' => 'device_index'},
5720             1 => +{'name' => 'battery_voltage', 'scale' => 256, 'unit' => 'V'},
5721             2 => +{'name' => 'battery_status', 'type_name' => 'battery_status'},
5722             },
5723              
5724             'training_file' => +{
5725             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5726             0 => +{'name' => 'type', 'type_name' => 'file'},
5727             1 => +{'name' => 'manufacturer', 'type_name' => 'manufacturer'},
5728              
5729             2 => +{
5730             'name' => 'product',
5731              
5732             'switch' => +{
5733             '_by' => 'manufacturer',
5734             'garmin' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5735             'dynastream' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5736             'dynastream_oem' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5737             'favero_electronics' => +{'name' => 'favero_product', 'type_name' => 'favero_product'},
5738             'tacx' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5739             },
5740             },
5741              
5742             3 => +{'name' => 'serial_number'},
5743             4 => +{'name' => 'time_created', 'type_name' => 'date_time'},
5744             },
5745              
5746             'weather_conditions' => +{
5747             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5748             0 => +{'name' => 'weather_report', 'type_name' => 'weather_report'},
5749             1 => +{'name' => 'temperature', 'unit' => 'deg.C'},
5750             2 => +{'name' => 'condition', 'type_name' => 'weather_status'},
5751             3 => +{'name' => 'wind_direction', 'unit' => 'degrees'},
5752             4 => +{'name' => 'wind_speed', 'scale' => 1000, 'unit' => 'm/s'},
5753             5 => +{'name' => 'precipitation_probability'},
5754             6 => +{'name' => 'temperature_feels_like', 'unit' => 'deg.C'},
5755             7 => +{'name' => 'relative_humidity', 'unit' => '%'},
5756             8 => +{'name' => 'location'},
5757             9 => +{'name' => 'observed_at_time', 'type_name' => 'date_time'},
5758             10 => +{'name' => 'observed_location_lat', 'unit' => 'semicircles'},
5759             11 => +{'name' => 'observed_location_long', 'unit' => 'semicircles'},
5760             12 => +{'name' => 'day_of_week', 'type_name' => 'day_of_week'},
5761             13 => +{'name' => 'high_temperature', 'unit' => 'deg.C'},
5762             14 => +{'name' => 'low_temperature', 'unit' => 'deg.C'},
5763             },
5764              
5765             'weather_alert' => +{
5766             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5767             0 => +{'name' => 'report_id'},
5768             1 => +{'name' => 'issue_time', 'type_name' => 'date_time'},
5769             2 => +{'name' => 'expire_time', 'type_name' => 'date_time'},
5770             3 => +{'name' => 'severity', 'type_name' => 'weather_severity'},
5771             4 => +{'name' => 'type', 'type_name' => 'weather_severe_type'},
5772             },
5773              
5774             'gps_metadata' => +{
5775             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5776             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5777             1 => +{'name' => 'position_lat', 'unit' => 'semicircles'},
5778             2 => +{'name' => 'position_long', 'unit' => 'semicircles'},
5779             3 => +{'name' => 'enhanced_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5780             4 => +{'name' => 'enhanced_speed', 'scale' => 1000, 'unit' => 'm/s'},
5781             5 => +{'name' => 'heading', 'scale' => 100, 'unit' => 'degrees'},
5782             6 => +{'name' => 'utc_timestamp', 'type_name' => 'date_time'},
5783             7 => +{'name' => 'velocity', 'scale' => 100, 'unit' => 'm/s'},
5784             },
5785              
5786             'camera_event' => +{
5787             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5788             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5789             1 => +{'name' => 'camera_event_type', 'type_name' => 'camera_event_type'},
5790             2 => +{'name' => 'camera_file_uuid'},
5791             3 => +{'name' => 'camera_orientation', 'type_name' => 'camera_orientation_type'},
5792             },
5793              
5794             'gyroscope_data' => +{
5795             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5796             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5797             1 => +{'name' => 'sample_time_offset', 'unit' => 'ms'},
5798             2 => +{'name' => 'gyro_x', 'unit' => 'counts'},
5799             3 => +{'name' => 'gyro_y', 'unit' => 'counts'},
5800             4 => +{'name' => 'gyro_z', 'unit' => 'counts'},
5801             5 => +{'name' => 'calibrated_gyro_x', 'unit' => 'deg/s'},
5802             6 => +{'name' => 'calibrated_gyro_y', 'unit' => 'deg/s'},
5803             7 => +{'name' => 'calibrated_gyro_z', 'unit' => 'deg/s'},
5804             },
5805              
5806             'accelerometer_data' => +{
5807             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5808             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5809             1 => +{'name' => 'sample_time_offset', 'unit' => 'ms'},
5810             2 => +{'name' => 'accel_x', 'unit' => 'counts'},
5811             3 => +{'name' => 'accel_y', 'unit' => 'counts'},
5812             4 => +{'name' => 'accel_z', 'unit' => 'counts'},
5813             5 => +{'name' => 'calibrated_accel_x', 'unit' => 'g'},
5814             6 => +{'name' => 'calibrated_accel_y', 'unit' => 'g'},
5815             7 => +{'name' => 'calibrated_accel_z', 'unit' => 'g'},
5816             8 => +{'name' => 'compressed_calibrated_accel_x', 'unit' => 'mG'},
5817             9 => +{'name' => 'compressed_calibrated_accel_y', 'unit' => 'mG'},
5818             10 => +{'name' => 'compressed_calibrated_accel_z', 'unit' => 'mG'},
5819             },
5820              
5821             'magnetometer_data' => +{
5822             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5823             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5824             1 => +{'name' => 'sample_time_offset', 'unit' => 'ms'},
5825             2 => +{'name' => 'mag_x', 'unit' => 'counts'},
5826             3 => +{'name' => 'mag_y', 'unit' => 'counts'},
5827             4 => +{'name' => 'mag_z', 'unit' => 'counts'},
5828             5 => +{'name' => 'calibrated_mag_x', 'unit' => 'G'},
5829             6 => +{'name' => 'calibrated_mag_y', 'unit' => 'G'},
5830             7 => +{'name' => 'calibrated_mag_z', 'unit' => 'G'},
5831             },
5832              
5833             'barometer_data' => +{
5834             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5835             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5836             1 => +{'name' => 'sample_time_offset', 'unit' => 'ms'},
5837             2 => +{'name' => 'baro_pres', 'unit' => 'Pa'},
5838             },
5839              
5840             'three_d_sensor_calibration' => +{
5841             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5842             0 => +{'name' => 'sensor_type', 'type_name' => 'sensor_type'},
5843              
5844             1 => +{
5845             'name' => 'calibration_factor',
5846              
5847             'switch' => +{
5848             '_by' => 'sensor_type',
5849             'accelerometer' => +{'name' => 'accel_cal_factor'},
5850             'gyroscope' => +{'name' => 'gyro_cal_factor'},
5851             },
5852             },
5853              
5854             2 => +{'name' => 'calibration_divisor', 'unit' => 'counts'},
5855             3 => +{'name' => 'level_shift'},
5856             4 => +{'name' => 'offset_cal'},
5857             5 => +{'name' => 'orientation_matrix', 'scale' => 65535},
5858             },
5859              
5860             'one_d_sensor_calibration' => +{
5861             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5862             0 => +{'name' => 'sensor_type', 'type_name' => 'sensor_type'},
5863              
5864             1 => +{
5865             'name' => 'calibration_factor',
5866              
5867             'switch' => +{
5868             '_by' => 'sensor_type',
5869             'barometer' => +{'name' => 'baro_cal_factor'},
5870             },
5871             },
5872              
5873             2 => +{'name' => 'calibration_divisor', 'unit' => 'counts'},
5874             3 => +{'name' => 'level_shift'},
5875             4 => +{'name' => 'offset_cal'},
5876             },
5877              
5878             'video_frame' => +{
5879             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5880             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5881             1 => +{'name' => 'frame_number'},
5882             },
5883              
5884             'obdii_data' => +{
5885             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5886             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5887             1 => +{'name' => 'time_offset', 'unit' => 'ms'},
5888             2 => +{'name' => 'pid'},
5889             3 => +{'name' => 'raw_data'},
5890             4 => +{'name' => 'pid_data_size'},
5891             5 => +{'name' => 'system_time'},
5892             6 => +{'name' => 'start_timestamp', 'type_name' => 'date_time'},
5893             7 => +{'name' => 'start_timestamp_ms', 'unit' => 'ms'},
5894             },
5895              
5896             'nmea_sentence' => +{
5897             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5898             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5899             1 => +{'name' => 'sentence'},
5900             },
5901              
5902             'aviation_attitude' => +{
5903             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5904             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5905             1 => +{'name' => 'system_time', 'unit' => 'ms'},
5906             2 => +{'name' => 'pitch', 'scale' => 10430.38, 'unit' => 'radians'},
5907             3 => +{'name' => 'roll', 'scale' => 10430.38, 'unit' => 'radians'},
5908             4 => +{'name' => 'accel_lateral', 'scale' => 100, 'unit' => 'm/s^2'},
5909             5 => +{'name' => 'accel_normal', 'scale' => 100, 'unit' => 'm/s^2'},
5910             6 => +{'name' => 'turn_rate', 'scale' => 1024, 'unit' => 'radians/second'},
5911             7 => +{'name' => 'stage', 'type_name' => 'attitude_stage'},
5912             8 => +{'name' => 'attitude_stage_complete', 'unit' => '%'},
5913             9 => +{'name' => 'track', 'scale' => 10430.38, 'unit' => 'radians'},
5914             10 => +{'name' => 'validity', 'type_name' => 'attitude_validity'},
5915             },
5916              
5917             'video' => +{
5918             0 => +{'name' => 'url'},
5919             1 => +{'name' => 'hosting_provider'},
5920             2 => +{'name' => 'duration', 'unit' => 'ms'},
5921             },
5922              
5923             'video_title' => +{
5924             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5925             0 => +{'name' => 'message_count'},
5926             1 => +{'name' => 'text'},
5927             },
5928              
5929             'video_description' => +{
5930             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5931             0 => +{'name' => 'message_count'},
5932             1 => +{'name' => 'text'},
5933             },
5934              
5935             'video_clip' => +{
5936             0 => +{'name' => 'clip_number'},
5937             1 => +{'name' => 'start_timestamp', 'type_name' => 'date_time'},
5938             2 => +{'name' => 'start_timestamp_ms', 'unit' => 'ms'},
5939             3 => +{'name' => 'end_timestamp', 'type_name' => 'date_time'},
5940             4 => +{'name' => 'end_timestamp_ms', 'unit' => 'ms'},
5941             6 => +{'name' => 'clip_start', 'unit' => 'ms'},
5942             7 => +{'name' => 'clip_end', 'unit' => 'ms'},
5943             },
5944              
5945             'set' => +{
5946             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5947             0 => +{'name' => 'duration', 'scale' => 1000, 'unit' => 's'},
5948             3 => +{'name' => 'repetitions'},
5949             4 => +{'name' => 'weight', 'scale' => 16, 'unit' => 'kg'},
5950             5 => +{'name' => 'set_type', 'type_name' => 'set_type'},
5951             6 => +{'name' => 'start_time', 'type_name' => 'date_time'},
5952             7 => +{'name' => 'category', 'type_name' => 'exercise_category'},
5953             8 => +{'name' => 'category_subtype'},
5954             9 => +{'name' => 'weight_display_unit', 'type_name' => 'fit_base_unit'},
5955             10 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5956             11 => +{'name' => 'wkt_step_index', 'type_name' => 'message_index'},
5957             },
5958              
5959             'jump' => +{
5960             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5961             0 => +{'name' => 'distance', 'unit' => 'm'},
5962             1 => +{'name' => 'height', 'unit' => 'm'},
5963             2 => +{'name' => 'rotations'},
5964             3 => +{'name' => 'hang_time', 'unit' => 's'},
5965             4 => +{'name' => 'score'},
5966             5 => +{'name' => 'position_lat', 'unit' => 'semicircles'},
5967             6 => +{'name' => 'position_long', 'unit' => 'semicircles'},
5968             7 => +{'name' => 'speed', 'scale' => 1000, 'unit' => 'm/s'},
5969             8 => +{'name' => 'enhanced_speed', 'scale' => 1000, 'unit' => 'm/s'},
5970             },
5971              
5972             'split' => +{
5973             254 => +{ 'name' => 'message_index', 'type_name' => 'message_index' },
5974             0 => +{ 'name' => 'split_type', 'type_name' => 'split_type' },
5975             1 => +{ 'name' => 'total_elapsed_time', 'unit' => 's', 'scale' => 1000 },
5976             2 => +{ 'name' => 'total_timer_time', 'unit' => 's', 'scale' => 1000 },
5977             3 => +{ 'name' => 'total_distance', 'unit' => 'm', 'scale' => 100 },
5978             4 => +{ 'name' => 'avg_speed', 'unit' => 'm/s', 'scale' => 1000 },
5979             9 => +{ 'name' => 'start_time', 'type_name' => 'date_time' },
5980             13 => +{ 'name' => 'total_ascent', 'unit' => 'm'},
5981             14 => +{ 'name' => 'total_descent', 'unit' => 'm'},
5982             21 => +{ 'name' => 'start_position_lat', 'unit' => 'semicircles'},
5983             22 => +{ 'name' => 'start_position_long', 'unit' => 'semicircles'},
5984             23 => +{ 'name' => 'end_position_lat', 'unit' => 'semicircles'},
5985             24 => +{ 'name' => 'end_position_long', 'unit' => 'semicircles'},
5986             25 => +{ 'name' => 'max_speed', 'unit' => 'm/s', 'scale' => 1000 },
5987             26 => +{ 'name' => 'avg_vert_speed', 'unit' => 'm/s', 'scale' => 1000 },
5988             27 => +{ 'name' => 'end_time', 'type_name' => 'date_time' },
5989             28 => +{ 'name' => 'total_calories', 'unit' => 'kcal'},
5990             74 => +{ 'name' => 'start_elevation', 'unit' => 'm', 'scale' => 5, 'offset' => 500 },
5991             110 => +{ 'name' => 'total_moving_time', 'unit' => 's', 'scale' => 1000 },
5992             },
5993              
5994             'split_summary' => +{
5995             254 => +{ 'name' => 'message_index', 'type_name' => 'message_index' },
5996             0 => +{ 'name' => 'split_type', 'type_name' => 'split_type' },
5997             3 => +{ 'name' => 'num_splits' },
5998             4 => +{ 'name' => 'total_timer_time', 'unit' => 's', 'scale' => 1000 },
5999             5 => +{ 'name' => 'total_distance', 'unit' => 'm', 'scale' => 100 },
6000             6 => +{ 'name' => 'avg_speed', 'unit' => 'm/s', 'scale' => 1000 },
6001             7 => +{ 'name' => 'max_speed', 'unit' => 'm/s', 'scale' => 1000 },
6002             8 => +{ 'name' => 'total_ascent', 'unit' => 'm' },
6003             9 => +{ 'name' => 'total_descent', 'unit' => 'm' },
6004             10 => +{ 'name' => 'avg_heart_rate', 'unit' => 'bpm' },
6005             11 => +{ 'name' => 'max_heart_rate', 'unit' => 'bpm' },
6006             12 => +{ 'name' => 'avg_vert_speed', 'unit' => 'm/s', 'scale' => 1000 },
6007             13 => +{ 'name' => 'total_calories', 'unit' => 'kcal' },
6008             77 => +{ 'name' => 'total_moving_time', 'unit' => 's', 'scale' => 1000 },
6009             },
6010              
6011             'climb_pro' => +{
6012             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6013             0 => +{'name' => 'position_lat', 'unit' => 'semicircles'},
6014             1 => +{'name' => 'position_long', 'unit' => 'semicircles'},
6015             2 => +{'name' => 'climb_pro_event', 'type_name' => 'climb_pro_event'},
6016             3 => +{'name' => 'climb_number'},
6017             4 => +{'name' => 'climb_category'},
6018             5 => +{'name' => 'current_dist', 'unit' => 'm'},
6019             },
6020              
6021             'field_description' => +{
6022             0 => +{'name' => 'developer_data_index'},
6023             1 => +{'name' => 'field_definition_number'},
6024             2 => +{'name' => 'fit_base_type_id', 'type_name' => 'fit_base_type'},
6025             3 => +{'name' => 'field_name'},
6026             4 => +{'name' => 'array'},
6027             5 => +{'name' => 'components'},
6028             6 => +{'name' => 'scale'},
6029             7 => +{'name' => 'offset'},
6030             8 => +{'name' => 'units'},
6031             9 => +{'name' => 'bits'},
6032             10 => +{'name' => 'accumulate'},
6033             13 => +{'name' => 'fit_base_unit_id', 'type_name' => 'fit_base_unit'},
6034             14 => +{'name' => 'native_mesg_num', 'type_name' => 'mesg_num'},
6035             15 => +{'name' => 'native_field_num'},
6036             },
6037              
6038             'developer_data_id' => +{
6039             0 => +{'name' => 'developer_id'},
6040             1 => +{'name' => 'application_id'},
6041             2 => +{'name' => 'manufacturer_id', 'type_name' => 'manufacturer'},
6042             3 => +{'name' => 'developer_data_index'},
6043             4 => +{'name' => 'application_version'},
6044             },
6045              
6046             'course' => +{ # begins === Course file messages === section
6047             4 => +{'name' => 'sport', 'type_name' => 'sport'},
6048             5 => +{'name' => 'name'},
6049             6 => +{'name' => 'capabilities', 'type_name' => 'course_capabilities'},
6050             7 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
6051             },
6052              
6053             'course_point' => +{
6054             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6055             1 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6056             2 => +{'name' => 'position_lat', 'unit' => 'semicircles'},
6057             3 => +{'name' => 'position_long', 'unit' => 'semicircles'},
6058             4 => +{'name' => 'distance', 'scale' => 100, 'unit' => 'm'},
6059             5 => +{'name' => 'type', 'type_name' => 'course_point'},
6060             6 => +{'name' => 'name'},
6061             8 => +{'name' => 'favorite', 'type_name' => 'bool'},
6062             },
6063              
6064             'segment_id' => +{ # begins === Segment file messages === section
6065             0 => +{'name' => 'name'},
6066             1 => +{'name' => 'uuid'},
6067             2 => +{'name' => 'sport', 'type_name' => 'sport'},
6068             3 => +{'name' => 'enabled', 'type_name' => 'bool'},
6069             4 => +{'name' => 'user_profile_primary_key'},
6070             5 => +{'name' => 'device_id'},
6071             6 => +{'name' => 'default_race_leader'},
6072             7 => +{'name' => 'delete_status', 'type_name' => 'segment_delete_status'},
6073             8 => +{'name' => 'selection_type', 'type_name' => 'segment_selection_type'},
6074             },
6075              
6076             'segment_leaderboard_entry' => +{
6077             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6078             0 => +{'name' => 'name'},
6079             1 => +{'name' => 'type', 'type_name' => 'segment_leaderboard_type'},
6080             2 => +{'name' => 'group_primary_key'},
6081             3 => +{'name' => 'activity_id'},
6082             4 => +{'name' => 'segment_time', 'scale' => 1000, 'unit' => 's'},
6083             5 => +{'name' => 'activity_id_string'},
6084             },
6085              
6086             'segment_point' => +{
6087             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6088             1 => +{'name' => 'position_lat', 'unit' => 'semicircles'},
6089             2 => +{'name' => 'position_long', 'unit' => 'semicircles'},
6090             3 => +{'name' => 'distance', 'scale' => 100, 'unit' => 'm'},
6091             4 => +{'name' => 'altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
6092             5 => +{'name' => 'leader_time', 'scale' => 1000, 'unit' => 's'},
6093             6 => +{ 'name' => 'enhanced_altitude', 'unit' => 'm', 'scale' => 5 }, # Accumulated altitude along the segment at the described point
6094             },
6095              
6096             'segment_lap' => +{
6097             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6098             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6099             0 => +{'name' => 'event', 'type_name' => 'event'},
6100             1 => +{'name' => 'event_type', 'type_name' => 'event_type'},
6101             2 => +{'name' => 'start_time', 'type_name' => 'date_time'},
6102             3 => +{'name' => 'start_position_lat', 'unit' => 'semicircles'},
6103             4 => +{'name' => 'start_position_long', 'unit' => 'semicircles'},
6104             5 => +{'name' => 'end_position_lat', 'unit' => 'semicircles'},
6105             6 => +{'name' => 'end_position_long', 'unit' => 'semicircles'},
6106             7 => +{'name' => 'total_elapsed_time', 'scale' => 1000, 'unit' => 's'},
6107             8 => +{'name' => 'total_timer_time', 'scale' => 1000, 'unit' => 's'},
6108             9 => +{'name' => 'total_distance', 'scale' => 100, 'unit' => 'm'},
6109              
6110             10 => +{
6111             'name' => 'total_cycles', 'unit' => 'cycles',
6112              
6113             'switch' => +{
6114             '_by' => 'sport',
6115             'walking' => +{'name' => 'total_steps', 'unit' => 'steps'},
6116             'running' => +{'name' => 'total_strides', 'unit' => 'strides'},
6117             'swimming' => +{'name' => 'total_strokes', 'unit' => 'strokes'},
6118             },
6119             },
6120              
6121             11 => +{'name' => 'total_calories', 'unit' => 'kcal'},
6122             12 => +{'name' => 'total_fat_calories', 'unit' => 'kcal'},
6123             13 => +{'name' => 'avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
6124             14 => +{'name' => 'max_speed', 'scale' => 1000, 'unit' => 'm/s'},
6125             15 => +{'name' => 'avg_heart_rate', 'unit' => 'bpm'},
6126             16 => +{'name' => 'max_heart_rate', 'unit' => 'bpm'},
6127             17 => +{'name' => 'avg_cadence', 'unit' => 'rpm'},
6128             18 => +{'name' => 'max_cadence', 'unit' => 'rpm'},
6129             19 => +{'name' => 'avg_power', 'unit' => 'watts'},
6130             20 => +{'name' => 'max_power', 'unit' => 'watts'},
6131             21 => +{'name' => 'total_ascent', 'unit' => 'm'},
6132             22 => +{'name' => 'total_descent', 'unit' => 'm'},
6133             23 => +{'name' => 'sport', 'type_name' => 'sport'},
6134             24 => +{'name' => 'event_group'},
6135             25 => +{'name' => 'nec_lat', 'unit' => 'semicircles'},
6136             26 => +{'name' => 'nec_long', 'unit' => 'semicircles'},
6137             27 => +{'name' => 'swc_lat', 'unit' => 'semicircles'},
6138             28 => +{'name' => 'swc_long', 'unit' => 'semicircles'},
6139             29 => +{'name' => 'name'},
6140             30 => +{'name' => 'normalized_power', 'unit' => 'watts'},
6141             31 => +{'name' => 'left_right_balance', 'type_name' => 'left_right_balance_100'},
6142             32 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
6143             33 => +{'name' => 'total_work', 'unit' => 'J'},
6144             34 => +{'name' => 'avg_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
6145             35 => +{'name' => 'max_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
6146             36 => +{'name' => 'gps_accuracy', 'unit' => 'm'},
6147             37 => +{'name' => 'avg_grade', 'scale' => 100, 'unit' => '%'},
6148             38 => +{'name' => 'avg_pos_grade', 'scale' => 100, 'unit' => '%'},
6149             39 => +{'name' => 'avg_neg_grade', 'scale' => 100, 'unit' => '%'},
6150             40 => +{'name' => 'max_pos_grade', 'scale' => 100, 'unit' => '%'},
6151             41 => +{'name' => 'max_neg_grade', 'scale' => 100, 'unit' => '%'},
6152             42 => +{'name' => 'avg_temperature', 'unit' => 'deg.C'},
6153             43 => +{'name' => 'max_temperature', 'unit' => 'deg.C'},
6154             44 => +{'name' => 'total_moving_time', 'scale' => 1000, 'unit' => 's'},
6155             45 => +{'name' => 'avg_pos_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
6156             46 => +{'name' => 'avg_neg_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
6157             47 => +{'name' => 'max_pos_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
6158             48 => +{'name' => 'max_neg_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
6159             49 => +{'name' => 'time_in_hr_zone', 'scale' => 1000, 'unit' => 's'},
6160             50 => +{'name' => 'time_in_speed_zone', 'scale' => 1000, 'unit' => 's'},
6161             51 => +{'name' => 'time_in_cadence_zone', 'scale' => 1000, 'unit' => 's'},
6162             52 => +{'name' => 'time_in_power_zone', 'scale' => 1000, 'unit' => 's'},
6163             53 => +{'name' => 'repetition_num'},
6164             54 => +{'name' => 'min_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
6165             55 => +{'name' => 'min_heart_rate', 'unit' => 'bpm'},
6166             56 => +{'name' => 'active_time', 'scale' => 1000, 'unit' => 's'},
6167             57 => +{'name' => 'wkt_step_index', 'type_name' => 'message_index'},
6168             58 => +{'name' => 'sport_event', 'type_name' => 'sport_event'},
6169             59 => +{'name' => 'avg_left_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
6170             60 => +{'name' => 'avg_right_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
6171             61 => +{'name' => 'avg_left_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
6172             62 => +{'name' => 'avg_right_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
6173             63 => +{'name' => 'avg_combined_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
6174             64 => +{'name' => 'status', 'type_name' => 'segment_lap_status'},
6175             65 => +{'name' => 'uuid'},
6176             66 => +{'name' => 'avg_fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
6177             67 => +{'name' => 'max_fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
6178             68 => +{'name' => 'total_fractional_cycles', 'scale' => 128, 'unit' => 'cycles'},
6179             69 => +{'name' => 'front_gear_shift_count'},
6180             70 => +{'name' => 'rear_gear_shift_count'},
6181             71 => +{'name' => 'time_standing', 'scale' => 1000, 'unit' => 's'},
6182             72 => +{'name' => 'stand_count'},
6183             73 => +{'name' => 'avg_left_pco', 'unit' => 'mm'},
6184             74 => +{'name' => 'avg_right_pco', 'unit' => 'mm'},
6185             75 => +{'name' => 'avg_left_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
6186             76 => +{'name' => 'avg_left_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
6187             77 => +{'name' => 'avg_right_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
6188             78 => +{'name' => 'avg_right_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
6189             79 => +{'name' => 'avg_power_position', 'unit' => 'watts'},
6190             80 => +{'name' => 'max_power_position', 'unit' => 'watts'},
6191             81 => +{'name' => 'avg_cadence_position', 'unit' => 'rpm'},
6192             82 => +{'name' => 'max_cadence_position', 'unit' => 'rpm'},
6193             83 => +{'name' => 'manufacturer', 'type_name' => 'manufacturer'},
6194             84 => +{'name' => 'total_grit', 'unit' => 'kGrit'},
6195             85 => +{'name' => 'total_flow', 'unit' => 'Flow'},
6196             86 => +{'name' => 'avg_grit', 'unit' => 'kGrit'},
6197             87 => +{'name' => 'avg_flow', 'unit' => 'Flow'},
6198             89 => +{'name' => 'total_fractional_ascent', 'unit' => 'm'},
6199             90 => +{'name' => 'total_fractional_descent', 'unit' => 'm'},
6200             91 => +{ 'name' => 'enhanced_avg_altitude', 'unit' => 'm', 'scale' => 5 },
6201             92 => +{ 'name' => 'enhanced_max_altitude', 'unit' => 'm', 'scale' => 5 },
6202             93 => +{ 'name' => 'enhanced_min_altitude', 'unit' => 'm', 'scale' => 5 },
6203             },
6204              
6205             'segment_file' => +{ # begins === Segment list file messages === section
6206             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6207             1 => +{'name' => 'file_uuid'},
6208             3 => +{'name' => 'enabled', 'type_name' => 'bool'},
6209             4 => +{'name' => 'user_profile_primary_key'},
6210             7 => +{'name' => 'leader_type', 'type_name' => 'segment_leaderboard_type'},
6211             8 => +{'name' => 'leader_group_primary_key'},
6212             9 => +{'name' => 'leader_activity_id'},
6213             10 => +{'name' => 'leader_activity_id_string'},
6214             11 => +{'name' => 'default_race_leader'},
6215             },
6216              
6217             'workout' => +{ # begins === Workout file messages === section
6218             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6219             4 => +{'name' => 'sport', 'type_name' => 'sport'},
6220             5 => +{'name' => 'capabilities', 'type_name' => 'workout_capabilities'},
6221             6 => +{'name' => 'num_valid_steps'},
6222             7 => +{'name' => 'protection'}, # not present?
6223             8 => +{'name' => 'wkt_name'},
6224             11 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
6225             14 => +{'name' => 'pool_length', 'scale' => 100, 'unit' => 'm'},
6226             15 => +{'name' => 'pool_length_unit', 'type_name' => 'display_measure'},
6227             },
6228              
6229             'workout_session' => +{
6230             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6231             0 => +{'name' => 'sport', 'type_name' => 'sport'},
6232             1 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
6233             2 => +{'name' => 'num_valid_steps'},
6234             3 => +{'name' => 'first_step_index'},
6235             4 => +{'name' => 'pool_length', 'scale' => 100, 'unit' => 'm'},
6236             5 => +{'name' => 'pool_length_unit', 'type_name' => 'display_measure'},
6237             },
6238              
6239             'workout_step' => +{
6240             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6241             0 => +{'name' => 'wkt_step_name'},
6242             1 => +{'name' => 'duration_type', 'type_name' => 'wkt_step_duration'},
6243              
6244             2 => +{
6245             'name' => 'duration_value',
6246              
6247             'switch' => +{
6248             '_by' => 'duration_type',
6249             'time' => +{'name' => 'duration_time', 'scale' => 1000, 'unit' => 's'},
6250             'repetition_time' => +{'name' => 'duration_time', 'scale' => 1000, 'unit' => 's'},
6251             'distance' => +{'name' => 'duration_distance', 'scale' => 100, 'unit' => 'm'},
6252             'hr_less_than' => +{'name' => 'duration_hr', 'type_name' => 'workout_hr', 'unit' => 'bpm'},
6253             'hr_greater_than' => +{'name' => 'duration_hr', 'type_name' => 'workout_hr', 'unit' => 'bpm'},
6254             'calories' => +{'name' => 'duration_calories', 'unit' => 'kcal'},
6255             'repeat_until_steps_cmplt' => +{'name' => 'duration_step'},
6256             'repeat_until_time' => +{'name' => 'duration_step'},
6257             'repeat_until_distance' => +{'name' => 'duration_step'},
6258             'repeat_until_calories' => +{'name' => 'duration_step'},
6259             'repeat_until_hr_less_than' => +{'name' => 'duration_step'},
6260             'repeat_until_hr_greater_than' => +{'name' => 'duration_step'},
6261             'repeat_until_power_less_than' => +{'name' => 'duration_step'},
6262             'repeat_until_power_greater_than' => +{'name' => 'duration_step'},
6263             'power_less_than' => +{'name' => 'duration_power', 'type_name' => 'workout_power', 'unit' => 'watts'},
6264             'power_greater_than' => +{'name' => 'duration_power', 'type_name' => 'workout_power', 'unit' => 'watts'},
6265             'reps' => +{'name' => 'duration_reps'},
6266             },
6267             },
6268              
6269             3 => +{'name' => 'target_type', 'type_name' => 'wkt_step_target'},
6270              
6271             4 => +{
6272             'name' => 'target_value',
6273              
6274             'switch' => +{
6275             '_by' => [qw(target_type duration_type)],
6276             'speed' => +{'name' => 'target_speed_zone'},
6277             'heart_rate' => +{'name' => 'target_hr_zone'},
6278             'cadence' => +{'name' => 'target_cadence_zone'},
6279             'power' => +{'name' => 'target_power_zone'},
6280             'repeat_until_steps_cmplt' => +{'name' => 'repeat_steps'},
6281             'repeat_until_time' => +{'name' => 'repeat_time', 'scale' => 1000, 'unit' => 's'},
6282             'repeat_until_distance' => +{'name' => 'repeat_distance', 'scale' => 100, 'unit' => 'm'},
6283             'repeat_until_calories' => +{'name' => 'repeat_calories', 'unit' => 'kcal'},
6284             'repeat_until_hr_less_than' => +{'name' => 'repeat_hr', 'type_name' => 'workout_hr', 'unit' => 'bpm'},
6285             'repeat_until_hr_greater_than' => +{'name' => 'repeat_hr', 'type_name' => 'workout_hr', 'unit' => 'bpm'},
6286             'repeat_until_power_less_than' => +{'name' => 'repeat_power', 'type_name' => 'workout_power', 'unit' => 'watts'},
6287             'repeat_until_power_greater_than' => +{'name' => 'repeat_power', 'type_name' => 'workout_power', 'unit' => 'watts'},
6288             'swim_stroke' => +{'name' => 'target_stroke_type', 'type_name' => 'swim_stroke'},
6289             },
6290             },
6291              
6292             5 => +{
6293             'name' => 'custom_target_value_low',
6294              
6295             'switch' => +{
6296             '_by' => 'target_type',
6297             'speed' => +{'name' => 'custom_target_speed_low', 'scale' => 1000, 'unit' => 'm/s'},
6298             'heart_rate' => +{'name' => 'custom_target_heart_rate_low', 'type_name' => 'workout_hr', 'unit' => 'bpm'},
6299             'cadence' => +{'name' => 'custom_target_cadence_low', 'unit' => 'rpm'},
6300             'power' => +{'name' => 'custom_target_power_low', 'type_name' => 'workout_power', 'unit' => 'watts'},
6301             },
6302             },
6303              
6304             6 => +{
6305             'name' => 'custom_target_value_high',
6306              
6307             'switch' => +{
6308             '_by' => 'target_type',
6309             'speed' => +{'name' => 'custom_target_speed_high', 'scale' => 1000, 'unit' => 'm/s'},
6310             'heart_rate' => +{'name' => 'custom_target_heart_rate_high', 'type_name' => 'workout_hr', 'unit' => 'bpm'},
6311             'cadence' => +{'name' => 'custom_target_cadence_high', 'unit' => 'rpm'},
6312             'power' => +{'name' => 'custom_target_power_high', 'type_name' => 'workout_power', 'unit' => 'watts'},
6313             },
6314             },
6315              
6316             7 => +{'name' => 'intensity', 'type_name' => 'intensity'},
6317             8 => +{'name' => 'notes', 'type_name' => 'string'},
6318             9 => +{'name' => 'equipment', 'type_name' => 'workout_equipment'},
6319             10 => +{'name' => 'exercise_category', 'type_name' => 'exercise_category'},
6320             11 => +{'name' => 'exercise_name'},
6321             12 => +{'name' => 'exercise_weight', 'scale' => 100, 'unit' => 'kg'},
6322             13 => +{'name' => 'weight_display_unit', 'type_name' => 'fit_base_unit'},
6323             19 => +{'name' => 'secondary_target_type', 'type_name' => 'wkt_step_target'},
6324             20 => +{
6325             'name' => 'secondary_target_value',
6326             'switch' => +{
6327             '_by' => 'secondary_target_type',
6328             'speed' => +{'name' => 'secondary_target_speed_zone'},
6329             'heart_rate' => +{'name' => 'secondary_target_hr_zone'},
6330             'cadence' => +{'name' => 'secondary_target_cadence_zone'},
6331             'power' => +{'name' => 'secondary_target_power_zone'},
6332             'swim_stroke' => +{'name' => 'secondary_target_stroke_type', 'type_name' => 'swim_stroke'},
6333             },
6334             },
6335             },
6336              
6337             'exercise_title' => +{
6338             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6339             0 => +{'name' => 'exercise_category', 'type_name' => 'exercise_category'},
6340             1 => +{'name' => 'exercise_name'},
6341             2 => +{'name' => 'wkt_step_name', 'type_name' => 'string'},
6342             },
6343              
6344             'schedule' => +{ # begins === Schedule file messages === section
6345             0 => +{'name' => 'manufacturer', 'type_name' => 'manufacturer'},
6346              
6347             1 => +{
6348             'name' => 'product',
6349              
6350             'switch' => +{
6351             '_by' => 'manufacturer',
6352             'garmin' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
6353             'dynastream' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
6354             'dynastream_oem' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
6355             'favero_electronics' => +{'name' => 'favero_product', 'type_name' => 'favero_product'},
6356             'tacx' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
6357             },
6358             },
6359              
6360             2 => +{'name' => 'serial_number'},
6361             3 => +{'name' => 'time_created', 'type_name' => 'date_time'},
6362             4 => +{'name' => 'completed', 'type_name' => 'bool'},
6363             5 => +{'name' => 'type', 'type_name' => 'schedule'},
6364             6 => +{'name' => 'schedule_time', 'type_name' => 'local_date_time'},
6365             },
6366              
6367             'totals' => +{ # begins === Totals file messages === section
6368             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6369             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6370             0 => +{'name' => 'timer_time', 'unit' => 's'},
6371             1 => +{'name' => 'distance', 'unit' => 'm'},
6372             2 => +{'name' => 'calories', 'unit' => 'kcal'},
6373             3 => +{'name' => 'sport', 'type_name' => 'sport'},
6374             4 => +{'name' => 'elapsed_time', 'unit' => 's'},
6375             5 => +{'name' => 'sessions'},
6376             6 => +{'name' => 'active_time', 'unit' => 's'},
6377             9 => +{'name' => 'sport_index'},
6378             10 => +{'name' => 'profile_name'}, # unknown STRING
6379             },
6380              
6381             'weight_scale' => +{ # begins === Weight scale file messages === section
6382             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6383             0 => +{'name' => 'weight', 'type_name' => 'weight', 'scale' => 100, 'unit' => 'kg'},
6384             1 => +{'name' => 'percent_fat', 'scale' => 100, 'unit' => '%'},
6385             2 => +{'name' => 'percent_hydration', 'scale' => 100, 'unit' => '%'},
6386             3 => +{'name' => 'visceral_fat_mass', 'scale' => 100, 'unit' => 'kg'},
6387             4 => +{'name' => 'bone_mass', 'scale' => 100, 'unit' => 'kg'},
6388             5 => +{'name' => 'muscle_mass', 'scale' => 100, 'unit' => 'kg'},
6389             7 => +{'name' => 'basal_met', 'scale' => 4, 'unit' => 'kcal/day'},
6390             8 => +{'name' => 'physique_rating'},
6391             9 => +{'name' => 'active_met', 'scale' => 4, 'unit' => 'kcal/day'},
6392             10 => +{'name' => 'metabolic_age', 'unit' => 'years'},
6393             11 => +{'name' => 'visceral_fat_rating'},
6394             12 => +{'name' => 'user_profile_index', 'type_name' => 'message_index'},
6395             13 => +{'name' => 'bmi', 'scale' => 10, 'unit' => 'kg/m^2'},
6396             },
6397              
6398             'blood_pressure' => +{ # begins === Blood pressure file messages === section
6399             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6400             0 => +{'name' => 'systolic_pressure', 'unit' => 'mmHg'},
6401             1 => +{'name' => 'diastolic_pressure', 'unit' => 'mmHg'},
6402             2 => +{'name' => 'mean_arterial_pressure', 'unit' => 'mmHg'},
6403             3 => +{'name' => 'map_3_sample_mean', 'unit' => 'mmHg'},
6404             4 => +{'name' => 'map_morning_values', 'unit' => 'mmHg'},
6405             5 => +{'name' => 'map_evening_values', 'unit' => 'mmHg'},
6406             6 => +{'name' => 'heart_rate', 'unit' => 'bpm'},
6407             7 => +{'name' => 'heart_rate_type', 'type_name' => 'hr_type'},
6408             8 => +{'name' => 'status', 'type_name' => 'bp_status'},
6409             9 => +{'name' => 'user_profile_index', 'type_name' => 'message_index'},
6410             },
6411              
6412             'monitoring_info' => +{ # begins === Monitoring file messages === section
6413             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6414             0 => +{'name' => 'local_timestamp', 'type_name' => 'local_date_time'},
6415             1 => +{'name' => 'activity_type', 'type_name' => 'activity_type'},
6416             3 => +{'name' => 'cycles_to_distance', 'scale' => 5000, 'unit' => 'm/cycle'},
6417             4 => +{'name' => 'cycles_to_calories', 'scale' => 5000, 'unit' => 'kcal/cycle'},
6418             5 => +{'name' => 'resting_metabolic_rate', 'unit' => 'kcal/day'},
6419             },
6420              
6421             'monitoring' => +{
6422             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6423             0 => +{'name' => 'device_index', 'type_name' => 'device_index'},
6424             1 => +{'name' => 'calories', 'unit' => 'kcal'},
6425             2 => +{'name' => 'distance', 'scale' => 100, 'unit' => 'm'},
6426              
6427             3 => +{
6428             'name' => 'cycles', 'scale' => 2, 'unit' => 'cycles',
6429              
6430             'switch' => +{
6431             '_by' => 'activity_type',
6432             'walking' => +{'name' => 'total_steps', 'scale' => 1, 'unit' => 'steps'},
6433             'running' => +{'name' => 'total_strides', 'scale' => 1, 'unit' => 'strides'},
6434             'cycling' => +{'name' => 'total_strokes', 'scale' => 2, 'unit' => 'strokes'},
6435             'swimming' => +{'name' => 'total_strokes', 'scale' => 2, 'unit' => 'strokes'},
6436             },
6437             },
6438              
6439             4 => +{'name' => 'active_time', 'scale' => 1000, 'unit' => 's'},
6440             5 => +{'name' => 'activity_type', 'type_name' => 'activity_type'},
6441             6 => +{'name' => 'activity_subtype', 'type_name' => 'activity_subtype'},
6442             7 => +{'name' => 'activity_level', 'type_name' => 'activity_level'},
6443             8 => +{'name' => 'distance_16', 'scale' => 100, 'unit' => 'm'},
6444             9 => +{'name' => 'cycles_16', 'scale' => 2, 'unit' => 'cycles'},
6445             10 => +{'name' => 'active_time_16', 'unit' => 's'},
6446             11 => +{'name' => 'local_timestamp', 'type_name' => 'local_date_time'},
6447             12 => +{'name' => 'temperature', 'scale' => 100, 'unit' => 'deg.C'},
6448             14 => +{'name' => 'temperature_min', 'scale' => 100, 'unit' => 'deg.C'},
6449             15 => +{'name' => 'temperature_max', 'scale' => 100, 'unit' => 'deg.C'},
6450             16 => +{'name' => 'activity_time', 'unit' => 'min'},
6451             19 => +{'name' => 'active_calories', 'unit' => 'kcal'},
6452             24 => +{'name' => 'current_activity_type_intensity'}, # complex decoding!
6453             25 => +{'name' => 'timestamp_min_8', 'unit' => 'min'},
6454             26 => +{'name' => 'timestamp_16', 'unit' => 's'},
6455             27 => +{'name' => 'heart_rate', 'unit' => 'bpm'},
6456             28 => +{'name' => 'intensity', 'scale' => 10},
6457             29 => +{'name' => 'duration_min', 'unit' => 'min'},
6458             30 => +{'name' => 'duration', 'unit' => 's'},
6459             31 => +{'name' => 'ascent', 'scale' => 1000, 'unit' => 'm'},
6460             32 => +{'name' => 'descent', 'scale' => 1000, 'unit' => 'm'},
6461             33 => +{'name' => 'moderate_activity_minutes', 'unit' => 'min'},
6462             34 => +{'name' => 'vigorous_activity_minutes', 'unit' => 'min'},
6463             },
6464              
6465             'monitoring_hr_data' => +{
6466             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'}, # Must align to logging interval, for example, time must be 00:00:00 for daily log.
6467             0 => +{'name' => 'resting_heart_rate', 'unit' => 'bpm'}, # 7-day rolling average
6468             1 => +{'name' => 'current_day_resting_heart_rate', 'unit' => 'bpm'}, # RHR for today only. (Feeds into 7-day average)
6469             },
6470              
6471             'spo2_data' => +{
6472             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6473             0 => +{'name' => 'reading_spo2', 'unit' => 'percent'},
6474             1 => +{'name' => 'reading_confidence'},
6475             2 => +{'name' => 'mode', 'type_name' => 'spo2_measurement_type'}, # Mode when data was captured
6476             },
6477              
6478             'hr' => +{
6479             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6480             0 => +{'name' => 'fractional_timestamp', 'scale' => 32768, 'unit' => 's'},
6481             1 => +{'name' => 'time256', 'scale' => 256, 'unit' => 's'},
6482             6 => +{'name' => 'filtered_bpm', 'unit' => 'bpm'},
6483             9 => +{'name' => 'event_timestamp', 'scale' => 1024, 'unit' => 's'},
6484             10 => +{'name' => 'event_timestamp_12', 'scale' => 1024, 'unit' => 's'},
6485             },
6486              
6487             'stress_level' => +{
6488             0 => +{'name' => 'stress_level_value'},
6489             1 => +{'name' => 'stress_level_time', 'type_name' => 'date_time', 'unit' => 's'},
6490             },
6491              
6492             'max_met_data' => +{
6493             0 => +{'name' => 'update_time', 'type_name' => 'date_time'}, # Time maxMET and vo2 were calculated
6494             2 => +{'name' => 'vo2_max', 'unit' => 'mL/kg/min'},
6495             5 => +{'name' => 'sport', 'type_name' => 'sport'},
6496             6 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
6497             8 => +{'name' => 'max_met_category', 'type_name' => 'max_met_category'},
6498             9 => +{'name' => 'calibrated_data', 'type_name' => 'bool'}, # Indicates if calibrated data was used in the calculation
6499             12 => +{'name' => 'hr_source', 'type_name' => 'max_met_heart_rate_source'}, # Indicates if the estimate was obtained using a chest strap or wrist heart rate
6500             13 => +{'name' => 'speed_source', 'type_name' => 'max_met_speed_source'}, # Indidcates if the estimate was obtained using onboard GPS or connected GPS
6501             },
6502              
6503             'hsa_body_battery_data' => +{ # Body battery data used for HSA custom data logging
6504             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6505             0 => +{'name' => 'processing_interval', 'unit' => 's'}, # Processing interval length in seconds
6506             1 => +{'name' => 'level', 'unit' => 'percent'}, # Body battery level
6507             2 => +{'name' => 'charged' }, # Body battery charged value
6508             3 => +{'name' => 'uncharged' }, # Body battery uncharged value
6509             },
6510              
6511             'hsa_event' => +{ # HSA events
6512             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6513             0 => +{'name' => 'event_id'}, # Event ID
6514             },
6515              
6516             'hsa_accelerometer_data' => +{ # Raw accelerometer data used for HSA custom data logging
6517             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6518             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'}, # Millisecond resolution of the timestamp
6519             1 => +{'name' => 'sampling_interval', 'unit' => 'ms'}, # Sampling Interval in Milliseconds
6520             2 => +{'name' => 'accel_x', 'scale' => 1.024, 'unit' => 'mG'}, # X-Axis Measurement
6521             3 => +{'name' => 'accel_y', 'scale' => 1.024, 'unit' => 'mG'}, # Y-Axis Measurement
6522             4 => +{'name' => 'accel_z', 'scale' => 1.024, 'unit' => 'mG'}, # Z-Axis Measurement
6523             5 => +{'name' => 'timestamp_32k'}, # 32 kHz timestamp
6524             },
6525              
6526             'hsa_gyroscope_data' => +{
6527             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6528             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'}, # Millisecond resolution of the timestamp
6529             1 => +{'name' => 'sampling_interval', 'unit' => '1/32768 s'}, # Sampling Interval in 32 kHz timescale
6530             2 => +{'name' => 'gyro_x', 'scale' => 28.57143, 'unit' => 'deg/s'}, # X-Axis Measurement
6531             3 => +{'name' => 'gyro_y', 'scale' => 28.57143, 'unit' => 'deg/s'}, # Y-Axis Measurement
6532             4 => +{'name' => 'gyro_z', 'scale' => 28.57143, 'unit' => 'deg/s'}, # Z-Axis Measurement
6533             5 => +{'name' => 'timestamp_32k', 'unit' => '1/32768 s'}, # 32 kHz timestamp
6534             },
6535              
6536             'hsa_step_data' => +{ # User's current daily step data used for HSA custom data logging
6537             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6538             0 => +{'name' => 'processing_interval', 'unit' => 's'}, # Processing interval length in seconds
6539             1 => +{'name' => 'steps', 'unit' => 'steps'}, # Total step sum
6540             },
6541              
6542             'hsa_spo2_data' => +{ # User's current SpO2 data used for HSA custom data logging
6543             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6544             0 => +{'name' => 'processing_interval', 'unit' => 's'}, # Processing interval length in seconds
6545             1 => +{'name' => 'reading_spo2', 'unit' => 'percent'}, # SpO2 Reading
6546             2 => +{'name' => 'confidence'}, # SpO2 Confidence
6547             },
6548              
6549             'hsa_stress_data' => +{ # User's current stress data used for HSA custom data logging
6550             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6551             0 => +{'name' => 'processing_interval', 'unit' => 's'}, # Processing interval length in seconds
6552             1 => +{'name' => 'stress_level', 'unit' => 's'}, # Stress Level ( 0 - 100 ) -300 indicates invalid -200 indicates large motion -100 indicates off wrist
6553             },
6554              
6555             'hsa_respiration_data' => +{ # User's current respiration data used for HSA custom data logging
6556             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6557             0 => +{'name' => 'processing_interval', 'unit' => 's'}, # Processing interval length in seconds
6558             1 => +{'name' => 'respiration_rate', 'scale' => 100, 'unit' => 'breaths/min'}, # Breaths * 100 /min -300 indicates invalid -200 indicates large motion -100 indicates off wrist
6559             },
6560              
6561             'hsa_heart_rate_data' => +{ # User's current heart rate data used for HSA custom data logging
6562             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6563             0 => +{'name' => 'processing_interval', 'unit' => 's'}, # Processing interval length in seconds
6564             1 => +{'name' => 'status'}, # Status of measurements in buffer - 0 indicates SEARCHING 1 indicates LOCKED
6565             2 => +{'name' => 'heart_rate', 'unit' => 'bpm'}, # Beats / min
6566             },
6567              
6568             'hsa_configuration_data' => +{ # Configuration data for HSA custom data logging
6569             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'}, # Encoded configuration data
6570             0 => +{'name' => 'data'},
6571             1 => +{'name' => 'data_size'}, # Size in bytes of data field
6572             },
6573              
6574             'hsa_wrist_temperature_data' => +{ # Wrist temperature data used for HSA custom data logging
6575             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6576             0 => +{'name' => 'processing_interval', 'unit' => 's'}, # Processing interval length in seconds
6577             1 => +{'name' => 'value', 'scale' => 1000, 'unit' => 'degC'}, # Wrist temperature reading
6578             },
6579              
6580             'memo_glob' => +{ # begins === Other messages === section
6581             250 => +{'name' => 'part_index'},
6582             0 => +{'name' => 'memo'},
6583             1 => +{'name' => 'message_number'},
6584             2 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6585             },
6586              
6587             'sleep_level' => +{
6588             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6589             0 => +{'name' => 'sleep_level', 'type_name' => 'sleep_level'},
6590             },
6591              
6592             'ant_channel_id' => +{
6593             0 => +{'name' => 'channel_number'},
6594             1 => +{'name' => 'device_type'},
6595             2 => +{'name' => 'device_number'},
6596             3 => +{'name' => 'transmission_type'},
6597             4 => +{'name' => 'device_index', 'type_name' => 'device_index'},
6598             },
6599              
6600             'ant_rx' => +{
6601             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6602             0 => +{'name' => 'fractional_timestamp', 'scale' => 32768, 'unit' => 's'},
6603             1 => +{'name' => 'mesg_id'},
6604             2 => +{'name' => 'mesg_data'},
6605             3 => +{'name' => 'channel_number'},
6606             4 => +{'name' => 'data'},
6607             },
6608              
6609             'ant_tx' => +{
6610             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6611             0 => +{'name' => 'fractional_timestamp', 'scale' => 32768, 'unit' => 's'},
6612             1 => +{'name' => 'mesg_id'},
6613             2 => +{'name' => 'mesg_data'},
6614             3 => +{'name' => 'channel_number'},
6615             4 => +{'name' => 'data'},
6616             },
6617              
6618             'exd_screen_configuration' => +{
6619             0 => +{'name' => 'screen_index'},
6620             1 => +{'name' => 'field_count'},
6621             2 => +{'name' => 'layout', 'type_name' => 'exd_layout'},
6622             3 => +{'name' => 'screen_enabled', 'type_name' => 'bool'},
6623             },
6624              
6625             'exd_data_field_configuration' => +{
6626             0 => +{'name' => 'screen_index'},
6627             1 => +{'name' => 'concept_field'}, # complex decoding!
6628             2 => +{'name' => 'field_id'},
6629             3 => +{'name' => 'concept_count'},
6630             4 => +{'name' => 'display_type', 'type_name' => 'exd_display_type'},
6631             5 => +{'name' => 'title'},
6632             },
6633              
6634             'exd_data_concept_configuration' => +{
6635             0 => +{'name' => 'screen_index'},
6636             1 => +{'name' => 'concept_field'}, # complex decoding!
6637             2 => +{'name' => 'field_id'},
6638             3 => +{'name' => 'concept_index'},
6639             4 => +{'name' => 'data_page'},
6640             5 => +{'name' => 'concept_key'},
6641             6 => +{'name' => 'scaling'},
6642             7 => +{'name' => 'unknown7'}, # unknown UINT8
6643             8 => +{'name' => 'data_units', 'type_name' => 'exd_data_units'},
6644             9 => +{'name' => 'qualifier', 'type_name' => 'exd_qualifiers'},
6645             10 => +{'name' => 'descriptor', 'type_name' => 'exd_descriptors'},
6646             11 => +{'name' => 'is_signed', 'type_name' => 'bool'},
6647             },
6648              
6649             'dive_summary' => +{
6650             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6651             0 => +{'name' => 'reference_mesg', 'type_name' => 'mesg_num'},
6652             1 => +{'name' => 'reference_index', 'type_name' => 'message_index'},
6653             2 => +{'name' => 'avg_depth', 'scale' => 1000, 'unit' => 'm'},
6654             3 => +{'name' => 'max_depth', 'scale' => 1000, 'unit' => 'm'},
6655             4 => +{'name' => 'surface_interval', 'unit' => 's'},
6656             5 => +{'name' => 'start_cns', 'unit' => '%'},
6657             6 => +{'name' => 'end_cns', 'unit' => '%'},
6658             7 => +{'name' => 'start_n2', 'unit' => '%'},
6659             8 => +{'name' => 'end_n2', 'unit' => '%'},
6660             9 => +{'name' => 'o2_toxicity'},
6661             10 => +{'name' => 'dive_number'},
6662             11 => +{'name' => 'bottom_time', 'scale' => 1000, 'unit' => 's'},
6663             12 => +{ 'name' => 'avg_pressure_sac', 'unit' => 'bar/min', 'scale' => 100 },# Average pressure-based surface air consumption
6664             13 => +{ 'name' => 'avg_volume_sac', 'unit' => 'l/min', 'scale' => 100 }, # Average volumetric surface air consumption
6665             14 => +{ 'name' => 'avg_rmv', 'unit' => 'l/min', 'scale' => 100 }, # Average respiratory minute volume
6666             15 => +{ 'name' => 'descent_time', 'unit' => 's', 'scale' => 1000 }, # Time to reach deepest level stop
6667             16 => +{ 'name' => 'ascent_time', 'unit' => 's', 'scale' => 1000 }, # Time after leaving bottom until reaching surface
6668             17 => +{ 'name' => 'avg_ascent_rate', 'unit' => 'm/s', 'scale' => 1000 }, # Average ascent rate, not including descents or stops
6669             22 => +{ 'name' => 'avg_descent_rate', 'unit' => 'm/s', 'scale' => 1000 }, # Average descent rate, not including ascents or stops
6670             23 => +{ 'name' => 'max_ascent_rate', 'unit' => 'm/s', 'scale' => 1000 }, # Maximum ascent rate
6671             24 => +{ 'name' => 'max_descent_rate', 'unit' => 'm/s', 'scale' => 1000 }, # Maximum descent rate
6672             25 => +{ 'name' => 'hang_time', 'unit' => 's', 'scale' => 1000 }, # Time spent neither ascending nor descending
6673             },
6674              
6675             'aad_accel_features' => +{ # Number of acclerometer zero crossings summed over the specified time interval
6676             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6677             0 => +{'name' => 'time', 'unit' => 's'}, # Time interval length in seconds
6678             1 => +{'name' => 'energy_total'}, # Total accelerometer energy in the interval
6679             2 => +{'name' => 'zero_cross_cnt'}, # Count of zero crossings
6680             3 => +{'name' => 'instance'}, # Instance ID of zero crossing algorithm
6681             4 => +{'name' => 'time_above_threshold', 'scale' => 25, 'unit' => 's'}, # Total accelerometer time above threshold in the interval
6682             },
6683              
6684             'hrv' => +{ # heart rate variability
6685             0 => +{'name' => 'time', 'scale' => 1000, 'unit' => 's'},
6686             },
6687              
6688             'beat_intervals' => +{
6689             253 => +{ 'name' => 'timestamp', 'type_name' => 'date_time' },
6690             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'}, # Milliseconds past date_time
6691             1 => +{'name' => 'time', 'unit' => 'ms'}, # Array of millisecond times between beats
6692             },
6693              
6694             'hrv_status_summary' => +{
6695             253 => +{ 'name' => 'timestamp', 'type_name' => 'date_time' },
6696             0 => +{'name' => 'weekly_average', 'scale' => 128, 'unit' => 'ms'}, # 5 minute RMSSD
6697             1 => +{'name' => 'last_night_average', 'scale' => 128, 'unit' => 'ms'}, # 7 day RMSSD average over sleep
6698             2 => +{'name' => 'last_night_5_min_high', 'scale' => 128, 'unit' => 'ms'}, # Last night RMSSD average over sleep
6699             3 => +{'name' => 'baseline_low_upper', 'scale' => 128, 'unit' => 'ms'}, # 5 minute high RMSSD value over sleep
6700             4 => +{'name' => 'baseline_balanced_lower', 'scale' => 128, 'unit' => 'ms'}, # 3 week baseline, upper boundary of low HRV status
6701             5 => +{'name' => 'baseline_balanced_upper', 'scale' => 128, 'unit' => 'ms'}, # 3 week baseline, lower boundary of balanced HRV status
6702             6 => +{'name' => 'hrv_status', 'type_name' => 'hrv_status'},
6703             },
6704              
6705             'hrv_value' => +{
6706             253 => +{ 'name' => 'timestamp', 'type_name' => 'date_time' },
6707             0 => +{'name' => 'value', 'scale' => 128, 'unit' => 'ms'}, # 5 minute RMSSD
6708             },
6709              
6710             'raw_bbi' => +{ # Raw Beat-to-Beat Interval values
6711             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6712             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'}, # ms since last overnight_raw_bbi message
6713             1 => +{'name' => 'data'}, # Complex decoding!
6714             2 => +{'name' => 'time', 'unit' => 'ms'}, # Array of millisecond times between beats
6715             3 => +{'name' => 'quality'},
6716             4 => +{'name' => 'gap'},
6717             },
6718              
6719             'respiration_rate' => +{
6720             253 => +{ 'name' => 'timestamp', 'type_name' => 'date_time' },
6721             0 => +{ 'name' => 'respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 }, # Breaths * 100 /min, -300 indicates invalid, -200 indicates large motion, -100 indicates off wrist
6722             },
6723              
6724             'chrono_shot_session' => +{ # Specifically used for XERO products.
6725             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6726             0 => +{'name' => 'min_speed', 'scale' => 1000, 'unit' => 'm/s'},
6727             1 => +{'name' => 'max_speed', 'scale' => 1000, 'unit' => 'm/s'},
6728             2 => +{'name' => 'avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
6729             3 => +{'name' => 'shot_count'},
6730             4 => +{'name' => 'projectile_type', 'type_name' => 'projectile_type'},
6731             5 => +{'name' => 'grain_weight', 'scale' => 10, 'unit' => 'gr'},
6732             },
6733              
6734             'chrono_shot_data' => +{ # Specifically used for XERO products.
6735             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6736             0 => +{'name' => 'shot_speed', 'scale' => 1000, 'unit' => 'm/s'},
6737             1 => +{'name' => 'shot_num'},
6738             },
6739              
6740             'tank_update' => +{
6741             253 => +{ 'name' => 'timestamp', 'type_name' => 'date_time' },
6742             0 => +{ 'name' => 'sensor', 'type_name' => 'ant_channel_id' },
6743             1 => +{ 'name' => 'pressure', 'unit' => 'bar', 'scale' => 100 },
6744             },
6745              
6746             'tank_summary' => +{
6747             253 => +{ 'name' => 'timestamp', 'type_name' => 'date_time' },
6748             0 => +{ 'name' => 'sensor', 'type_name' => 'ant_channel_id' },
6749             1 => +{ 'name' => 'start_pressure', 'unit' => 'bar', 'scale' => 100 },
6750             2 => +{ 'name' => 'end_pressure', 'unit' => 'bar', 'scale' => 100 },
6751             3 => +{ 'name' => 'volume_used', 'unit' => 'l', 'scale' => 100 },
6752             },
6753              
6754             'sleep_assessment' => +{
6755             0 => +{ 'name' => 'combined_awake_score' }, # Average of awake_time_score and awakenings_count_score. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID.
6756             1 => +{ 'name' => 'awake_time_score' }, # Score that evaluates the total time spent awake between sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID.
6757             2 => +{ 'name' => 'awakenings_count_score' }, # Score that evaluates the number of awakenings that interrupt sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID.
6758             3 => +{ 'name' => 'deep_sleep_score' }, # Score that evaluates the amount of deep sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID.
6759             4 => +{ 'name' => 'sleep_duration_score' }, # Score that evaluates the quality of sleep based on sleep stages, heart-rate variability and possible awakenings during the night. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID.
6760             5 => +{ 'name' => 'light_sleep_score', }, # Score that evaluates the amount of light sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID.
6761             6 => +{ 'name' => 'overall_sleep_score' }, # Total score that summarizes the overall quality of sleep, combining sleep duration and quality. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID.
6762             7 => +{ 'name' => 'sleep_quality_score' }, # Score that evaluates the quality of sleep based on sleep stages, heart-rate variability and possible awakenings during the night. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID.
6763             8 => +{ 'name' => 'sleep_recovery_score' }, # Score that evaluates stress and recovery during sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID.
6764             9 => +{ 'name' => 'rem_sleep_score' }, # Score that evaluates the amount of REM sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID.
6765             10 => +{ 'name' => 'sleep_restlessness_score' }, # Score that evaluates the amount of restlessness during sleep. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID.
6766             11 => +{ 'name' => 'awakenings_count' }, # The number of awakenings during sleep.
6767             14 => +{ 'name' => 'interruptions_score' }, # Score that evaluates the sleep interruptions. If valid: 0 (worst) to 100 (best). If unknown: FIT_UINT8_INVALID.
6768             15 => +{ 'name' => 'average_stress_during_sleep' }, # Excludes stress during awake periods in the sleep window
6769             },
6770              
6771             'skin_temp_overnight' => +{
6772             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6773             0 => +{'name' => 'local_timestamp', 'type_name' => 'local_date_time'},
6774             1 => +{'name' => 'average_deviation'}, # The average overnight deviation from baseline temperature in degrees C
6775             2 => +{'name' => 'average_7_day_deviation'}, # The average 7 day overnight deviation from baseline temperature in degrees C
6776             4 => +{'name' => 'nightly_value'}, # Final overnight temperature value
6777             },
6778              
6779             'pad' => +{
6780             0 => +{'name' => 'pad'},
6781             },
6782              
6783             'source' => +{ # begins === Undocumented messages === section
6784             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6785             # device_index in device_info
6786             0 => +{'name' => 'unknown0', 'type_name' => 'device_index'}, # unknown UINT8
6787             1 => +{'name' => 'unknown1', 'type_name' => 'device_index'}, # unknown UINT8
6788             2 => +{'name' => 'unknown2', 'type_name' => 'device_index'}, # unknown UINT8
6789             3 => +{'name' => 'unknown3', 'type_name' => 'device_index'}, # unknown UINT8
6790             4 => +{'name' => 'unknown4', 'type_name' => 'device_index'}, # unknown UINT8
6791             5 => +{'name' => 'unknown5'}, # unknown ENUM
6792             6 => +{'name' => 'unknown6'}, # unknown UINT8
6793             7 => +{'name' => 'unknown7'}, # unknown UINT8
6794             8 => +{'name' => 'unknown8'}, # unknown UINT8
6795             9 => +{'name' => 'unknown9'}, # unknown UINT8
6796             },
6797              
6798             'location' => +{
6799             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6800             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6801             0 => +{'name' => 'name'}, # unknown STRING
6802             1 => +{'name' => 'position_lat', 'unit' => 'semicircles'}, # unknown SINT32
6803             2 => +{'name' => 'position_long', 'unit' => 'semicircles'}, # unknown SINT32
6804             3 => +{'name' => 'unknown3'}, # unknown UINT16 (elevation?)
6805             4 => +{'name' => 'unknown4'}, # unknown UINT16
6806             5 => +{'name' => 'unknown5'}, # unknown UINT16
6807             6 => +{'name' => 'unknown6'}, # unknown STRING
6808             },
6809              
6810             'battery' => +{
6811             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6812             0 => +{'name' => 'unknown0'}, # unknown UINT16 (voltage with scale?)
6813             1 => +{'name' => 'unknown1'}, # unknown SINT16
6814             2 => +{'name' => 'charge_level', 'unit' => '%'}, # unknown UINT8
6815             3 => +{'name' => 'temperature', 'unit' => 'deg.C'}, # unknown SINT8
6816             },
6817              
6818             'sensor' => +{
6819             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6820             0 => +{'name' => 'unknown0'}, # unknown UINT32Z
6821             1 => +{'name' => 'unknown1'}, # unknown UINT8
6822             2 => +{'name' => 'sensor_id'}, # unknown STRING
6823             3 => +{'name' => 'unknown3'}, # unknown ENUM
6824             4 => +{'name' => 'unknown4'}, # unknown ENUM
6825             5 => +{'name' => 'unknown5'}, # unknown ENUM
6826             6 => +{'name' => 'unknown6'}, # unknown ENUM
6827             7 => +{'name' => 'unknown7'}, # unknown ENUM
6828             8 => +{'name' => 'unknown8'}, # unknown ENUM
6829             9 => +{'name' => 'unknown9'}, # unknown UINT8
6830             10 => +{'name' => 'wheel_size', 'unit' => 'mm'}, # unknown UINT16
6831             11 => +{'name' => 'unknown11'}, # unknown UINT16
6832             12 => +{'name' => 'unknown12'}, # unknown UINT8
6833             13 => +{'name' => 'unknown13'}, # unknown UINT32
6834             14 => +{'name' => 'unknown14'}, # unknown UINT8
6835             15 => +{'name' => 'unknown15'}, # unknown UINT8
6836             16 => +{'name' => 'unknown16'}, # unknown UINT8
6837             17 => +{'name' => 'unknown17'}, # unknown UINT8Z
6838             18 => +{'name' => 'unknown18'}, # unknown UINT8Z (array[4])
6839             19 => +{'name' => 'unknown19'}, # unknown UINT8Z
6840             20 => +{'name' => 'unknown20'}, # unknown UINT8Z (array[12])
6841             21 => +{'name' => 'unknown21'}, # unknown UINT16
6842             25 => +{'name' => 'unknown25'}, # unknown UINT16
6843             26 => +{'name' => 'unknown26'}, # unknown UINT16
6844             27 => +{'name' => 'unknown27'}, # unknown UINT8
6845             28 => +{'name' => 'unknown28'}, # unknown UINT8 (array[4])
6846             29 => +{'name' => 'unknown29'}, # unknown UINT8 (array[4])
6847             30 => +{'name' => 'unknown30'}, # unknown UINT8 (array[4])
6848             31 => +{'name' => 'unknown31'}, # unknown UINT8
6849             32 => +{'name' => 'unknown32'}, # unknown UINT16
6850             33 => +{'name' => 'unknown33'}, # unknown UINT16
6851             34 => +{'name' => 'unknown34'}, # unknown UINT16
6852             35 => +{'name' => 'unknown35'}, # unknown UINT16
6853             36 => +{'name' => 'unknown36'}, # unknown ENUM
6854             37 => +{'name' => 'unknown37'}, # unknown ENUM (array[7])
6855             38 => +{'name' => 'unknown38'}, # unknown ENUM (array[7])
6856             39 => +{'name' => 'unknown39'}, # unknown ENUM (array[7])
6857             40 => +{'name' => 'unknown40'}, # unknown UINT16Z
6858             41 => +{'name' => 'unknown41'}, # unknown UINT8 (array[7])
6859             42 => +{'name' => 'unknown42'}, # unknown ENUM
6860             43 => +{'name' => 'unknown43'}, # unknown ENUM
6861             44 => +{'name' => 'unknown44'}, # unknown UINT8Z
6862             47 => +{'name' => 'unknown47'}, # unknown ENUM
6863             48 => +{'name' => 'unknown48'}, # unknown ENUM
6864             },
6865              
6866             );
6867              
6868             my %msgtype_by_num = (
6869             13 => +{ # begins === Unknown messages === section
6870             '_number' => 13,
6871             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6872             1 => +{'name' => 'unknown1'}, # unknown ENUM
6873             2 => +{'name' => 'unknown2'}, # unknown UINT16
6874             3 => +{'name' => 'unknown3'}, # unknown ENUM
6875             4 => +{'name' => 'unknown4'}, # unknown UINT32
6876             5 => +{'name' => 'unknown5'}, # unknown SINT32
6877             6 => +{'name' => 'unknown6'}, # unknown SINT32
6878             7 => +{'name' => 'unknown7'}, # unknown ENUM
6879             8 => +{'name' => 'unknown8'}, # unknown UINT16
6880             9 => +{'name' => 'unknown9'}, # unknown ENUM
6881             10 => +{'name' => 'unknown10'}, # unknown UINT16
6882             11 => +{'name' => 'unknown11'}, # unknown UINT8
6883             12 => +{'name' => 'unknown12'}, # unknown ENUM
6884             13 => +{'name' => 'unknown13'}, # unknown ENUM
6885             14 => +{'name' => 'unknown14'}, # unknown ENUM
6886             15 => +{'name' => 'unknown15'}, # unknown ENUM
6887             16 => +{'name' => 'unknown16'}, # unknown ENUM
6888             17 => +{'name' => 'unknown17'}, # unknown ENUM
6889             18 => +{'name' => 'unknown18'}, # unknown ENUM
6890             19 => +{'name' => 'unknown19'}, # unknown UINT16
6891             25 => +{'name' => 'unknown25'}, # unknown ENUM
6892             27 => +{'name' => 'unknown27'}, # unknown ENUM
6893             30 => +{'name' => 'unknown30'}, # unknown ENUM
6894             31 => +{'name' => 'unknown31'}, # unknown UINT32
6895             32 => +{'name' => 'unknown32'}, # unknown UINT16
6896             33 => +{'name' => 'unknown33'}, # unknown UINT32
6897             34 => +{'name' => 'unknown34'}, # unknown ENUM
6898             50 => +{'name' => 'unknown50'}, # unknown ENUM
6899             51 => +{'name' => 'unknown51'}, # unknown ENUM
6900             52 => +{'name' => 'unknown52'}, # unknown UINT16
6901             53 => +{'name' => 'unknown53'}, # unknown ENUM
6902             56 => +{'name' => 'unknown56'}, # unknown ENUM
6903             },
6904              
6905             14 => +{
6906             '_number' => 14,
6907             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6908             1 => +{'name' => 'unknown1'}, # unknown ENUM
6909             3 => +{'name' => 'unknown3'}, # unknown UINT8
6910             4 => +{'name' => 'unknown4'}, # unknown UINT8 (array[10])
6911             5 => +{'name' => 'unknown5'}, # unknown ENUM (array[10])
6912             6 => +{'name' => 'unknown6'}, # unknown STRING
6913             7 => +{'name' => 'unknown7'}, # unknown UINT16 (array[10])
6914             },
6915              
6916             16 => +{
6917             '_number' => 16,
6918             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6919             1 => +{'name' => 'unknown1'}, # unknown ENUM
6920             2 => +{'name' => 'unknown2'}, # unknown UINT32
6921             3 => +{'name' => 'unknown3'}, # unknown ENUM
6922             },
6923              
6924             17 => +{
6925             '_number' => 17,
6926             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6927             1 => +{'name' => 'unknown1'}, # unknown ENUM
6928             2 => +{'name' => 'unknown2'}, # unknown ENUM
6929             3 => +{'name' => 'unknown3'}, # unknown UINT16
6930             4 => +{'name' => 'unknown4'}, # unknown ENUM
6931             5 => +{'name' => 'unknown5'}, # unknown UINT16
6932             },
6933              
6934             70 => +{
6935             '_number' => 70,
6936             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6937             0 => +{'name' => 'unknown0'}, # unknown ENUM
6938             1 => +{'name' => 'unknown1'}, # unknown ENUM
6939             2 => +{'name' => 'unknown2'}, # unknown ENUM
6940             3 => +{'name' => 'unknown3'}, # unknown ENUM
6941             4 => +{'name' => 'unknown4'}, # unknown ENUM
6942             5 => +{'name' => 'unknown5'}, # unknown ENUM
6943             6 => +{'name' => 'unknown6'}, # unknown ENUM
6944             7 => +{'name' => 'unknown7'}, # unknown ENUM
6945             8 => +{'name' => 'unknown8'}, # unknown ENUM
6946             9 => +{'name' => 'unknown9'}, # unknown ENUM
6947             10 => +{'name' => 'unknown10'}, # unknown ENUM
6948             11 => +{'name' => 'unknown11'}, # unknown ENUM
6949             12 => +{'name' => 'unknown12'}, # unknown ENUM
6950             13 => +{'name' => 'unknown13'}, # unknown ENUM
6951             14 => +{'name' => 'unknown14'}, # unknown ENUM
6952             15 => +{'name' => 'unknown15'}, # unknown ENUM
6953             },
6954              
6955             71 => +{
6956             '_number' => 71,
6957             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6958             0 => +{'name' => 'unknown0'}, # unknown ENUM
6959             1 => +{'name' => 'unknown1'}, # unknown ENUM
6960             2 => +{'name' => 'unknown2'}, # unknown ENUM
6961             3 => +{'name' => 'unknown3'}, # unknown UINT16
6962             4 => +{'name' => 'unknown4'}, # unknown ENUM
6963             },
6964              
6965             79 => +{
6966             '_number' => 79,
6967             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6968             0 => +{'name' => 'unknown0'}, # unknown UINT16
6969             1 => +{'name' => 'unknown1'}, # unknown UINT8
6970             2 => +{'name' => 'unknown2'}, # unknown UINT8
6971             3 => +{'name' => 'unknown3'}, # unknown UINT16
6972             4 => +{'name' => 'unknown4'}, # unknown ENUM
6973             5 => +{'name' => 'unknown5'}, # unknown ENUM
6974             6 => +{'name' => 'unknown6'}, # unknown UINT8
6975             7 => +{'name' => 'unknown7'}, # unknown SINT8
6976             8 => +{'name' => 'unknown8'}, # unknown UINT16
6977             9 => +{'name' => 'unknown9'}, # unknown UINT16
6978             10 => +{'name' => 'unknown10'}, # unknown UINT8
6979             11 => +{'name' => 'unknown11'}, # unknown UINT16
6980             12 => +{'name' => 'unknown12'}, # unknown UINT16
6981             13 => +{'name' => 'unknown13'}, # unknown UINT16
6982             14 => +{'name' => 'unknown14'}, # unknown UINT8
6983             },
6984              
6985             113 => +{
6986             '_number' => 113,
6987             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6988             0 => +{'name' => 'unknown0'}, # unknown UINT16
6989             1 => +{'name' => 'unknown1'}, # unknown ENUM
6990             2 => +{'name' => 'unknown2'}, # unknown UINT32
6991             3 => +{'name' => 'unknown3'}, # unknown UINT32
6992             4 => +{'name' => 'unknown4'}, # unknown UINT32
6993             5 => +{'name' => 'unknown5'}, # unknown ENUM
6994             },
6995              
6996             114 => +{
6997             '_number' => 114,
6998             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6999             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
7000             0 => +{'name' => 'unknown0'}, # unknown UINT16
7001             1 => +{'name' => 'unknown1'}, # unknown ENUM
7002             2 => +{'name' => 'unknown2'}, # unknown UINT32
7003             3 => +{'name' => 'unknown3'}, # unknown UINT32
7004             4 => +{'name' => 'unknown4'}, # unknown UINT32
7005             5 => +{'name' => 'unknown5'}, # unknown UINT32
7006             6 => +{'name' => 'unknown6'}, # unknown UINT32Z
7007             7 => +{'name' => 'unknown7'}, # unknown UINT32
7008             },
7009              
7010             139 => +{
7011             '_number' => 139,
7012             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
7013             0 => +{'name' => 'unknown0'}, # unknown ENUM
7014             1 => +{'name' => 'unknown1'}, # unknown UINT16Z
7015             3 => +{'name' => 'unknown3'}, # unknown UINT8Z
7016             4 => +{'name' => 'unknown4'}, # unknown ENUM
7017             5 => +{'name' => 'unknown5'}, # unknown UINT16
7018             },
7019              
7020             140 => +{
7021             '_number' => 140,
7022             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
7023             0 => +{'name' => 'unknown0'}, # unknown UINT8
7024             1 => +{'name' => 'unknown1'}, # unknown UINT8
7025             2 => +{'name' => 'unknown2'}, # unknown SINT32
7026             3 => +{'name' => 'unknown3'}, # unknown SINT32
7027             4 => +{'name' => 'unknown4'}, # unknown UINT8
7028             5 => +{'name' => 'unknown5'}, # unknown SINT32
7029             6 => +{'name' => 'unknown6'}, # unknown SINT32
7030             7 => +{'name' => 'unknown7'}, # unknown SINT32
7031             8 => +{'name' => 'unknown8'}, # unknown UINT8
7032             9 => +{'name' => 'unknown9'}, # unknown UINT16
7033             10 => +{'name' => 'unknown10'}, # unknown UINT16
7034             11 => +{'name' => 'unknown11'}, # unknown ENUM
7035             12 => +{'name' => 'unknown12'}, # unknown ENUM
7036             13 => +{'name' => 'unknown13'}, # unknown UINT8
7037             14 => +{'name' => 'unknown14'}, # unknown UINT16
7038             15 => +{'name' => 'unknown15'}, # unknown UINT16
7039             16 => +{'name' => 'unknown16'}, # unknown UINT16
7040             17 => +{'name' => 'unknown17'}, # unknown SINT8
7041             18 => +{'name' => 'unknown18'}, # unknown UINT8
7042             19 => +{'name' => 'unknown19'}, # unknown UINT8
7043             },
7044              
7045             203 => +{
7046             '_number' => 203,
7047             0 => +{'name' => 'unknown0'}, # unknown ENUM
7048             1 => +{'name' => 'unknown1'}, # unknown ENUM
7049             2 => +{'name' => 'unknown2'}, # unknown ENUM
7050             },
7051              
7052             );
7053              
7054             my $mesg_name_vs_num = $named_type{mesg_num};
7055              
7056             for my $msgname (keys %msgtype_by_name) {
7057             my $msgtype = $msgtype_by_name{$msgname};
7058              
7059             $msgtype->{_name} = $msgname;
7060             $msgtype->{_number} = $mesg_name_vs_num->{$msgname};
7061             $msgtype_by_num{$msgtype->{_number}} = $msgtype;
7062              
7063             for my $fldnum (grep {/^\d+$/} keys %$msgtype) {
7064             my $flddesc = $msgtype->{$fldnum};
7065              
7066             $flddesc->{number} = $fldnum;
7067             $msgtype->{$flddesc->{name}} = $flddesc;
7068             }
7069             }
7070              
7071             =head2 Constructor Methods
7072              
7073             =over 4
7074              
7075             =item new()
7076              
7077             creates a new object and returns it.
7078              
7079             =back
7080              
7081             =cut
7082              
7083             sub new {
7084 12     12 1 902337 my $class = shift;
7085 12         31 my $self = +{};
7086 12         63 bless $self, $class;
7087 12         86 $self->initialize(@_);
7088              
7089             # defaults
7090 12         83 $self->use_gmtime(1);
7091 12         55 $self->semicircles_to_degree(1);
7092 12         2635 return $self
7093             }
7094              
7095             =over 4
7096              
7097             =item clone()
7098              
7099             Returns a copy of a C instance.
7100              
7101             C is experimental and support for it may be removed at any time. Use with caution particularly if there are open filehandles, it which case it is recommended to C before cloning.
7102              
7103             It also does not return a full deep copy if any callbacks are registered, it creates a reference to them. There is no known way to make deep copies of anonymous subroutines in Perl (if you know of one, please make a pull request).
7104              
7105             The main use for c is immediately after C, and C, to create a copy for later use.
7106              
7107             =back
7108              
7109             =cut
7110              
7111             sub clone {
7112 1     1 1 9 my $self = shift;
7113              
7114 1         984 require Clone;
7115 1         901 my $clone = Clone::clone( $self );
7116 1         5 return $clone
7117             }
7118              
7119             =head2 Class methods
7120              
7121             =over 4
7122              
7123             =item profile_version_string()
7124              
7125             returns a string representing the .FIT profile version on which this class based.
7126              
7127             =back
7128              
7129             =cut
7130              
7131             my $profile_current = '21.141';
7132             my $protocol_current = '2.3'; # is there such a thing as current protocol for the class?
7133             # don't think so, pod was removed for protocol_* above
7134              
7135             my $protocol_version_major_shift = 4;
7136             my $protocol_version_minor_mask = (1 << $protocol_version_major_shift) - 1;
7137             my $protocol_version_header_crc_started = _protocol_version_from_string("1.0");
7138             my $profile_version_scale = 100;
7139              
7140             sub _protocol_version_from_string {
7141 18     18   54 my $s = shift;
7142 18         122 my ($major, $minor) = split /\./, $s, 2;
7143 18 50       159 return ($major + 0, $minor & $protocol_version_minor_mask) if wantarray;
7144 18         99 return ($major << $protocol_version_major_shift) | ($minor & $protocol_version_minor_mask)
7145             }
7146              
7147             sub protocol_version {
7148 3     3 0 10 my $self = shift;
7149 3         9 my $version;
7150 3 50       12 if (@_) { $version = shift }
  3         7  
7151 0         0 else { $version = _protocol_version_from_string($protocol_current) }
7152 3 50       14 return ($version >> $protocol_version_major_shift, $version & $protocol_version_minor_mask) if wantarray;
7153 3         9 return $version
7154             }
7155              
7156             sub _profile_version_from_string {
7157 23     23   5678 my $str = shift;
7158 23 50       55 croak '_profile_version_from_string() expects a string as argument' unless $str;
7159 23         64 my ($major, $minor) = split /\./, $str, 2;
7160 23 100       57 if ($minor >= 100) { $major += 1 } # kludge to deal with three-digit minor versions
  15         26  
7161 23         48 return $major * $profile_version_scale + $minor % $profile_version_scale
7162             }
7163              
7164             sub profile_version {
7165 32     32 0 11840 my $self = shift;
7166 32         45 my $version;
7167 32 100       73 if (@_) {
7168 29         56 $version = shift;
7169 29 100       146 $version = _profile_version_from_string($version) if $version =~ /\./
7170 3         7 } else { $version = _profile_version_from_string($profile_current) }
7171              
7172 32 100       106 if (wantarray) {
7173 21         59 my $major = int($version / $profile_version_scale);
7174 21         32 my $minor = $version % $profile_version_scale;
7175 21 100       48 if ($version >= 2200) { # kludge to deal with three-digit minor versions
7176 11         14 $major -= 1;
7177 11         12 $minor += 100
7178             }
7179 21         64 return ($major, $minor)
7180             }
7181 11         25 return $version
7182             }
7183              
7184             sub profile_version_string {
7185 7     7 1 3526 my $self = shift;
7186 7         8 my @version;
7187 7 100       16 if (blessed $self) {
7188 1 50       4 croak 'fetch_header() has not been called yet to obtain the version from the header, call fetch_header() first' unless defined $self->{profile_version};
7189 1 50       3 croak 'object method expects no arguments' if @_;
7190             @version = profile_version(undef, $self->{profile_version} )
7191 1         3 } else {
7192 6         13 @version = profile_version(undef, @_)
7193             }
7194 7         34 return sprintf '%u.%03u', @version
7195             }
7196              
7197 1     1 0 6 sub profile_version_major { profile_version( @_) };
7198 0     0 0 0 sub protocol_version_string { sprintf '%u.%u', ( protocol_version(@_) ) }
7199 0     0 0 0 sub protocol_version_major { protocol_version(@_) };
7200              
7201             # CRC calculation routine taken from
7202             # Haruhiko Okumura, C gengo ni yoru algorithm dai jiten (1st ed.), GijutsuHyouronsha 1991.
7203              
7204             my $crc_poly = 2 ** 16 + 2 ** 15 + 2 ** 2 + 2 ** 0; # CRC-16
7205             my ($crc_poly_deg, $crc_poly_rev);
7206             my ($x, $y, $i);
7207             for ($crc_poly_deg = 0, $x = $crc_poly ; $x >>= 1 ;) {
7208             ++$crc_poly_deg;
7209             }
7210             my $crc_octets = int($crc_poly_deg / 8 + 0.5);
7211             for ($crc_poly_rev = 0, $y = 1, $x = 2 ** ($crc_poly_deg - 1) ; $x ;) {
7212             $crc_poly_rev |= $y if $x & $crc_poly;
7213             $y <<= 1;
7214             $x >>= 1;
7215             }
7216             my @crc_table = ();
7217             for ($i = 0 ; $i < 2 ** 8 ; ++$i) {
7218             my $r = $i;
7219             my $j;
7220             for ($j = 0 ; $j < 8 ; ++$j) {
7221             if ($r & 1) {
7222             $r = ($r >> 1) ^ $crc_poly_rev;
7223             } else {
7224             $r >>= 1;
7225             }
7226             }
7227             $crc_table[$i] = $r;
7228             }
7229              
7230             # delete
7231             sub dump {
7232 0     0 0 0 my ($self, $s, $fh) = @_;
7233 0         0 my ($i, $d);
7234 0         0 for ($i = 0 ; $i < length($s) ;) {
7235 0         0 $fh->printf(' %03u', ord(substr($s, $i++, 1)));
7236             }
7237             }
7238              
7239             # delete
7240             sub safe_isa {
7241 0     0 0 0 eval {$_[0]->isa($_[1])};
  0         0  
7242             }
7243              
7244             # make internal
7245             # move to a section on internal accessors
7246             sub file_read {
7247 54     54 0 120 my $self = shift;
7248 54 100       113 if (@_) {
7249 18         89 $self->{file_read} = $_[0];
7250             } else {
7251 36         216 $self->{file_read};
7252             }
7253             }
7254              
7255             # make internal (or add POD)
7256             # move to a section on internal accessors (or to object methods)
7257             sub file_size {
7258 1481     1481 0 2338 my $self = shift;
7259 1481 100       2764 if (@_) {
7260 13         52 $self->{file_size} = $_[0];
7261             } else {
7262 1468         3614 $self->{file_size};
7263             }
7264             }
7265              
7266             # make internal (or add POD)
7267             # move to a section on internal accessors (or to object methods)
7268             sub file_processed {
7269 1450     1450 0 2221 my $self = shift;
7270 1450 100       2603 if (@_) {
7271 13         40 $self->{file_processed} = $_[0];
7272             } else {
7273 1437         3321 $self->{file_processed};
7274             }
7275             }
7276              
7277             # make internal
7278             # move to a section on internal accessors
7279             sub offset {
7280 7707     7707 1 12152 my $self = shift;
7281 7707 100       12966 if (@_) {
7282 1605         3372 $self->{offset} = $_[0];
7283             } else {
7284 6102         12238 $self->{offset};
7285             }
7286             }
7287              
7288             # make internal
7289             # move to a section on internal accessors
7290             sub buffer {
7291 5920     5920 0 10319 my $self = shift;
7292 5920 50       10483 if (@_) {
7293 0         0 $self->{buffer} = $_[0];
7294             } else {
7295 5920         12999 $self->{buffer};
7296             }
7297             }
7298              
7299             # make internal
7300             # move to a section on internal accessors
7301             sub maybe_chained {
7302 20     20 0 38 my $self = shift;
7303 20 100       50 if (@_) {
7304 4         12 $self->{maybe_chained} = $_[0];
7305             } else {
7306 16         128 $self->{maybe_chained};
7307             }
7308             }
7309              
7310             # make internal
7311             # move to a section on internal methods
7312             sub clear_buffer {
7313 26     26 0 48 my $self = shift;
7314 26 100       93 if ($self->offset > 0) {
7315 13         37 my $buffer = $self->{buffer};
7316 13 50       69 $self->crc_calc(length($$buffer)) if !defined $self->crc;
7317 13         75 $self->file_processed($self->file_processed + $self->offset);
7318 13         68 substr($$buffer, 0, $self->offset) = '';
7319 13         41 $self->offset(0)
7320             }
7321 26         54 return 1
7322             }
7323              
7324             =head2 Object methods
7325              
7326             =over 4
7327              
7328             =item file( $filename )
7329              
7330             returns the name of a .FIT file. Sets the name to I<$filename> if called with an argument (raises an exception if the file does not exist).
7331              
7332             =back
7333              
7334             =cut
7335              
7336             sub file {
7337 38     38 1 5272 my $self = shift;
7338 38 100       124 if (@_) {
7339 12         29 my $fname = $_[0];
7340 12 50       496 croak "file $fname specified in file() does not exist: $!" unless -f $fname;
7341 12         68 $self->{file} = $fname
7342             }
7343             return $self->{file}
7344 38         116 }
7345              
7346             =over 4
7347              
7348             =item open()
7349              
7350             opens the .FIT file.
7351              
7352             =back
7353              
7354             =cut
7355              
7356             sub open {
7357 13     13 1 636 my $self = shift;
7358 13         60 my $fn = $self->file;
7359              
7360 13 50       49 if ($fn ne '') {
7361 13         265 my $fh = $self->fh;
7362              
7363 13 50       142 if ($fh->open("< $fn")) {
7364 13 50       1427 if (binmode $fh, ':raw') {
7365 13         69 1;
7366             } else {
7367 0         0 $self->error("binmode \$fh, ':raw': $!");
7368             }
7369             } else {
7370 0         0 $self->error("\$fh->open(\"< $fn\"): $!");
7371             }
7372             } else {
7373 0         0 $self->error('no file name given');
7374             }
7375             }
7376              
7377             # make internal
7378             # move to a section on internal accessors
7379             sub fh {
7380 2939     2939 0 4581 my $self = shift;
7381 2939 50       5428 if (@_) {
7382 0         0 $self->{fh} = $_[0];
7383             } else {
7384 2939         11110 $self->{fh};
7385             }
7386             }
7387              
7388             # make internal
7389             # move to a section on internal accessors
7390             sub EOF {
7391 10     10   20 my $self = shift;
7392 10 100       28 if (@_) {
7393 8         26 $self->{EOF} = $_[0];
7394             } else {
7395 2         29 $self->{EOF};
7396             }
7397             }
7398              
7399             # make internal
7400             # move to a section on internal accessors
7401             sub end_of_chunk {
7402 9     9 0 20 my $self = shift;
7403 9 100       31 if (@_) {
7404 8         119 $self->{end_of_chunk} = $_[0];
7405             } else {
7406 1         4 $self->{end_of_chunk};
7407             }
7408             }
7409              
7410             # make internal
7411             # move to a section on internal functions
7412             sub fill_buffer {
7413 26     26 0 72 my $self = shift;
7414 26         94 my $buffer = $self->buffer;
7415 26 50       96 croak 'fill_buffer() expects no argument' if @_;
7416              
7417 26         123 $self->clear_buffer;
7418              
7419 26         80 my $n = $self->fh->read($$buffer, BUFSIZ, length($$buffer));
7420              
7421 26 100       1237 if ($n > 0) {
7422 18         109 $self->file_read($self->file_read + $n);
7423              
7424 18 100       113 if (defined $self->file_size) {
7425 5 50       24 if (defined $self->crc) {
7426 5         26 $self->crc_calc($n);
7427             } else {
7428 0         0 $self->crc_calc(length($$buffer));
7429             }
7430             }
7431             } else {
7432 8 50       64 if (defined $n) {
7433 8         80 $self->error("unexpected EOF");
7434 8         36 $self->EOF(1);
7435             } else {
7436 0         0 $self->error("read(fh): $!");
7437             }
7438             return undef
7439 8         81 }
7440 18         110 return 1
7441             }
7442              
7443             # move to a section on internal functions
7444             sub _buffer_needs_updating {
7445 3016     3016   5985 my ($self, $bytes_needed) = @_;
7446 3016         5635 my ($buffer, $offset) = ($self->buffer, $self->offset); # easier to debug with variables
7447 3016 100       6584 if ( length($$buffer) - $offset < $bytes_needed ) { return 1 }
  26         151  
7448 2990         8025 else { return 0 }
7449             }
7450              
7451             my $header_template = 'C C v V V';
7452             my $header_length = length(pack($header_template));
7453              
7454             sub FIT_HEADER_LENGTH {
7455 0     0 1 0 $header_length;
7456             }
7457              
7458             my $FIT_signature_string = '.FIT';
7459             my $FIT_signature = unpack('V', $FIT_signature_string);
7460              
7461             my $header_crc_template = 'v';
7462             my $header_crc_length = length(pack($header_crc_template));
7463              
7464             =over 4
7465              
7466             =item fetch_header()
7467              
7468             reads .FIT file header, and returns an array of the file size (excluding the trailing CRC-16), the protocol version, the profile version, extra octets in the header other than documented 4 values, the header CRC-16 recorded in the header, and the calculated header CRC-16.
7469              
7470             =back
7471              
7472             =cut
7473              
7474             sub fetch_header {
7475 13     13 1 65 my $self = shift;
7476 13 50       57 croak 'call the open() method before fetching the header' if $self->fh->tell < 0;
7477 13 50       161 croak '.FIT file header has already been fetched' if $self->fh->tell > 0;
7478              
7479 13         133 my $buffer = $self->buffer;
7480              
7481 13 50       57 if ( $self->_buffer_needs_updating( $header_length ) ) {
7482 13 50       67 $self->fill_buffer or return undef
7483             }
7484 13         55 my $h_min = substr($$buffer, $self->offset, $header_length);
7485 13         90 my ($h_len, $proto_ver, $prof_ver, $f_len, $sig) = unpack($header_template, $h_min);
7486 13         41 my $f_size = $f_len + $h_len;
7487 13         44 $self->offset($self->offset + $header_length);
7488              
7489 13         25 my ($extra, $header_extra_bytes, $crc_expected, $crc_calculated);
7490 13         29 $header_extra_bytes = $h_len - $header_length; # headers are now typically 14 bytes instead of 12
7491              
7492 13 50       62 if ($header_extra_bytes < 0) {
7493 0         0 $self->error("not a .FIT header ($h_len < $header_length)");
7494             return ()
7495 0         0 }
7496              
7497 13 50       43 if ($header_extra_bytes) {
7498 13 50       77 if ( $self->_buffer_needs_updating( $header_extra_bytes ) ) {
7499 0 0       0 $self->fill_buffer or return undef
7500             }
7501 13         143 $extra = substr($$buffer, $self->offset, $header_extra_bytes );
7502 13         42 $self->offset($self->offset + $header_extra_bytes );
7503             }
7504              
7505 13 50       188 if ($sig != $FIT_signature) {
7506             $self->error("not a .FIT header (" .
7507 0 0 0     0 join('', map {($_ ne "\\" && 0x20 >= ord($_) && ord($_) <= 0x7E) ? $_ : sprintf("\\x%02X", ord($_))} split //, pack('V', $sig))
  0         0  
7508             . " ne '$FIT_signature_string')");
7509             return ()
7510 0         0 }
7511              
7512 13 50 33     160 if ($proto_ver >= $protocol_version_header_crc_started && length($extra) >= $header_crc_length) {
7513 13         56 $crc_expected = unpack($header_crc_template, substr($extra, -$header_crc_length));
7514 13         45 substr($extra, -$header_crc_length) = '';
7515 13         74 $crc_calculated = $self->crc_of_string(0, \$h_min, 0, $header_length);
7516             }
7517              
7518 13         106 $self->file_size($f_size);
7519 13 50       63 unless (defined $self->crc) {
7520 13         54 $self->crc(0);
7521 13         56 $self->crc_calc(length($$buffer));
7522             }
7523              
7524 13         86 $self->{profile_version} = $prof_ver;
7525 13         151 return ($f_size, $proto_ver, $prof_ver, $extra, $crc_expected, $crc_calculated)
7526             }
7527              
7528             =over 4
7529              
7530             =item fetch()
7531              
7532             reads a message in the .FIT file, and returns C<1> on success, or C on failure or EOF. C must have been called before the first attempt to C after opening the file.
7533              
7534             If a data message callback is registered, C will return the value returned by the callback. It is therefore important to define explicit return statements and values in any callback (this includes returning true if that is the desired outcome after C).
7535              
7536             =back
7537              
7538             =cut
7539              
7540             sub fetch {
7541 1432     1432 1 97951 my $self = shift;
7542 1432 50       3664 croak 'call the fetch_header() method before calling fetch()' if $self->fh->tell == 0;
7543 1432 50       10196 croak 'open() and fetch_header() methods need to be called before calling fetch()' if $self->fh->tell < 0;
7544              
7545 1432         8461 my $buffer = $self->buffer;
7546              
7547 1432 100       3439 if ( $self->_buffer_needs_updating( $crc_octets ) ) {
7548 8 50       74 $self->fill_buffer or return undef
7549             }
7550 1424         2929 my $i = $self->offset;
7551 1424         3227 my $j = $self->file_processed + $i;
7552              
7553 1424         2175 my $ret_val;
7554              
7555 1424 100 33     3456 if ($j < $self->file_size) {
    50          
7556 1416         3241 my $record_header = ord(substr($$buffer, $i, 1));
7557 1416         2105 my $local_msg_type;
7558              
7559 1416 50       3713 if ($record_header & $rechd_mask_compressed_timestamp_header) {
    100          
7560 0         0 $local_msg_type = ($record_header & $rechd_mask_cth_local_message_type) >> $rechd_offset_cth_local_message_type
7561             } elsif ($record_header & $rechd_mask_definition_message) {
7562 143         465 $ret_val = $self->fetch_definition_message; # always 1
7563 143         1123 return $ret_val
7564             } else {
7565 1273         2260 $local_msg_type = $record_header & $rechd_mask_local_message_type
7566             }
7567              
7568 1273         3202 my $desc = $self->data_message_descriptor->[$local_msg_type];
7569              
7570 1273 100       2814 if (ref $desc eq 'HASH') {
7571 1272         3101 $ret_val = $self->fetch_data_message($desc)
7572             } else {
7573 1         28 $ret_val = $self->error(sprintf("%d at %ld: not defined", $record_header, $j))
7574             }
7575             } elsif (!$self->maybe_chained && $j > $self->file_size) {
7576 0         0 $self->trailing_garbages($self->trailing_garbages + length($$buffer) - $i);
7577 0         0 $self->offset(length($$buffer));
7578 0         0 $ret_val = 1
7579             } else {
7580 8 50       48 $self->crc_calc(length($$buffer)) if !defined $self->crc;
7581              
7582 8         18 my ($crc_expected, $k);
7583              
7584 8         38 for ($crc_expected = 0, $k = $crc_octets ; $k > 0 ;) {
7585 16         65 $crc_expected = ($crc_expected << 8) + ord(substr($$buffer, $i + --$k, 1));
7586             }
7587              
7588 8         40 $self->crc_expected($crc_expected);
7589 8         46 $self->offset($i + $crc_octets);
7590 8         38 $self->end_of_chunk(1);
7591 8         54 $ret_val = !$self->maybe_chained
7592             }
7593 1281         5811 return $ret_val
7594             }
7595              
7596             sub error_callback { # consider adding POD for error_callback otherwise make it internal (_error_callback)
7597 1     1 0 63 my $self = shift;
7598 1 50       4 if (@_) {
7599             # if (&safe_isa($_[0], 'CODE')) {
7600 1 50 33     10 if (ref $_[0] and ref $_[0] eq 'CODE') {
7601 1         10 $self->{error_callback_argv} = [@_[1 .. $#_]];
7602 1         5 $self->{error_callback} = $_[0];
7603             } else {
7604 0         0 undef;
7605             }
7606             } else {
7607 0         0 $self->{error_callback};
7608             }
7609             }
7610              
7611             =over 4
7612              
7613             =item error()
7614              
7615             returns an error message recorded by a method.
7616              
7617             =back
7618              
7619             =cut
7620              
7621             sub error {
7622 13     13 1 29 my $self = shift;
7623 13 50       48 if (@_) {
7624 13         43 my ($p, $fn, $l, $subr, $fit);
7625              
7626 13         102 (undef, $fn, $l) = caller(0);
7627 13         70 ($p, undef, undef, $subr) = caller(1);
7628 13         61 $fit = $self->file;
7629 13 50       62 $fit .= ': ' if $fit ne '';
7630              
7631 13         60 $self->{error} = "${p}::$subr\#$l\@$fn: $fit$_[0]";
7632              
7633             # if (&safe_isa($self->{error_callback}, 'CODE')) {
7634             # my $argv = &safe_isa($self->{error_callback_argv}, 'ARRAY') ? $self->{error_callback_argv} : [];
7635 13         32 my $is_cb = $self->{error_callback};
7636 13         31 my $is_cb_argv = $self->{error_callback_argv};
7637 13 50 33     136 if (ref $is_cb and ref $is_cb eq 'CODE') {
7638 0 0 0     0 my $argv = (ref $is_cb_argv and ref $is_cb_argv eq 'ARRAY') ? $is_cb_argv : [];
7639              
7640 0         0 $self->{error_callback}->($self, @$argv);
7641             } else {
7642 13         238 undef;
7643             }
7644             } else {
7645 0         0 $self->{error};
7646             }
7647             }
7648              
7649             =over 4
7650              
7651             =item crc()
7652              
7653             CRC-16 calculated from the contents of a .FIT file.
7654              
7655             =back
7656              
7657             =cut
7658              
7659             sub crc {
7660 89     89 1 154 my $self = shift;
7661 89 100       204 if (@_) {
7662 31         146 $self->{crc} = $_[0];
7663             } else {
7664 58         252 $self->{crc};
7665             }
7666             }
7667              
7668             sub crc_of_string {
7669 31     31 0 84 my ($self, $crc, $p, $b, $n) = @_;
7670 31         94 my $e = $b + $n;
7671 31         195 while ($b < $e) {
7672 97812         228162 $crc = ($crc >> 8) ^ $crc_table[($crc & (2 ** 8 - 1)) ^ ord(substr($$p, $b++, 1))];
7673             }
7674 31         270 $crc;
7675             }
7676              
7677             sub crc_calc {
7678 18     18 0 46 my ($self, $m) = @_;
7679 18         99 my $over = $self->file_read - $self->file_size;
7680 18 100       92 $over = 0 if $over < 0;
7681              
7682 18 50       114 if ($m > $over) {
7683 18         66 my $buffer = $self->buffer;
7684 18         54 $self->crc($self->crc_of_string($self->crc, $buffer, length($$buffer) - $m, $m - $over));
7685             }
7686             }
7687              
7688             =over 4
7689              
7690             =item crc_expected()
7691              
7692             CRC-16 attached to the end of a .FIT file. Only available after all contents of the file has been read.
7693              
7694             =back
7695              
7696             =cut
7697              
7698             sub crc_expected {
7699 9     9 1 21 my $self = shift;
7700 9 100       27 if (@_) {
7701 8         48 $self->{crc_expected} = $_[0];
7702             } else {
7703 1         3 $self->{crc_expected};
7704             }
7705             }
7706              
7707             =over 4
7708              
7709             =item trailing_garbages()
7710              
7711             number of octets after CRC-16, 0 usually.
7712              
7713             =back
7714              
7715             =cut
7716              
7717             sub trailing_garbages {
7718 1     1 1 2 my $self = shift;
7719 1 50       4 if (@_) {
7720 0         0 $self->{trailing_garbages} = $_[0];
7721             } else {
7722 1         3 $self->{trailing_garbages};
7723             }
7724             }
7725              
7726             sub numeric_date_time {
7727 682     682 0 1064 my $self = shift;
7728 682 100       1351 if (@_) {
7729 7         27 $self->{numeric_date_time} = $_[0];
7730             } else {
7731 675         1927 $self->{numeric_date_time};
7732             }
7733             }
7734              
7735             sub date_string {
7736 675     675 0 1430 my ($self, $time) = @_;
7737 675 50       1721 my ($s, $mi, $h, $d, $mo, $y, $gmt) = $self->use_gmtime ? ((gmtime($time))[0 .. 5], 'Z') : (localtime($time))[0 .. 5];
7738 675         15341 sprintf('%04u-%02u-%02uT%02u:%02u:%02u%s', $y + 1900, $mo + 1, $d, $h, $mi, $s, $gmt);
7739             }
7740              
7741             sub named_type_value {
7742 1038     1038 0 4877 my ($self, $type_name, $val) = @_;
7743 1038         2694 my $typedesc = $named_type{$type_name};
7744              
7745 1038 100       4039 if (ref $typedesc ne 'HASH') {
    100          
    100          
7746 4         47 $self->error("$type_name is not a named type");
7747             } elsif ($typedesc->{_mask}) {
7748 22 100       192 if ($val !~ /^[-+]?\d+$/) {
7749 3         7 my $num = 0;
7750              
7751 3         16 for my $expr (split /,/, $val) {
7752 9         69 $expr =~ s/^.*=//;
7753              
7754 9 50       26 if ($expr =~ s/^0[xX]//) {
7755 0         0 $num |= hex($expr);
7756             } else {
7757 9         24 $num |= $expr + 0;
7758             }
7759             }
7760              
7761 3         12 $num;
7762             } else {
7763 19         43 my $mask = 0;
7764 19         73 my @key;
7765              
7766 19         168 for my $key (sort {$typedesc->{$b} <=> $typedesc->{$a}} grep {/^[A-Za-z]/} keys %$typedesc) {
  51         193  
  152         446  
7767 57         340 push @key, $key . '=' . ($val & $typedesc->{$key});
7768 57         123 $mask |= $typedesc->{$key};
7769             }
7770              
7771 19         105 my $rest = $val & ~$mask & ((1 << ($size[$typedesc->{_base_type}] * 8)) - 1);
7772              
7773 19 50       69 if ($rest) {
    50          
7774 0         0 my $width = $size[$typedesc->{_base_type}] * 2;
7775              
7776 0         0 join(',', @key, sprintf("0x%0${width}X", $rest));
7777             } elsif (@key) {
7778 19         143 join(',', @key);
7779             } else {
7780 0         0 0;
7781             }
7782             }
7783             } elsif ($type_name eq 'date_time') {
7784 679 100 33     6484 if ($val !~ /^[-+]?\d+$/) {
    50          
7785 4         39 my ($y, $mo, $d, $h, $mi, $s, $gmt) = $val =~ /(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)([zZ]?)/;
7786              
7787 4 50       46 ($gmt ne '' ? timegm($s, $mi, $h, $d, $mo - 1, $y - 1900) : timelocal($s, $mi, $h, $d, $mo - 1, $y - 1900)) + $typedesc->{_offset};
7788             } elsif ($val >= $typedesc->{_min} && $val != $invalid[$typedesc->{_base_type}]) {
7789 675 100       2018 if ($self->numeric_date_time) {
7790 9         31 $val - $typedesc->{_offset};
7791             } else {
7792 666         1766 $self->date_string($val - $typedesc->{_offset});
7793             }
7794             } else {
7795 0         0 undef;
7796             }
7797             } else {
7798 333         1264 $typedesc->{$val};
7799             }
7800             }
7801              
7802             sub data_message_descriptor {
7803 1416     1416 0 2347 my $self = shift;
7804              
7805 1416 50       2659 if (@_) {
7806 0         0 $self->{data_message_descriptor} = $_[0];
7807             } else {
7808 1416 100       4175 $self->{data_message_descriptor} = [] if ref $self->{data_message_descriptor} ne 'ARRAY';
7809 1416         4138 $self->{data_message_descriptor};
7810             }
7811             }
7812              
7813             sub data_message_callback {
7814 202     202 0 512 $_[0]->{data_message_callback};
7815             }
7816              
7817             =over 4
7818              
7819             =item data_message_callback_by_num(I, I[, I, ...])
7820              
7821             register a function I which is called when a data message with the messag number I is fetched.
7822              
7823             =back
7824              
7825             =cut
7826              
7827             my $msgnum_anon = $invalid[FIT_UINT16];
7828             my $msgname_anon = '';
7829              
7830             sub data_message_callback_by_num {
7831 0     0 1 0 my $self = shift;
7832 0         0 my $num = shift;
7833 0         0 my $cbmap = $self->data_message_callback;
7834 0         0 my ($msgtype, $name);
7835              
7836 0 0       0 if ($num == $msgnum_anon) {
    0          
    0          
7837 0 0       0 if (@_) {
7838 0 0       0 if (ref $_[0] eq 'CODE') {
7839 0         0 $cbmap->{$msgname_anon} = $cbmap->{$msgnum_anon} = [@_];
7840             } else {
7841 0         0 $self->error('not a CODE');
7842             }
7843             } else {
7844 0         0 my %res;
7845 0         0 foreach $num (keys %msgtype_by_num) {
7846 0         0 my $cb = $cbmap->{$num};
7847 0 0       0 ref $cb eq 'ARRAY' and $res{$num} = [@$cb];
7848             }
7849 0         0 \%res;
7850             }
7851             } elsif (!defined($msgtype = $msgtype_by_num{$num})) {
7852 0         0 $self->error("$num is not a message type number");
7853             } elsif (@_) {
7854 0 0       0 if (ref $_[0] eq 'CODE') {
7855 0         0 $cbmap->{$num} = [@_];
7856 0 0       0 $msgtype->{_name} ne '' and $cbmap->{$msgtype->{_name}} = $cbmap->{$num};
7857 0         0 $cbmap->{$num};
7858             } else {
7859 0         0 $self->error('not a CODE');
7860             }
7861             } else {
7862 0         0 my $cb = $cbmap->{$num};
7863 0 0       0 ref $cb eq 'ARRAY' ? [@$cb] : [];
7864             }
7865             }
7866              
7867             =over 4
7868              
7869             =item data_message_callback_by_name(I, I[, I, ...])
7870              
7871             register a function I which is called when a data message with the name I is fetched.
7872              
7873             =back
7874              
7875             =cut
7876              
7877             sub data_message_callback_by_name {
7878 59     59 1 423 my $self = shift;
7879 59         107 my $name = shift;
7880 59         147 my $cbmap = $self->data_message_callback;
7881 59         93 my $msgtype;
7882              
7883 59 100       414 if ($name eq $msgname_anon) {
    50          
    50          
7884 7 100       36 if (@_) {
7885 5 50       19 if (ref $_[0] eq 'CODE') {
7886 5         37 $cbmap->{$msgname_anon} = $cbmap->{$msgnum_anon} = [@_];
7887             } else {
7888 0         0 $self->error('not a CODE');
7889             }
7890             } else {
7891 2         5 my %res;
7892 2         100 foreach $name (keys %msgtype_by_name) {
7893 248         295 my $cb = $cbmap->{$name};
7894 248 100       416 ref $cb eq 'ARRAY' and $res{$name} = [@$cb];
7895             }
7896 2         24 \%res;
7897             }
7898             } elsif (!defined($msgtype = $msgtype_by_name{$name})) {
7899 0         0 $self->error("$name is not a message type name");
7900             } elsif (@_) {
7901 52 50       139 if (ref $_[0] eq 'CODE') {
7902 52         385 $cbmap->{$msgtype->{_number}} = $cbmap->{$name} = [@_];
7903             } else {
7904 0         0 $self->error('not a CODE');
7905             }
7906             } else {
7907 0         0 my $cb = $cbmap->{$name};
7908 0 0       0 ref $cb eq 'ARRAY' ? [@$cb] : [];
7909             }
7910             }
7911              
7912             sub undocumented_field_name {
7913 790     790 0 1660 my ($self, $index, $size, $type, $i_string) = @_;
7914             # 'xxx' . $i_string . '_' . $index . '_' . $size . '_' . $type;
7915 790         2092 'xxx' . $index;
7916             }
7917              
7918             sub syscallback_devdata_id {
7919 0     0 0 0 my ($self, $desc, $v) = @_;
7920 0         0 my ($i_id, $T_id, $c_id, $i_index) = @$desc{qw(i_application_id T_application_id c_application_id i_developer_data_index)};
7921 0         0 my ($emsg, $warn);
7922              
7923 0 0 0     0 if (!defined $i_id) {
    0          
    0          
7924 0         0 $emsg = "no application_id";
7925 0         0 $warn = 1;
7926             } elsif ($T_id != FIT_UINT8 && $T_id != FIT_BYTE) {
7927 0         0 croak "There is a bug here, this should be resolved soon";
7928             # $type_name has not been declared in this scope need to figure out what it should be
7929             # $emsg = "base type of application_id is $type_name [$T_id] ($T_id)";
7930             } elsif (!defined $i_index) {
7931 0         0 $emsg = "no developer_data_index";
7932             }
7933              
7934 0 0       0 if ($emsg ne '') {
7935 0 0       0 if ($warn) {
7936 0         0 $self->error("suspicious developer data id message ($emsg)");
7937             } else {
7938 0         0 $self->error("broken developer data id message ($emsg)");
7939 0         0 return undef;
7940             }
7941             }
7942              
7943 0         0 my $devdata_by_index = $self->{devdata_by_index};
7944 0 0       0 ref $devdata_by_index eq 'HASH' or $devdata_by_index = $self->{devdata_by_index} = +{};
7945              
7946 0         0 my $devdata_by_id = $self->{devdata_by_id};
7947 0 0       0 ref $devdata_by_id eq 'HASH' or $devdata_by_id = $self->{devdata_by_id} = +{};
7948              
7949 0         0 my $id;
7950 0 0       0 if ($T_id == FIT_UINT8) {
7951 0         0 $id = pack('C*', @$v[$i_id .. ($i_id + $c_id - 1)]);
7952             } else {
7953 0         0 $id = $v->[$i_id];
7954             }
7955              
7956 0         0 my %devdata = (id => $id, index => $v->[$i_index]);
7957 0         0 $devdata_by_id->{$devdata{id}} = $devdata_by_index->{$devdata{index}} = \%devdata;
7958             }
7959              
7960             sub syscallback_devdata_field_desc {
7961 0     0 0 0 my ($self, $desc, $v) = @_;
7962              
7963             my ($i_index, $I_index, $i_field_num, $I_field_num,
7964             $i_base_type_id, $T_base_type_id, $I_base_type_id,
7965             $i_field_name, $T_field_name, $c_field_name)
7966 0         0 = @$desc{qw(i_developer_data_index I_developer_data_index i_field_definition_number I_field_definition_number
7967             i_fit_base_type_id T_fit_base_type_id I_fit_base_type_id
7968             i_field_name T_field_name c_field_name)};
7969              
7970 0         0 my ($emsg, $warn, $o_name);
7971              
7972 0 0 0     0 if (!defined $i_index) {
    0          
    0          
    0          
    0          
    0          
7973 0         0 $emsg = 'no developer_data_index';
7974             } elsif (!defined $i_field_num) {
7975 0         0 $emsg = 'no field_num';
7976             } elsif (!defined $i_base_type_id) {
7977 0         0 $emsg = 'no base_type_id';
7978             } elsif ($T_base_type_id != FIT_UINT8) {
7979 0         0 croak "There is a bug here, this should be resolved soon";
7980             # $type_name has not been declared in this scope need to figure out what it should be
7981             # $emsg = "base type of base_type_id is $type_name [$T_base_type_id] ($T_base_type_id)";
7982             } elsif (!defined $i_field_name) {
7983 0         0 $emsg = 'no field_name';
7984 0         0 $warn = 1;
7985             } elsif ($T_field_name != FIT_STRING || $c_field_name <= 0) {
7986 0         0 $emsg = "field_name is not a non-empty string";
7987 0         0 $warn = 1;
7988             } else {
7989 0         0 $o_name = _string_value($v, $i_field_name, $c_field_name);
7990             }
7991              
7992 0 0       0 if ($emsg ne '') {
7993 0 0       0 if ($warn) {
7994 0         0 $self->error("suspicious field description message ($emsg)");
7995             } else {
7996 0         0 $self->error("broken field description message ($emsg)");
7997 0         0 return undef;
7998             }
7999             }
8000              
8001 0         0 my $base_type = $v->[$i_base_type_id];
8002              
8003 0 0       0 if ($base_type == $I_base_type_id) {
8004 0         0 $self->error("invalid base type ($base_type)");
8005 0         0 return undef;
8006             }
8007              
8008 0 0       0 if ($base_type < 0) {
8009 0         0 $self->error("unknown base type ($base_type)");
8010 0         0 return undef;
8011             }
8012              
8013 0         0 $base_type &= $deffld_mask_type;
8014              
8015 0 0       0 unless ($base_type <= FIT_BASE_TYPE_MAX) {
8016 0         0 $self->error("unknown base type ($base_type)");
8017 0         0 return undef;
8018             }
8019              
8020 0         0 my $devdata_by_index = $self->{devdata_by_index};
8021              
8022 0 0       0 unless (ref $devdata_by_index eq 'HASH') {
8023 0         0 $self->error('no developer data id message before a field description message');
8024 0         0 return undef;
8025             }
8026              
8027 0         0 my $index = $v->[$i_index];
8028              
8029 0 0       0 if ($index == $I_index) {
8030 0         0 $self->error("invalid developer data index ($index)");
8031 0         0 return undef;
8032             }
8033              
8034 0         0 my $num = $v->[$i_field_num];
8035              
8036 0 0       0 if ($num == $I_field_num) {
8037 0         0 $self->error("invalid field definition number ($num)");
8038 0         0 return undef;
8039             }
8040              
8041 0         0 my $devdata = $devdata_by_index->{$index};
8042              
8043 0 0       0 unless (ref $devdata eq 'HASH') {
8044 0         0 $self->error("No developer data id message with the index $index before a field description message");
8045 0         0 return undef;
8046             }
8047              
8048 0         0 my $field_desc_by_num = $devdata->{field_desc_by_num};
8049 0 0       0 ref $field_desc_by_num eq 'HASH' or $field_desc_by_num = $devdata->{field_desc_by_num} = +{};
8050              
8051 0         0 my $field_desc_by_name = $devdata->{field_desc_by_name};
8052 0 0       0 ref $field_desc_by_name eq 'HASH' or $field_desc_by_name = $devdata->{field_desc_by_name} = +{};
8053              
8054 0         0 my $name = $o_name;
8055              
8056 0 0       0 if (defined $name) {
8057 0         0 $name =~ s/\s+/_/g;
8058 0         0 $name =~ s/\W/sprintf('_%02x_', ord($&))/ge;
  0         0  
8059             }
8060              
8061 0         0 my %fdesc = (
8062             '_index' => $index,
8063             '_num' => $num,
8064             '_name' => $name,
8065             'field_name' => $o_name,
8066             '_type' => $base_type,
8067             );
8068              
8069 0         0 for my $i_aname (grep {/^i_/} keys %$desc) {
  0         0  
8070 0 0       0 if ($i_aname !~ /^i_(developer_data_index|field_definition_number|fit_base_type_id|field_name)$/) {
8071 0         0 my $i = $desc->{$i_aname};
8072 0         0 my $aname = $i_aname;
8073              
8074 0         0 $aname =~ s/^i_//;
8075              
8076 0         0 my $I_aname = 'I_' . $aname;
8077 0         0 my $T_aname = 'T_' . $aname;
8078 0         0 my $c_aname = 'c_' . $aname;
8079              
8080 0 0       0 if ($desc->{$T_aname} == FIT_STRING) {
    0          
8081 0         0 $fdesc{$aname} = _string_value($v, $i, $desc->{$c_aname});
8082             } elsif ($v->[$i] != $desc->{$I_aname}) {
8083 0         0 $fdesc{$aname} = $v->[$i];
8084             }
8085             }
8086             }
8087              
8088 0 0       0 defined $name and $field_desc_by_name->{$name} = \%fdesc;
8089 0         0 $field_desc_by_num->{$num} = \%fdesc;
8090             }
8091              
8092             sub add_endian_converter {
8093 3410     3410 0 6930 my ($self, $endian, $type, $c, $i_string, $cvt) = @_;
8094              
8095 3410 50 33     7761 if ($endian != $my_endian && $size[$type] > 1) {
8096 0         0 my ($p, $unp, $n);
8097              
8098 0 0       0 if ($size[$type] == 2) {
    0          
8099 0         0 ($p, $unp) = (qw(n v));
8100             } elsif ($size[$type] == 4) {
8101 0         0 ($p, $unp) = (qw(N V));
8102             } else {
8103 0         0 ($p, $unp, $n) = (qw(N V), 2);
8104             }
8105              
8106 0         0 push @$cvt, $p . $n, $unp . $n, $i_string, $size[$type], $c;
8107 0         0 1;
8108             } else {
8109 3410         5420 0;
8110             }
8111             }
8112              
8113             sub fetch_definition_message {
8114 143     143 0 220 my $self = shift;
8115 143         318 my $buffer = $self->buffer;
8116              
8117 143 50       323 if ( $self->_buffer_needs_updating( $defmsg_min_length ) ) {
8118 0 0       0 $self->fill_buffer or return undef
8119             }
8120 143         328 my $i = $self->offset;
8121 143         1556 my ($record_header, $reserved, $endian, $msgnum, $nfields) = unpack($defmsg_min_template, substr($$buffer, $i, $defmsg_min_length));
8122              
8123 143 50       567 $endian = $endian ? 1 : 0;
8124 143         388 $self->offset($i + $defmsg_min_length);
8125              
8126 143         270 my $len = $nfields * $deffld_length;
8127              
8128 143 50       406 if ( $self->_buffer_needs_updating( $len ) ) {
8129 0 0       0 $self->fill_buffer or return undef
8130             }
8131 143         304 $i = $self->offset;
8132 143 50       456 $msgnum = unpack('n', pack('v', $msgnum)) if $endian != $my_endian;
8133              
8134 143         544 my $msgtype = $msgtype_by_num{$msgnum};
8135 143         428 my $cbmap = $self->data_message_callback;
8136 143         251 my $e = $i + $len;
8137 143         284 my ($cb, %desc, $i_array, $i_array_t, $i_string, @cvt, @pi);
8138              
8139 143         630 $desc{local_message_type} = $record_header & $rechd_mask_local_message_type;
8140 143         446 $desc{message_number} = $msgnum;
8141 143 100 100     1195 $desc{message_name} = $msgtype->{_name} if ref $msgtype eq 'HASH' && exists $msgtype->{_name};
8142 143 100       517 $cb = $cbmap->{$msgnum} if ref $cbmap->{$msgnum} eq 'ARRAY';
8143 143 100       440 $cb = $cbmap->{$msgnum_anon} if ref $cb ne 'ARRAY';
8144 143 100       450 $desc{callback} = $cb if ref $cb eq 'ARRAY';
8145 143         334 $desc{endian} = $endian;
8146 143         403 $desc{template} = 'C';
8147 143         447 $self->data_message_descriptor->[$desc{local_message_type}] = \%desc;
8148              
8149 143         506 for ($i_array = $i_array_t = $i_string = 1 ; $i + $deffld_length <= $e ; $i += $deffld_length) {
8150 3410         10574 my ($index, $size, $type) = unpack($deffld_template, substr($$buffer, $i, $deffld_length));
8151 3410         5515 my ($name, $tname, %attr, );
8152              
8153 3410 100       7711 if (ref $msgtype eq 'HASH') {
8154 3319         7138 my $fldtype = $msgtype->{$index};
8155              
8156 3319 100       6553 if (ref $fldtype eq 'HASH') {
8157 2620         12442 %attr = %$fldtype;
8158 2620         6037 ($name, $tname) = @attr{qw(name type_name)};
8159 2620         4337 delete $attr{name};
8160 2620         4346 delete $attr{type_name};
8161             }
8162             }
8163              
8164 3410 100       7336 $name = $self->undocumented_field_name($index, $size, $type, $i_string) if !defined $name;
8165 3410         8620 $desc{$index} = $name;
8166 3410         5095 $type &= $deffld_mask_type;
8167              
8168 3410         7160 my $c = int($size / $size[$type] + 0.5);
8169              
8170 3410         9382 $desc{'i_' . $name} = $i_array;
8171 3410         7535 $desc{'o_' . $name} = $i_string;
8172 3410         8344 $desc{'c_' . $name} = $c;
8173 3410         9422 $desc{'s_' . $name} = $size[$type];
8174 3410 100       9200 $desc{'a_' . $name} = \%attr if %attr;
8175 3410 100       7241 $desc{'t_' . $name} = $tname if defined $tname;
8176 3410         8036 $desc{'T_' . $name} = $type;
8177 3410         9453 $desc{'N_' . $name} = $index;
8178 3410         9079 $desc{'I_' . $name} = $invalid[$type];
8179              
8180 3410         9459 $self->add_endian_converter($endian, $type, $c, $i_string, \@cvt);
8181              
8182 3410         5183 $i_array += $c;
8183 3410         4728 $i_string += $size;
8184 3410         6944 $desc{template} .= ' ' . $template[$type];
8185              
8186 3410 50       6895 if ($packfactor[$type] > 1) {
8187 0         0 push @pi, $i_array_t, $c, $i_array;
8188 0         0 $c *= $packfactor[$type];
8189             }
8190              
8191 3410 100       6378 $desc{template} .= $c if $c > 1;
8192 3410         9060 $i_array_t += $c;
8193             }
8194              
8195 143         451 $desc{template_without_devdata} = $desc{template};
8196 143         299 $desc{devdata_first} = $i_array;
8197 143         360 $desc{devdata_nfields} = 0;
8198              
8199 143 50       371 if ($record_header & $rechd_mask_devdata_message) {
8200 0         0 $self->offset($e);
8201 0 0       0 if ( $self->_buffer_needs_updating( $devdata_min_length ) ) {
8202 0 0       0 $self->fill_buffer or return undef
8203             }
8204 0         0 $i = $self->offset;
8205 0         0 ($nfields) = unpack($devdata_min_template, substr($$buffer, $i, $devdata_min_length));
8206              
8207 0         0 $self->offset($i + $devdata_min_length);
8208 0         0 $len = $nfields * $devdata_deffld_length;
8209 0 0       0 if ( $self->_buffer_needs_updating( $len ) ) {
8210 0 0       0 $self->fill_buffer or return undef
8211             }
8212              
8213 0         0 my $devdata_by_index = $self->{devdata_by_index};
8214 0         0 my @emsg;
8215              
8216 0 0       0 if (ref $devdata_by_index ne 'HASH') {
8217 0         0 push @emsg, 'No developer data id';
8218 0         0 $devdata_by_index = +{};
8219             }
8220              
8221 0         0 for ($i = $self->offset, $e = $i + $len ; $i + $devdata_deffld_length <= $e ; $i += $devdata_deffld_length) {
8222 0         0 my ($fnum, $size, $index) = unpack($devdata_deffld_template, substr($$buffer, $i, $devdata_deffld_length));
8223 0         0 my $devdata = $devdata_by_index->{$index};
8224 0         0 my ($fdesc, $name, $type, %attr);
8225              
8226 0 0       0 if (ref $devdata eq 'HASH') {
8227 0         0 my $fdesc_by_num = $devdata->{field_desc_by_num};
8228              
8229 0 0       0 if (ref $fdesc_by_num eq 'HASH') {
8230 0         0 $fdesc = $fdesc_by_num->{$fnum};
8231             } else {
8232 0         0 push @emsg, "No field description message for developer data with index $index";
8233             }
8234             } else {
8235 0         0 push @emsg, "No developer data id with index $index";
8236             }
8237              
8238 0 0       0 if (ref $fdesc eq 'HASH') {
8239 0         0 %attr = %$fdesc;
8240 0         0 ($type, $name) = @attr{qw(_type _name)};
8241             } else {
8242 0         0 push @emsg, "No field with number $fnum for developer data with index $index";
8243 0         0 $type = FIT_UINT8;
8244             }
8245              
8246 0 0       0 $name = $self->undocumented_field_name($fnum, $size, $type, $i_string) if !defined $name;
8247 0         0 $name = "${index}_${fnum}_$name";
8248              
8249 0         0 my $c = int($size / $size[$type] + 0.5);
8250              
8251 0         0 $desc{'i_' . $name} = $i_array;
8252 0         0 $desc{'o_' . $name} = $i_string;
8253 0         0 $desc{'c_' . $name} = $c;
8254 0         0 $desc{'s_' . $name} = $size[$type];
8255 0 0       0 $desc{'a_' . $name} = \%attr if %attr;
8256 0         0 $desc{'T_' . $name} = $type;
8257 0         0 $desc{'N_' . $name} = $fnum;
8258 0         0 $desc{'I_' . $name} = $invalid[$type];
8259              
8260 0         0 $self->add_endian_converter($endian, $type, $c, $i_string, \@cvt);
8261              
8262 0         0 $i_array += $c;
8263 0         0 $i_string += $size;
8264 0         0 $desc{template} .= ' ' . $template[$type];
8265              
8266 0 0       0 if ($packfactor[$type] > 1) {
8267 0         0 push @pi, $type, $i_array_t, $c, $i_array;
8268 0         0 $c *= $packfactor[$type];
8269             }
8270              
8271 0 0       0 $desc{template} .= $c if $c > 1;
8272 0         0 $i_array_t += $c;
8273             }
8274              
8275 0         0 $desc{devdata_nfields} = $nfields;
8276 0 0       0 $self->error(join(' / ', @emsg)) if (@emsg);
8277             }
8278              
8279 143 50       370 $desc{endian_converter} = \@cvt if @cvt;
8280 143 50       309 $desc{packfilter_index} = \@pi if @pi;
8281 143         499 $desc{message_length} = $i_string;
8282 143         405 $desc{array_length} = $i_array;
8283 143         503 $self->offset($e);
8284 143         496 return 1
8285             }
8286              
8287             sub endian_convert {
8288 0     0 0 0 my ($self, $cvt, $buffer, $i) = @_;
8289 0         0 my $j;
8290              
8291 0         0 for ($j = 4 ; $j < @$cvt ; $j += 5) {
8292 0         0 my ($b, $size, $c) = @$cvt[$j - 2, $j - 1, $j];
8293              
8294 0         0 for ($b += $i ; $c > 0 ; $b += $size, --$c) {
8295 0         0 my @v = unpack($cvt->[$j - 3], substr($$buffer, $b, $size));
8296 0         0 my ($k, $l);
8297              
8298 0         0 for ($k = 0, $l = $#v ; $k < $l ; ++$k, --$l) {
8299 0         0 @v[$k, $l] = @v[$l, $k];
8300             }
8301 0         0 substr($$buffer, $b, $size) = pack($cvt->[$j - 4], @v);
8302             }
8303             }
8304             }
8305              
8306             sub last_timestamp {
8307 233     233 0 518 my $self = shift;
8308 233 50       481 if (@_) {
8309 233         616 $self->{last_timestamp} = $_[0];
8310             } else {
8311 0         0 $self->{last_timestamp};
8312             }
8313             }
8314              
8315             sub fetch_data_message {
8316 1272     1272 0 2577 my ($self, $desc) = @_;
8317 1272         2768 my $buffer = $self->buffer;
8318              
8319 1272 100       3344 if ( $self->_buffer_needs_updating( $desc->{message_length} ) ) {
8320 5 50       34 $self->fill_buffer or return undef
8321             }
8322 1272 50       3487 $self->endian_convert($desc->{endian_converter}, $self->buffer, $self->offset) if ref $desc->{endian_converter} eq 'ARRAY';
8323              
8324 1272         2540 my $i = $self->offset;
8325             # unpack('f'/'d', ...) unpacks to NaN
8326 1272         8920 my @v = unpack($desc->{template}, substr($$buffer, $i, $desc->{message_length}));
8327              
8328 1272 50       3711 if (ref $desc->{packfilter_index} eq 'ARRAY') {
8329 0         0 my $piv = $desc->{packfilter_index};
8330 0         0 my ($i, $j);
8331 0         0 my @v_t = @v;
8332              
8333 0         0 @v = ($v_t[0]);
8334              
8335 0         0 for ($i = 1, $j = 3 ; $j < @$piv ; $j += 4) {
8336 0         0 my ($type, $i_array_t, $c, $i_array) = @$piv[($j - 3) .. $j];
8337 0         0 my $delta = $packfactor[$type];
8338              
8339 0 0       0 $i < $i_array_t and push @v, @v_t[$i .. ($i_array_t - 1)];
8340 0         0 $i = $i_array_t + $c * $delta;
8341              
8342 0         0 for (; $i_array_t < $i ; $i_array_t += $delta) {
8343 0         0 push @v, $unpackfilter[$type]->(@v_t[$i_array_t .. ($i_array_t + $delta - 1)]);
8344             }
8345             }
8346             }
8347              
8348 1272         3899 $self->offset($i + $desc->{message_length});
8349              
8350 1272         2355 my $cb = $desc->{callback};
8351              
8352 1272         2050 my $ret_val;
8353 1272 100       3366 if (ref $cb eq 'ARRAY') {
8354 749 50       1769 $v[0] & $rechd_mask_compressed_timestamp_header and push @v, $self->last_timestamp + ($v[0] & $rechd_mask_cth_timestamp);
8355 749         4062 $ret_val = $cb->[0]->($self, $desc, \@v, @$cb[1 .. $#$cb]);
8356             } else {
8357 523         864 $ret_val = 1;
8358             }
8359 1272         29112 return $ret_val
8360             }
8361              
8362             sub pack_data_message {
8363 0     0 0 0 my ($self, $desc, $v) = @_;
8364 0         0 my $drop_devdata = $self->drop_developer_data;
8365              
8366 0 0 0     0 if ($drop_devdata && ($desc->{message_name} eq 'developer_data_id' || $desc->{message_name} eq 'field_description')) {
      0        
8367 0         0 '';
8368             } else {
8369 0         0 my $rv = $v;
8370              
8371 0 0       0 if (ref $desc->{packfilter_index} eq 'ARRAY') {
8372 0         0 my @v = ($v->[0]);
8373 0         0 my $piv = $desc->{packfilter_index};
8374 0         0 my ($i, $j);
8375              
8376 0         0 for ($i = 1, $j = 3 ; $j < @$piv ; $j += 4) {
8377 0         0 my ($type, $i_array_t, $c, $i_array) = @$piv[($j - 3) .. $j];
8378              
8379 0 0       0 $i < $i_array and push @v, @$v[$i .. ($i_array - 1)];
8380 0         0 $i = $i_array + $c;
8381              
8382 0         0 for (; $i_array < $i ; ++$i_array) {
8383 0         0 push @v, $packfilter[$type]->($v->[$i_array]);
8384             }
8385             }
8386 0         0 $rv = \@v;
8387             }
8388              
8389 0 0       0 if ($drop_devdata) {
8390 0 0       0 if ($desc->{devdata_first} > 0) {
8391 0         0 pack($desc->{template_without_devdata}, @$rv[0 .. ($desc->{devdata_first} - 1)]);
8392             } else {
8393 0         0 '';
8394             }
8395             } else {
8396 0         0 pack($desc->{template}, @$rv);
8397             }
8398             }
8399             }
8400              
8401             =over 4
8402              
8403             =item switched(I, I, I)
8404              
8405             returns real data type attributes for a C's union like field.
8406              
8407             =back
8408              
8409             =cut
8410              
8411             sub switched {
8412 68     68 1 967 my ($self, $desc, $v, $sw) = @_;
8413 68         139 my ($keyv, $key, $attr);
8414              
8415 68 50       234 if (ref $sw->{_by} eq 'ARRAY') {
8416 0         0 $keyv = $sw->{_by};
8417             } else {
8418 68         307 $keyv = [$sw->{_by}];
8419             }
8420              
8421 68         159 for $key (@$keyv) {
8422 68         150 my $i_name = 'i_' . $key;
8423 68         109 my $val;
8424              
8425 68 100 66     587 if (defined $desc->{$i_name} && ($val = $v->[$desc->{$i_name}]) != $desc->{'I_' . $key}) {
8426 66         185 my $key_tn = $desc->{'t_' . $key};
8427              
8428 66 50       159 if (defined $key_tn) {
8429 66         185 my $t_val = $self->named_type_value($key_tn, $val);
8430              
8431 66 100       204 $val = $t_val if defined $t_val;
8432             }
8433              
8434 66 100       209 if (ref $sw->{$val} eq 'HASH') {
8435 51         91 $attr = $sw->{$val};
8436 51         123 last;
8437             }
8438             }
8439             }
8440 68         179 $attr;
8441             }
8442              
8443             sub _string_value {
8444 14     14   40 my ($v, $i, $n) = @_; # array of values, offset, count
8445 14         23 my $j;
8446              
8447 14         41 for ($j = 0 ; $j < $n ; ++$j) {
8448 175 100       409 $v->[$i + $j] == 0 && last;
8449             }
8450 14         49 pack('C*', @{$v}[$i .. ($i + $j - 1)]);
  14         123  
8451             }
8452              
8453             sub value_processed {
8454 4661     4661 0 16904 my ($self, $num, $attr) = @_;
8455              
8456 4661 50       11202 if (ref $attr eq 'HASH') {
8457 4661         7192 my ($unit, $offset, $scale) = @{$attr}{qw(unit offset scale)};
  4661         12527  
8458              
8459 4661 100 66     14404 $num /= $scale if defined $scale and $scale > 0;
8460 4661 100       9711 $num -= $offset if $offset;
8461              
8462 4661 100 66     10023 if (defined $unit) {
    100          
8463 3525         8394 my $unit_tab = $self->unit_table($unit);
8464              
8465 3525 100       8029 if (ref $unit_tab eq 'HASH') {
8466 1524         2452 my ($unit1, $offset1, $scale1) = @{$unit_tab}{qw(unit offset scale)};
  1524         3872  
8467              
8468 1524 50       3351 if ($scale1 > 0) {
8469 1524         2825 $num /= $scale1;
8470 1524         2421 $scale += $scale1;
8471             }
8472 1524 50       2913 $num -= $offset1 if $offset1;
8473 1524 50       3673 $unit = $unit1 if defined $unit1
8474             }
8475              
8476 3525 100 66     12273 if (defined $scale and $scale > 0) {
    100          
8477 3242         8272 my $below_pt = int(log($scale + 9) / log(10));
8478              
8479 3242 100       6979 if ($self->without_unit) {
8480 2148         33717 sprintf("%.${below_pt}f", $num);
8481             } else {
8482 1094         11128 sprintf("%.${below_pt}f %s", $num, $unit);
8483             }
8484             } elsif ($self->without_unit) {
8485 22         98 $num;
8486             } else {
8487 261         1123 $num . " " . $unit;
8488             }
8489             } elsif (defined $scale and $scale > 0) {
8490 20         90 my $below_pt = int(log($scale + 9) / log(10));
8491 20         236 sprintf("%.${below_pt}f", $num);
8492             } else {
8493 1116         2840 $num;
8494             }
8495             } else {
8496 0         0 $num;
8497             }
8498             }
8499              
8500             sub value_unprocessed {
8501 35     35 0 88 my ($self, $str, $attr) = @_;
8502              
8503 35 50       98 if (ref $attr eq 'HASH') {
8504 35         56 my ($unit, $offset, $scale) = @{$attr}{qw(unit offset scale)};
  35         104  
8505 35         70 my $num = $str;
8506              
8507 35 100       144 if (defined $unit) {
8508 30         83 my $unit_tab = $self->unit_table($unit);
8509              
8510 30 100       83 if (ref $unit_tab eq 'HASH') {
8511 10         19 my ($unit1, $offset1, $scale1) = @{$unit_tab}{qw(unit offset scale)};
  10         32  
8512              
8513 10 50 33     67 $scale += $scale1 if defined $scale1 and $scale1 > 0;
8514 10 50       92 $offset += $offset1 if $offset1;
8515 10 50       31 $unit = $unit1 if defined $unit1
8516             }
8517              
8518 30 50 33     190 length($num) >= length($unit) && substr($num, -length($unit)) eq $unit
8519             and substr($num, -length($unit)) = '';
8520             }
8521              
8522 35 50       94 $num += $offset if $offset;
8523 35 100 66     249 $num *= $scale if defined $scale and $scale > 0;
8524 35         98 $num;
8525             } else {
8526 0         0 $str;
8527             }
8528             }
8529              
8530             =over 4
8531              
8532             =item fields_list( $descriptor [, keep_unknown => $boole )
8533              
8534             Given a data message descriptor (I<$descriptor>), returns the list of fields described in it. If C is set to true, unknown field names will also be listed.
8535              
8536             =back
8537              
8538             =cut
8539              
8540             sub fields_list {
8541 44     44 1 289 my ($self, $desc) = (shift, shift);
8542 44         90 my %opts = @_;
8543 44 50       169 croak "argument to fields_list() does not look like a data descriptor hash" if ref $desc ne 'HASH';
8544              
8545 44         82 my (@keys, %fields, @fields);
8546              
8547 44         4063 @keys = grep /^i_/, keys %$desc;
8548 44 50       1140 @keys = grep !/^i_unknown/, @keys unless $opts{keep_unknown};
8549              
8550             # %fields = %$desc{ @keys };
8551 44         112 for my $key (@keys) { # hash slices not supported in versions prior to 5.20
8552 834         1688 $fields{$key} = $desc->{$key};
8553             }
8554              
8555             # sort for easy comparison with values aref passed to callbacks
8556 44         320 @fields = sort { $fields{$a} <=> $fields{$b} } keys %fields;
  3050         5442  
8557 44         997 map s/^i_//, @fields;
8558             return @fields
8559 44         563 }
8560              
8561             =over 4
8562              
8563             =item fields_defined( $descriptor, $values )
8564              
8565             Given a data message descriptor (I<$descriptor>) and a corresponding data array reference of values (I<$values>), returns the list of fields whose value is defined. Unknow field names are never listed.
8566              
8567             =back
8568              
8569             =cut
8570              
8571             sub fields_defined {
8572 22     22 1 21529 my ($self, $descriptor, $values) = @_;
8573              
8574 22         89 my @fields = $self->fields_list( $descriptor );
8575              
8576 22         73 my @defined;
8577 22         51 for my $field (@fields) {
8578 417         801 my $index = $descriptor->{ 'i_' . $field };
8579 417 100       1185 push @defined, $field if $values->[ $index ] != $descriptor->{'I_' . $field}
8580             }
8581             return @defined
8582 22         172 }
8583              
8584              
8585             =over 4
8586              
8587             =item field_value( I<$field>, I<$descriptor>, I<$values> )
8588              
8589             Returns the value of the field named I<$field> (a string).
8590              
8591             The other arguments consist of the data message descriptor (I<$descriptor>, a hash reference) and the values fetched from a data message (I<$values>, an array reference). These are simply the references passed to data message callbacks by C, if any are registered, and are simply to be passed on to this method (please do not modifiy them).
8592              
8593             For example, we can define and register a callback for C data messages and get the name of the manufacturer of the device that recorded the FIT file:
8594              
8595             my $file_id_callback = sub {
8596             my ($self, $descriptor, $values) = @_;
8597             my $value = $self->field_value( 'manufacturer', $descriptor, $values );
8598              
8599             print "The manufacturer is: ", $value, "\n"
8600             };
8601              
8602             $fit->data_message_callback_by_name('file_id', $file_id_callback ) or die $fit->error;
8603              
8604             1 while ( $fit->fetch );
8605              
8606             =back
8607              
8608             =cut
8609              
8610             sub field_value {
8611 143     143 1 133748 my ($self, $field_name, $descriptor, $values_aref) = @_;
8612              
8613 143         891 my @keys = map $_ . $field_name, qw( t_ a_ I_ );
8614             my ($type_name, $attr, $invalid, $value) =
8615 143         254 ( @{$descriptor}{ @keys }, $values_aref->[ $descriptor->{'i_' . $field_name} ] );
  143         845  
8616              
8617 143 100       492 if (defined $attr->{switch}) {
8618 14         56 my $t_attr = $self->switched($descriptor, $values_aref, $attr->{switch});
8619 14 100       45 if (ref $t_attr eq 'HASH') {
8620 13         22 $attr = $t_attr;
8621             $type_name = $attr->{type_name}
8622 13         37 }
8623             }
8624              
8625 143         325 my $ret_val = $value;
8626 143 50       378 if ($value != $invalid) {
8627 143 100       364 if (defined $type_name) {
8628 74         226 $ret_val = $self->named_type_value($type_name, $value);
8629 74 100       406 return $ret_val if defined $ret_val
8630             }
8631              
8632 79 50       271 if (ref $attr eq 'HASH') {
8633 79         224 $ret_val = $self->value_processed($value, $attr)
8634             }
8635             }
8636 79         332 return $ret_val
8637             }
8638              
8639             =over 4
8640              
8641             =item field_value_as_read( I<$field>, I<$descriptor>, I<$value> [, $type_name_or_aref ] )
8642              
8643             Converts the value parsed and returned by C back to what it was when read from the FIT file and returns it.
8644              
8645             This method is mostly for developers or if there is a particular need to inspect the data more closely, it should seldomly be used. Arguments are similar to C except that a single value I<$value> is passed instead of an array reference. That value corresponds to the value the former method has or would have returned.
8646              
8647             As an example, we can obtain the actual value recorded in the FIT file for the manufacturer by adding these lines to the callback defined above:
8648              
8649             my $as_read = $self->field_value_as_read( 'manufacturer', $descriptor, $value );
8650             print "The manufacturer's value as recorded in the FIT file is: ", $as_read, "\n"
8651              
8652             The method will raise an exception if I<$value> would have been obtained by C via an internal call to C. In that case, the type name or the original array reference of values that was passed to the callback must be provided as the last argument. Otherwise, there is no way to guess what the value read from the file may have been.
8653              
8654             =back
8655              
8656             =cut
8657              
8658             sub field_value_as_read {
8659 141     141 1 129540 my ($self, $field_name, $descriptor, $value, $optional_last_arg) = @_;
8660              
8661 141         794 my @keys = map $_ . $field_name, qw( t_ a_ I_ );
8662 141         309 my ($type_name, $attr, $invalid) = ( @{$descriptor}{ @keys } );
  141         525  
8663              
8664 141 100       540 if (defined $attr->{switch}) {
8665              
8666 15         61 my $croak_msg = 'this field\'s value was derived from a call to switched(), please provide as the ';
8667 15         31 $croak_msg .= 'last argument the type name or the array reference containing the original values';
8668 15 50       41 croak $croak_msg unless defined $optional_last_arg;
8669              
8670 15 100       49 if (ref $optional_last_arg eq 'ARRAY') {
8671 14         49 my $t_attr = $self->switched($descriptor, $optional_last_arg, $attr->{switch});
8672 14 100       44 if (ref $t_attr eq 'HASH') {
8673 13         23 $attr = $t_attr;
8674             $type_name = $attr->{type_name}
8675 13         28 }
8676 1         3 } else { $type_name = $optional_last_arg }
8677             }
8678              
8679 141         234 my $ret_val = $value;
8680 141 100       873 if ($value !~ /^[-+]?\d+$/) {
8681 100 100       217 if (defined $type_name ) {
8682 65         196 my $ret_val = $self->named_type_value($type_name, $value);
8683 65 50       556 return $ret_val if defined $ret_val
8684             }
8685              
8686 35 50       113 if (ref $attr eq 'HASH') {
8687 35         127 $ret_val = $self->value_unprocessed($value, $attr)
8688             }
8689             }
8690 76         335 return $ret_val
8691             }
8692              
8693             =over 4
8694              
8695             =item value_cooked(I, I, I, I)
8696              
8697             This method is now deprecated and is no longer supported. Please use C instead.
8698              
8699             converts I to a (hopefully) human readable form.
8700              
8701             =back
8702              
8703             =cut
8704              
8705             sub value_cooked {
8706 3639     3639 1 8237 my ($self, $tname, $attr, $invalid, $val) = @_;
8707              
8708 3639 100       7196 if ($val == $invalid) {
8709 781         1499 $val;
8710             } else {
8711 2858 100       5742 if (defined $tname) {
8712 393         1185 my $vname = $self->named_type_value($tname, $val);
8713              
8714 393 100       1717 defined $vname && return $vname;
8715             }
8716              
8717 2478 50       5566 if (ref $attr eq 'HASH') {
8718 2478         5509 $self->value_processed($val, $attr);
8719             } else {
8720 0         0 $val;
8721             }
8722             }
8723             }
8724              
8725             =over 4
8726              
8727             =item value_uncooked(I, I, I, I)
8728              
8729             This method is now deprecated and is no longer supported. Please use C instead.
8730              
8731             converts a human readable representation of a datum to an original form.
8732              
8733             =back
8734              
8735             =cut
8736              
8737             sub value_uncooked {
8738 0     0 1 0 my ($self, $tname, $attr, $invalid, $val) = @_;
8739              
8740 0 0       0 if ($val !~ /^[-+]?\d+$/) {
8741 0 0       0 if ($tname ne '') {
8742 0         0 my $vnum = $self->named_type_value($tname, $val);
8743              
8744 0 0       0 defined $vnum && return $vnum;
8745             }
8746              
8747 0 0       0 if (ref $attr eq 'HASH') {
8748 0         0 $self->value_unprocessed($val, $attr);
8749             } else {
8750 0         0 $val;
8751             }
8752             } else {
8753 0         0 $val;
8754             }
8755             }
8756              
8757             sub seconds_to_hms {
8758 0     0 0 0 my ($self, $s) = @_;
8759 0         0 my $sign = 1;
8760              
8761 0 0       0 if ($s < 0) {
8762 0         0 $sign = -1;
8763 0         0 $s = -$s;
8764             }
8765              
8766 0         0 my $h = int($s / 3600);
8767 0         0 my $m = int(($s - $h * 3600) / 60);
8768              
8769 0         0 $s -= $h * 3600 + $m * 60;
8770              
8771 0 0       0 my $hms = sprintf('%s%uh%um%g', $sign < 0 ? '-' : '', $h, $m, $s);
8772              
8773 0 0       0 $hms =~ s/\./s/ or $hms .= 's';
8774 0         0 $hms;
8775             }
8776              
8777             sub drop_developer_data {
8778 3     3 0 7 my $self = shift;
8779 3 50       14 if (@_) {
8780 3         10 $self->{drop_developer_data} = $_[0];
8781             } else {
8782 0         0 $self->{drop_developer_data};
8783             }
8784             }
8785              
8786             sub initialize {
8787 12     12 0 32 my $self = shift;
8788 12         31 my $buffer = '';
8789              
8790 12         175 %$self = (
8791             'error' => undef,
8792             'file_read' => 0,
8793             'file_processed' => 0,
8794             'offset' => 0,
8795             'buffer' => \$buffer,
8796             'fh' => new FileHandle,
8797             'data_message_callback' => +{},
8798             'unit_table' => +{},
8799             'drop_developer_data' => 0,
8800             );
8801              
8802 12         1036 $self->data_message_callback_by_name(developer_data_id => \&syscallback_devdata_id);
8803 12         103 $self->data_message_callback_by_name(field_description => \&syscallback_devdata_field_desc);
8804 12         28 $self;
8805             }
8806              
8807             # undocumented (used by fitdump.pl is chained files)
8808             sub reset {
8809 0     0 0 0 my $self = shift;
8810 0         0 $self->clear_buffer;
8811              
8812 0         0 %$self = map {($_ => $self->{$_})} qw(error buffer fh data_message_callback unit_table profile_version
  0         0  
8813             EOF use_gmtime numeric_date_time without_unit maybe_chained);
8814              
8815 0         0 my $buffer = $self->buffer;
8816 0         0 $self->file_read(length($$buffer));
8817 0         0 $self->file_processed(0);
8818 0         0 $self;
8819             }
8820              
8821             my @type_name = ();
8822              
8823             $type_name[FIT_ENUM] = 'ENUM';
8824             $type_name[FIT_SINT8] = 'SINT8';
8825             $type_name[FIT_UINT8] = 'UINT8';
8826             $type_name[FIT_SINT16] = 'SINT16';
8827             $type_name[FIT_UINT16] = 'UINT16';
8828             $type_name[FIT_SINT32] = 'SINT32';
8829             $type_name[FIT_UINT32] = 'UINT32';
8830             $type_name[FIT_STRING] = 'STRING';
8831             $type_name[FIT_FLOAT32] = 'FLOAT32';
8832             $type_name[FIT_FLOAT64] = 'FLOAT64';
8833             $type_name[FIT_UINT8Z] = 'UINT8Z';
8834             $type_name[FIT_UINT16Z] = 'UINT16Z';
8835             $type_name[FIT_UINT32Z] = 'UINT32Z';
8836             $type_name[FIT_BYTE] = 'BYTE';
8837              
8838             sub isnan {
8839 4151     4151 0 5511 my $ret_val;
8840 18     18   1358352 do { no warnings 'numeric'; $ret_val = !defined( $_[0] <=> 9**9**9 ) };
  18         47  
  18         92275  
  4151         5460  
  4151         8395  
8841 4151         9881 return $ret_val
8842             }
8843              
8844             sub print_all_fields {
8845 243     243 0 965 my ($self, $desc, $v, %opt) = @_;
8846 243         632 my ($indent, $fh, $skip_invalid) = @opt{qw(indent fh skip_invalid)};
8847              
8848 243 50       656 $fh = \*STDOUT if !defined $fh;
8849 243 50       661 $fh->print($indent, 'compressed_timestamp: ', $self->named_type_value('date_time', $v->[$#$v]), "\n") if $desc->{array_length} == $#$v;
8850              
8851 243         5417 for my $i_name (sort {$desc->{$a} <=> $desc->{$b}} grep {/^i_/} keys %$desc) {
  9978         16828  
  33079         57127  
8852 3366         21805 my $name = $i_name;
8853 3366         11139 $name =~ s/^i_//;
8854              
8855 3366         8114 my $attr = $desc->{'a_' . $name};
8856 3366         6135 my $tname = $desc->{'t_' . $name};
8857 3366         5137 my $pname = $name;
8858              
8859 3366 100       8473 if (ref $attr->{switch} eq 'HASH') {
8860 32         116 my $t_attr = $self->switched($desc, $v, $attr->{switch});
8861              
8862 32 100       66 if (ref $t_attr eq 'HASH') {
8863 17         28 $attr = $t_attr;
8864 17         27 $tname = $attr->{type_name};
8865 17         29 $pname = $attr->{name};
8866             }
8867             }
8868              
8869 3366         5304 my $i = $desc->{$i_name};
8870 3366         6221 my $c = $desc->{'c_' . $name};
8871 3366         5882 my $type = $desc->{'T_' . $name};
8872 3366         6964 my $invalid = $desc->{'I_' . $name};
8873 3366         4768 my $j;
8874              
8875 3366         7850 for ($j = 0 ; $j < $c ; ++$j) {
8876 3542 100       8143 isnan($v->[$i + $j]) && next;
8877 3541 100       9415 $v->[$i + $j] != $invalid && last;
8878             }
8879              
8880 3366 50 66     11022 if ($j < $c || !$skip_invalid) {
8881 3366 100       7757 if (defined $tname) {
8882 370 100 100     2455 $self->last_timestamp($v->[$i]) if $type == FIT_UINT32 && $tname eq 'date_time' && $pname eq 'timestamp';
      100        
8883             }
8884 3366 50       15198 $fh->print($indent, $pname, ' (', $desc->{'N_' . $name}, '-', $c, '-', $type_name[$type] ne '' ? $type_name[$type] : $type);
8885 3366 100       26168 $fh->print(', original name: ', $name) if $name ne $pname;
8886 3366 100       7468 $fh->print(', INVALID') if $j >= $c;
8887 3366         12166 $fh->print('): ');
8888              
8889 3366 100       19558 if ($type == FIT_STRING) {
8890 5         23 $fh->print("\"", _string_value($v, $i, $c), "\"\n");
8891             } else {
8892 3361 100       6789 $fh->print('{') if $c > 1;
8893              
8894 3361         10458 my $pval = $self->value_cooked($tname, $attr, $invalid, $v->[$i]);
8895              
8896 3361         10672 $fh->print($pval);
8897 3361 100       30258 $fh->print(' (', $v->[$i], ')') if $v->[$i] ne $pval;
8898              
8899 3361 100       17284 if ($c > 1) {
8900 54         77 my ($j, $k);
8901              
8902 54         148 for ($j = $i + 1, $k = $i + $c ; $j < $k ; ++$j) {
8903 174         324 $pval = $self->value_cooked($tname, $attr, $invalid, $v->[$j]);
8904 174         498 $fh->print(', ', $pval);
8905 174 50       1167 $fh->print(' (', $v->[$j], ')') if $v->[$j] ne $pval;
8906             }
8907 54         110 $fh->print('}');
8908             }
8909 3361         7027 $fh->print("\n");
8910             }
8911             }
8912             }
8913 243         4382 1;
8914             }
8915              
8916             sub print_all_json {
8917 0     0 0 0 my ($self, $desc, $v, %opt) = @_;
8918 0         0 my ($indent, $fh, $skip_invalid) = @opt{qw(indent fh skip_invalid)};
8919              
8920 0         0 my $out = 0;
8921              
8922 0 0       0 $fh = \*STDOUT if !defined $fh;
8923 0 0       0 if ($desc->{array_length} == $#$v) {
8924 0         0 $fh->print($indent, '"compressed_timestamp": "', $self->named_type_value('date_time', $v->[$#$v]), '"');
8925 0         0 $out = $out + 1;
8926             }
8927              
8928 0         0 for my $i_name (sort {$desc->{$a} <=> $desc->{$b}} grep {/^i_/} keys %$desc) {
  0         0  
  0         0  
8929 0         0 my $name = $i_name;
8930 0         0 $name =~ s/^i_//;
8931              
8932 0         0 my $attr = $desc->{'a_' . $name};
8933 0         0 my $tname = $desc->{'t_' . $name};
8934 0         0 my $pname = $name;
8935              
8936 0 0       0 if (ref $attr->{switch} eq 'HASH') {
8937 0         0 my $t_attr = $self->switched($desc, $v, $attr->{switch});
8938              
8939 0 0       0 if (ref $t_attr eq 'HASH') {
8940 0         0 $attr = $t_attr;
8941 0         0 $tname = $attr->{type_name};
8942 0         0 $pname = $attr->{name};
8943             }
8944             }
8945              
8946 0         0 my $i = $desc->{$i_name};
8947 0         0 my $c = $desc->{'c_' . $name};
8948 0         0 my $type = $desc->{'T_' . $name};
8949 0         0 my $invalid = $desc->{'I_' . $name};
8950 0         0 my $j;
8951              
8952 0         0 for ($j = 0 ; $j < $c ; ++$j) {
8953 0 0       0 isnan($v->[$i + $j]) && next;
8954 0 0       0 $v->[$i + $j] != $invalid && last;
8955             }
8956              
8957 0 0 0     0 if ($j < $c || !$skip_invalid) {
8958 0 0 0     0 $self->last_timestamp($v->[$i]) if $type == FIT_UINT32 && $tname eq 'date_time' && $pname eq 'timestamp';
      0        
8959 0 0       0 $fh->print(",\n") if $out;
8960 0         0 $fh->print($indent, '"', $pname, '": ');
8961              
8962 0 0       0 if ($type == FIT_STRING) {
8963 0         0 $fh->print("\"", _string_value($v, $i, $c), "\"");
8964             } else {
8965 0 0       0 $fh->print('[') if $c > 1;
8966              
8967 0         0 my $pval = $self->value_cooked($tname, $attr, $invalid, $v->[$i]);
8968              
8969 0 0       0 if (looks_like_number($pval)) {
8970 0         0 $fh->print($pval);
8971             } else {
8972 0         0 $fh->print("\"$pval\"");
8973             }
8974              
8975 0 0       0 if ($c > 1) {
8976 0         0 my ($j, $k);
8977              
8978 0         0 for ($j = $i + 1, $k = $i + $c ; $j < $k ; ++$j) {
8979 0         0 $pval = $self->value_cooked($tname, $attr, $invalid, $v->[$j]);
8980 0         0 $fh->print(', ');
8981 0 0       0 if (looks_like_number($pval)) {
8982 0         0 $fh->print($pval);
8983             } else {
8984 0         0 $fh->print("\"$pval\"");
8985             }
8986             }
8987              
8988 0         0 $fh->print(']');
8989             }
8990             }
8991 0         0 $out = $out + 1;
8992             }
8993             }
8994 0         0 1;
8995             }
8996              
8997             =over 4
8998              
8999             =item use_gmtime(I)
9000              
9001             sets the flag which of GMT or local timezone is used for C type value conversion. Defaults to true.
9002              
9003             =back
9004              
9005             =cut
9006              
9007             my $use_gmtime = 0;
9008              
9009             sub use_gmtime {
9010 695     695 1 1290 my $self = shift;
9011              
9012 695 100       1929 if (@_) {
    50          
9013 20 50       69 if (ref $self eq '') {
9014 0         0 $use_gmtime = $_[0];
9015             } else {
9016 20         56 $self->{use_gmtime} = $_[0];
9017             }
9018             } elsif (ref $self eq '') {
9019 0         0 $use_gmtime;
9020             } else {
9021 675         4250 $self->{use_gmtime};
9022             }
9023             }
9024              
9025             =over 4
9026              
9027             =item unit_table(I => I)
9028              
9029             sets I for I.
9030              
9031             =back
9032              
9033             =cut
9034              
9035             sub unit_table {
9036 3583     3583 1 5566 my $self = shift;
9037 3583         5203 my $unit = shift;
9038              
9039 3583 100       6771 if (@_) {
9040 28         101 $self->{unit_table}->{$unit} = $_[0];
9041             } else {
9042 3555         9853 $self->{unit_table}->{$unit};
9043             }
9044             }
9045              
9046             sub without_unit {
9047 3532     3532 0 5530 my $self = shift;
9048 3532 100       6143 if (@_) {
9049 7         28 $self->{without_unit} = $_[0];
9050             } else {
9051 3525         8141 $self->{without_unit};
9052             }
9053             }
9054              
9055             =over 4
9056              
9057             =item semicircles_to_degree(I)
9058              
9059             =item mps_to_kph(I)
9060              
9061             wrapper methods of C method. C defaults to true.
9062              
9063             =back
9064              
9065             =cut
9066              
9067             sub semicircles_to_degree {
9068 20     20 1 67 my ($self, $on) = @_;
9069 20 50       130 $self->unit_table('semicircles' => $on ? +{'unit' => 'deg', 'scale' => 2 ** 31 / 180} : undef);
9070             }
9071              
9072             sub mps_to_kph {
9073 8     8 1 32 my ($self, $on) = @_;
9074 8 100       43 $self->unit_table('m/s' => $on ? +{'unit' => 'km/h', 'scale' => 1 / 3.6} : undef);
9075             }
9076              
9077             =over 4
9078              
9079             =item close()
9080              
9081             closes opened file handles.
9082              
9083             =back
9084              
9085             =cut
9086              
9087             sub close {
9088 10     10 1 3429 my $self = shift;
9089 10         46 my $fh = $self->fh;
9090 10 50       110 $fh->close if $fh->opened;
9091             }
9092              
9093             =over 4
9094              
9095             =item profile_version_string()
9096              
9097             Returns a string representation of the profile version used by the device or application that created the FIT file opened in the instance.
9098              
9099             C<< fetch_header() >> must have been called at least once for this method to be able to return a value, will raise an exception otherwise.
9100              
9101             =back
9102              
9103             =head2 Functions
9104              
9105             The following functions are provided. None are exported, they may be called as C<< Geo::FIT::message_name(20) >>, C<< Geo::FIT::field_name('device_info', 4) >> C<< Geo::FIT::field_number('device_info', 'product') >>, etc.
9106              
9107             =over 4
9108              
9109             =item message_name(I)
9110              
9111             returns the message name for I or undef.
9112              
9113             =item message_number(I)
9114              
9115             returns the message number for I or undef.
9116              
9117             =back
9118              
9119             =cut
9120              
9121             sub message_name {
9122 0     0 1   my $mspec = shift;
9123 0 0         my $msgtype = $mspec =~ /^\d+$/ ? $msgtype_by_num{$mspec} : $msgtype_by_name{$mspec};
9124              
9125 0 0         if (ref $msgtype eq 'HASH') {
9126 0           $msgtype->{_name};
9127             } else {
9128 0           undef;
9129             }
9130             }
9131              
9132             sub message_number {
9133 0     0 1   my $mspec = shift;
9134 0 0         my $msgtype = $mspec =~ /^\d+$/ ? $msgtype_by_num{$mspec} : $msgtype_by_name{$mspec};
9135              
9136 0 0         if (ref $msgtype eq 'HASH') {
9137 0           $msgtype->{_number};
9138             } else {
9139 0           undef;
9140             }
9141             }
9142              
9143             =over 4
9144              
9145             =item field_name(I, I)
9146              
9147             returns the field name for I in I or undef.
9148              
9149             =item field_number(I, I)
9150              
9151             returns the field index for I in I or undef.
9152              
9153             =back
9154              
9155             =cut
9156              
9157             sub field_name {
9158 0     0 1   my ($mspec, $fspec) = @_;
9159 0 0         my $msgtype = $mspec =~ /^\d+$/ ? $msgtype_by_num{$mspec} : $msgtype_by_name{$mspec};
9160              
9161 0 0         if (ref $msgtype eq 'HASH') {
9162 0           my $flddesc = $msgtype->{$fspec};
9163             ref $flddesc eq 'HASH'
9164 0 0         and return $flddesc->{name};
9165             }
9166 0           undef;
9167             }
9168              
9169             sub field_number {
9170 0     0 1   my ($mspec, $fspec) = @_;
9171 0 0         my $msgtype = $mspec =~ /^\d+$/ ? $msgtype_by_num{$mspec} : $msgtype_by_name{$mspec};
9172              
9173 0 0         if (ref $msgtype eq 'HASH') {
9174 0           my $flddesc = $msgtype->{$fspec};
9175             ref $flddesc eq 'HASH'
9176 0 0         and return $flddesc->{number};
9177             }
9178 0           undef;
9179             }
9180              
9181             =head2 Constants
9182              
9183             Following constants are exported: C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C.
9184              
9185             Also exported are:
9186              
9187             =over 4
9188              
9189             =item FIT_BYTE
9190              
9191             numbers representing base types of field values in data messages.
9192              
9193             =item FIT_BASE_TYPE_MAX
9194              
9195             the maximal number representing base types of field values in data messages.
9196              
9197             =item FIT_HEADER_LENGTH
9198              
9199             length of a .FIT file header.
9200              
9201             =back
9202              
9203             =head2 Data message descriptor
9204              
9205             When C method meets a definition message, it creates a hash which includes various information about the corresponding data message. We call the hash a data message descriptor. It includes the following key value pairs.
9206              
9207             =over 4
9208              
9209             =item I => I
9210              
9211             in a global .FIT profile.
9212              
9213             =item C => I
9214              
9215             necessarily.
9216              
9217             =item C => I
9218              
9219             necessarily.
9220              
9221             =item C => I
9222              
9223             only if the message is documented.
9224              
9225             =item C => I
9226              
9227             of a callback function and callback data, only if a C is registered.
9228              
9229             =item C => I
9230              
9231             of multi-octets data in this message, where 0 for littel-endian and 1 for big-endian.
9232              
9233             =item C