File Coverage

blib/lib/Geo/FIT.pm
Criterion Covered Total %
statement 330 926 35.6
branch 123 520 23.6
condition 7 66 10.6
subroutine 47 94 50.0
pod 29 82 35.3
total 536 1688 31.7


line stmt bran cond sub pod time code
1             package Geo::FIT;
2 6     6   416153 use strict;
  6         71  
  6         167  
3 6     6   29 use warnings;
  6         9  
  6         256  
4              
5             our $VERSION = '1.07';
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             $fit = Geo::FIT->new();
17             $fit->file( $fname )
18             $fit->open;
19             $fit->fetch_header;
20             $fit->fetch;
21             $fit->data_message_callback_by_name( $message name, \&callback_function [, \%callback_data, ... ] );
22             $fit->data_message_callback_by_num( $message number, \&callback_function [, \%callback_data, ... ] );
23             $fit->close;
24              
25             =head1 DESCRIPTION
26              
27             C is a Perl class to provide interfaces to decode Garmin FIT files (*.fit).
28              
29             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).
30              
31             =cut
32              
33 6     6   30 use Carp qw/ croak /;
  6         9  
  6         663  
34 6     6   2881 use FileHandle;
  6         60582  
  6         30  
35 6     6   4958 use POSIX qw(BUFSIZ);
  6         39151  
  6         32  
36 6     6   11999 use Time::Local;
  6         14409  
  6         379  
37 6     6   50 use Scalar::Util qw(blessed looks_like_number);
  6         11  
  6         631  
38              
39             my $uint64_invalid;
40             BEGIN {
41 6     6   20 eval { $uint64_invalid = unpack('Q', pack('a', -1)) };
  6         42  
42 6 50       25 unless (defined $uint64_invalid) {
43 6         7969 require Math::BigInt;
44 6         167575 import Math::BigInt
45             }
46             }
47              
48             require Exporter;
49             our @ISA = qw(Exporter);
50             our @EXPORT = qw(
51             FIT_ENUM
52             FIT_SINT8
53             FIT_UINT8
54             FIT_SINT16
55             FIT_UINT16
56             FIT_SINT32
57             FIT_UINT32
58             FIT_SINT64
59             FIT_UINT64
60             FIT_STRING
61             FIT_FLOAT32
62             FIT_FLOAT64
63             FIT_UINT8Z
64             FIT_UINT16Z
65             FIT_UINT32Z
66             FIT_UINT64Z
67             FIT_BYTE
68             FIT_BASE_TYPE_MAX
69             FIT_HEADER_LENGTH
70             );
71              
72             sub FIT_ENUM() {0;}
73             sub FIT_SINT8() {1;}
74             sub FIT_UINT8() {2;}
75             sub FIT_SINT16() {3;}
76             sub FIT_UINT16() {4;}
77             sub FIT_SINT32() {5;}
78             sub FIT_UINT32() {6;}
79             sub FIT_STRING() {7;}
80             sub FIT_FLOAT32() {8;}
81             sub FIT_FLOAT64() {9;}
82             sub FIT_UINT8Z() {10;}
83             sub FIT_UINT16Z() {11;}
84             sub FIT_UINT32Z() {12;}
85             sub FIT_BYTE() {13;}
86             sub FIT_SINT64() {14;}
87             sub FIT_UINT64() {15;}
88             sub FIT_UINT64Z() {16;}
89             sub FIT_BASE_TYPE_MAX() {FIT_UINT64Z;}
90              
91             my $rechd_offset_compressed_timestamp_header = 7;
92             my $rechd_mask_compressed_timestamp_header = 1 << $rechd_offset_compressed_timestamp_header;
93             my $rechd_offset_cth_local_message_type = 5;
94             my $rechd_length_cth_local_message_type = 2;
95             my $rechd_mask_cth_local_message_type = ((1 << $rechd_length_cth_local_message_type) - 1) << $rechd_offset_cth_local_message_type;
96             my $rechd_length_cth_timestamp = $rechd_offset_cth_local_message_type;
97             my $rechd_mask_cth_timestamp = (1 << $rechd_length_cth_timestamp) - 1;
98             my $rechd_offset_definition_message = 6;
99             my $rechd_mask_definition_message = 1 << $rechd_offset_definition_message;
100             my $rechd_offset_devdata_message = 5;
101             my $rechd_mask_devdata_message = 1 << $rechd_offset_devdata_message;
102             my $rechd_length_local_message_type = 4;
103             my $rechd_mask_local_message_type = (1 << $rechd_length_local_message_type) - 1;
104             my $cthd_offset_local_message_type = 5;
105             my $cthd_length_local_message_type = 2;
106             my $cthd_mask_local_message_type = (1 << $cthd_length_local_message_type) - 1;
107             my $cthd_length_time_offset = 5;
108             my $cthd_mask_time_offset = (1 << $cthd_length_time_offset) - 1;
109              
110             my $defmsg_min_template = 'C C C S C';
111             my $defmsg_min_length = length(pack($defmsg_min_template));
112              
113             my $deffld_template = 'C C C';
114             my $deffld_length = length(pack($deffld_template));
115             my $deffld_mask_endian_p = 1 << 7;
116             my $deffld_mask_type = (1 << 5) - 1;
117              
118             my $devdata_min_template = 'C';
119             my $devdata_min_length = length(pack($devdata_min_template));
120             my $devdata_deffld_template = 'C C C';
121             my $devdata_deffld_length = length(pack($deffld_template));
122              
123             my @invalid = (0xFF) x ($deffld_mask_type + 1);
124              
125             $invalid[FIT_SINT8] = 0x7F;
126             $invalid[FIT_SINT16] = 0x7FFF;
127             $invalid[FIT_UINT16] = 0xFFFF;
128             $invalid[FIT_SINT32] = 0x7FFFFFFF;
129             $invalid[FIT_UINT32] = 0xFFFFFFFF;
130             $invalid[FIT_STRING] = $invalid[FIT_UINT8Z] = $invalid[FIT_UINT16Z] = $invalid[FIT_UINT32Z] = $invalid[FIT_UINT64Z] = 0;
131             #$invalid[FIT_FLOAT32] = NaN;
132             #$invalid[FIT_FLOAT64] = NaN;
133             $invalid[FIT_FLOAT32] = unpack('f', pack('V', 0xFFFFFFFF));
134             $invalid[FIT_FLOAT64] = unpack('d', pack('V V', 0xFFFFFFFF, 0xFFFFFFFF));
135              
136             my ($big_int_base32, $sint64_2c_mask, $sint64_2c_base, $sint64_2c_sign);
137              
138             if (defined $uint64_invalid) {
139             $invalid[FIT_UINT64] = $uint64_invalid;
140             $invalid[FIT_SINT64] = eval '0x7FFFFFFFFFFFFFFF';
141             } else {
142             $invalid[FIT_UINT64] = Math::BigInt->new('0xFFFFFFFFFFFFFFFF');
143             $invalid[FIT_SINT64] = Math::BigInt->new('0x7FFFFFFFFFFFFFFF');
144             $big_int_base32 = Math::BigInt->new('0x100000000');
145             $sint64_2c_mask = Math::BigInt->new('0xFFFFFFFFFFFFFFFF');
146             $sint64_2c_base = Math::BigInt->new('0x10000000000000000');
147             $sint64_2c_sign = Math::BigInt->new('0x1000000000000000');
148             }
149              
150             sub packfilter_uint64_big_endian {
151 0     0 0 0 my @res = $_[0]->bdiv($big_int_base32);
152 0         0 @res;
153             }
154              
155             sub packfilter_uint64_little_endian {
156 0     0 0 0 my @res = $_[0]->bdiv($big_int_base32);
157 0         0 @res[1, 0];
158             }
159              
160             my $my_endian = unpack('L', pack('N', 1)) == 1 ? 1 : 0;
161              
162             *packfilter_uint64 = $my_endian ? \&packfilter_uint64_big_endian : \&packfilter_uint64_little_endian;
163              
164             sub unpackfilter_uint64_big_endian {
165 0     0 0 0 my ($hi, $lo) = @_;
166 0         0 Math::BigInt->new($hi)->blsft(32)->badd($lo);
167             }
168              
169             sub unpackfilter_uint64_little_endian {
170 0     0 0 0 &unpackfilter_uint64_big_endian(@_[1, 0]);
171             }
172              
173             *unpackfilter_uint64 = $my_endian ? \&unpackfilter_uint64_big_endian : \&unpackfilter_uint64_little_endian;
174              
175             sub packfilter_sint64_big_endian {
176 0 0   0 0 0 if ($_[0]->bcmp(0) < 0) {
177 0         0 &packfilter_uint64_big_endian($sint64_2c_mask->band($sint64_2c_base->badd($_[0])));
178             } else {
179 0         0 &packfilter_uint64_big_endian($_[0]);
180             }
181             }
182              
183             sub packfilter_sint64_little_endian {
184 0 0   0 0 0 if ($_[0]->bcmp(0) < 0) {
185 0         0 &packfilter_uint64_little_endian($sint64_2c_mask->band($sint64_2c_base->badd($_[0])));
186             } else {
187 0         0 &packfilter_uint64_little_endian($_[0]);
188             }
189             }
190              
191             *packfilter_sint64 = $my_endian ? \&packfilter_sint64_big_endian : \&packfilter_sint64_little_endian;
192              
193             sub unpackfilter_sint64_big_endian {
194 0     0 0 0 my ($hi, $lo) = @_;
195 0         0 my $n = Math::BigInt->new($hi)->blsft(32)->badd($lo)->band($sint64_2c_mask);
196              
197 0 0       0 if ($n->band($sint64_2c_sign)->bcmp(0) == 0) {
198 0         0 $n;
199             } else {
200 0         0 $n->bsub($sint64_2c_base);
201             }
202             }
203              
204             sub unpackfilter_sint64_little_endian {
205 0     0 0 0 &unpackfilter_sint64_big_endian(@_[1, 0]);
206             }
207              
208             *unpackfilter_sint64 = $my_endian ? \&unpackfilter_sint64_big_endian : \&unpackfilter_sint64_little_endian;
209              
210             sub invalid {
211 0     0 1 0 my ($self, $type) = @_;
212 0         0 $invalid[$type & $deffld_mask_type];
213             }
214              
215             my @size = (1) x ($deffld_mask_type + 1);
216              
217             $size[FIT_SINT16] = $size[FIT_UINT16] = $size[FIT_UINT16Z] = 2;
218             $size[FIT_SINT32] = $size[FIT_UINT32] = $size[FIT_UINT32Z] = $size[FIT_FLOAT32] = 4;
219             $size[FIT_FLOAT64] = $size[FIT_SINT64] = $size[FIT_UINT64] = $size[FIT_UINT64Z] = 8;
220              
221             my @template = ('C') x ($deffld_mask_type + 1);
222             my @packfactor = (1) x ($deffld_mask_type + 1);
223             my @packfilter = (undef) x ($deffld_mask_type + 1);
224             my @unpackfilter = (undef) x ($deffld_mask_type + 1);
225              
226             $template[FIT_SINT8] = 'c';
227             $template[FIT_SINT16] = 's';
228             $template[FIT_UINT16] = $template[FIT_UINT16Z] = 'S';
229             $template[FIT_SINT32] = 'l';
230             $template[FIT_UINT32] = $template[FIT_UINT32Z] = 'L';
231             $template[FIT_FLOAT32] = 'f';
232             $template[FIT_FLOAT64] = 'd';
233              
234             if (defined $uint64_invalid) {
235             $template[FIT_SINT64] = 'q';
236             $template[FIT_UINT64] = $template[FIT_UINT64Z] = 'Q';
237             } else {
238             $template[FIT_SINT64] = $template[FIT_UINT64] = $template[FIT_UINT64Z] = 'L';
239             $packfactor[FIT_SINT64] = $packfactor[FIT_UINT64] = $packfactor[FIT_UINT64Z] = 2;
240             $packfilter[FIT_SINT64] = \&packfilter_sint64;
241             $unpackfilter[FIT_SINT64] = \&unpackfilter_sint64;
242             $packfilter[FIT_UINT64] = $packfilter[FIT_UINT64Z] = \&packfilter_uint64;
243             $unpackfilter[FIT_UINT64] = $unpackfilter[FIT_UINT64Z] = \&unpackfilter_uint64;
244             }
245              
246             my %named_type = (
247             'file' => +{
248             '_base_type' => FIT_ENUM,
249             'device' => 1,
250             'settings' => 2,
251             'sport' => 3,
252             'activity' => 4,
253             'workout' => 5,
254             'course' => 6,
255             'schedules' => 7,
256             'weight' => 9,
257             'totals' => 10,
258             'goals' => 11,
259             'blood_pressure' => 14,
260             'monitoring_a' => 15,
261             'activity_summary' => 20,
262             'monitoring_daily' => 28,
263             'monitoring_b' => 32,
264             'segment' => 34,
265             'segment_list' => 35,
266             'exd_configuration' => 40,
267             'mfg_range_min' => 0xF7,
268             'mfg_range_max' => 0xFE,
269             },
270              
271             'mesg_num' => +{
272             '_base_type' => FIT_UINT16,
273             'file_id' => 0,
274             'capabilities' => 1,
275             'device_settings' => 2,
276             'user_profile' => 3,
277             'hrm_profile' => 4,
278             'sdm_profile' => 5,
279             'bike_profile' => 6,
280             'zones_target' => 7,
281             'hr_zone' => 8,
282             'power_zone' => 9,
283             'met_zone' => 10,
284             'sport' => 12,
285             'goal' => 15,
286             'session' => 18,
287             'lap' => 19,
288             'record' => 20,
289             'event' => 21,
290             'source' => 22, # undocumented
291             'device_info' => 23,
292             'workout' => 26,
293             'workout_step' => 27,
294             'schedule' => 28,
295             'location' => 29, # undocumented
296             'weight_scale' => 30,
297             'course' => 31,
298             'course_point' => 32,
299             'totals' => 33,
300             'activity' => 34,
301             'software' => 35,
302             'file_capabilities' => 37,
303             'mesg_capabilities' => 38,
304             'field_capabilities' => 39,
305             'file_creator' => 49,
306             'blood_pressure' => 51,
307             'speed_zone' => 53,
308             'monitoring' => 55,
309             'training_file' => 72,
310             'hrv' => 78,
311             'ant_rx' => 80,
312             'ant_tx' => 81,
313             'ant_channel_id' => 82,
314             'length' => 101,
315             'monitoring_info' => 103,
316             'battery' => 104, # undocumented
317             'pad' => 105,
318             'slave_device' => 106,
319             'connectivity' => 127,
320             'weather_conditions' => 128,
321             'weather_alert' => 129,
322             'cadence_zone' => 131,
323             'hr' => 132,
324             'segment_lap' => 142,
325             'memo_glob' => 145,
326             'sensor' => 147, # undocumented
327             'segment_id' => 148,
328             'segment_leaderboard_entry' => 149,
329             'segment_point' => 150,
330             'segment_file' => 151,
331             'workout_session' => 158,
332             'watchface_settings' => 159,
333             'gps_metadata' => 160,
334             'camera_event' => 161,
335             'timestamp_correlation' => 162,
336             'gyroscope_data' => 164,
337             'accelerometer_data' => 165,
338             'three_d_sensor_calibration' => 167,
339             'video_frame' => 169,
340             'obdii_data' => 174,
341             'nmea_sentence' => 177,
342             'aviation_attitude' => 178,
343             'video' => 184,
344             'video_title' => 185,
345             'video_description' => 186,
346             'video_clip' => 187,
347             'ohr_settings' => 188,
348             'exd_screen_configuration' => 200,
349             'exd_data_field_configuration' => 201,
350             'exd_data_concept_configuration' => 202,
351             'field_description' => 206,
352             'developer_data_id' => 207,
353             'magnetometer_data' => 208,
354             'barometer_data' => 209,
355             'one_d_sensor_calibration' => 210,
356             'time_in_zone' => 216,
357             'set' => 225,
358             'stress_level' => 227,
359             'dive_settings' => 258,
360             'dive_gas' => 259,
361             'dive_alarm' => 262,
362             'exercise_title' => 264,
363             'dive_summary' => 268,
364             'jump' => 285,
365             'split' => 312,
366             'climb_pro' => 317,
367             'tank_update' => 319,
368             'tank_summary' => 323,
369             'device_aux_battery_info' => 375,
370             'dive_apnea_alarm' => 393,
371             'mfg_range_min' => 0xFF00,
372             'mfg_range_max' => 0xFFFE,
373             },
374              
375             'checksum' => +{
376             '_base_type' => FIT_UINT8,
377             'clear' => 0,
378             'ok' => 1,
379             },
380              
381             'file_flags' => +{
382             '_base_type' => FIT_UINT8Z,
383             '_mask' => 1,
384             'read' => 0x02,
385             'write' => 0x04,
386             'erase' => 0x08,
387             },
388              
389             'mesg_count' => +{
390             '_base_type' => FIT_ENUM,
391             'num_per_file' => 0,
392             'max_per_file' => 1,
393             'max_per_file_type' => 2,
394             },
395              
396             'date_time' => +{
397             '_base_type' => FIT_UINT32,
398             'min' => 0x10000000,
399             '_min' => 0x10000000,
400             '_out_of_range' => 'seconds from device power on',
401             '_offset' => -timegm(0, 0, 0, 31, 11, 1989), # 1989-12-31 00:00:00 UTC
402             },
403              
404             'local_date_time' => +{ # same as above, but in local time zone
405             '_base_type' => FIT_UINT32,
406             'min' => 0x10000000,
407             },
408              
409             'message_index' => +{
410             '_base_type' => FIT_UINT16,
411             '_mask' => 1,
412             'selected' => 0x8000,
413             'reserved' => 0x7000,
414             'mask' => 0x0FFF,
415             },
416              
417             'device_index' => +{ # dynamically created, as devices are added
418             '_base_type' => FIT_UINT8,
419             'creator' => 0, # creator of the file is always device index 0
420             'device1' => 1, # local, v6.00, garmin prod. edge520 (2067)
421             'device2' => 2, # local, v3.00, garmin prod. 1619
422             'heart_rate' => 3, # antplus
423             'speed' => 4, # antplus
424             'cadence' => 5, # antplus
425             'device6' => 6, # antplus power?
426             },
427              
428             'gender' => +{
429             '_base_type' => FIT_ENUM,
430             'female' => 0,
431             'male' => 1,
432             },
433              
434             'language' => +{
435             '_base_type' => FIT_ENUM,
436             'english' => 0,
437             'french' => 1,
438             'italian' => 2,
439             'german' => 3,
440             'spanish' => 4,
441             'croatian' => 5,
442             'czech' => 6,
443             'danish' => 7,
444             'dutch' => 8,
445             'finnish' => 9,
446             'greek' => 10,
447             'hungarian' => 11,
448             'norwegian' => 12,
449             'polish' => 13,
450             'portuguese' => 14,
451             'slovakian' => 15,
452             'slovenian' => 16,
453             'swedish' => 17,
454             'russian' => 18,
455             'turkish' => 19,
456             'latvian' => 20,
457             'ukrainian' => 21,
458             'arabic' => 22,
459             'farsi' => 23,
460             'bulgarian' => 24,
461             'romanian' => 25,
462             'chinese' => 26,
463             'japanese' => 27,
464             'korean' => 28,
465             'taiwanese' => 29,
466             'thai' => 30,
467             'hebrew' => 31,
468             'brazilian_portuguese' => 32,
469             'indonesian' => 33,
470             'malaysian' => 34,
471             'vietnamese' => 35,
472             'burmese' => 36,
473             'mongolian' => 37,
474             'custom' => 254,
475             },
476              
477             'language_bits_0' => +{
478             '_base_type' => FIT_UINT8Z,
479             'english' => 0x01,
480             'french' => 0x02,
481             'italian' => 0x04,
482             'german' => 0x08,
483             'spanish' => 0x10,
484             'croatian' => 0x20,
485             'czech' => 0x40,
486             'danish' => 0x80,
487             },
488              
489             'language_bits_1' => +{
490             '_base_type' => FIT_UINT8Z,
491             'dutch' => 0x01,
492             'finnish' => 0x02,
493             'greek' => 0x04,
494             'hungarian' => 0x08,
495             'norwegian' => 0x10,
496             'polish' => 0x20,
497             'portuguese' => 0x40,
498             'slovakian' => 0x80,
499             },
500              
501             'language_bits_2' => +{
502             '_base_type' => FIT_UINT8Z,
503             'slovenian' => 0x01,
504             'swedish' => 0x02,
505             'russian' => 0x04,
506             'turkish' => 0x08,
507             'latvian' => 0x10,
508             'ukrainian' => 0x20,
509             'arabic' => 0x40,
510             'farsi' => 0x80,
511             },
512              
513             'language_bits_3' => +{
514             '_base_type' => FIT_UINT8Z,
515             'bulgarian' => 0x01,
516             'romanian' => 0x02,
517             'chinese' => 0x04,
518             'japanese' => 0x08,
519             'korean' => 0x10,
520             'taiwanese' => 0x20,
521             'thai' => 0x40,
522             'hebrew' => 0x80,
523             },
524              
525             'language_bits_4' => +{
526             '_base_type' => FIT_UINT8Z,
527             'brazilian_portuguese' => 0x01,
528             'indonesian' => 0x02,
529             'malaysian' => 0x04,
530             'vietnamese' => 0x08,
531             'burmese' => 0x10,
532             'mongolian' => 0x20,
533             },
534              
535             'time_zone' => +{
536             '_base_type' => FIT_ENUM,
537             'almaty' => 0,
538             'bangkok' => 1,
539             'bombay' => 2,
540             'brasilia' => 3,
541             'cairo' => 4,
542             'cape_verde_is' => 5,
543             'darwin' => 6,
544             'eniwetok' => 7,
545             'fiji' => 8,
546             'hong_kong' => 9,
547             'islamabad' => 10,
548             'kabul' => 11,
549             'magadan' => 12,
550             'mid_atlantic' => 13,
551             'moscow' => 14,
552             'muscat' => 15,
553             'newfoundland' => 16,
554             'samoa' => 17,
555             'sydney' => 18,
556             'tehran' => 19,
557             'tokyo' => 20,
558             'us_alaska' => 21,
559             'us_atlantic' => 22,
560             'us_central' => 23,
561             'us_eastern' => 24,
562             'us_hawaii' => 25,
563             'us_mountain' => 26,
564             'us_pacific' => 27,
565             'other' => 28,
566             'auckland' => 29,
567             'kathmandu' => 30,
568             'europe_western_wet' => 31,
569             'europe_central_cet' => 32,
570             'europe_eastern_eet' => 33,
571             'jakarta' => 34,
572             'perth' => 35,
573             'adelaide' => 36,
574             'brisbane' => 37,
575             'tasmania' => 38,
576             'iceland' => 39,
577             'amsterdam' => 40,
578             'athens' => 41,
579             'barcelona' => 42,
580             'berlin' => 43,
581             'brussels' => 44,
582             'budapest' => 45,
583             'copenhagen' => 46,
584             'dublin' => 47,
585             'helsinki' => 48,
586             'lisbon' => 49,
587             'london' => 50,
588             'madrid' => 51,
589             'munich' => 52,
590             'oslo' => 53,
591             'paris' => 54,
592             'prague' => 55,
593             'reykjavik' => 56,
594             'rome' => 57,
595             'stockholm' => 58,
596             'vienna' => 59,
597             'warsaw' => 60,
598             'zurich' => 61,
599             'quebec' => 62,
600             'ontario' => 63,
601             'manitoba' => 64,
602             'saskatchewan' => 65,
603             'alberta' => 66,
604             'british_columbia' => 67,
605             'boise' => 68,
606             'boston' => 69,
607             'chicago' => 70,
608             'dallas' => 71,
609             'denver' => 72,
610             'kansas_city' => 73,
611             'las_vegas' => 74,
612             'los_angeles' => 75,
613             'miami' => 76,
614             'minneapolis' => 77,
615             'new_york' => 78,
616             'new_orleans' => 79,
617             'phoenix' => 80,
618             'santa_fe' => 81,
619             'seattle' => 82,
620             'washington_dc' => 83,
621             'us_arizona' => 84,
622             'chita' => 85,
623             'ekaterinburg' => 86,
624             'irkutsk' => 87,
625             'kaliningrad' => 88,
626             'krasnoyarsk' => 89,
627             'novosibirsk' => 90,
628             'petropavlovsk_kamchatskiy' => 91,
629             'samara' => 92,
630             'vladivostok' => 93,
631             'mexico_central' => 94,
632             'mexico_mountain' => 95,
633             'mexico_pacific' => 96,
634             'cape_town' => 97,
635             'winkhoek' => 98,
636             'lagos' => 99,
637             'riyahd' => 100,
638             'venezuela' => 101,
639             'australia_lh' => 102,
640             'santiago' => 103,
641             'manual' => 253,
642             'automatic' => 254,
643             },
644              
645             'display_measure' => +{
646             '_base_type' => FIT_ENUM,
647             'metric' => 0,
648             'statute' => 1,
649             'nautical' => 2,
650             },
651              
652             'display_heart' => +{
653             '_base_type' => FIT_ENUM,
654             'bpm' => 0,
655             'max' => 1,
656             'reserve' => 2,
657             },
658              
659             'display_power' => +{
660             '_base_type' => FIT_ENUM,
661             'watts' => 0,
662             'percent_ftp' => 1,
663             },
664              
665             'display_position' => +{
666             '_base_type' => FIT_ENUM,
667             'degree' => 0,
668             'degree_minute' => 1,
669             'degree_minute_second' => 2,
670             'austrian_grid' => 3,
671             'british_grid' => 4,
672             'dutch_grid' => 5,
673             'hungarian_grid' => 6,
674             'finnish_grid' => 7,
675             'german_grid' => 8,
676             'icelandic_grid' => 9,
677             'indonesian_equatorial' => 10,
678             'indonesian_irian' => 11,
679             'indonesian_southern' => 12,
680             'india_zone_0' => 13,
681             'india_zone_IA' => 14,
682             'india_zone_IB' => 15,
683             'india_zone_IIA' => 16,
684             'india_zone_IIB' => 17,
685             'india_zone_IIIA' => 18,
686             'india_zone_IIIB' => 19,
687             'india_zone_IVA' => 20,
688             'india_zone_IVB' => 21,
689             'irish_transverse' => 22,
690             'irish_grid' => 23,
691             'loran' => 24,
692             'maidenhead_grid' => 25,
693             'mgrs_grid' => 26,
694             'new_zealand_grid' => 27,
695             'new_zealand_transverse' => 28,
696             'qatar_grid' => 29,
697             'modified_swedish_grid' => 30,
698             'swedish_grid' => 31,
699             'south_african_grid' => 32,
700             'swiss_grid' => 33,
701             'taiwan_grid' => 34,
702             'united_states_grid' => 35,
703             'utm_ups_grid' => 36,
704             'west_malayan' => 37,
705             'borneo_rso' => 38,
706             'estonian_grid' => 39,
707             'latvian_grid' => 40,
708             'swedish_ref_99_grid' => 41,
709             },
710              
711             'switch' => +{
712             '_base_type' => FIT_ENUM,
713             'off' => 0,
714             'on' => 1,
715             'auto' => 2,
716             },
717              
718             'sport' => +{
719             '_base_type' => FIT_ENUM,
720             'generic' => 0,
721             'running' => 1,
722             'cycling' => 2,
723             'transition' => 3, # multisport transition
724             'fitness_equipment' => 4,
725             'swimming' => 5,
726             'basketball' => 6,
727             'soccer' => 7,
728             'tennis' => 8,
729             'american_football' => 9,
730             'training' => 10,
731             'walking' => 11,
732             'cross_country_skiing' => 12,
733             'alpine_skiing' => 13,
734             'snowboarding' => 14,
735             'rowing' => 15,
736             'mountaineering' => 16,
737             'hiking' => 17,
738             'multisport' => 18,
739             'paddling' => 19,
740             'flying' => 20,
741             'e_biking' => 21,
742             'motorcycling' => 22,
743             'boating' => 23,
744             'driving' => 24,
745             'golf' => 25,
746             'hang_gliding' => 26,
747             'horseback_riding' => 27,
748             'hunting' => 28,
749             'fishing' => 29,
750             'inline_skating' => 30,
751             'rock_climbing' => 31,
752             'sailing' => 32,
753             'ice_skating' => 33,
754             'sky_diving' => 34,
755             'snowshoeing' => 35,
756             'snowmobiling' => 36,
757             'stand_up_paddleboarding' => 37,
758             'surfing' => 38,
759             'wakeboarding' => 39,
760             'water_skiing' => 40,
761             'kayaking' => 41,
762             'rafting' => 42,
763             'windsurfing' => 43,
764             'kitesurfing' => 44,
765             'tactical' => 45,
766             'jumpmaster' => 46,
767             'boxing' => 47,
768             'floor_climbing' => 48,
769             'diving' => 53,
770             'hiit' => 62,
771             'racket' => 64,
772             'water_tubing' => 76,
773             'wakesurfing' => 77,
774             'all' => 254,
775             },
776              
777             'sport_bits_0' => +{
778             '_base_type' => FIT_UINT8Z,
779             'generic' => 0x01,
780             'running' => 0x02,
781             'cycling' => 0x04,
782             'transition' => 0x08,
783             'fitness_equipment' => 0x10,
784             'swimming' => 0x20,
785             'basketball' => 0x40,
786             'soccer' => 0x80,
787             },
788              
789             'sport_bits_1' => +{
790             '_base_type' => FIT_UINT8Z,
791             'tennis' => 0x01,
792             'american_football' => 0x02,
793             'training' => 0x04,
794             'walking' => 0x08,
795             'cross_country_skiing' => 0x10,
796             'alpine_skiing' => 0x20,
797             'snowboarding' => 0x40,
798             'rowing' => 0x80,
799             },
800              
801             'sport_bits_2' => +{
802             '_base_type' => FIT_UINT8Z,
803             'mountaineering' => 0x01,
804             'hiking' => 0x02,
805             'multisport' => 0x04,
806             'paddling' => 0x08,
807             'flying' => 0x10,
808             'e_biking' => 0x20,
809             'motorcycling' => 0x40,
810             'boating' => 0x80,
811             },
812              
813             'sport_bits_3' => +{
814             '_base_type' => FIT_UINT8Z,
815             'driving' => 0x01,
816             'golf' => 0x02,
817             'hang_gliding' => 0x04,
818             'horseback_riding' => 0x08,
819             'hunting' => 0x10,
820             'fishing' => 0x20,
821             'inline_skating' => 0x40,
822             'rock_climbing' => 0x80,
823             },
824              
825             'sport_bits_4' => +{
826             '_base_type' => FIT_UINT8Z,
827             'sailing' => 0x01,
828             'ice_skating' => 0x02,
829             'sky_diving' => 0x04,
830             'snowshoeing' => 0x08,
831             'snowmobiling' => 0x10,
832             'stand_up_paddleboarding' => 0x20,
833             'surfing' => 0x40,
834             'wakeboarding' => 0x80,
835             },
836              
837             'sport_bits_5' => +{
838             '_base_type' => FIT_UINT8Z,
839             'water_skiing' => 0x01,
840             'kayaking' => 0x02,
841             'rafting' => 0x04,
842             'windsurfing' => 0x08,
843             'kitesurfing' => 0x10,
844             'tactical' => 0x20,
845             'jumpmaster' => 0x40,
846             'boxing' => 0x80,
847             },
848              
849             'sport_bits_6' => +{
850             '_base_type' => FIT_UINT8Z,
851             'floor_climbing' => 0x01,
852             },
853              
854             'sub_sport' => +{
855             '_base_type' => FIT_ENUM,
856             'generic' => 0,
857             'treadmill' => 1, # run/fitness equipment
858             'street' => 2, # run
859             'trail' => 3, # run
860             'track' => 4, # run
861             'spin' => 5, # cycling
862             'indoor_cycling' => 6, # cycling/fitness equipment
863             'road' => 7, # cycling
864             'mountain' => 8, # cycling
865             'downhill' => 9, # cycling
866             'recumbent' => 10, # cycling
867             'cyclocross' => 11, # cycling
868             'hand_cycling' => 12, # cycling
869             'track_cycling' => 13, # cycling
870             'indoor_rowing' => 14, # fitness equipment
871             'elliptical' => 15, # fitness equipment
872             'stair_climbing' => 16, # fitness equipment
873             'lap_swimming' => 17, # swimming
874             'open_water' => 18, # swimming
875             'flexibility_training' => 19, # training
876             'strength_training' => 20, # training
877             'warm_up' => 21, # tennis
878             'match' => 22, # tennis
879             'exercise' => 23, # tennis
880             'challenge' => 24,
881             'indoor_skiing' => 25, # fitness equipment
882             'cardio_training' => 26, # training
883             'indoor_walking' => 27, # walking/fitness equipment
884             'e_bike_fitness' => 28, # e-biking
885             'bmx' => 29, # cycling
886             'casual_walking' => 30, # walking
887             'speed_walking' => 31, # walking
888             'bike_to_run_transition' => 32, # transition
889             'run_to_bike_transition' => 33, # transition
890             'swim_to_bike_transition' => 34, # transition
891             'atv' => 35, # motorcycling
892             'motocross' => 36, # motorcycling
893             'backcountry' => 37, # alpine skiing/snowboarding
894             'resort' => 38, # alpine skiing/snowboarding
895             'rc_drone' => 39, # flying
896             'wingsuit' => 40, # flying
897             'whitewater' => 41, # kayaking/rafting
898             'skate_skiing' => 42, # cross country skiing
899             'yoga' => 43, # training
900             'pilates' => 44, # fitness equipment
901             'indoor_running' => 45, # run/fitness equipment
902             'gravel_cycling' => 46, # cycling
903             'e_bike_mountain' => 47, # cycling
904             'commuting' => 48, # cycling
905             'mixed_surface' => 49, # cycling
906             'navigate' => 50,
907             'track_me' => 51,
908             'map' => 52,
909             'single_gas_diving' => 53, # Diving
910             'multi_gas_diving' => 54, # Diving
911             'gauge_diving' => 55, # Diving
912             'apnea_diving' => 56, # Diving
913             'apnea_hunting' => 57, # Diving
914             'virtual_activity' => 58,
915             'obstacle' => 59, # Used for events where participants run, crawl through mud, climb over walls, etc.
916             'breathing' => 62,
917             'sail_race' => 65, # Sailing
918             'ultra' => 67, # Ultramarathon
919             'indoor_climbing' => 68, # Climbing
920             'bouldering' => 69, # Climbing
921             'hiit' => 70, # High Intensity Interval Training
922             'amrap' => 73, # HIIT
923             'emom' => 74, # HIIT
924             'tabata' => 75, # HIIT
925             'pickleball' => 84, # Racket
926             'padel' => 85, # Racket
927             'all' => 254,
928             },
929              
930             'sport_event' => +{
931             '_base_type' => FIT_ENUM,
932             'uncategorized' => 0,
933             'geocaching' => 1,
934             'fitness' => 2,
935             'recreation' => 3,
936             'race' => 4,
937             'special_event' => 5,
938             'training' => 6,
939             'transportation' => 7,
940             'touring' => 8,
941             },
942              
943             'activity' => +{
944             '_base_type' => FIT_ENUM,
945             'manual' => 0,
946             'auto_multi_sport' => 1,
947             },
948              
949             'intensity' => +{
950             '_base_type' => FIT_ENUM,
951             'active' => 0,
952             'rest' => 1,
953             'warmup' => 2,
954             'cooldown' => 3,
955             },
956              
957             'session_trigger' => +{
958             '_base_type' => FIT_ENUM,
959             'activity_end' => 0,
960             'manual' => 1,
961             'auto_multi_sport' => 2,
962             'fitness_equipment' => 3,
963             },
964              
965             'autolap_trigger' => +{
966             '_base_type' => FIT_ENUM,
967             'time' => 0,
968             'distance' => 1,
969             'position_start' => 2,
970             'position_lap' => 3,
971             'position_waypoint' => 4,
972             'position_marked' => 5,
973             'off' => 6,
974             },
975              
976             'lap_trigger' => +{
977             '_base_type' => FIT_ENUM,
978             'manual' => 0,
979             'time' => 1,
980             'distance' => 2,
981             'position_start' => 3,
982             'position_lap' => 4,
983             'position_waypoint' => 5,
984             'position_marked' => 6,
985             'session_end' => 7,
986             'fitness_equipment' => 8,
987             },
988              
989             'time_mode' => +{
990             '_base_type' => FIT_ENUM,
991             'hour12' => 0,
992             'hour24' => 1,
993             'military' => 2,
994             'hour_12_with_seconds' => 3,
995             'hour_24_with_seconds' => 4,
996             'utc' => 5,
997             },
998              
999             'backlight_mode' => +{
1000             '_base_type' => FIT_ENUM,
1001             'off' => 0,
1002             'manual' => 1,
1003             'key_and_messages' => 2,
1004             'auto_brightness' => 3,
1005             'smart_notifications' => 4,
1006             'key_and_messages_night' => 5,
1007             'key_and_messages_and_smart_notifications' => 6,
1008             },
1009              
1010             'date_mode' => +{
1011             '_base_type' => FIT_ENUM,
1012             'day_month' => 0,
1013             'month_day' => 1,
1014             },
1015              
1016             'backlight_timeout' => +{
1017             '_base_type' => FIT_UINT8,
1018             'infinite' => 0,
1019             },
1020              
1021             'event' => +{
1022             '_base_type' => FIT_ENUM,
1023             'timer' => 0,
1024             'workout' => 3,
1025             'workout_step' => 4,
1026             'power_down' => 5,
1027             'power_up' => 6,
1028             'off_course' => 7,
1029             'session' => 8,
1030             'lap' => 9,
1031             'course_point' => 10,
1032             'battery' => 11,
1033             'virtual_partner_pace' => 12,
1034             'hr_high_alert' => 13,
1035             'hr_low_alert' => 14,
1036             'speed_high_alert' => 15,
1037             'speed_low_alert' => 16,
1038             'cad_high_alert' => 17,
1039             'cad_low_alert' => 18,
1040             'power_high_alert' => 19,
1041             'power_low_alert' => 20,
1042             'recovery_hr' => 21,
1043             'battery_low' => 22,
1044             'time_duration_alert' => 23,
1045             'distance_duration_alert' => 24,
1046             'calorie_duration_alert' => 25,
1047             'activity' => 26,
1048             'fitness_equipment' => 27,
1049             'length' => 28,
1050             'user_marker' => 32,
1051             'sport_point' => 33,
1052             'calibration' => 36,
1053             'front_gear_change' => 42,
1054             'rear_gear_change' => 43,
1055             'rider_position_change' => 44,
1056             'elev_high_alert' => 45,
1057             'elev_low_alert' => 46,
1058             'comm_timeout' => 47,
1059             'dive_alert' => 56, # marker
1060             'dive_gas_switched' => 57, # marker
1061             'tank_pressure_reserve' => 71, # marker
1062             'tank_pressure_critical' => 72, # marker
1063             'tank_lost' => 73, # marker
1064             'radar_threat_alert' => 75, # start/stop/marker
1065             'tank_battery_low' => 76, # marker
1066             'tank_pod_connected' => 81, # marker - tank pod has connected
1067             'tank_pod_disconnected' => 82, # marker - tank pod has lost connection
1068             },
1069              
1070             'event_type' => +{
1071             '_base_type' => FIT_ENUM,
1072             'start' => 0,
1073             'stop' => 1,
1074             'consecutive_depreciated' => 2,
1075             'marker' => 3,
1076             'stop_all' => 4,
1077             'begin_depreciated' => 5,
1078             'end_depreciated' => 6,
1079             'end_all_depreciated' => 7,
1080             'stop_disable' => 8,
1081             'stop_disable_all' => 9,
1082             },
1083              
1084             'timer_trigger' => +{
1085             '_base_type' => FIT_ENUM,
1086             'manual' => 0,
1087             'auto' => 1,
1088             'fitness_equipment' => 2,
1089             },
1090              
1091             'fitness_equipment_state' => +{
1092             '_base_type' => FIT_ENUM,
1093             'ready' => 0,
1094             'in_use' => 1,
1095             'paused' => 2,
1096             'unknown' => 3,
1097             },
1098              
1099             'tone' => +{
1100             '_base_type' => FIT_ENUM,
1101             'off' => 0,
1102             'tone' => 1,
1103             'vibrate' => 2,
1104             'tone_and_vibrate' => 3,
1105             },
1106             'autoscroll' => +{
1107             '_base_type' => FIT_ENUM,
1108             'none' => 0,
1109             'slow' => 1,
1110             'medium' => 2,
1111             'fast' => 3,
1112             },
1113              
1114             'activity_class' => +{
1115             '_base_type' => FIT_ENUM,
1116             '_mask' => 1,
1117             'level' => 0x7f,
1118             'level_max' => 100,
1119             'athlete' => 0x80,
1120             },
1121              
1122             'hr_zone_calc' => +{
1123             '_base_type' => FIT_ENUM,
1124             'custom' => 0,
1125             'percent_max_hr' => 1,
1126             'percent_hrr' => 2,
1127             'percent_lthr' => 3,
1128             },
1129              
1130             'pwr_zone_calc' => +{
1131             '_base_type' => FIT_ENUM,
1132             'custom' => 0,
1133             'percent_ftp' => 1,
1134             },
1135              
1136             'wkt_step_duration' => +{
1137             '_base_type' => FIT_ENUM,
1138             'time' => 0,
1139             'distance' => 1,
1140             'hr_less_than' => 2,
1141             'hr_greater_than' => 3,
1142             'calories' => 4,
1143             'open' => 5,
1144             'repeat_until_steps_cmplt' => 6,
1145             'repeat_until_time' => 7,
1146             'repeat_until_distance' => 8,
1147             'repeat_until_calories' => 9,
1148             'repeat_until_hr_less_than' => 10,
1149             'repeat_until_hr_greater_than' => 11,
1150             'repeat_until_power_less_than' => 12,
1151             'repeat_until_power_greater_than' => 13,
1152             'power_less_than' => 14,
1153             'power_greater_than' => 15,
1154             'training_peaks_tss' => 16,
1155             'repeat_until_power_last_lap_less_than' => 17,
1156             'repeat_until_max_power_last_lap_less_than' => 18,
1157             'power_3s_less_than' => 19,
1158             'power_10s_less_than' => 20,
1159             'power_30s_less_than' => 21,
1160             'power_3s_greater_than' => 22,
1161             'power_10s_greater_than' => 23,
1162             'power_30s_greater_than' => 24,
1163             'power_lap_less_than' => 25,
1164             'power_lap_greater_than' => 26,
1165             'repeat_until_training_peaks_tss' => 27,
1166             'repetition_time' => 28,
1167             'reps' => 29,
1168             'time_only' => 31,
1169             },
1170              
1171             'wkt_step_target' => +{
1172             '_base_type' => FIT_ENUM,
1173             'speed' => 0,
1174             'heart_rate' => 1,
1175             'open' => 2,
1176             'cadence' => 3,
1177             'power' => 4,
1178             'grade' => 5,
1179             'resistance' => 6,
1180             'power_3s' => 7,
1181             'power_10s' => 8,
1182             'power_30s' => 9,
1183             'power_lap' => 10,
1184             'swim_stroke' => 11,
1185             'speed_lap' => 12,
1186             'heart_rate_lap' => 13,
1187             },
1188              
1189             'goal' => +{
1190             '_base_type' => FIT_ENUM,
1191             'time' => 0,
1192             'distance' => 1,
1193             'calories' => 2,
1194             'frequency' => 3,
1195             'steps' => 4,
1196             'ascent' => 5,
1197             'active_minutes' => 6,
1198             },
1199              
1200             'goal_recurrence' => +{
1201             '_base_type' => FIT_ENUM,
1202             'off' => 0,
1203             'daily' => 1,
1204             'weekly' => 2,
1205             'monthly' => 3,
1206             'yearly' => 4,
1207             'custom' => 5,
1208             },
1209              
1210             'goal_source' => +{
1211             '_base_type' => FIT_ENUM,
1212             'auto' => 0,
1213             'community' => 1,
1214             'user' => 2,
1215             },
1216              
1217             'schedule' => +{
1218             '_base_type' => FIT_ENUM,
1219             'workout' => 0,
1220             'course' => 1,
1221             },
1222              
1223             'course_point' => +{
1224             '_base_type' => FIT_ENUM,
1225             'generic' => 0,
1226             'summit' => 1,
1227             'valley' => 2,
1228             'water' => 3,
1229             'food' => 4,
1230             'danger' => 5,
1231             'left' => 6,
1232             'right' => 7,
1233             'straight' => 8,
1234             'first_aid' => 9,
1235             'fourth_category' => 10,
1236             'third_category' => 11,
1237             'second_category' => 12,
1238             'first_category' => 13,
1239             'hors_category' => 14,
1240             'sprint' => 15,
1241             'left_fork' => 16,
1242             'right_fork' => 17,
1243             'middle_fork' => 18,
1244             'slight_left' => 19,
1245             'sharp_left' => 20,
1246             'slight_right' => 21,
1247             'sharp_right' => 22,
1248             'u_turn' => 23,
1249             'segment_start' => 24,
1250             'segment_end' => 25,
1251             },
1252              
1253             'manufacturer' => +{
1254             '_base_type' => FIT_UINT16,
1255             'garmin' => 1,
1256             'garmin_fr405_antfs' => 2,
1257             'zephyr' => 3,
1258             'dayton' => 4,
1259             'idt' => 5,
1260             'srm' => 6,
1261             'quarq' => 7,
1262             'ibike' => 8,
1263             'saris' => 9,
1264             'spark_hk' => 10,
1265             'tanita' => 11,
1266             'echowell' => 12,
1267             'dynastream_oem' => 13,
1268             'nautilus' => 14,
1269             'dynastream' => 15,
1270             'timex' => 16,
1271             'metrigear' => 17,
1272             'xelic' => 18,
1273             'beurer' => 19,
1274             'cardiosport' => 20,
1275             'a_and_d' => 21,
1276             'hmm' => 22,
1277             'suunto' => 23,
1278             'thita_elektronik' => 24,
1279             'gpulse' => 25,
1280             'clean_mobile' => 26,
1281             'pedal_brain' => 27,
1282             'peaksware' => 28,
1283             'saxonar' => 29,
1284             'lemond_fitness' => 30,
1285             'dexcom' => 31,
1286             'wahoo_fitness' => 32,
1287             'octane_fitness' => 33,
1288             'archinoetics' => 34,
1289             'the_hurt_box' => 35,
1290             'citizen_systems' => 36,
1291             'magellan' => 37,
1292             'osynce' => 38,
1293             'holux' => 39,
1294             'concept2' => 40,
1295             'one_giant_leap' => 42,
1296             'ace_sensor' => 43,
1297             'brim_brothers' => 44,
1298             'xplova' => 45,
1299             'perception_digital' => 46,
1300             'bf1systems' => 47,
1301             'pioneer' => 48,
1302             'spantec' => 49,
1303             'metalogics' => 50,
1304             '4iiiis' => 51,
1305             'seiko_epson' => 52,
1306             'seiko_epson_oem' => 53,
1307             'ifor_powell' => 54,
1308             'maxwell_guider' => 55,
1309             'star_trac' => 56,
1310             'breakaway' => 57,
1311             'alatech_technology_ltd' => 58,
1312             'mio_technology_europe' => 59,
1313             'rotor' => 60,
1314             'geonaute' => 61,
1315             'id_bike' => 62,
1316             'specialized' => 63,
1317             'wtek' => 64,
1318             'physical_enterprises' => 65,
1319             'north_pole_engineering' => 66,
1320             'bkool' => 67,
1321             'cateye' => 68,
1322             'stages_cycling' => 69,
1323             'sigmasport' => 70,
1324             'tomtom' => 71,
1325             'peripedal' => 72,
1326             'wattbike' => 73,
1327             'moxy' => 76,
1328             'ciclosport' => 77,
1329             'powerbahn' => 78,
1330             'acorn_projects_aps' => 79,
1331             'lifebeam' => 80,
1332             'bontrager' => 81,
1333             'wellgo' => 82,
1334             'scosche' => 83,
1335             'magura' => 84,
1336             'woodway' => 85,
1337             'elite' => 86,
1338             'nielsen_kellerman' => 87,
1339             'dk_city' => 88,
1340             'tacx' => 89,
1341             'direction_technology' => 90,
1342             'magtonic' => 91,
1343             '1partcarbon' => 92,
1344             'inside_ride_technologies' => 93,
1345             'sound_of_motion' => 94,
1346             'stryd' => 95,
1347             'icg' => 96,
1348             'mipulse' => 97,
1349             'bsx_athletics' => 98,
1350             'look' => 99,
1351             'campagnolo_srl' => 100,
1352             'body_bike_smart' => 101,
1353             'praxisworks' => 102,
1354             'limits_technology' => 103,
1355             'topaction_technology' => 104,
1356             'cosinuss' => 105,
1357             'fitcare' => 106,
1358             'magene' => 107,
1359             'giant_manufacturing_co' => 108,
1360             'tigrasport' => 109,
1361             'salutron' => 110,
1362             'technogym' => 111,
1363             'bryton_sensors' => 112,
1364             'latitude_limited' => 113,
1365             'soaring_technology' => 114,
1366             'igpsport' => 115,
1367             'thinkrider' => 116,
1368             'gopher_sport' => 117,
1369             'waterrower' => 118,
1370             'orangetheory' => 119,
1371             'inpeak' => 120,
1372             'kinetic' => 121,
1373             'johnson_health_tech' => 122,
1374             'polar_electro' => 123,
1375             'seesense' => 124,
1376             'nci_technology' => 125,
1377             'iqsquare' => 126,
1378             'leomo' => 127,
1379             'ifit_com' => 128,
1380             'coros_byte' => 129,
1381             'versa_design' => 130,
1382             'chileaf' => 131,
1383             'cycplus' => 132,
1384             'gravaa_byte' => 133,
1385             'sigeyi' => 134,
1386             'coospo' => 135,
1387             'geoid' => 136,
1388             'bosch' => 137,
1389             'kyto' => 138,
1390             'kinetic_sports' => 139,
1391             'decathlon_byte' => 140,
1392             'tq_systems' => 141,
1393             'tag_heuer' => 142,
1394             'keiser_fitness' => 143,
1395             'zwift_byte' => 144,
1396             'porsche_ep' => 145,
1397             'development' => 255,
1398             'healthandlife' => 257,
1399             'lezyne' => 258,
1400             'scribe_labs' => 259,
1401             'zwift' => 260,
1402             'watteam' => 261,
1403             'recon' => 262,
1404             'favero_electronics' => 263,
1405             'dynovelo' => 264,
1406             'strava' => 265,
1407             'precor' => 266,
1408             'bryton' => 267,
1409             'sram' => 268,
1410             'navman' => 269,
1411             'cobi' => 270,
1412             'spivi' => 271,
1413             'mio_magellan' => 272,
1414             'evesports' => 273,
1415             'sensitivus_gauge' => 274,
1416             'podoon' => 275,
1417             'life_time_fitness' => 276,
1418             'falco_e_motors' => 277,
1419             'minoura' => 278,
1420             'cycliq' => 279,
1421             'luxottica' => 280,
1422             'trainer_road' => 281,
1423             'the_sufferfest' => 282,
1424             'fullspeedahead' => 283,
1425             'virtualtraining' => 284,
1426             'feedbacksports' => 285,
1427             'omata' => 286,
1428             'vdo' => 287,
1429             'magneticdays' => 288,
1430             'hammerhead' => 289,
1431             'kinetic_by_kurt' => 290,
1432             'shapelog' => 291,
1433             'dabuziduo' => 292,
1434             'jetblack' => 293,
1435             'coros' => 294,
1436             'virtugo' => 295,
1437             'velosense' => 296,
1438             'cycligentinc' => 297,
1439             'trailforks' => 298,
1440             'mahle_ebikemotion' => 299,
1441             'nurvv' => 300,
1442             'microprogram' => 301,
1443             'zone5cloud' => 302,
1444             'greenteg' => 303,
1445             'yamaha_motors' => 304,
1446             'whoop' => 305,
1447             'gravaa' => 306,
1448             'onelap' => 307,
1449             'monark_exercise' => 308,
1450             'form' => 309,
1451             'decathlon' => 310,
1452             'syncros' => 311,
1453             'heatup' => 312,
1454             'cannondale' => 313,
1455             'true_fitness' => 314,
1456             'RGT_cycling' => 315,
1457             'vasa' => 316,
1458             'race_republic' => 317,
1459             'fazua' => 318,
1460             'oreka_training' => 319,
1461             'lsec' => 320, # Lishun Electric & Communication
1462             'lululemon_studio' => 321,
1463             'shanyue' => 322,
1464             'actigraphcorp' => 5759,
1465             },
1466              
1467             'garmin_product' => +{
1468             '_base_type' => FIT_UINT16,
1469             'hrm1' => 1,
1470             'axh01' => 2, # AXH01 HRM chipset
1471             'axb01' => 3,
1472             'axb02' => 4,
1473             'hrm2ss' => 5,
1474             'dsi_alf02' => 6,
1475             'hrm3ss' => 7,
1476             'hrm_run_single_byte_product_id' => 8, # hrm_run model for HRM ANT+ messaging
1477             'bsm' => 9, # BSM model for ANT+ messaging
1478             'bcm' => 10, # BCM model for ANT+ messaging
1479             'axs01' => 11, # AXS01 HRM Bike Chipset model for ANT+ messaging
1480             'hrm_tri_single_byte_product_id' => 12, # hrm_tri model for HRM ANT+ messaging
1481             'hrm4_run_single_byte_product_id' => 13, # hrm4 run model for HRM ANT+ messaging
1482             'fr225_single_byte_product_id' => 14, # fr225 model for HRM ANT+ messaging
1483             'gen3_bsm_single_byte_product_id' => 15, # gen3_bsm model for Bike Speed ANT+ messaging
1484             'gen3_bcm_single_byte_product_id' => 16, # gen3_bcm model for Bike Cadence ANT+ messaging
1485             'OHR' => 255, # Garmin Wearable Optical Heart Rate Sensor for ANT+ HR Profile Broadcasting
1486             'fr301_china' => 473,
1487             'fr301_japan' => 474,
1488             'fr301_korea' => 475,
1489             'fr301_taiwan' => 494,
1490             'fr405' => 717, # Forerunner 405
1491             'fr50' => 782, # Forerunner 50
1492             'fr405_japan' => 987,
1493             'fr60' => 988, # Forerunner 60
1494             'dsi_alf01' => 1011,
1495             'fr310xt' => 1018, # Forerunner 310
1496             'edge500' => 1036,
1497             'fr110' => 1124, # Forerunner 110
1498             'edge800' => 1169,
1499             'edge500_taiwan' => 1199,
1500             'edge500_japan' => 1213,
1501             'chirp' => 1253,
1502             'fr110_japan' => 1274,
1503             'edge200' => 1325,
1504             'fr910xt' => 1328,
1505             'edge800_taiwan' => 1333,
1506             'edge800_japan' => 1334,
1507             'alf04' => 1341,
1508             'fr610' => 1345,
1509             'fr210_japan' => 1360,
1510             'vector_ss' => 1380,
1511             'vector_cp' => 1381,
1512             'edge800_china' => 1386,
1513             'edge500_china' => 1387,
1514             'approach_g10' => 1405,
1515             'fr610_japan' => 1410,
1516             'edge500_korea' => 1422,
1517             'fr70' => 1436,
1518             'fr310xt_4t' => 1446,
1519             'amx' => 1461,
1520             'fr10' => 1482,
1521             'edge800_korea' => 1497,
1522             'swim' => 1499,
1523             'fr910xt_china' => 1537,
1524             'fenix' => 1551,
1525             'edge200_taiwan' => 1555,
1526             'edge510' => 1561,
1527             'edge810' => 1567,
1528             'tempe' => 1570,
1529             'fr910xt_japan' => 1600,
1530             'fr620' => 1623,
1531             'fr220' => 1632,
1532             'fr910xt_korea' => 1664,
1533             'fr10_japan' => 1688,
1534             'edge810_japan' => 1721,
1535             'virb_elite' => 1735,
1536             'edge_touring' => 1736, # Also Edge Touring Plus
1537             'edge510_japan' => 1742,
1538             'hrm_tri' => 1743, # Also HRM-Swim
1539             'hrm_run' => 1752,
1540             'fr920xt' => 1765,
1541             'edge510_asia' => 1821,
1542             'edge810_china' => 1822,
1543             'edge810_taiwan' => 1823,
1544             'edge1000' => 1836,
1545             'vivo_fit' => 1837,
1546             'virb_remote' => 1853,
1547             'vivo_ki' => 1885,
1548             'fr15' => 1903,
1549             'vivo_active' => 1907,
1550             'edge510_korea' => 1918,
1551             'fr620_japan' => 1928,
1552             'fr620_china' => 1929,
1553             'fr220_japan' => 1930,
1554             'fr220_china' => 1931,
1555             'approach_s6' => 1936,
1556             'vivo_smart' => 1956,
1557             'fenix2' => 1967,
1558             'epix' => 1988,
1559             'fenix3' => 2050,
1560             'edge1000_taiwan' => 2052,
1561             'edge1000_japan' => 2053,
1562             'fr15_japan' => 2061,
1563             'edge520' => 2067,
1564             'edge1000_china' => 2070,
1565             'fr620_russia' => 2072,
1566             'fr220_russia' => 2073,
1567             'vector_s' => 2079,
1568             'edge1000_korea' => 2100,
1569             'fr920xt_taiwan' => 2130,
1570             'fr920xt_china' => 2131,
1571             'fr920xt_japan' => 2132,
1572             'virbx' => 2134,
1573             'vivo_smart_apac' => 2135,
1574             'etrex_touch' => 2140,
1575             'edge25' => 2147,
1576             'fr25' => 2148,
1577             'vivo_fit2' => 2150,
1578             'fr225' => 2153,
1579             'fr630' => 2156,
1580             'fr230' => 2157,
1581             'fr735xt' => 2158,
1582             'vivo_active_apac' => 2160,
1583             'vector_2' => 2161,
1584             'vector_2s' => 2162,
1585             'virbxe' => 2172,
1586             'fr620_taiwan' => 2173,
1587             'fr220_taiwan' => 2174,
1588             'truswing' => 2175,
1589             'd2airvenu' => 2187,
1590             'fenix3_china' => 2188,
1591             'fenix3_twn' => 2189,
1592             'varia_headlight' => 2192,
1593             'varia_taillight_old' => 2193,
1594             'edge_explore_1000' => 2204,
1595             'fr225_asia' => 2219,
1596             'varia_radar_taillight' => 2225,
1597             'varia_radar_display' => 2226,
1598             'edge20' => 2238,
1599             'edge520_asia' => 2260,
1600             'edge520_japan' => 2261,
1601             'd2_bravo' => 2262,
1602             'approach_s20' => 2266,
1603             'vivo_smart2' => 2271,
1604             'edge1000_thai' => 2274,
1605             'varia_remote' => 2276,
1606             'edge25_asia' => 2288,
1607             'edge25_jpn' => 2289,
1608             'edge20_asia' => 2290,
1609             'approach_x40' => 2292,
1610             'fenix3_japan' => 2293,
1611             'vivo_smart_emea' => 2294,
1612             'fr630_asia' => 2310,
1613             'fr630_jpn' => 2311,
1614             'fr230_jpn' => 2313,
1615             'hrm4_run' => 2327,
1616             'epix_japan' => 2332,
1617             'vivo_active_hr' => 2337,
1618             'vivo_smart_gps_hr' => 2347,
1619             'vivo_smart_hr' => 2348,
1620             'vivo_smart_hr_asia' => 2361,
1621             'vivo_smart_gps_hr_asia' => 2362,
1622             'vivo_move' => 2368,
1623             'varia_taillight' => 2379,
1624             'fr235_asia' => 2396,
1625             'fr235_japan' => 2397,
1626             'varia_vision' => 2398,
1627             'vivo_fit3' => 2406,
1628             'fenix3_korea' => 2407,
1629             'fenix3_sea' => 2408,
1630             'fenix3_hr' => 2413,
1631             'virb_ultra_30' => 2417,
1632             'index_smart_scale' => 2429,
1633             'fr235' => 2431,
1634             'fenix3_chronos' => 2432,
1635             'oregon7xx' => 2441,
1636             'rino7xx' => 2444,
1637             'epix_korea' => 2457,
1638             'fenix3_hr_chn' => 2473,
1639             'fenix3_hr_twn' => 2474,
1640             'fenix3_hr_jpn' => 2475,
1641             'fenix3_hr_sea' => 2476,
1642             'fenix3_hr_kor' => 2477,
1643             'nautix' => 2496,
1644             'vivo_active_hr_apac' => 2497,
1645             'fr35' => 2503,
1646             'oregon7xx_ww' => 2512,
1647             'edge_820' => 2530,
1648             'edge_explore_820' => 2531,
1649             'fr735xt_apac' => 2533,
1650             'fr735xt_japan' => 2534,
1651             'fenix5s' => 2544,
1652             'd2_bravo_titanium' => 2547,
1653             'varia_ut800' => 2567, # Varia UT 800 SW
1654             'running_dynamics_pod' => 2593,
1655             'edge_820_china' => 2599,
1656             'edge_820_japan' => 2600,
1657             'fenix5x' => 2604,
1658             'vivo_fit_jr' => 2606,
1659             'vivo_smart3' => 2622,
1660             'vivo_sport' => 2623,
1661             'edge_820_taiwan' => 2628,
1662             'edge_820_korea' => 2629,
1663             'edge_820_sea' => 2630,
1664             'fr35_hebrew' => 2650,
1665             'approach_s60' => 2656,
1666             'fr35_apac' => 2667,
1667             'fr35_japan' => 2668,
1668             'fenix3_chronos_asia' => 2675,
1669             'virb_360' => 2687,
1670             'fr935' => 2691,
1671             'fenix5' => 2697,
1672             'vivoactive3' => 2700,
1673             'fr235_china_nfc' => 2733,
1674             'foretrex_601_701' => 2769,
1675             'vivo_move_hr' => 2772,
1676             'edge_1030' => 2713,
1677             'fr35_sea' => 2727,
1678             'vector_3' => 2787,
1679             'fenix5_asia' => 2796,
1680             'fenix5s_asia' => 2797,
1681             'fenix5x_asia' => 2798,
1682             'approach_z80' => 2806,
1683             'fr35_korea' => 2814,
1684             'd2charlie' => 2819,
1685             'vivo_smart3_apac' => 2831,
1686             'vivo_sport_apac' => 2832,
1687             'fr935_asia' => 2833,
1688             'descent' => 2859,
1689             'vivo_fit4' => 2878,
1690             'fr645' => 2886,
1691             'fr645m' => 2888,
1692             'fr30' => 2891,
1693             'fenix5s_plus' => 2900,
1694             'Edge_130' => 2909,
1695             'edge_1030_asia' => 2924,
1696             'vivosmart_4' => 2927,
1697             'vivo_move_hr_asia' => 2945,
1698             'approach_x10' => 2962,
1699             'fr30_asia' => 2977,
1700             'vivoactive3m_w' => 2988,
1701             'fr645_asia' => 3003,
1702             'fr645m_asia' => 3004,
1703             'edge_explore' => 3011,
1704             'gpsmap66' => 3028,
1705             'approach_s10' => 3049,
1706             'vivoactive3m_l' => 3066,
1707             'approach_g80' => 3085,
1708             'edge_130_asia' => 3092,
1709             'edge_1030_bontrager' => 3095,
1710             'fenix5_plus' => 3110,
1711             'fenix5x_plus' => 3111,
1712             'edge_520_plus' => 3112,
1713             'fr945' => 3113,
1714             'edge_530' => 3121,
1715             'edge_830' => 3122,
1716             'instinct_esports' => 3126,
1717             'fenix5s_plus_apac' => 3134,
1718             'fenix5x_plus_apac' => 3135,
1719             'edge_520_plus_apac' => 3142,
1720             'fr235l_asia' => 3144,
1721             'fr245_asia' => 3145,
1722             'vivo_active3m_apac' => 3163,
1723             'gen3_bsm' => 3192, # gen3 bike speed sensor
1724             'gen3_bcm' => 3193, # gen3 bike cadence sensor
1725             'vivo_smart4_asia' => 3218,
1726             'vivoactive4_small' => 3224,
1727             'vivoactive4_large' => 3225,
1728             'venu' => 3226,
1729             'marq_driver' => 3246,
1730             'marq_aviator' => 3247,
1731             'marq_captain' => 3248,
1732             'marq_commander' => 3249,
1733             'marq_expedition' => 3250,
1734             'marq_athlete' => 3251,
1735             'descent_mk2' => 3258,
1736             'gpsmap66i' => 3284,
1737             'fenix6S_sport' => 3287,
1738             'fenix6S' => 3288,
1739             'fenix6_sport' => 3289,
1740             'fenix6' => 3290,
1741             'fenix6x' => 3291,
1742             'hrm_dual' => 3299, # HRM-Dual
1743             'hrm_pro' => 3300, # HRM-Pro
1744             'vivo_move3_premium' => 3308,
1745             'approach_s40' => 3314,
1746             'fr245m_asia' => 3321,
1747             'edge_530_apac' => 3349,
1748             'edge_830_apac' => 3350,
1749             'vivo_move3' => 3378,
1750             'vivo_active4_small_asia' => 3387,
1751             'vivo_active4_large_asia' => 3388,
1752             'vivo_active4_oled_asia' => 3389,
1753             'swim2' => 3405,
1754             'marq_driver_asia' => 3420,
1755             'marq_aviator_asia' => 3421,
1756             'vivo_move3_asia' => 3422,
1757             'fr945_asia' => 3441,
1758             'vivo_active3t_chn' => 3446,
1759             'marq_captain_asia' => 3448,
1760             'marq_commander_asia' => 3449,
1761             'marq_expedition_asia' => 3450,
1762             'marq_athlete_asia' => 3451,
1763             'instinct_solar' => 3466,
1764             'fr45_asia' => 3469,
1765             'vivoactive3_daimler' => 3473,
1766             'legacy_rey' => 3498,
1767             'legacy_darth_vader' => 3499,
1768             'legacy_captain_marvel' => 3500,
1769             'legacy_first_avenger' => 3501,
1770             'fenix6s_sport_asia' => 3512,
1771             'fenix6s_asia' => 3513,
1772             'fenix6_sport_asia' => 3514,
1773             'fenix6_asia' => 3515,
1774             'fenix6x_asia' => 3516,
1775             'legacy_captain_marvel_asia' => 3535,
1776             'legacy_first_avenger_asia' => 3536,
1777             'legacy_rey_asia' => 3537,
1778             'legacy_darth_vader_asia' => 3538,
1779             'descent_mk2s' => 3542,
1780             'edge_130_plus' => 3558,
1781             'edge_1030_plus' => 3570,
1782             'rally_200' => 3578, # Rally 100/200 Power Meter Series
1783             'fr745' => 3589,
1784             'venusq' => 3600,
1785             'lily' => 3615,
1786             'marq_adventurer' => 3624,
1787             'enduro' => 3638,
1788             'swim2_apac' => 3639,
1789             'marq_adventurer_asia' => 3648,
1790             'fr945_lte' => 3652,
1791             'descent_mk2_asia' => 3702, # Mk2 and Mk2i
1792             'venu2' => 3703,
1793             'venu2s' => 3704,
1794             'venu_daimler_asia' => 3737,
1795             'marq_golfer' => 3739,
1796             'venu_daimler' => 3740,
1797             'fr745_asia' => 3794,
1798             'lily_asia' => 3809,
1799             'edge_1030_plus_asia' => 3812,
1800             'edge_130_plus_asia' => 3813,
1801             'approach_s12' => 3823,
1802             'enduro_asia' => 3872,
1803             'venusq_asia' => 3837,
1804             'edge_1040' => 3843,
1805             'marq_golfer_asia' => 3850,
1806             'venu2_plus' => 3851,
1807             'fr55' => 3869,
1808             'instinct_2' => 3888,
1809             'fenix7s' => 3905,
1810             'fenix7' => 3906,
1811             'fenix7x' => 3907,
1812             'fenix7s_apac' => 3908,
1813             'fenix7_apac' => 3909,
1814             'fenix7x_apac' => 3910,
1815             'approach_g12' => 3927,
1816             'descent_mk2s_asia' => 3930,
1817             'approach_s42' => 3934,
1818             'epix_gen2' => 3943,
1819             'epix_gen2_apac' => 3944,
1820             'venu2s_asia' => 3949,
1821             'venu2_asia' => 3950,
1822             'fr945_lte_asia' => 3978,
1823             'vivo_move_sport' => 3982,
1824             'vivomove_trend' => 3983,
1825             'approach_S12_asia' => 3986,
1826             'fr255_music' => 3990,
1827             'fr255_small_music' => 3991,
1828             'fr255' => 3992,
1829             'fr255_small' => 3993,
1830             'approach_g12_asia' => 4001,
1831             'approach_s42_asia' => 4002,
1832             'descent_g1' => 4005,
1833             'venu2_plus_asia' => 4017,
1834             'fr955' => 4024,
1835             'fr55_asia' => 4033,
1836             'vivosmart_5' => 4063,
1837             'instinct_2_asia' => 4071,
1838             'marq_gen2' => 4105, # Adventurer, Athlete, Captain, Golfer
1839             'venusq2' => 4115,
1840             'venusq2music' => 4116,
1841             'marq_gen2_aviator' => 4124,
1842             'd2_air_x10' => 4125,
1843             'hrm_pro_plus' => 4130,
1844             'descent_g1_asia' => 4132,
1845             'tactix7' => 4135,
1846             'instinct_crossover' => 4155,
1847             'edge_explore2' => 4169,
1848             'tacx_neo_smart' => 4265, # Neo Smart, Tacx
1849             'tacx_neo2_smart' => 4266, # Neo 2 Smart, Tacx
1850             'tacx_neo2_t_smart' => 4267, # Neo 2T Smart, Tacx
1851             'tacx_neo_smart_bike' => 4268, # Neo Smart Bike, Tacx
1852             'tacx_satori_smart' => 4269, # Satori Smart, Tacx
1853             'tacx_flow_smart' => 4270, # Flow Smart, Tacx
1854             'tacx_vortex_smart' => 4271, # Vortex Smart, Tacx
1855             'tacx_bushido_smart' => 4272, # Bushido Smart, Tacx
1856             'tacx_genius_smart' => 4273, # Genius Smart, Tacx
1857             'tacx_flux_flux_s_smart' => 4274, # Flux/Flux S Smart, Tacx
1858             'tacx_flux2_smart' => 4275, # Flux 2 Smart, Tacx
1859             'tacx_magnum' => 4276, # Magnum, Tacx
1860             'edge_1040_asia' => 4305,
1861             'enduro2' => 4341,
1862             'sdm4' => 10007, # SDM4 footpod
1863             'edge_remote' => 10014,
1864             'tacx_training_app_win' => 20533,
1865             'tacx_training_app_mac' => 20534,
1866             'tacx_training_app_mac_catalyst' => 20565,
1867             'training_center' => 20119,
1868             'tacx_training_app_android' => 30045,
1869             'tacx_training_app_ios' => 30046,
1870             'tacx_training_app_legacy' => 30047,
1871             'connectiq_simulator' => 65531,
1872             'android_antplus_plugin' => 65532,
1873             'connect' => 65534, # Garmin Connect website
1874             },
1875              
1876             'device_type' => +{
1877             '_moved_to' => 'antplus_device_type',
1878             },
1879              
1880             'antplus_device_type' => +{
1881             '_base_type' => FIT_UINT8,
1882             'antfs' => 1,
1883             'bike_power' => 11,
1884             'environment_sensor_legacy' => 12,
1885             'multi_sport_speed_distance' => 15,
1886             'control' => 16,
1887             'fitness_equipment' => 17,
1888             'blood_pressure' => 18,
1889             'geocache_node' => 19,
1890             'light_electric_vehicle' => 20,
1891             'env_sensor' => 25,
1892             'racquet' => 26,
1893             'control_hub' => 27,
1894             'muscle_oxygen' => 31,
1895             'shifting' => 34,
1896             'bike_light_main' => 35,
1897             'bike_light_shared' => 36,
1898             'exd' => 38,
1899             'bike_radar' => 40,
1900             'bike_aero' => 46,
1901             'weight_scale' => 119,
1902             'heart_rate' => 120,
1903             'bike_speed_cadence' => 121,
1904             'bike_cadence' => 122,
1905             'bike_speed' => 123,
1906             'stride_speed_distance' => 124,
1907             },
1908              
1909             'ant_network' => +{
1910             '_base_type' => FIT_ENUM,
1911             'public' => 0,
1912             'antplus' => 1,
1913             'antfs' => 2,
1914             'private' => 3,
1915             },
1916              
1917             'workout_capabilities' => +{
1918             '_base_type' => FIT_UINT32Z,
1919             '_mask' => 1,
1920             'interval' => 0x00000001,
1921             'custom' => 0x00000002,
1922             'fitness_equipment' => 0x00000004,
1923             'firstbeat' => 0x00000008,
1924             'new_leaf' => 0x00000010,
1925             'tcx' => 0x00000020,
1926             'speed' => 0x00000080,
1927             'heart_rate' => 0x00000100,
1928             'distance' => 0x00000200,
1929             'cadence' => 0x00000400,
1930             'power' => 0x00000800,
1931             'grade' => 0x00001000,
1932             'resistance' => 0x00002000,
1933             'protected' => 0x00004000,
1934             },
1935              
1936             'battery_status' => +{
1937             '_base_type' => FIT_UINT8,
1938             'new' => 1,
1939             'good' => 2,
1940             'ok' => 3,
1941             'low' => 4,
1942             'critical' => 5,
1943             'charging' => 6,
1944             'unknown' => 7,
1945             },
1946              
1947             'hr_type' => +{
1948             '_base_type' => FIT_ENUM,
1949             'normal' => 0,
1950             'irregular' => 1,
1951             },
1952              
1953             'course_capabilities' => +{
1954             '_base_type' => FIT_UINT32Z,
1955             '_mask' => 1,
1956             'processed' => 0x00000001,
1957             'valid' => 0x00000002,
1958             'time' => 0x00000004,
1959             'distance' => 0x00000008,
1960             'position' => 0x00000010,
1961             'heart_rate' => 0x00000020,
1962             'power' => 0x00000040,
1963             'cadence' => 0x00000080,
1964             'training' => 0x00000100,
1965             'navigation' => 0x00000200,
1966             'bikeway' => 0x00000400,
1967             'aviation'=> 0x00001000, # Denote course files to be used as flight plans
1968             },
1969              
1970             'weight' => +{
1971             '_base_type' => FIT_UINT16,
1972             'calculating' => 0xFFFE,
1973             },
1974              
1975             'workout_hr' => +{
1976             '_base_type' => FIT_UINT32,
1977             'bpm_offset' => 100,
1978             },
1979              
1980             'workout_power' => +{
1981             '_base_type' => FIT_UINT32,
1982             'watts_offset' => 1000,
1983             },
1984              
1985             'bp_status' => +{
1986             '_base_type' => FIT_ENUM,
1987             'no_error' => 0,
1988             'error_incomplete_data' => 1,
1989             'error_no_measurement' => 2,
1990             'error_data_out_of_range' => 3,
1991             'error_irregular_heart_rate' => 4,
1992             },
1993              
1994             'user_local_id' => +{
1995             '_base_type' => FIT_UINT16,
1996             'local_min' => 0x0001,
1997             'local_max' => 0x000F,
1998             'stationary_min' => 0x0010,
1999             'stationary_max' => 0x00FF,
2000             'portable_min' => 0x0100,
2001             'portable_max' => 0xFFFE,
2002             },
2003              
2004             'swim_stroke' => +{
2005             '_base_type' => FIT_ENUM,
2006             'freestyle' => 0,
2007             'backstroke' => 1,
2008             'breaststroke' => 2,
2009             'butterfly' => 3,
2010             'drill' => 4,
2011             'mixed' => 5,
2012             'im' => 6,
2013             },
2014              
2015             'activity_type' => +{
2016             '_base_type' => FIT_ENUM,
2017             'generic' => 0,
2018             'running' => 1,
2019             'cycling' => 2,
2020             'transition' => 3,
2021             'fitness_equipment' => 4,
2022             'swimming' => 5,
2023             'walking' => 6,
2024             'sedentary' => 8,
2025             'all' => 254,
2026             },
2027              
2028             'activity_subtype' => +{
2029             '_base_type' => FIT_ENUM,
2030             'generic' => 0,
2031             'treadmill' => 1, # run
2032             'street' => 2, # run
2033             'trail' => 3, # run
2034             'track' => 4, # run
2035             'spin' => 5, # cycling
2036             'indoor_cycling' => 6, # cycling
2037             'road' => 7, # cycling
2038             'mountain' => 8, # cycling
2039             'downhill' => 9, # cycling
2040             'recumbent' => 10, # cycling
2041             'cyclocross' => 11, # cycling
2042             'hand_cycling' => 12, # cycling
2043             'track_cycling' => 13, # cycling
2044             'indoor_rowing' => 14, # fitness equipment
2045             'elliptical' => 15, # fitness equipment
2046             'stair_climbing' => 16, # fitness equipment
2047             'lap_swimming' => 17, # swimming
2048             'open_water' => 18, # swimming
2049             'all' => 254,
2050             },
2051              
2052             'activity_level' => +{
2053             '_base_type' => FIT_ENUM,
2054             'low' => 0,
2055             'medium' => 1,
2056             'high' => 2,
2057             },
2058              
2059             'side' => +{
2060             '_base_type' => FIT_ENUM,
2061             'right' => 0,
2062             'left' => 1,
2063             },
2064              
2065             'left_right_balance' => +{
2066             '_base_type' => FIT_UINT8,
2067             'mask' => 0x7F,
2068             'right' => 0x80,
2069             },
2070              
2071             'left_right_balance_100' => +{
2072             '_base_type' => FIT_UINT16,
2073             'mask' => 0x3FFF,
2074             'right' => 0x8000,
2075             },
2076              
2077             'length_type' => +{
2078             '_base_type' => FIT_ENUM,
2079             'idle' => 0,
2080             'active' => 1,
2081             },
2082              
2083             'day_of_week' => +{
2084             '_base_type' => FIT_ENUM,
2085             'sunday' => 0,
2086             'monday' => 1,
2087             'tuesday' => 2,
2088             'wednesday' => 3,
2089             'thursday' => 4,
2090             'friday' => 5,
2091             'saturday' => 6,
2092             },
2093              
2094             'connectivity_capabilities' => +{
2095             '_base_type' => FIT_UINT32Z,
2096             'bluetooth' => 0x00000001,
2097             'bluetooth_le' => 0x00000002,
2098             'ant' => 0x00000004,
2099             'activity_upload' => 0x00000008,
2100             'course_download' => 0x00000010,
2101             'workout_download' => 0x00000020,
2102             'live_track' => 0x00000040,
2103             'weather_conditions' => 0x00000080,
2104             'weather_alerts' => 0x00000100,
2105             'gps_ephemeris_download' => 0x00000200,
2106             'explicit_archive' => 0x00000400,
2107             'setup_incomplete' => 0x00000800,
2108             'continue_sync_after_software_update' => 0x00001000,
2109             'connect_iq_app_download' => 0x00002000,
2110             'golf_course_download' => 0x00004000,
2111             'device_initiates_sync' => 0x00008000,
2112             'connect_iq_watch_app_download' => 0x00010000,
2113             'connect_iq_widget_download' => 0x00020000,
2114             'connect_iq_watch_face_download' => 0x00040000,
2115             'connect_iq_data_field_download' => 0x00080000,
2116             'connect_iq_app_managment' => 0x00100000,
2117             'swing_sensor' => 0x00200000,
2118             'swing_sensor_remote' => 0x00400000,
2119             'incident_detection' => 0x00800000,
2120             'audio_prompts' => 0x01000000,
2121             'wifi_verification' => 0x02000000,
2122             'true_up' => 0x04000000,
2123             'find_my_watch' => 0x08000000,
2124             'remote_manual_sync' => 0x10000000,
2125             'live_track_auto_start' => 0x20000000,
2126             'live_track_messaging' => 0x40000000,
2127             'instant_input' => 0x80000000, # Device supports instant input feature
2128             },
2129              
2130             'weather_report' => +{
2131             '_base_type' => FIT_ENUM,
2132             'current' => 0,
2133             # 'forecast' => 1, # deprecated, use hourly_forecast instead
2134             'hourly_forecast' => 1,
2135             'daily_forecast' => 2,
2136             },
2137              
2138             'weather_status' => +{
2139             '_base_type' => FIT_ENUM,
2140             'clear' => 0,
2141             'partly_cloudy' => 1,
2142             'mostly_cloudy' => 2,
2143             'rain' => 3,
2144             'snow' => 4,
2145             'windy' => 5,
2146             'thunderstorms' => 6,
2147             'wintry_mix' => 7,
2148             'fog' => 8,
2149             'hazy' => 11,
2150             'hail' => 12,
2151             'scattered_showers' => 13,
2152             'scattered_thunderstorms' => 14,
2153             'unknown_precipitation' => 15,
2154             'light_rain' => 16,
2155             'heavy_rain' => 17,
2156             'light_snow' => 18,
2157             'heavy_snow' => 19,
2158             'light_rain_snow' => 20,
2159             'heavy_rain_snow' => 21,
2160             'cloudy' => 22,
2161             },
2162              
2163             'weather_severity' => +{
2164             '_base_type' => FIT_ENUM,
2165             'unknown' => 0,
2166             'warning' => 1,
2167             'watch' => 2,
2168             'advisory' => 3,
2169             'statement' => 4,
2170             },
2171              
2172             'weather_severe_type' => +{
2173             '_base_type' => FIT_ENUM,
2174             'unspecified' => 0,
2175             'tornado' => 1,
2176             'tsunami' => 2,
2177             'hurricane' => 3,
2178             'extreme_wind' => 4,
2179             'typhoon' => 5,
2180             'inland_hurricane' => 6,
2181             'hurricane_force_wind' => 7,
2182             'waterspout' => 8,
2183             'severe_thunderstorm' => 9,
2184             'wreckhouse_winds' => 10,
2185             'les_suetes_wind' => 11,
2186             'avalanche' => 12,
2187             'flash_flood' => 13,
2188             'tropical_storm' => 14,
2189             'inland_tropical_storm' => 15,
2190             'blizzard' => 16,
2191             'ice_storm' => 17,
2192             'freezing_rain' => 18,
2193             'debris_flow' => 19,
2194             'flash_freeze' => 20,
2195             'dust_storm' => 21,
2196             'high_wind' => 22,
2197             'winter_storm' => 23,
2198             'heavy_freezing_spray' => 24,
2199             'extreme_cold' => 25,
2200             'wind_chill' => 26,
2201             'cold_wave' => 27,
2202             'heavy_snow_alert' => 28,
2203             'lake_effect_blowing_snow' => 29,
2204             'snow_squall' => 30,
2205             'lake_effect_snow' => 31,
2206             'winter_weather' => 32,
2207             'sleet' => 33,
2208             'snowfall' => 34,
2209             'snow_and_blowing_snow' => 35,
2210             'blowing_snow' => 36,
2211             'snow_alert' => 37,
2212             'arctic_outflow' => 38,
2213             'freezing_drizzle' => 39,
2214             'storm' => 40,
2215             'storm_surge' => 41,
2216             'rainfall' => 42,
2217             'areal_flood' => 43,
2218             'coastal_flood' => 44,
2219             'lakeshore_flood' => 45,
2220             'excessive_heat' => 46,
2221             'heat' => 47,
2222             'weather' => 48,
2223             'high_heat_and_humidity' => 49,
2224             'humidex_and_health' => 50,
2225             'humidex' => 51,
2226             'gale' => 52,
2227             'freezing_spray' => 53,
2228             'special_marine' => 54,
2229             'squall' => 55,
2230             'strong_wind' => 56,
2231             'lake_wind' => 57,
2232             'marine_weather' => 58,
2233             'wind' => 59,
2234             'small_craft_hazardous_seas' => 60,
2235             'hazardous_seas' => 61,
2236             'small_craft' => 62,
2237             'small_craft_winds' => 63,
2238             'small_craft_rough_bar' => 64,
2239             'high_water_level' => 65,
2240             'ashfall' => 66,
2241             'freezing_fog' => 67,
2242             'dense_fog' => 68,
2243             'dense_smoke' => 69,
2244             'blowing_dust' => 70,
2245             'hard_freeze' => 71,
2246             'freeze' => 72,
2247             'frost' => 73,
2248             'fire_weather' => 74,
2249             'flood' => 75,
2250             'rip_tide' => 76,
2251             'high_surf' => 77,
2252             'smog' => 78,
2253             'air_quality' => 79,
2254             'brisk_wind' => 80,
2255             'air_stagnation' => 81,
2256             'low_water' => 82,
2257             'hydrological' => 83,
2258             'special_weather' => 84,
2259             },
2260              
2261             'time_into_day' => +{ # since 00:00:00 UTC
2262             '_base_type' => FIT_UINT32,
2263             },
2264              
2265             'localtime_into_day' => +{ # same as above, but in local time zone
2266             '_base_type' => FIT_UINT32,
2267             },
2268              
2269             'stroke_type' => +{
2270             '_base_type' => FIT_ENUM,
2271             'no_event' => 0,
2272             'other' => 1,
2273             'serve' => 2,
2274             'forehand' => 3,
2275             'backhand' => 4,
2276             'smash' => 5,
2277             },
2278              
2279             'body_location' => +{
2280             '_base_type' => FIT_ENUM,
2281             'left_leg' => 0,
2282             'left_calf' => 1,
2283             'left_shin' => 2,
2284             'left_hamstring' => 3,
2285             'left_quad' => 4,
2286             'left_glute' => 5,
2287             'right_leg' => 6,
2288             'right_calf' => 7,
2289             'right_shin' => 8,
2290             'right_hamstring' => 9,
2291             'right_quad' => 10,
2292             'right_glute' => 11,
2293             'torso_back' => 12,
2294             'left_lower_back' => 13,
2295             'left_upper_back' => 14,
2296             'right_lower_back' => 15,
2297             'right_upper_back' => 16,
2298             'torso_front' => 17,
2299             'left_abdomen' => 18,
2300             'left_chest' => 19,
2301             'right_abdomen' => 20,
2302             'right_chest' => 21,
2303             'left_arm' => 22,
2304             'left_shoulder' => 23,
2305             'left_bicep' => 24,
2306             'left_tricep' => 25,
2307             'left_brachioradialis' => 26,
2308             'left_forearm_extensors' => 27,
2309             'right_arm' => 28,
2310             'right_shoulder' => 29,
2311             'right_bicep' => 30,
2312             'right_tricep' => 31,
2313             'right_brachioradialis' => 32,
2314             'right_forearm_extensors' => 33,
2315             'neck' => 34,
2316             'throat' => 35,
2317             'waist_mid_back' => 36,
2318             'waist_front' => 37,
2319             'waist_left' => 38,
2320             'waist_right' => 39,
2321             },
2322              
2323             'segment_lap_status' => +{
2324             '_base_type' => FIT_ENUM,
2325             'end' => 0,
2326             'fail' => 1,
2327             },
2328              
2329             'segment_leaderboard_type' => +{
2330             '_base_type' => FIT_ENUM,
2331             'overall' => 0,
2332             'personal_best' => 1,
2333             'connections' => 2,
2334             'group' => 3,
2335             'challenger' => 4,
2336             'kom' => 5,
2337             'qom' => 6,
2338             'pr' => 7,
2339             'goal' => 8,
2340             'rival' => 9,
2341             'club_leader' => 10,
2342             },
2343              
2344             'segment_delete_status' => +{
2345             '_base_type' => FIT_ENUM,
2346             'do_not_delete' => 0,
2347             'delete_one' => 1,
2348             'delete_all' => 2,
2349             },
2350              
2351             'segment_selection_type' => +{
2352             '_base_type' => FIT_ENUM,
2353             'starred' => 0,
2354             'suggested' => 1,
2355             },
2356              
2357             'source_type' => +{
2358             '_base_type' => FIT_ENUM,
2359             'ant' => 0,
2360             'antplus' => 1,
2361             'bluetooth' => 2,
2362             'bluetooth_low_energy' => 3,
2363             'wifi' => 4,
2364             'local' => 5,
2365             },
2366              
2367             'local_device_type' => +{
2368             '_base_type' => FIT_UINT8,
2369             'gps' => 0, # Onboard gps receiver
2370             'glonass' => 1, # Onboard glonass receiver
2371             'gps_glonass' => 2, # Onboard gps glonass receiver
2372             'accelerometer' => 3, # Onboard sensor
2373             'barometer' => 4, # Onboard sensor
2374             'temperature' => 5, # Onboard sensor
2375             'whr' => 10, # Onboard wrist HR sensor
2376             'sensor_hub' => 12, # Onboard software package
2377             },
2378              
2379             'ble_device_type' => +{
2380             '_base_type' => FIT_ENUM,
2381             'connected_gps' => 0, # GPS that is provided over a proprietary bluetooth service
2382             'heart_rate' => 1,
2383             'bike_power' => 2,
2384             'bike_speed_cadence' => 3,
2385             'bike_speed' => 4,
2386             'bike_cadence' => 5,
2387             'footpod' => 6,
2388             'bike_trainer' => 7, # Indoor-Bike FTMS protocol
2389             },
2390              
2391             'ant_channel_id' => +{
2392             '_base_type' => FIT_UINT32Z,
2393             'ant_extended_device_number_upper_nibble' => 0xF0000000,
2394             'ant_transmission_type_lower_nibble' => 0x0F000000,
2395             'ant_device_type' => 0x00FF0000,
2396             'ant_device_number' => 0x0000FFFF,
2397             },
2398              
2399             'display_orientation' => +{
2400             '_base_type' => FIT_ENUM,
2401             'auto' => 0,
2402             'portrait' => 1,
2403             'landscape' => 2,
2404             'portrait_flipped' => 3,
2405             'landscape_flipped' => 4,
2406             },
2407              
2408             'workout_equipment' => +{
2409             '_base_type' => FIT_ENUM,
2410             'none' => 0,
2411             'swim_fins' => 1,
2412             'swim_kickboard' => 2,
2413             'swim_paddles' => 3,
2414             'swim_pull_buoy' => 4,
2415             'swim_snorkel' => 5,
2416             },
2417             'watchface_mode' => +{
2418             '_base_type' => FIT_ENUM,
2419             'digital' => 0,
2420             'analog' => 1,
2421             'connect_iq' => 2,
2422             'disabled' => 3,
2423             },
2424              
2425             'digital_watchface_layout' => +{
2426             '_base_type' => FIT_ENUM,
2427             'traditional' => 0,
2428             'modern' => 1,
2429             'bold' => 2,
2430             },
2431              
2432             'analog_watchface_layout' => +{
2433             '_base_type' => FIT_ENUM,
2434             'minimal' => 0,
2435             'traditional' => 1,
2436             'modern' => 2,
2437             },
2438              
2439             'rider_position_type' => +{
2440             '_base_type' => FIT_ENUM,
2441             'seated' => 0,
2442             'standing' => 1,
2443             'transition_to_seated' => 2,
2444             'transition_to_standing' => 3,
2445             },
2446              
2447             'power_phase_type' => +{
2448             '_base_type' => FIT_ENUM,
2449             'power_phase_start_angle' => 0,
2450             'power_phase_end_angle' => 1,
2451             'power_phase_arc_length' => 2,
2452             'power_phase_center' => 3,
2453             },
2454              
2455             'camera_event_type' => +{
2456             '_base_type' => FIT_ENUM,
2457             'video_start' => 0,
2458             'video_split' => 1,
2459             'video_end' => 2,
2460             'photo_taken' => 3,
2461             'video_second_stream_start' => 4,
2462             'video_second_stream_split' => 5,
2463             'video_second_stream_end' => 6,
2464             'video_split_start' => 7,
2465             'video_second_stream_split_start' => 8,
2466             'video_pause' => 11,
2467             'video_second_stream_pause' => 12,
2468             'video_resume' => 13,
2469             'video_second_stream_resume' => 14,
2470             },
2471              
2472             'sensor_type' => +{
2473             '_base_type' => FIT_ENUM,
2474             'accelerometer' => 0,
2475             'gyroscope' => 1,
2476             'compass' => 2, # Magnetometer
2477             'barometer' => 3,
2478             },
2479              
2480             'bike_light_network_config_type' => +{
2481             '_base_type' => FIT_ENUM,
2482             'auto' => 0,
2483             'individual' => 4,
2484             'high_visibility' => 5,
2485             'trail' => 6,
2486             },
2487              
2488             'comm_timeout_type' => +{
2489             '_base_type' => FIT_UINT16,
2490             'wildcard_pairing_timeout' => 0,
2491             'pairing_timeout' => 1,
2492             'connection_lost' => 2,
2493             'connection_timeout' => 3,
2494             },
2495              
2496             'camera_orientation_type' => +{
2497             '_base_type' => FIT_ENUM,
2498             'camera_orientation_0' => 0,
2499             'camera_orientation_90' => 1,
2500             'camera_orientation_180' => 2,
2501             'camera_orientation_270' => 3,
2502             },
2503              
2504             'attitude_stage' => +{
2505             '_base_type' => FIT_ENUM,
2506             'failed' => 0,
2507             'aligning' => 1,
2508             'degraded' => 2,
2509             'valid' => 3,
2510             },
2511              
2512             'attitude_validity' => +{
2513             '_base_type' => FIT_UINT16,
2514             'track_angle_heading_valid' => 0x0001,
2515             'pitch_valid' => 0x0002,
2516             'roll_valid' => 0x0004,
2517             'lateral_body_accel_valid' => 0x0008,
2518             'normal_body_accel_valid' => 0x0010,
2519             'turn_rate_valid' => 0x0020,
2520             'hw_fail' => 0x0040,
2521             'mag_invalid' => 0x0080,
2522             'no_gps' => 0x0100,
2523             'gps_invalid' => 0x0200,
2524             'solution_coasting' => 0x0400,
2525             'true_track_angle' => 0x0800,
2526             'magnetic_heading' => 0x1000,
2527             },
2528              
2529             'auto_sync_frequency' => +{
2530             '_base_type' => FIT_ENUM,
2531             'never' => 0,
2532             'occasionally' => 1,
2533             'frequent' => 2,
2534             'once_a_day' => 3,
2535             'remote' => 4,
2536             },
2537              
2538             'exd_layout' => +{
2539             '_base_type' => FIT_ENUM,
2540             'full_screen' => 0,
2541             'half_vertical' => 1,
2542             'half_horizontal' => 2,
2543             'half_vertical_right_split' => 3,
2544             'half_horizontal_bottom_split' => 4,
2545             'full_quarter_split' => 5,
2546             'half_vertical_left_split' => 6,
2547             'half_horizontal_top_split' => 7,
2548             'dynamic' => 8, # The EXD may display the configured concepts in any layout it sees fit.
2549             },
2550              
2551             'exd_display_type' => +{
2552             '_base_type' => FIT_ENUM,
2553             'numerical' => 0,
2554             'simple' => 1,
2555             'graph' => 2,
2556             'bar' => 3,
2557             'circle_graph' => 4,
2558             'virtual_partner' => 5,
2559             'balance' => 6,
2560             'string_list' => 7,
2561             'string' => 8,
2562             'simple_dynamic_icon' => 9,
2563             'gauge' => 10,
2564             },
2565              
2566             'exd_data_units' => +{
2567             '_base_type' => FIT_ENUM,
2568             'no_units' => 0,
2569             'laps' => 1,
2570             'miles_per_hour' => 2,
2571             'kilometers_per_hour' => 3,
2572             'feet_per_hour' => 4,
2573             'meters_per_hour' => 5,
2574             'degrees_celsius' => 6,
2575             'degrees_farenheit' => 7,
2576             'zone' => 8,
2577             'gear' => 9,
2578             'rpm' => 10,
2579             'bpm' => 11,
2580             'degrees' => 12,
2581             'millimeters' => 13,
2582             'meters' => 14,
2583             'kilometers' => 15,
2584             'feet' => 16,
2585             'yards' => 17,
2586             'kilofeet' => 18,
2587             'miles' => 19,
2588             'time' => 20,
2589             'enum_turn_type' => 21,
2590             'percent' => 22,
2591             'watts' => 23,
2592             'watts_per_kilogram' => 24,
2593             'enum_battery_status' => 25,
2594             'enum_bike_light_beam_angle_mode' => 26,
2595             'enum_bike_light_battery_status' => 27,
2596             'enum_bike_light_network_config_type' => 28,
2597             'lights' => 29,
2598             'seconds' => 30,
2599             'minutes' => 31,
2600             'hours' => 32,
2601             'calories' => 33,
2602             'kilojoules' => 34,
2603             'milliseconds' => 35,
2604             'second_per_mile' => 36,
2605             'second_per_kilometer' => 37,
2606             'centimeter' => 38,
2607             'enum_course_point' => 39,
2608             'bradians' => 40,
2609             'enum_sport' => 41,
2610             'inches_hg' => 42,
2611             'mm_hg' => 43,
2612             'mbars' => 44,
2613             'hecto_pascals' => 45,
2614             'feet_per_min' => 46,
2615             'meters_per_min' => 47,
2616             'meters_per_sec' => 48,
2617             'eight_cardinal' => 49,
2618             },
2619              
2620             'exd_qualifiers' => +{
2621             '_base_type' => FIT_ENUM,
2622             'no_qualifier' => 0,
2623             'instantaneous' => 1,
2624             'average' => 2,
2625             'lap' => 3,
2626             'maximum' => 4,
2627             'maximum_average' => 5,
2628             'maximum_lap' => 6,
2629             'last_lap' => 7,
2630             'average_lap' => 8,
2631             'to_destination' => 9,
2632             'to_go' => 10,
2633             'to_next' => 11,
2634             'next_course_point' => 12,
2635             'total' => 13,
2636             'three_second_average' => 14,
2637             'ten_second_average' => 15,
2638             'thirty_second_average' => 16,
2639             'percent_maximum' => 17,
2640             'percent_maximum_average' => 18,
2641             'lap_percent_maximum' => 19,
2642             'elapsed' => 20,
2643             'sunrise' => 21,
2644             'sunset' => 22,
2645             'compared_to_virtual_partner' => 23,
2646             'maximum_24h' => 24,
2647             'minimum_24h' => 25,
2648             'minimum' => 26,
2649             'first' => 27,
2650             'second' => 28,
2651             'third' => 29,
2652             'shifter' => 30,
2653             'last_sport' => 31,
2654             'moving' => 32,
2655             'stopped' => 33,
2656             'estimated_total' => 34,
2657             'zone_9' => 242,
2658             'zone_8' => 243,
2659             'zone_7' => 244,
2660             'zone_6' => 245,
2661             'zone_5' => 246,
2662             'zone_4' => 247,
2663             'zone_3' => 248,
2664             'zone_2' => 249,
2665             'zone_1' => 250,
2666             },
2667              
2668             'exd_descriptors' => +{
2669             '_base_type' => FIT_ENUM,
2670             'bike_light_battery_status' => 0,
2671             'beam_angle_status' => 1,
2672             'batery_level' => 2,
2673             'light_network_mode' => 3,
2674             'number_lights_connected' => 4,
2675             'cadence' => 5,
2676             'distance' => 6,
2677             'estimated_time_of_arrival' => 7,
2678             'heading' => 8,
2679             'time' => 9,
2680             'battery_level' => 10,
2681             'trainer_resistance' => 11,
2682             'trainer_target_power' => 12,
2683             'time_seated' => 13,
2684             'time_standing' => 14,
2685             'elevation' => 15,
2686             'grade' => 16,
2687             'ascent' => 17,
2688             'descent' => 18,
2689             'vertical_speed' => 19,
2690             'di2_battery_level' => 20,
2691             'front_gear' => 21,
2692             'rear_gear' => 22,
2693             'gear_ratio' => 23,
2694             'heart_rate' => 24,
2695             'heart_rate_zone' => 25,
2696             'time_in_heart_rate_zone' => 26,
2697             'heart_rate_reserve' => 27,
2698             'calories' => 28,
2699             'gps_accuracy' => 29,
2700             'gps_signal_strength' => 30,
2701             'temperature' => 31,
2702             'time_of_day' => 32,
2703             'balance' => 33,
2704             'pedal_smoothness' => 34,
2705             'power' => 35,
2706             'functional_threshold_power' => 36,
2707             'intensity_factor' => 37,
2708             'work' => 38,
2709             'power_ratio' => 39,
2710             'normalized_power' => 40,
2711             'training_stress_score' => 41,
2712             'time_on_zone' => 42,
2713             'speed' => 43,
2714             'laps' => 44,
2715             'reps' => 45,
2716             'workout_step' => 46,
2717             'course_distance' => 47,
2718             'navigation_distance' => 48,
2719             'course_estimated_time_of_arrival' => 49,
2720             'navigation_estimated_time_of_arrival' => 50,
2721             'course_time' => 51,
2722             'navigation_time' => 52,
2723             'course_heading' => 53,
2724             'navigation_heading' => 54,
2725             'power_zone' => 55,
2726             'torque_effectiveness' => 56,
2727             'timer_time' => 57,
2728             'power_weight_ratio' => 58,
2729             'left_platform_center_offset' => 59,
2730             'right_platform_center_offset' => 60,
2731             'left_power_phase_start_angle' => 61,
2732             'right_power_phase_start_angle' => 62,
2733             'left_power_phase_finish_angle' => 63,
2734             'right_power_phase_finish_angle' => 64,
2735             'gears' => 65,
2736             'pace' => 66,
2737             'training_effect' => 67,
2738             'vertical_oscillation' => 68,
2739             'vertical_ratio' => 69,
2740             'ground_contact_time' => 70,
2741             'left_ground_contact_time_balance' => 71,
2742             'right_ground_contact_time_balance' => 72,
2743             'stride_length' => 73,
2744             'running_cadence' => 74,
2745             'performance_condition' => 75,
2746             'course_type' => 76,
2747             'time_in_power_zone' => 77,
2748             'navigation_turn' => 78,
2749             'course_location' => 79,
2750             'navigation_location' => 80,
2751             'compass' => 81,
2752             'gear_combo' => 82,
2753             'muscle_oxygen' => 83,
2754             'icon' => 84,
2755             'compass_heading' => 85,
2756             'gps_heading' => 86,
2757             'gps_elevation' => 87,
2758             'anaerobic_training_effect' => 88,
2759             'course' => 89,
2760             'off_course' => 90,
2761             'glide_ratio' => 91,
2762             'vertical_distance' => 92,
2763             'vmg' => 93,
2764             'ambient_pressure' => 94,
2765             'pressure' => 95,
2766             'vam' => 96,
2767             },
2768              
2769             'auto_activity_detect' => +{
2770             '_base_type' => FIT_UINT32,
2771             'none' => 0x00000000,
2772             'running' => 0x00000001,
2773             'cycling' => 0x00000002,
2774             'swimming' => 0x00000004,
2775             'walking' => 0x00000008,
2776             'elliptical' => 0x00000020,
2777             'sedentary' => 0x00000400,
2778             },
2779              
2780             'supported_exd_screen_layouts' => +{
2781             '_base_type' => FIT_UINT32Z,
2782             'full_screen' => 0x00000001,
2783             'half_vertical' => 0x00000002,
2784             'half_horizontal' => 0x00000004,
2785             'half_vertical_right_split' => 0x00000008,
2786             'half_horizontal_bottom_split' => 0x00000010,
2787             'full_quarter_split' => 0x00000020,
2788             'half_vertical_left_split' => 0x00000040,
2789             'half_horizontal_top_split' => 0x00000080,
2790             },
2791              
2792             'fit_base_type' => +{
2793             '_base_type' => FIT_UINT8,
2794             'enum' => 0,
2795             'sint8' => 1,
2796             'uint8' => 2,
2797             'sint16' => 131,
2798             'uint16' => 132,
2799             'sint32' => 133,
2800             'uint32' => 134,
2801             'string' => 7,
2802             'float32' => 136,
2803             'float64' => 137,
2804             'uint8z' => 10,
2805             'uint16z' => 139,
2806             'uint32z' => 140,
2807             'byte' => 13,
2808             'sint64' => 142,
2809             'uint64' => 143,
2810             'uint64z' => 144,
2811             },
2812              
2813             'turn_type' => +{
2814             '_base_type' => FIT_ENUM,
2815             'arriving_idx' => 0,
2816             'arriving_left_idx' => 1,
2817             'arriving_right_idx' => 2,
2818             'arriving_via_idx' => 3,
2819             'arriving_via_left_idx' => 4,
2820             'arriving_via_right_idx' => 5,
2821             'bear_keep_left_idx' => 6,
2822             'bear_keep_right_idx' => 7,
2823             'continue_idx' => 8,
2824             'exit_left_idx' => 9,
2825             'exit_right_idx' => 10,
2826             'ferry_idx' => 11,
2827             'roundabout_45_idx' => 12,
2828             'roundabout_90_idx' => 13,
2829             'roundabout_135_idx' => 14,
2830             'roundabout_180_idx' => 15,
2831             'roundabout_225_idx' => 16,
2832             'roundabout_270_idx' => 17,
2833             'roundabout_315_idx' => 18,
2834             'roundabout_360_idx' => 19,
2835             'roundabout_neg_45_idx' => 20,
2836             'roundabout_neg_90_idx' => 21,
2837             'roundabout_neg_135_idx' => 22,
2838             'roundabout_neg_180_idx' => 23,
2839             'roundabout_neg_225_idx' => 24,
2840             'roundabout_neg_270_idx' => 25,
2841             'roundabout_neg_315_idx' => 26,
2842             'roundabout_neg_360_idx' => 27,
2843             'roundabout_generic_idx' => 28,
2844             'roundabout_neg_generic_idx' => 29,
2845             'sharp_turn_left_idx' => 30,
2846             'sharp_turn_right_idx' => 31,
2847             'turn_left_idx' => 32,
2848             'turn_right_idx' => 33,
2849             'uturn_left_idx' => 34,
2850             'uturn_right_idx' => 35,
2851             'icon_inv_idx' => 36,
2852             'icon_idx_cnt' => 37,
2853             },
2854              
2855             'bike_light_beam_angle_mode' => +{
2856             '_base_type' => FIT_ENUM,
2857             'manual' => 0,
2858             'auto' => 1,
2859             },
2860              
2861             'fit_base_unit' => +{
2862             '_base_type' => FIT_UINT16,
2863             'other' => 0,
2864             'kilogram' => 1,
2865             'pound' => 2,
2866             },
2867              
2868             'set_type' => +{
2869             '_base_type' => FIT_UINT8,
2870             'rest' => 0,
2871             'active' => 1,
2872             },
2873              
2874             'exercise_category' => +{
2875             '_base_type' => FIT_UINT16,
2876             'bench_press' => 0,
2877             'calf_raise' => 1,
2878             'cardio' => 2,
2879             'carry' => 3,
2880             'chop' => 4,
2881             'core' => 5,
2882             'crunch' => 6,
2883             'curl' => 7,
2884             'deadlift' => 8,
2885             'flye' => 9,
2886             'hip_raise' => 10,
2887             'hip_stability' => 11,
2888             'hip_swing' => 12,
2889             'hyperextension' => 13,
2890             'lateral_raise' => 14,
2891             'leg_curl' => 15,
2892             'leg_raise' => 16,
2893             'lunge' => 17,
2894             'olympic_lift' => 18,
2895             'plank' => 19,
2896             'plyo' => 20,
2897             'pull_up' => 21,
2898             'push_up' => 22,
2899             'row' => 23,
2900             'shoulder_press' => 24,
2901             'shoulder_stability' => 25,
2902             'shrug' => 26,
2903             'sit_up' => 27,
2904             'squat' => 28,
2905             'total_body' => 29,
2906             'triceps_extension' => 30,
2907             'warm_up' => 31,
2908             'run' => 32,
2909             'unknown' => 65534,
2910             },
2911              
2912             'bench_press_exercise_name' => +{
2913             '_base_type' => FIT_UINT16,
2914             'alternating_dumbbell_chest_press_on_swiss_ball' => 0,
2915             'barbell_bench_press' => 1,
2916             'barbell_board_bench_press' => 2,
2917             'barbell_floor_press' => 3,
2918             'close_grip_barbell_bench_press' => 4,
2919             'decline_dumbbell_bench_press' => 5,
2920             'dumbbell_bench_press' => 6,
2921             'dumbbell_floor_press' => 7,
2922             'incline_barbell_bench_press' => 8,
2923             'incline_dumbbell_bench_press' => 9,
2924             'incline_smith_machine_bench_press' => 10,
2925             'isometric_barbell_bench_press' => 11,
2926             'kettlebell_chest_press' => 12,
2927             'neutral_grip_dumbbell_bench_press' => 13,
2928             'neutral_grip_dumbbell_incline_bench_press' => 14,
2929             'one_arm_floor_press' => 15,
2930             'weighted_one_arm_floor_press' => 16,
2931             'partial_lockout' => 17,
2932             'reverse_grip_barbell_bench_press' => 18,
2933             'reverse_grip_incline_bench_press' => 19,
2934             'single_arm_cable_chest_press' => 20,
2935             'single_arm_dumbbell_bench_press' => 21,
2936             'smith_machine_bench_press' => 22,
2937             'swiss_ball_dumbbell_chest_press' => 23,
2938             'triple_stop_barbell_bench_press' => 24,
2939             'wide_grip_barbell_bench_press' => 25,
2940             'alternating_dumbbell_chest_press' => 26,
2941             },
2942              
2943             'calf_raise_exercise_name' => +{
2944             '_base_type' => FIT_UINT16,
2945             '3_way_calf_raise' => 0,
2946             '3_way_weighted_calf_raise' => 1,
2947             '3_way_single_leg_calf_raise' => 2,
2948             '3_way_weighted_single_leg_calf_raise' => 3,
2949             'donkey_calf_raise' => 4,
2950             'weighted_donkey_calf_raise' => 5,
2951             'seated_calf_raise' => 6,
2952             'weighted_seated_calf_raise' => 7,
2953             'seated_dumbbell_toe_raise' => 8,
2954             'single_leg_bent_knee_calf_raise' => 9,
2955             'weighted_single_leg_bent_knee_calf_raise' => 10,
2956             'single_leg_decline_push_up' => 11,
2957             'single_leg_donkey_calf_raise' => 12,
2958             'weighted_single_leg_donkey_calf_raise' => 13,
2959             'single_leg_hip_raise_with_knee_hold' => 14,
2960             'single_leg_standing_calf_raise' => 15,
2961             'single_leg_standing_dumbbell_calf_raise' => 16,
2962             'standing_barbell_calf_raise' => 17,
2963             'standing_calf_raise' => 18,
2964             'weighted_standing_calf_raise' => 19,
2965             'standing_dumbbell_calf_raise' => 20,
2966             },
2967              
2968             'cardio_exercise_name' => +{
2969             '_base_type' => FIT_UINT16,
2970             'bob_and_weave_circle' => 0,
2971             'weighted_bob_and_weave_circle' => 1,
2972             'cardio_core_crawl' => 2,
2973             'weighted_cardio_core_crawl' => 3,
2974             'double_under' => 4,
2975             'weighted_double_under' => 5,
2976             'jump_rope' => 6,
2977             'weighted_jump_rope' => 7,
2978             'jump_rope_crossover' => 8,
2979             'weighted_jump_rope_crossover' => 9,
2980             'jump_rope_jog' => 10,
2981             'weighted_jump_rope_jog' => 11,
2982             'jumping_jacks' => 12,
2983             'weighted_jumping_jacks' => 13,
2984             'ski_moguls' => 14,
2985             'weighted_ski_moguls' => 15,
2986             'split_jacks' => 16,
2987             'weighted_split_jacks' => 17,
2988             'squat_jacks' => 18,
2989             'weighted_squat_jacks' => 19,
2990             'triple_under' => 20,
2991             'weighted_triple_under' => 21,
2992             },
2993              
2994             'carry_exercise_name' => +{
2995             '_base_type' => FIT_UINT16,
2996             'bar_holds' => 0,
2997             'farmers_walk' => 1,
2998             'farmers_walk_on_toes' => 2,
2999             'hex_dumbbell_hold' => 3,
3000             'overhead_carry' => 4,
3001             },
3002              
3003             'chop_exercise_name' => +{
3004             '_base_type' => FIT_UINT16,
3005             'cable_pull_through' => 0,
3006             'cable_rotational_lift' => 1,
3007             'cable_woodchop' => 2,
3008             'cross_chop_to_knee' => 3,
3009             'weighted_cross_chop_to_knee' => 4,
3010             'dumbbell_chop' => 5,
3011             'half_kneeling_rotation' => 6,
3012             'weighted_half_kneeling_rotation' => 7,
3013             'half_kneeling_rotational_chop' => 8,
3014             'half_kneeling_rotational_reverse_chop' => 9,
3015             'half_kneeling_stability_chop' => 10,
3016             'half_kneeling_stability_reverse_chop' => 11,
3017             'kneeling_rotational_chop' => 12,
3018             'kneeling_rotational_reverse_chop' => 13,
3019             'kneeling_stability_chop' => 14,
3020             'kneeling_woodchopper' => 15,
3021             'medicine_ball_wood_chops' => 16,
3022             'power_squat_chops' => 17,
3023             'weighted_power_squat_chops' => 18,
3024             'standing_rotational_chop' => 19,
3025             'standing_split_rotational_chop' => 20,
3026             'standing_split_rotational_reverse_chop' => 21,
3027             'standing_stability_reverse_chop' => 22,
3028             },
3029              
3030             'core_exercise_name' => +{
3031             '_base_type' => FIT_UINT16,
3032             'abs_jabs' => 0,
3033             'weighted_abs_jabs' => 1,
3034             'alternating_plate_reach' => 2,
3035             'barbell_rollout' => 3,
3036             'weighted_barbell_rollout' => 4,
3037             'body_bar_oblique_twist' => 5,
3038             'cable_core_press' => 6,
3039             'cable_side_bend' => 7,
3040             'side_bend' => 8,
3041             'weighted_side_bend' => 9,
3042             'crescent_circle' => 10,
3043             'weighted_crescent_circle' => 11,
3044             'cycling_russian_twist' => 12,
3045             'weighted_cycling_russian_twist' => 13,
3046             'elevated_feet_russian_twist' => 14,
3047             'weighted_elevated_feet_russian_twist' => 15,
3048             'half_turkish_get_up' => 16,
3049             'kettlebell_windmill' => 17,
3050             'kneeling_ab_wheel' => 18,
3051             'weighted_kneeling_ab_wheel' => 19,
3052             'modified_front_lever' => 20,
3053             'open_knee_tucks' => 21,
3054             'weighted_open_knee_tucks' => 22,
3055             'side_abs_leg_lift' => 23,
3056             'weighted_side_abs_leg_lift' => 24,
3057             'swiss_ball_jackknife' => 25,
3058             'weighted_swiss_ball_jackknife' => 26,
3059             'swiss_ball_pike' => 27,
3060             'weighted_swiss_ball_pike' => 28,
3061             'swiss_ball_rollout' => 29,
3062             'weighted_swiss_ball_rollout' => 30,
3063             'triangle_hip_press' => 31,
3064             'weighted_triangle_hip_press' => 32,
3065             'trx_suspended_jackknife' => 33,
3066             'weighted_trx_suspended_jackknife' => 34,
3067             'u_boat' => 35,
3068             'weighted_u_boat' => 36,
3069             'windmill_switches' => 37,
3070             'weighted_windmill_switches' => 38,
3071             'alternating_slide_out' => 39,
3072             'weighted_alternating_slide_out' => 40,
3073             'ghd_back_extensions' => 41,
3074             'weighted_ghd_back_extensions' => 42,
3075             'overhead_walk' => 43,
3076             'inchworm' => 44,
3077             'weighted_modified_front_lever' => 45,
3078             'russian_twist' => 46,
3079             'abdominal_leg_rotations' => 47, # Deprecated do not use
3080             'arm_and_leg_extension_on_knees' => 48,
3081             'bicycle' => 49,
3082             'bicep_curl_with_leg_extension' => 50,
3083             'cat_cow' => 51,
3084             'corkscrew' => 52,
3085             'criss_cross' => 53,
3086             'criss_cross_with_ball' => 54, # Deprecated do not use
3087             'double_leg_stretch' => 55,
3088             'knee_folds' => 56,
3089             'lower_lift' => 57,
3090             'neck_pull' => 58,
3091             'pelvic_clocks' => 59,
3092             'roll_over' => 60,
3093             'roll_up' => 61,
3094             'rolling' => 62,
3095             'rowing_1' => 63,
3096             'rowing_2' => 64,
3097             'scissors' => 65,
3098             'single_leg_circles' => 66,
3099             'single_leg_stretch' => 67,
3100             'snake_twist_1_and_2' => 68, # Deprecated do not use
3101             'swan' => 69,
3102             'swimming' => 70,
3103             'teaser' => 71,
3104             'the_hundred' => 72,
3105             },
3106              
3107             'crunch_exercise_name' => +{
3108             '_base_type' => FIT_UINT16,
3109             'bicycle_crunch' => 0,
3110             'cable_crunch' => 1,
3111             'circular_arm_crunch' => 2,
3112             'crossed_arms_crunch' => 3,
3113             'weighted_crossed_arms_crunch' => 4,
3114             'cross_leg_reverse_crunch' => 5,
3115             'weighted_cross_leg_reverse_crunch' => 6,
3116             'crunch_chop' => 7,
3117             'weighted_crunch_chop' => 8,
3118             'double_crunch' => 9,
3119             'weighted_double_crunch' => 10,
3120             'elbow_to_knee_crunch' => 11,
3121             'weighted_elbow_to_knee_crunch' => 12,
3122             'flutter_kicks' => 13,
3123             'weighted_flutter_kicks' => 14,
3124             'foam_roller_reverse_crunch_on_bench' => 15,
3125             'weighted_foam_roller_reverse_crunch_on_bench' => 16,
3126             'foam_roller_reverse_crunch_with_dumbbell' => 17,
3127             'foam_roller_reverse_crunch_with_medicine_ball' => 18,
3128             'frog_press' => 19,
3129             'hanging_knee_raise_oblique_crunch' => 20,
3130             'weighted_hanging_knee_raise_oblique_crunch' => 21,
3131             'hip_crossover' => 22,
3132             'weighted_hip_crossover' => 23,
3133             'hollow_rock' => 24,
3134             'weighted_hollow_rock' => 25,
3135             'incline_reverse_crunch' => 26,
3136             'weighted_incline_reverse_crunch' => 27,
3137             'kneeling_cable_crunch' => 28,
3138             'kneeling_cross_crunch' => 29,
3139             'weighted_kneeling_cross_crunch' => 30,
3140             'kneeling_oblique_cable_crunch' => 31,
3141             'knees_to_elbow' => 32,
3142             'leg_extensions' => 33,
3143             'weighted_leg_extensions' => 34,
3144             'leg_levers' => 35,
3145             'mcgill_curl_up' => 36,
3146             'weighted_mcgill_curl_up' => 37,
3147             'modified_pilates_roll_up_with_ball' => 38,
3148             'weighted_modified_pilates_roll_up_with_ball' => 39,
3149             'pilates_crunch' => 40,
3150             'weighted_pilates_crunch' => 41,
3151             'pilates_roll_up_with_ball' => 42,
3152             'weighted_pilates_roll_up_with_ball' => 43,
3153             'raised_legs_crunch' => 44,
3154             'weighted_raised_legs_crunch' => 45,
3155             'reverse_crunch' => 46,
3156             'weighted_reverse_crunch' => 47,
3157             'reverse_crunch_on_a_bench' => 48,
3158             'weighted_reverse_crunch_on_a_bench' => 49,
3159             'reverse_curl_and_lift' => 50,
3160             'weighted_reverse_curl_and_lift' => 51,
3161             'rotational_lift' => 52,
3162             'weighted_rotational_lift' => 53,
3163             'seated_alternating_reverse_crunch' => 54,
3164             'weighted_seated_alternating_reverse_crunch' => 55,
3165             'seated_leg_u' => 56,
3166             'weighted_seated_leg_u' => 57,
3167             'side_to_side_crunch_and_weave' => 58,
3168             'weighted_side_to_side_crunch_and_weave' => 59,
3169             'single_leg_reverse_crunch' => 60,
3170             'weighted_single_leg_reverse_crunch' => 61,
3171             'skater_crunch_cross' => 62,
3172             'weighted_skater_crunch_cross' => 63,
3173             'standing_cable_crunch' => 64,
3174             'standing_side_crunch' => 65,
3175             'step_climb' => 66,
3176             'weighted_step_climb' => 67,
3177             'swiss_ball_crunch' => 68,
3178             'swiss_ball_reverse_crunch' => 69,
3179             'weighted_swiss_ball_reverse_crunch' => 70,
3180             'swiss_ball_russian_twist' => 71,
3181             'weighted_swiss_ball_russian_twist' => 72,
3182             'swiss_ball_side_crunch' => 73,
3183             'weighted_swiss_ball_side_crunch' => 74,
3184             'thoracic_crunches_on_foam_roller' => 75,
3185             'weighted_thoracic_crunches_on_foam_roller' => 76,
3186             'triceps_crunch' => 77,
3187             'weighted_bicycle_crunch' => 78,
3188             'weighted_crunch' => 79,
3189             'weighted_swiss_ball_crunch' => 80,
3190             'toes_to_bar' => 81,
3191             'weighted_toes_to_bar' => 82,
3192             'crunch' => 83,
3193             'straight_leg_crunch_with_ball' => 84,
3194             },
3195              
3196             'curl_exercise_name' => +{
3197             '_base_type' => FIT_UINT16,
3198             'alternating_dumbbell_biceps_curl' => 0,
3199             'alternating_dumbbell_biceps_curl_on_swiss_ball' => 1,
3200             'alternating_incline_dumbbell_biceps_curl' => 2,
3201             'barbell_biceps_curl' => 3,
3202             'barbell_reverse_wrist_curl' => 4,
3203             'barbell_wrist_curl' => 5,
3204             'behind_the_back_barbell_reverse_wrist_curl' => 6,
3205             'behind_the_back_one_arm_cable_curl' => 7,
3206             'cable_biceps_curl' => 8,
3207             'cable_hammer_curl' => 9,
3208             'cheating_barbell_biceps_curl' => 10,
3209             'close_grip_ez_bar_biceps_curl' => 11,
3210             'cross_body_dumbbell_hammer_curl' => 12,
3211             'dead_hang_biceps_curl' => 13,
3212             'decline_hammer_curl' => 14,
3213             'dumbbell_biceps_curl_with_static_hold' => 15,
3214             'dumbbell_hammer_curl' => 16,
3215             'dumbbell_reverse_wrist_curl' => 17,
3216             'dumbbell_wrist_curl' => 18,
3217             'ez_bar_preacher_curl' => 19,
3218             'forward_bend_biceps_curl' => 20,
3219             'hammer_curl_to_press' => 21,
3220             'incline_dumbbell_biceps_curl' => 22,
3221             'incline_offset_thumb_dumbbell_curl' => 23,
3222             'kettlebell_biceps_curl' => 24,
3223             'lying_concentration_cable_curl' => 25,
3224             'one_arm_preacher_curl' => 26,
3225             'plate_pinch_curl' => 27,
3226             'preacher_curl_with_cable' => 28,
3227             'reverse_ez_bar_curl' => 29,
3228             'reverse_grip_wrist_curl' => 30,
3229             'reverse_grip_barbell_biceps_curl' => 31,
3230             'seated_alternating_dumbbell_biceps_curl' => 32,
3231             'seated_dumbbell_biceps_curl' => 33,
3232             'seated_reverse_dumbbell_curl' => 34,
3233             'split_stance_offset_pinky_dumbbell_curl' => 35,
3234             'standing_alternating_dumbbell_curls' => 36,
3235             'standing_dumbbell_biceps_curl' => 37,
3236             'standing_ez_bar_biceps_curl' => 38,
3237             'static_curl' => 39,
3238             'swiss_ball_dumbbell_overhead_triceps_extension' => 40,
3239             'swiss_ball_ez_bar_preacher_curl' => 41,
3240             'twisting_standing_dumbbell_biceps_curl' => 42,
3241             'wide_grip_ez_bar_biceps_curl' => 43,
3242             },
3243              
3244             'deadlift_exercise_name' => +{
3245             '_base_type' => FIT_UINT16,
3246             'barbell_deadlift' => 0,
3247             'barbell_straight_leg_deadlift' => 1,
3248             'dumbbell_deadlift' => 2,
3249             'dumbbell_single_leg_deadlift_to_row' => 3,
3250             'dumbbell_straight_leg_deadlift' => 4,
3251             'kettlebell_floor_to_shelf' => 5,
3252             'one_arm_one_leg_deadlift' => 6,
3253             'rack_pull' => 7,
3254             'rotational_dumbbell_straight_leg_deadlift' => 8,
3255             'single_arm_deadlift' => 9,
3256             'single_leg_barbell_deadlift' => 10,
3257             'single_leg_barbell_straight_leg_deadlift' => 11,
3258             'single_leg_deadlift_with_barbell' => 12,
3259             'single_leg_rdl_circuit' => 13,
3260             'single_leg_romanian_deadlift_with_dumbbell' => 14,
3261             'sumo_deadlift' => 15,
3262             'sumo_deadlift_high_pull' => 16,
3263             'trap_bar_deadlift' => 17,
3264             'wide_grip_barbell_deadlift' => 18,
3265             },
3266              
3267             'flye_exercise_name' => +{
3268             '_base_type' => FIT_UINT16,
3269             'cable_crossover' => 0,
3270             'decline_dumbbell_flye' => 1,
3271             'dumbbell_flye' => 2,
3272             'incline_dumbbell_flye' => 3,
3273             'kettlebell_flye' => 4,
3274             'kneeling_rear_flye' => 5,
3275             'single_arm_standing_cable_reverse_flye' => 6,
3276             'swiss_ball_dumbbell_flye' => 7,
3277             'arm_rotations' => 8,
3278             'hug_a_tree' => 9,
3279             },
3280              
3281             'hip_raise_exercise_name' => +{
3282             '_base_type' => FIT_UINT16,
3283             'barbell_hip_thrust_on_floor' => 0,
3284             'barbell_hip_thrust_with_bench' => 1,
3285             'bent_knee_swiss_ball_reverse_hip_raise' => 2,
3286             'weighted_bent_knee_swiss_ball_reverse_hip_raise' => 3,
3287             'bridge_with_leg_extension' => 4,
3288             'weighted_bridge_with_leg_extension' => 5,
3289             'clam_bridge' => 6,
3290             'front_kick_tabletop' => 7,
3291             'weighted_front_kick_tabletop' => 8,
3292             'hip_extension_and_cross' => 9,
3293             'weighted_hip_extension_and_cross' => 10,
3294             'hip_raise' => 11,
3295             'weighted_hip_raise' => 12,
3296             'hip_raise_with_feet_on_swiss_ball' => 13,
3297             'weighted_hip_raise_with_feet_on_swiss_ball' => 14,
3298             'hip_raise_with_head_on_bosu_ball' => 15,
3299             'weighted_hip_raise_with_head_on_bosu_ball' => 16,
3300             'hip_raise_with_head_on_swiss_ball' => 17,
3301             'weighted_hip_raise_with_head_on_swiss_ball' => 18,
3302             'hip_raise_with_knee_squeeze' => 19,
3303             'weighted_hip_raise_with_knee_squeeze' => 20,
3304             'incline_rear_leg_extension' => 21,
3305             'weighted_incline_rear_leg_extension' => 22,
3306             'kettlebell_swing' => 23,
3307             'marching_hip_raise' => 24,
3308             'weighted_marching_hip_raise' => 25,
3309             'marching_hip_raise_with_feet_on_a_swiss_ball' => 26,
3310             'weighted_marching_hip_raise_with_feet_on_a_swiss_ball' => 27,
3311             'reverse_hip_raise' => 28,
3312             'weighted_reverse_hip_raise' => 29,
3313             'single_leg_hip_raise' => 30,
3314             'weighted_single_leg_hip_raise' => 31,
3315             'single_leg_hip_raise_with_foot_on_bench' => 32,
3316             'weighted_single_leg_hip_raise_with_foot_on_bench' => 33,
3317             'single_leg_hip_raise_with_foot_on_bosu_ball' => 34,
3318             'weighted_single_leg_hip_raise_with_foot_on_bosu_ball' => 35,
3319             'single_leg_hip_raise_with_foot_on_foam_roller' => 36,
3320             'weighted_single_leg_hip_raise_with_foot_on_foam_roller' => 37,
3321             'single_leg_hip_raise_with_foot_on_medicine_ball' => 38,
3322             'weighted_single_leg_hip_raise_with_foot_on_medicine_ball' => 39,
3323             'single_leg_hip_raise_with_head_on_bosu_ball' => 40,
3324             'weighted_single_leg_hip_raise_with_head_on_bosu_ball' => 41,
3325             'weighted_clam_bridge' => 42,
3326             'single_leg_swiss_ball_hip_raise_and_leg_curl' => 43,
3327             'clams' => 44,
3328             'inner_thigh_circles' => 45, # Deprecated do not use
3329             'inner_thigh_side_lift' => 46, # Deprecated do not use
3330             'leg_circles' => 47,
3331             'leg_lift' => 48,
3332             'leg_lift_in_external_rotation' => 49,
3333             },
3334              
3335             'hip_stability_exercise_name' => +{
3336             '_base_type' => FIT_UINT16,
3337             'band_side_lying_leg_raise' => 0,
3338             'dead_bug' => 1,
3339             'weighted_dead_bug' => 2,
3340             'external_hip_raise' => 3,
3341             'weighted_external_hip_raise' => 4,
3342             'fire_hydrant_kicks' => 5,
3343             'weighted_fire_hydrant_kicks' => 6,
3344             'hip_circles' => 7,
3345             'weighted_hip_circles' => 8,
3346             'inner_thigh_lift' => 9,
3347             'weighted_inner_thigh_lift' => 10,
3348             'lateral_walks_with_band_at_ankles' => 11,
3349             'pretzel_side_kick' => 12,
3350             'weighted_pretzel_side_kick' => 13,
3351             'prone_hip_internal_rotation' => 14,
3352             'weighted_prone_hip_internal_rotation' => 15,
3353             'quadruped' => 16,
3354             'quadruped_hip_extension' => 17,
3355             'weighted_quadruped_hip_extension' => 18,
3356             'quadruped_with_leg_lift' => 19,
3357             'weighted_quadruped_with_leg_lift' => 20,
3358             'side_lying_leg_raise' => 21,
3359             'weighted_side_lying_leg_raise' => 22,
3360             'sliding_hip_adduction' => 23,
3361             'weighted_sliding_hip_adduction' => 24,
3362             'standing_adduction' => 25,
3363             'weighted_standing_adduction' => 26,
3364             'standing_cable_hip_abduction' => 27,
3365             'standing_hip_abduction' => 28,
3366             'weighted_standing_hip_abduction' => 29,
3367             'standing_rear_leg_raise' => 30,
3368             'weighted_standing_rear_leg_raise' => 31,
3369             'supine_hip_internal_rotation' => 32,
3370             'weighted_supine_hip_internal_rotation' => 33,
3371             },
3372              
3373             'hip_swing_exercise_name' => +{
3374             '_base_type' => FIT_UINT16,
3375             'single_arm_kettlebell_swing' => 0,
3376             'single_arm_dumbbell_swing' => 1,
3377             'step_out_swing' => 2,
3378             },
3379              
3380             'hyperextension_exercise_name' => +{
3381             '_base_type' => FIT_UINT16,
3382             'back_extension_with_opposite_arm_and_leg_reach' => 0,
3383             'weighted_back_extension_with_opposite_arm_and_leg_reach' => 1,
3384             'base_rotations' => 2,
3385             'weighted_base_rotations' => 3,
3386             'bent_knee_reverse_hyperextension' => 4,
3387             'weighted_bent_knee_reverse_hyperextension' => 5,
3388             'hollow_hold_and_roll' => 6,
3389             'weighted_hollow_hold_and_roll' => 7,
3390             'kicks' => 8,
3391             'weighted_kicks' => 9,
3392             'knee_raises' => 10,
3393             'weighted_knee_raises' => 11,
3394             'kneeling_superman' => 12,
3395             'weighted_kneeling_superman' => 13,
3396             'lat_pull_down_with_row' => 14,
3397             'medicine_ball_deadlift_to_reach' => 15,
3398             'one_arm_one_leg_row' => 16,
3399             'one_arm_row_with_band' => 17,
3400             'overhead_lunge_with_medicine_ball' => 18,
3401             'plank_knee_tucks' => 19,
3402             'weighted_plank_knee_tucks' => 20,
3403             'side_step' => 21,
3404             'weighted_side_step' => 22,
3405             'single_leg_back_extension' => 23,
3406             'weighted_single_leg_back_extension' => 24,
3407             'spine_extension' => 25,
3408             'weighted_spine_extension' => 26,
3409             'static_back_extension' => 27,
3410             'weighted_static_back_extension' => 28,
3411             'superman_from_floor' => 29,
3412             'weighted_superman_from_floor' => 30,
3413             'swiss_ball_back_extension' => 31,
3414             'weighted_swiss_ball_back_extension' => 32,
3415             'swiss_ball_hyperextension' => 33,
3416             'weighted_swiss_ball_hyperextension' => 34,
3417             'swiss_ball_opposite_arm_and_leg_lift' => 35,
3418             'weighted_swiss_ball_opposite_arm_and_leg_lift' => 36,
3419             'superman_on_swiss_ball' => 37,
3420             'cobra' => 38,
3421             'supine_floor_barre' => 39, # Deprecated do not use
3422             },
3423              
3424             'lateral_raise_exercise_name' => +{
3425             '_base_type' => FIT_UINT16,
3426             '45_degree_cable_external_rotation' => 0,
3427             'alternating_lateral_raise_with_static_hold' => 1,
3428             'bar_muscle_up' => 2,
3429             'bent_over_lateral_raise' => 3,
3430             'cable_diagonal_raise' => 4,
3431             'cable_front_raise' => 5,
3432             'calorie_row' => 6,
3433             'combo_shoulder_raise' => 7,
3434             'dumbbell_diagonal_raise' => 8,
3435             'dumbbell_v_raise' => 9,
3436             'front_raise' => 10,
3437             'leaning_dumbbell_lateral_raise' => 11,
3438             'lying_dumbbell_raise' => 12,
3439             'muscle_up' => 13,
3440             'one_arm_cable_lateral_raise' => 14,
3441             'overhand_grip_rear_lateral_raise' => 15,
3442             'plate_raises' => 16,
3443             'ring_dip' => 17,
3444             'weighted_ring_dip' => 18,
3445             'ring_muscle_up' => 19,
3446             'weighted_ring_muscle_up' => 20,
3447             'rope_climb' => 21,
3448             'weighted_rope_climb' => 22,
3449             'scaption' => 23,
3450             'seated_lateral_raise' => 24,
3451             'seated_rear_lateral_raise' => 25,
3452             'side_lying_lateral_raise' => 26,
3453             'standing_lift' => 27,
3454             'suspended_row' => 28,
3455             'underhand_grip_rear_lateral_raise' => 29,
3456             'wall_slide' => 30,
3457             'weighted_wall_slide' => 31,
3458             'arm_circles' => 32,
3459             'shaving_the_head' => 33,
3460             },
3461              
3462             'leg_curl_exercise_name' => +{
3463             '_base_type' => FIT_UINT16,
3464             'leg_curl' => 0,
3465             'weighted_leg_curl' => 1,
3466             'good_morning' => 2,
3467             'seated_barbell_good_morning' => 3,
3468             'single_leg_barbell_good_morning' => 4,
3469             'single_leg_sliding_leg_curl' => 5,
3470             'sliding_leg_curl' => 6,
3471             'split_barbell_good_morning' => 7,
3472             'split_stance_extension' => 8,
3473             'staggered_stance_good_morning' => 9,
3474             'swiss_ball_hip_raise_and_leg_curl' => 10,
3475             'zercher_good_morning' => 11,
3476             },
3477              
3478             'leg_raise_exercise_name' => +{
3479             '_base_type' => FIT_UINT16,
3480             'hanging_knee_raise' => 0,
3481             'hanging_leg_raise' => 1,
3482             'weighted_hanging_leg_raise' => 2,
3483             'hanging_single_leg_raise' => 3,
3484             'weighted_hanging_single_leg_raise' => 4,
3485             'kettlebell_leg_raises' => 5,
3486             'leg_lowering_drill' => 6,
3487             'weighted_leg_lowering_drill' => 7,
3488             'lying_straight_leg_raise' => 8,
3489             'weighted_lying_straight_leg_raise' => 9,
3490             'medicine_ball_leg_drops' => 10,
3491             'quadruped_leg_raise' => 11,
3492             'weighted_quadruped_leg_raise' => 12,
3493             'reverse_leg_raise' => 13,
3494             'weighted_reverse_leg_raise' => 14,
3495             'reverse_leg_raise_on_swiss_ball' => 15,
3496             'weighted_reverse_leg_raise_on_swiss_ball' => 16,
3497             'single_leg_lowering_drill' => 17,
3498             'weighted_single_leg_lowering_drill' => 18,
3499             'weighted_hanging_knee_raise' => 19,
3500             'lateral_stepover' => 20,
3501             'weighted_lateral_stepover' => 21,
3502             },
3503              
3504             'lunge_exercise_name' => +{
3505             '_base_type' => FIT_UINT16,
3506             'overhead_lunge' => 0,
3507             'lunge_matrix' => 1,
3508             'weighted_lunge_matrix' => 2,
3509             'alternating_barbell_forward_lunge' => 3,
3510             'alternating_dumbbell_lunge_with_reach' => 4,
3511             'back_foot_elevated_dumbbell_split_squat' => 5,
3512             'barbell_box_lunge' => 6,
3513             'barbell_bulgarian_split_squat' => 7,
3514             'barbell_crossover_lunge' => 8,
3515             'barbell_front_split_squat' => 9,
3516             'barbell_lunge' => 10,
3517             'barbell_reverse_lunge' => 11,
3518             'barbell_side_lunge' => 12,
3519             'barbell_split_squat' => 13,
3520             'core_control_rear_lunge' => 14,
3521             'diagonal_lunge' => 15,
3522             'drop_lunge' => 16,
3523             'dumbbell_box_lunge' => 17,
3524             'dumbbell_bulgarian_split_squat' => 18,
3525             'dumbbell_crossover_lunge' => 19,
3526             'dumbbell_diagonal_lunge' => 20,
3527             'dumbbell_lunge' => 21,
3528             'dumbbell_lunge_and_rotation' => 22,
3529             'dumbbell_overhead_bulgarian_split_squat' => 23,
3530             'dumbbell_reverse_lunge_to_high_knee_and_press' => 24,
3531             'dumbbell_side_lunge' => 25,
3532             'elevated_front_foot_barbell_split_squat' => 26,
3533             'front_foot_elevated_dumbbell_split_squat' => 27,
3534             'gunslinger_lunge' => 28,
3535             'lawnmower_lunge' => 29,
3536             'low_lunge_with_isometric_adduction' => 30,
3537             'low_side_to_side_lunge' => 31,
3538             'lunge' => 32,
3539             'weighted_lunge' => 33,
3540             'lunge_with_arm_reach' => 34,
3541             'lunge_with_diagonal_reach' => 35,
3542             'lunge_with_side_bend' => 36,
3543             'offset_dumbbell_lunge' => 37,
3544             'offset_dumbbell_reverse_lunge' => 38,
3545             'overhead_bulgarian_split_squat' => 39,
3546             'overhead_dumbbell_reverse_lunge' => 40,
3547             'overhead_dumbbell_split_squat' => 41,
3548             'overhead_lunge_with_rotation' => 42,
3549             'reverse_barbell_box_lunge' => 43,
3550             'reverse_box_lunge' => 44,
3551             'reverse_dumbbell_box_lunge' => 45,
3552             'reverse_dumbbell_crossover_lunge' => 46,
3553             'reverse_dumbbell_diagonal_lunge' => 47,
3554             'reverse_lunge_with_reach_back' => 48,
3555             'weighted_reverse_lunge_with_reach_back' => 49,
3556             'reverse_lunge_with_twist_and_overhead_reach' => 50,
3557             'weighted_reverse_lunge_with_twist_and_overhead_reach' => 51,
3558             'reverse_sliding_box_lunge' => 52,
3559             'weighted_reverse_sliding_box_lunge' => 53,
3560             'reverse_sliding_lunge' => 54,
3561             'weighted_reverse_sliding_lunge' => 55,
3562             'runners_lunge_to_balance' => 56,
3563             'weighted_runners_lunge_to_balance' => 57,
3564             'shifting_side_lunge' => 58,
3565             'side_and_crossover_lunge' => 59,
3566             'weighted_side_and_crossover_lunge' => 60,
3567             'side_lunge' => 61,
3568             'weighted_side_lunge' => 62,
3569             'side_lunge_and_press' => 63,
3570             'side_lunge_jump_off' => 64,
3571             'side_lunge_sweep' => 65,
3572             'weighted_side_lunge_sweep' => 66,
3573             'side_lunge_to_crossover_tap' => 67,
3574             'weighted_side_lunge_to_crossover_tap' => 68,
3575             'side_to_side_lunge_chops' => 69,
3576             'weighted_side_to_side_lunge_chops' => 70,
3577             'siff_jump_lunge' => 71,
3578             'weighted_siff_jump_lunge' => 72,
3579             'single_arm_reverse_lunge_and_press' => 73,
3580             'sliding_lateral_lunge' => 74,
3581             'weighted_sliding_lateral_lunge' => 75,
3582             'walking_barbell_lunge' => 76,
3583             'walking_dumbbell_lunge' => 77,
3584             'walking_lunge' => 78,
3585             'weighted_walking_lunge' => 79,
3586             'wide_grip_overhead_barbell_split_squat' => 80,
3587             },
3588              
3589             'olympic_lift_exercise_name' => +{
3590             '_base_type' => FIT_UINT16,
3591             'barbell_hang_power_clean' => 0,
3592             'barbell_hang_squat_clean' => 1,
3593             'barbell_power_clean' => 2,
3594             'barbell_power_snatch' => 3,
3595             'barbell_squat_clean' => 4,
3596             'clean_and_jerk' => 5,
3597             'barbell_hang_power_snatch' => 6,
3598             'barbell_hang_pull' => 7,
3599             'barbell_high_pull' => 8,
3600             'barbell_snatch' => 9,
3601             'barbell_split_jerk' => 10,
3602             'clean' => 11,
3603             'dumbbell_clean' => 12,
3604             'dumbbell_hang_pull' => 13,
3605             'one_hand_dumbbell_split_snatch' => 14,
3606             'push_jerk' => 15,
3607             'single_arm_dumbbell_snatch' => 16,
3608             'single_arm_hang_snatch' => 17,
3609             'single_arm_kettlebell_snatch' => 18,
3610             'split_jerk' => 19,
3611             'squat_clean_and_jerk' => 20,
3612             },
3613              
3614             'plank_exercise_name' => +{
3615             '_base_type' => FIT_UINT16,
3616             '45_degree_plank' => 0,
3617             'weighted_45_degree_plank' => 1,
3618             '90_degree_static_hold' => 2,
3619             'weighted_90_degree_static_hold' => 3,
3620             'bear_crawl' => 4,
3621             'weighted_bear_crawl' => 5,
3622             'cross_body_mountain_climber' => 6,
3623             'weighted_cross_body_mountain_climber' => 7,
3624             'elbow_plank_pike_jacks' => 8,
3625             'weighted_elbow_plank_pike_jacks' => 9,
3626             'elevated_feet_plank' => 10,
3627             'weighted_elevated_feet_plank' => 11,
3628             'elevator_abs' => 12,
3629             'weighted_elevator_abs' => 13,
3630             'extended_plank' => 14,
3631             'weighted_extended_plank' => 15,
3632             'full_plank_passe_twist' => 16,
3633             'weighted_full_plank_passe_twist' => 17,
3634             'inching_elbow_plank' => 18,
3635             'weighted_inching_elbow_plank' => 19,
3636             'inchworm_to_side_plank' => 20,
3637             'weighted_inchworm_to_side_plank' => 21,
3638             'kneeling_plank' => 22,
3639             'weighted_kneeling_plank' => 23,
3640             'kneeling_side_plank_with_leg_lift' => 24,
3641             'weighted_kneeling_side_plank_with_leg_lift' => 25,
3642             'lateral_roll' => 26,
3643             'weighted_lateral_roll' => 27,
3644             'lying_reverse_plank' => 28,
3645             'weighted_lying_reverse_plank' => 29,
3646             'medicine_ball_mountain_climber' => 30,
3647             'weighted_medicine_ball_mountain_climber' => 31,
3648             'modified_mountain_climber_and_extension' => 32,
3649             'weighted_modified_mountain_climber_and_extension' => 33,
3650             'mountain_climber' => 34,
3651             'weighted_mountain_climber' => 35,
3652             'mountain_climber_on_sliding_discs' => 36,
3653             'weighted_mountain_climber_on_sliding_discs' => 37,
3654             'mountain_climber_with_feet_on_bosu_ball' => 38,
3655             'weighted_mountain_climber_with_feet_on_bosu_ball' => 39,
3656             'mountain_climber_with_hands_on_bench' => 40,
3657             'mountain_climber_with_hands_on_swiss_ball' => 41,
3658             'weighted_mountain_climber_with_hands_on_swiss_ball' => 42,
3659             'plank' => 43,
3660             'plank_jacks_with_feet_on_sliding_discs' => 44,
3661             'weighted_plank_jacks_with_feet_on_sliding_discs' => 45,
3662             'plank_knee_twist' => 46,
3663             'weighted_plank_knee_twist' => 47,
3664             'plank_pike_jumps' => 48,
3665             'weighted_plank_pike_jumps' => 49,
3666             'plank_pikes' => 50,
3667             'weighted_plank_pikes' => 51,
3668             'plank_to_stand_up' => 52,
3669             'weighted_plank_to_stand_up' => 53,
3670             'plank_with_arm_raise' => 54,
3671             'weighted_plank_with_arm_raise' => 55,
3672             'plank_with_knee_to_elbow' => 56,
3673             'weighted_plank_with_knee_to_elbow' => 57,
3674             'plank_with_oblique_crunch' => 58,
3675             'weighted_plank_with_oblique_crunch' => 59,
3676             'plyometric_side_plank' => 60,
3677             'weighted_plyometric_side_plank' => 61,
3678             'rolling_side_plank' => 62,
3679             'weighted_rolling_side_plank' => 63,
3680             'side_kick_plank' => 64,
3681             'weighted_side_kick_plank' => 65,
3682             'side_plank' => 66,
3683             'weighted_side_plank' => 67,
3684             'side_plank_and_row' => 68,
3685             'weighted_side_plank_and_row' => 69,
3686             'side_plank_lift' => 70,
3687             'weighted_side_plank_lift' => 71,
3688             'side_plank_with_elbow_on_bosu_ball' => 72,
3689             'weighted_side_plank_with_elbow_on_bosu_ball' => 73,
3690             'side_plank_with_feet_on_bench' => 74,
3691             'weighted_side_plank_with_feet_on_bench' => 75,
3692             'side_plank_with_knee_circle' => 76,
3693             'weighted_side_plank_with_knee_circle' => 77,
3694             'side_plank_with_knee_tuck' => 78,
3695             'weighted_side_plank_with_knee_tuck' => 79,
3696             'side_plank_with_leg_lift' => 80,
3697             'weighted_side_plank_with_leg_lift' => 81,
3698             'side_plank_with_reach_under' => 82,
3699             'weighted_side_plank_with_reach_under' => 83,
3700             'single_leg_elevated_feet_plank' => 84,
3701             'weighted_single_leg_elevated_feet_plank' => 85,
3702             'single_leg_flex_and_extend' => 86,
3703             'weighted_single_leg_flex_and_extend' => 87,
3704             'single_leg_side_plank' => 88,
3705             'weighted_single_leg_side_plank' => 89,
3706             'spiderman_plank' => 90,
3707             'weighted_spiderman_plank' => 91,
3708             'straight_arm_plank' => 92,
3709             'weighted_straight_arm_plank' => 93,
3710             'straight_arm_plank_with_shoulder_touch' => 94,
3711             'weighted_straight_arm_plank_with_shoulder_touch' => 95,
3712             'swiss_ball_plank' => 96,
3713             'weighted_swiss_ball_plank' => 97,
3714             'swiss_ball_plank_leg_lift' => 98,
3715             'weighted_swiss_ball_plank_leg_lift' => 99,
3716             'swiss_ball_plank_leg_lift_and_hold' => 100,
3717             'swiss_ball_plank_with_feet_on_bench' => 101,
3718             'weighted_swiss_ball_plank_with_feet_on_bench' => 102,
3719             'swiss_ball_prone_jackknife' => 103,
3720             'weighted_swiss_ball_prone_jackknife' => 104,
3721             'swiss_ball_side_plank' => 105,
3722             'weighted_swiss_ball_side_plank' => 106,
3723             'three_way_plank' => 107,
3724             'weighted_three_way_plank' => 108,
3725             'towel_plank_and_knee_in' => 109,
3726             'weighted_towel_plank_and_knee_in' => 110,
3727             't_stabilization' => 111,
3728             'weighted_t_stabilization' => 112,
3729             'turkish_get_up_to_side_plank' => 113,
3730             'weighted_turkish_get_up_to_side_plank' => 114,
3731             'two_point_plank' => 115,
3732             'weighted_two_point_plank' => 116,
3733             'weighted_plank' => 117,
3734             'wide_stance_plank_with_diagonal_arm_lift' => 118,
3735             'weighted_wide_stance_plank_with_diagonal_arm_lift' => 119,
3736             'wide_stance_plank_with_diagonal_leg_lift' => 120,
3737             'weighted_wide_stance_plank_with_diagonal_leg_lift' => 121,
3738             'wide_stance_plank_with_leg_lift' => 122,
3739             'weighted_wide_stance_plank_with_leg_lift' => 123,
3740             'wide_stance_plank_with_opposite_arm_and_leg_lift' => 124,
3741             'weighted_mountain_climber_with_hands_on_bench' => 125,
3742             'weighted_swiss_ball_plank_leg_lift_and_hold' => 126,
3743             'weighted_wide_stance_plank_with_opposite_arm_and_leg_lift' => 127,
3744             'plank_with_feet_on_swiss_ball' => 128,
3745             'side_plank_to_plank_with_reach_under' => 129,
3746             'bridge_with_glute_lower_lift' => 130,
3747             'bridge_one_leg_bridge' => 131,
3748             'plank_with_arm_variations' => 132,
3749             'plank_with_leg_lift' => 133,
3750             'reverse_plank_with_leg_pull' => 134,
3751             },
3752              
3753             'plyo_exercise_name' => +{
3754             '_base_type' => FIT_UINT16,
3755             'alternating_jump_lunge' => 0,
3756             'weighted_alternating_jump_lunge' => 1,
3757             'barbell_jump_squat' => 2,
3758             'body_weight_jump_squat' => 3,
3759             'weighted_jump_squat' => 4,
3760             'cross_knee_strike' => 5,
3761             'weighted_cross_knee_strike' => 6,
3762             'depth_jump' => 7,
3763             'weighted_depth_jump' => 8,
3764             'dumbbell_jump_squat' => 9,
3765             'dumbbell_split_jump' => 10,
3766             'front_knee_strike' => 11,
3767             'weighted_front_knee_strike' => 12,
3768             'high_box_jump' => 13,
3769             'weighted_high_box_jump' => 14,
3770             'isometric_explosive_body_weight_jump_squat' => 15,
3771             'weighted_isometric_explosive_jump_squat' => 16,
3772             'lateral_leap_and_hop' => 17,
3773             'weighted_lateral_leap_and_hop' => 18,
3774             'lateral_plyo_squats' => 19,
3775             'weighted_lateral_plyo_squats' => 20,
3776             'lateral_slide' => 21,
3777             'weighted_lateral_slide' => 22,
3778             'medicine_ball_overhead_throws' => 23,
3779             'medicine_ball_side_throw' => 24,
3780             'medicine_ball_slam' => 25,
3781             'side_to_side_medicine_ball_throws' => 26,
3782             'side_to_side_shuffle_jump' => 27,
3783             'weighted_side_to_side_shuffle_jump' => 28,
3784             'squat_jump_onto_box' => 29,
3785             'weighted_squat_jump_onto_box' => 30,
3786             'squat_jumps_in_and_out' => 31,
3787             'weighted_squat_jumps_in_and_out' => 32,
3788             },
3789              
3790             'pull_up_exercise_name' => +{
3791             '_base_type' => FIT_UINT16,
3792             'banded_pull_ups' => 0,
3793             '30_degree_lat_pulldown' => 1,
3794             'band_assisted_chin_up' => 2,
3795             'close_grip_chin_up' => 3,
3796             'weighted_close_grip_chin_up' => 4,
3797             'close_grip_lat_pulldown' => 5,
3798             'crossover_chin_up' => 6,
3799             'weighted_crossover_chin_up' => 7,
3800             'ez_bar_pullover' => 8,
3801             'hanging_hurdle' => 9,
3802             'weighted_hanging_hurdle' => 10,
3803             'kneeling_lat_pulldown' => 11,
3804             'kneeling_underhand_grip_lat_pulldown' => 12,
3805             'lat_pulldown' => 13,
3806             'mixed_grip_chin_up' => 14,
3807             'weighted_mixed_grip_chin_up' => 15,
3808             'mixed_grip_pull_up' => 16,
3809             'weighted_mixed_grip_pull_up' => 17,
3810             'reverse_grip_pulldown' => 18,
3811             'standing_cable_pullover' => 19,
3812             'straight_arm_pulldown' => 20,
3813             'swiss_ball_ez_bar_pullover' => 21,
3814             'towel_pull_up' => 22,
3815             'weighted_towel_pull_up' => 23,
3816             'weighted_pull_up' => 24,
3817             'wide_grip_lat_pulldown' => 25,
3818             'wide_grip_pull_up' => 26,
3819             'weighted_wide_grip_pull_up' => 27,
3820             'burpee_pull_up' => 28,
3821             'weighted_burpee_pull_up' => 29,
3822             'jumping_pull_ups' => 30,
3823             'weighted_jumping_pull_ups' => 31,
3824             'kipping_pull_up' => 32,
3825             'weighted_kipping_pull_up' => 33,
3826             'l_pull_up' => 34,
3827             'weighted_l_pull_up' => 35,
3828             'suspended_chin_up' => 36,
3829             'weighted_suspended_chin_up' => 37,
3830             'pull_up' => 38,
3831             },
3832              
3833             'push_up_exercise_name' => +{
3834             '_base_type' => FIT_UINT16,
3835             'chest_press_with_band' => 0,
3836             'alternating_staggered_push_up' => 1,
3837             'weighted_alternating_staggered_push_up' => 2,
3838             'alternating_hands_medicine_ball_push_up' => 3,
3839             'weighted_alternating_hands_medicine_ball_push_up' => 4,
3840             'bosu_ball_push_up' => 5,
3841             'weighted_bosu_ball_push_up' => 6,
3842             'clapping_push_up' => 7,
3843             'weighted_clapping_push_up' => 8,
3844             'close_grip_medicine_ball_push_up' => 9,
3845             'weighted_close_grip_medicine_ball_push_up' => 10,
3846             'close_hands_push_up' => 11,
3847             'weighted_close_hands_push_up' => 12,
3848             'decline_push_up' => 13,
3849             'weighted_decline_push_up' => 14,
3850             'diamond_push_up' => 15,
3851             'weighted_diamond_push_up' => 16,
3852             'explosive_crossover_push_up' => 17,
3853             'weighted_explosive_crossover_push_up' => 18,
3854             'explosive_push_up' => 19,
3855             'weighted_explosive_push_up' => 20,
3856             'feet_elevated_side_to_side_push_up' => 21,
3857             'weighted_feet_elevated_side_to_side_push_up' => 22,
3858             'hand_release_push_up' => 23,
3859             'weighted_hand_release_push_up' => 24,
3860             'handstand_push_up' => 25,
3861             'weighted_handstand_push_up' => 26,
3862             'incline_push_up' => 27,
3863             'weighted_incline_push_up' => 28,
3864             'isometric_explosive_push_up' => 29,
3865             'weighted_isometric_explosive_push_up' => 30,
3866             'judo_push_up' => 31,
3867             'weighted_judo_push_up' => 32,
3868             'kneeling_push_up' => 33,
3869             'weighted_kneeling_push_up' => 34,
3870             'medicine_ball_chest_pass' => 35,
3871             'medicine_ball_push_up' => 36,
3872             'weighted_medicine_ball_push_up' => 37,
3873             'one_arm_push_up' => 38,
3874             'weighted_one_arm_push_up' => 39,
3875             'weighted_push_up' => 40,
3876             'push_up_and_row' => 41,
3877             'weighted_push_up_and_row' => 42,
3878             'push_up_plus' => 43,
3879             'weighted_push_up_plus' => 44,
3880             'push_up_with_feet_on_swiss_ball' => 45,
3881             'weighted_push_up_with_feet_on_swiss_ball' => 46,
3882             'push_up_with_one_hand_on_medicine_ball' => 47,
3883             'weighted_push_up_with_one_hand_on_medicine_ball' => 48,
3884             'shoulder_push_up' => 49,
3885             'weighted_shoulder_push_up' => 50,
3886             'single_arm_medicine_ball_push_up' => 51,
3887             'weighted_single_arm_medicine_ball_push_up' => 52,
3888             'spiderman_push_up' => 53,
3889             'weighted_spiderman_push_up' => 54,
3890             'stacked_feet_push_up' => 55,
3891             'weighted_stacked_feet_push_up' => 56,
3892             'staggered_hands_push_up' => 57,
3893             'weighted_staggered_hands_push_up' => 58,
3894             'suspended_push_up' => 59,
3895             'weighted_suspended_push_up' => 60,
3896             'swiss_ball_push_up' => 61,
3897             'weighted_swiss_ball_push_up' => 62,
3898             'swiss_ball_push_up_plus' => 63,
3899             'weighted_swiss_ball_push_up_plus' => 64,
3900             't_push_up' => 65,
3901             'weighted_t_push_up' => 66,
3902             'triple_stop_push_up' => 67,
3903             'weighted_triple_stop_push_up' => 68,
3904             'wide_hands_push_up' => 69,
3905             'weighted_wide_hands_push_up' => 70,
3906             'parallette_handstand_push_up' => 71,
3907             'weighted_parallette_handstand_push_up' => 72,
3908             'ring_handstand_push_up' => 73,
3909             'weighted_ring_handstand_push_up' => 74,
3910             'ring_push_up' => 75,
3911             'weighted_ring_push_up' => 76,
3912             'push_up' => 77,
3913             'pilates_pushup' => 78,
3914             },
3915              
3916             'row_exercise_name' => +{
3917             '_base_type' => FIT_UINT16,
3918             'barbell_straight_leg_deadlift_to_row' => 0,
3919             'cable_row_standing' => 1,
3920             'dumbbell_row' => 2,
3921             'elevated_feet_inverted_row' => 3,
3922             'weighted_elevated_feet_inverted_row' => 4,
3923             'face_pull' => 5,
3924             'face_pull_with_external_rotation' => 6,
3925             'inverted_row_with_feet_on_swiss_ball' => 7,
3926             'weighted_inverted_row_with_feet_on_swiss_ball' => 8,
3927             'kettlebell_row' => 9,
3928             'modified_inverted_row' => 10,
3929             'weighted_modified_inverted_row' => 11,
3930             'neutral_grip_alternating_dumbbell_row' => 12,
3931             'one_arm_bent_over_row' => 13,
3932             'one_legged_dumbbell_row' => 14,
3933             'renegade_row' => 15,
3934             'reverse_grip_barbell_row' => 16,
3935             'rope_handle_cable_row' => 17,
3936             'seated_cable_row' => 18,
3937             'seated_dumbbell_row' => 19,
3938             'single_arm_cable_row' => 20,
3939             'single_arm_cable_row_and_rotation' => 21,
3940             'single_arm_inverted_row' => 22,
3941             'weighted_single_arm_inverted_row' => 23,
3942             'single_arm_neutral_grip_dumbbell_row' => 24,
3943             'single_arm_neutral_grip_dumbbell_row_and_rotation' => 25,
3944             'suspended_inverted_row' => 26,
3945             'weighted_suspended_inverted_row' => 27,
3946             't_bar_row' => 28,
3947             'towel_grip_inverted_row' => 29,
3948             'weighted_towel_grip_inverted_row' => 30,
3949             'underhand_grip_cable_row' => 31,
3950             'v_grip_cable_row' => 32,
3951             'wide_grip_seated_cable_row' => 33,
3952             },
3953              
3954             'shoulder_press_exercise_name' => +{
3955             '_base_type' => FIT_UINT16,
3956             'alternating_dumbbell_shoulder_press' => 0,
3957             'arnold_press' => 1,
3958             'barbell_front_squat_to_push_press' => 2,
3959             'barbell_push_press' => 3,
3960             'barbell_shoulder_press' => 4,
3961             'dead_curl_press' => 5,
3962             'dumbbell_alternating_shoulder_press_and_twist' => 6,
3963             'dumbbell_hammer_curl_to_lunge_to_press' => 7,
3964             'dumbbell_push_press' => 8,
3965             'floor_inverted_shoulder_press' => 9,
3966             'weighted_floor_inverted_shoulder_press' => 10,
3967             'inverted_shoulder_press' => 11,
3968             'weighted_inverted_shoulder_press' => 12,
3969             'one_arm_push_press' => 13,
3970             'overhead_barbell_press' => 14,
3971             'overhead_dumbbell_press' => 15,
3972             'seated_barbell_shoulder_press' => 16,
3973             'seated_dumbbell_shoulder_press' => 17,
3974             'single_arm_dumbbell_shoulder_press' => 18,
3975             'single_arm_step_up_and_press' => 19,
3976             'smith_machine_overhead_press' => 20,
3977             'split_stance_hammer_curl_to_press' => 21,
3978             'swiss_ball_dumbbell_shoulder_press' => 22,
3979             'weight_plate_front_raise' => 23,
3980             },
3981              
3982             'shoulder_stability_exercise_name' => +{
3983             '_base_type' => FIT_UINT16,
3984             '90_degree_cable_external_rotation' => 0,
3985             'band_external_rotation' => 1,
3986             'band_internal_rotation' => 2,
3987             'bent_arm_lateral_raise_and_external_rotation' => 3,
3988             'cable_external_rotation' => 4,
3989             'dumbbell_face_pull_with_external_rotation' => 5,
3990             'floor_i_raise' => 6,
3991             'weighted_floor_i_raise' => 7,
3992             'floor_t_raise' => 8,
3993             'weighted_floor_t_raise' => 9,
3994             'floor_y_raise' => 10,
3995             'weighted_floor_y_raise' => 11,
3996             'incline_i_raise' => 12,
3997             'weighted_incline_i_raise' => 13,
3998             'incline_l_raise' => 14,
3999             'weighted_incline_l_raise' => 15,
4000             'incline_t_raise' => 16,
4001             'weighted_incline_t_raise' => 17,
4002             'incline_w_raise' => 18,
4003             'weighted_incline_w_raise' => 19,
4004             'incline_y_raise' => 20,
4005             'weighted_incline_y_raise' => 21,
4006             'lying_external_rotation' => 22,
4007             'seated_dumbbell_external_rotation' => 23,
4008             'standing_l_raise' => 24,
4009             'swiss_ball_i_raise' => 25,
4010             'weighted_swiss_ball_i_raise' => 26,
4011             'swiss_ball_t_raise' => 27,
4012             'weighted_swiss_ball_t_raise' => 28,
4013             'swiss_ball_w_raise' => 29,
4014             'weighted_swiss_ball_w_raise' => 30,
4015             'swiss_ball_y_raise' => 31,
4016             'weighted_swiss_ball_y_raise' => 32,
4017             },
4018              
4019             'shrug_exercise_name' => +{
4020             '_base_type' => FIT_UINT16,
4021             'barbell_jump_shrug' => 0,
4022             'barbell_shrug' => 1,
4023             'barbell_upright_row' => 2,
4024             'behind_the_back_smith_machine_shrug' => 3,
4025             'dumbbell_jump_shrug' => 4,
4026             'dumbbell_shrug' => 5,
4027             'dumbbell_upright_row' => 6,
4028             'incline_dumbbell_shrug' => 7,
4029             'overhead_barbell_shrug' => 8,
4030             'overhead_dumbbell_shrug' => 9,
4031             'scaption_and_shrug' => 10,
4032             'scapular_retraction' => 11,
4033             'serratus_chair_shrug' => 12,
4034             'weighted_serratus_chair_shrug' => 13,
4035             'serratus_shrug' => 14,
4036             'weighted_serratus_shrug' => 15,
4037             'wide_grip_jump_shrug' => 16,
4038             },
4039              
4040             'sit_up_exercise_name' => +{
4041             '_base_type' => FIT_UINT16,
4042             'alternating_sit_up' => 0,
4043             'weighted_alternating_sit_up' => 1,
4044             'bent_knee_v_up' => 2,
4045             'weighted_bent_knee_v_up' => 3,
4046             'butterfly_sit_up' => 4,
4047             'weighted_butterfly_situp' => 5,
4048             'cross_punch_roll_up' => 6,
4049             'weighted_cross_punch_roll_up' => 7,
4050             'crossed_arms_sit_up' => 8,
4051             'weighted_crossed_arms_sit_up' => 9,
4052             'get_up_sit_up' => 10,
4053             'weighted_get_up_sit_up' => 11,
4054             'hovering_sit_up' => 12,
4055             'weighted_hovering_sit_up' => 13,
4056             'kettlebell_sit_up' => 14,
4057             'medicine_ball_alternating_v_up' => 15,
4058             'medicine_ball_sit_up' => 16,
4059             'medicine_ball_v_up' => 17,
4060             'modified_sit_up' => 18,
4061             'negative_sit_up' => 19,
4062             'one_arm_full_sit_up' => 20,
4063             'reclining_circle' => 21,
4064             'weighted_reclining_circle' => 22,
4065             'reverse_curl_up' => 23,
4066             'weighted_reverse_curl_up' => 24,
4067             'single_leg_swiss_ball_jackknife' => 25,
4068             'weighted_single_leg_swiss_ball_jackknife' => 26,
4069             'the_teaser' => 27,
4070             'the_teaser_weighted' => 28,
4071             'three_part_roll_down' => 29,
4072             'weighted_three_part_roll_down' => 30,
4073             'v_up' => 31,
4074             'weighted_v_up' => 32,
4075             'weighted_russian_twist_on_swiss_ball' => 33,
4076             'weighted_sit_up' => 34,
4077             'x_abs' => 35,
4078             'weighted_x_abs' => 36,
4079             'sit_up' => 37,
4080             },
4081              
4082             'squat_exercise_name' => +{
4083             '_base_type' => FIT_UINT16,
4084             'leg_press' => 0,
4085             'back_squat_with_body_bar' => 1,
4086             'back_squats' => 2,
4087             'weighted_back_squats' => 3,
4088             'balancing_squat' => 4,
4089             'weighted_balancing_squat' => 5,
4090             'barbell_back_squat' => 6,
4091             'barbell_box_squat' => 7,
4092             'barbell_front_squat' => 8,
4093             'barbell_hack_squat' => 9,
4094             'barbell_hang_squat_snatch' => 10,
4095             'barbell_lateral_step_up' => 11,
4096             'barbell_quarter_squat' => 12,
4097             'barbell_siff_squat' => 13,
4098             'barbell_squat_snatch' => 14,
4099             'barbell_squat_with_heels_raised' => 15,
4100             'barbell_stepover' => 16,
4101             'barbell_step_up' => 17,
4102             'bench_squat_with_rotational_chop' => 18,
4103             'weighted_bench_squat_with_rotational_chop' => 19,
4104             'body_weight_wall_squat' => 20,
4105             'weighted_wall_squat' => 21,
4106             'box_step_squat' => 22,
4107             'weighted_box_step_squat' => 23,
4108             'braced_squat' => 24,
4109             'crossed_arm_barbell_front_squat' => 25,
4110             'crossover_dumbbell_step_up' => 26,
4111             'dumbbell_front_squat' => 27,
4112             'dumbbell_split_squat' => 28,
4113             'dumbbell_squat' => 29,
4114             'dumbbell_squat_clean' => 30,
4115             'dumbbell_stepover' => 31,
4116             'dumbbell_step_up' => 32,
4117             'elevated_single_leg_squat' => 33,
4118             'weighted_elevated_single_leg_squat' => 34,
4119             'figure_four_squats' => 35,
4120             'weighted_figure_four_squats' => 36,
4121             'goblet_squat' => 37,
4122             'kettlebell_squat' => 38,
4123             'kettlebell_swing_overhead' => 39,
4124             'kettlebell_swing_with_flip_to_squat' => 40,
4125             'lateral_dumbbell_step_up' => 41,
4126             'one_legged_squat' => 42,
4127             'overhead_dumbbell_squat' => 43,
4128             'overhead_squat' => 44,
4129             'partial_single_leg_squat' => 45,
4130             'weighted_partial_single_leg_squat' => 46,
4131             'pistol_squat' => 47,
4132             'weighted_pistol_squat' => 48,
4133             'plie_slides' => 49,
4134             'weighted_plie_slides' => 50,
4135             'plie_squat' => 51,
4136             'weighted_plie_squat' => 52,
4137             'prisoner_squat' => 53,
4138             'weighted_prisoner_squat' => 54,
4139             'single_leg_bench_get_up' => 55,
4140             'weighted_single_leg_bench_get_up' => 56,
4141             'single_leg_bench_squat' => 57,
4142             'weighted_single_leg_bench_squat' => 58,
4143             'single_leg_squat_on_swiss_ball' => 59,
4144             'weighted_single_leg_squat_on_swiss_ball' => 60,
4145             'squat' => 61,
4146             'weighted_squat' => 62,
4147             'squats_with_band' => 63,
4148             'staggered_squat' => 64,
4149             'weighted_staggered_squat' => 65,
4150             'step_up' => 66,
4151             'weighted_step_up' => 67,
4152             'suitcase_squats' => 68,
4153             'sumo_squat' => 69,
4154             'sumo_squat_slide_in' => 70,
4155             'weighted_sumo_squat_slide_in' => 71,
4156             'sumo_squat_to_high_pull' => 72,
4157             'sumo_squat_to_stand' => 73,
4158             'weighted_sumo_squat_to_stand' => 74,
4159             'sumo_squat_with_rotation' => 75,
4160             'weighted_sumo_squat_with_rotation' => 76,
4161             'swiss_ball_body_weight_wall_squat' => 77,
4162             'weighted_swiss_ball_wall_squat' => 78,
4163             'thrusters' => 79,
4164             'uneven_squat' => 80,
4165             'weighted_uneven_squat' => 81,
4166             'waist_slimming_squat' => 82,
4167             'wall_ball' => 83,
4168             'wide_stance_barbell_squat' => 84,
4169             'wide_stance_goblet_squat' => 85,
4170             'zercher_squat' => 86,
4171             'kbs_overhead' => 87, # Deprecated do not use
4172             'squat_and_side_kick' => 88,
4173             'squat_jumps_in_n_out' => 89,
4174             'pilates_plie_squats_parallel_turned_out_flat_and_heels' => 90,
4175             'releve_straight_leg_and_knee_bent_with_one_leg_variation' => 91,
4176             },
4177              
4178             'total_body_exercise_name' => +{
4179             '_base_type' => FIT_UINT16,
4180             'burpee' => 0,
4181             'weighted_burpee' => 1,
4182             'burpee_box_jump' => 2,
4183             'weighted_burpee_box_jump' => 3,
4184             'high_pull_burpee' => 4,
4185             'man_makers' => 5,
4186             'one_arm_burpee' => 6,
4187             'squat_thrusts' => 7,
4188             'weighted_squat_thrusts' => 8,
4189             'squat_plank_push_up' => 9,
4190             'weighted_squat_plank_push_up' => 10,
4191             'standing_t_rotation_balance' => 11,
4192             'weighted_standing_t_rotation_balance' => 12,
4193             },
4194              
4195             'triceps_extension_exercise_name' => +{
4196             '_base_type' => FIT_UINT16,
4197             'bench_dip' => 0,
4198             'weighted_bench_dip' => 1,
4199             'body_weight_dip' => 2,
4200             'cable_kickback' => 3,
4201             'cable_lying_triceps_extension' => 4,
4202             'cable_overhead_triceps_extension' => 5,
4203             'dumbbell_kickback' => 6,
4204             'dumbbell_lying_triceps_extension' => 7,
4205             'ez_bar_overhead_triceps_extension' => 8,
4206             'incline_dip' => 9,
4207             'weighted_incline_dip' => 10,
4208             'incline_ez_bar_lying_triceps_extension' => 11,
4209             'lying_dumbbell_pullover_to_extension' => 12,
4210             'lying_ez_bar_triceps_extension' => 13,
4211             'lying_triceps_extension_to_close_grip_bench_press' => 14,
4212             'overhead_dumbbell_triceps_extension' => 15,
4213             'reclining_triceps_press' => 16,
4214             'reverse_grip_pressdown' => 17,
4215             'reverse_grip_triceps_pressdown' => 18,
4216             'rope_pressdown' => 19,
4217             'seated_barbell_overhead_triceps_extension' => 20,
4218             'seated_dumbbell_overhead_triceps_extension' => 21,
4219             'seated_ez_bar_overhead_triceps_extension' => 22,
4220             'seated_single_arm_overhead_dumbbell_extension' => 23,
4221             'single_arm_dumbbell_overhead_triceps_extension' => 24,
4222             'single_dumbbell_seated_overhead_triceps_extension' => 25,
4223             'single_leg_bench_dip_and_kick' => 26,
4224             'weighted_single_leg_bench_dip_and_kick' => 27,
4225             'single_leg_dip' => 28,
4226             'weighted_single_leg_dip' => 29,
4227             'static_lying_triceps_extension' => 30,
4228             'suspended_dip' => 31,
4229             'weighted_suspended_dip' => 32,
4230             'swiss_ball_dumbbell_lying_triceps_extension' => 33,
4231             'swiss_ball_ez_bar_lying_triceps_extension' => 34,
4232             'swiss_ball_ez_bar_overhead_triceps_extension' => 35,
4233             'tabletop_dip' => 36,
4234             'weighted_tabletop_dip' => 37,
4235             'triceps_extension_on_floor' => 38,
4236             'triceps_pressdown' => 39,
4237             'weighted_dip' => 40,
4238             },
4239              
4240             'warm_up_exercise_name' => +{
4241             '_base_type' => FIT_UINT16,
4242             'quadruped_rocking' => 0,
4243             'neck_tilts' => 1,
4244             'ankle_circles' => 2,
4245             'ankle_dorsiflexion_with_band' => 3,
4246             'ankle_internal_rotation' => 4,
4247             'arm_circles' => 5,
4248             'bent_over_reach_to_sky' => 6,
4249             'cat_camel' => 7,
4250             'elbow_to_foot_lunge' => 8,
4251             'forward_and_backward_leg_swings' => 9,
4252             'groiners' => 10,
4253             'inverted_hamstring_stretch' => 11,
4254             'lateral_duck_under' => 12,
4255             'neck_rotations' => 13,
4256             'opposite_arm_and_leg_balance' => 14,
4257             'reach_roll_and_lift' => 15,
4258             'scorpion' => 16, # Deprecated do not use
4259             'shoulder_circles' => 17,
4260             'side_to_side_leg_swings' => 18,
4261             'sleeper_stretch' => 19,
4262             'slide_out' => 20,
4263             'swiss_ball_hip_crossover' => 21,
4264             'swiss_ball_reach_roll_and_lift' => 22,
4265             'swiss_ball_windshield_wipers' => 23,
4266             'thoracic_rotation' => 24,
4267             'walking_high_kicks' => 25,
4268             'walking_high_knees' => 26,
4269             'walking_knee_hugs' => 27,
4270             'walking_leg_cradles' => 28,
4271             'walkout' => 29,
4272             'walkout_from_push_up_position' => 30,
4273             },
4274              
4275             'run_exercise_name' => +{
4276             '_base_type' => FIT_UINT16,
4277             'run' => 0,
4278             'walk' => 1,
4279             'jog' => 2,
4280             'sprint' => 3,
4281             },
4282              
4283             'water_type' => +{
4284             '_base_type' => FIT_ENUM,
4285             'fresh' => 0,
4286             'salt' => 1,
4287             'en13319' => 2,
4288             'custom' => 3,
4289             },
4290              
4291             'tissue_model_type' => +{
4292             '_base_type' => FIT_ENUM,
4293             'zhl_16c' => 0, # Buhlmann's decompression algorithm, version C
4294             },
4295              
4296             'dive_gas_status' => +{
4297             '_base_type' => FIT_ENUM,
4298             'disabled' => 0,
4299             'enabled' => 1,
4300             'backup_only' => 2,
4301             },
4302              
4303             'dive_alert' => +{
4304             '_base_type' => FIT_ENUM,
4305             'ndl_reached' => 0,
4306             'gas_switch_prompted' => 1,
4307             'near_surface' => 2,
4308             'approaching_ndl' => 3,
4309             'po2_warn' => 4,
4310             'po2_crit_high' => 5,
4311             'po2_crit_low' => 6,
4312             'time_alert' => 7,
4313             'depth_alert' => 8,
4314             'deco_ceiling_broken' => 9,
4315             'deco_complete' => 10,
4316             'safety_stop_broken' => 11,
4317             'safety_stop_complete' => 12,
4318             'cns_warning' => 13,
4319             'cns_critical' => 14,
4320             'otu_warning' => 15,
4321             'otu_critical' => 16,
4322             'ascent_critical' => 17,
4323             'alert_dismissed_by_key' => 18,
4324             'alert_dismissed_by_timeout' => 19,
4325             'battery_low' => 20,
4326             'battery_critical' => 21,
4327             'safety_stop_started' => 22,
4328             'approaching_first_deco_stop' => 23,
4329             'setpoint_switch_auto_low' => 24,
4330             'setpoint_switch_auto_high' => 25,
4331             'setpoint_switch_manual_low' => 26,
4332             'setpoint_switch_manual_high' => 27,
4333             'auto_setpoint_switch_ignored' => 28,
4334             'switched_to_open_circuit' => 29,
4335             'switched_to_closed_circuit' => 30,
4336             'tank_battery_low' => 32,
4337             'po2_ccr_dil_low' => 33, # ccr diluent has low po2
4338             'deco_stop_cleared' => 34, # a deco stop has been cleared
4339             'apnea_neutral_buoyancy' => 35, # Target Depth Apnea Alarm triggered
4340             'apnea_target_depth' => 36, # Neutral Buoyance Apnea Alarm triggered
4341             'apnea_surface' => 37, # Surface Apnea Alarm triggered
4342             'apnea_high_speed' => 38, # High Speed Apnea Alarm triggered
4343             'apnea_low_speed' => 39, # Low Speed Apnea Alarm triggered
4344             },
4345              
4346             'dive_alarm_type' => +{
4347             '_base_type' => FIT_ENUM,
4348             'depth' => 0,
4349             'time' => 1,
4350             'speed' => 2, # Alarm when a certain ascent or descent rate is exceeded
4351             },
4352              
4353             'dive_backlight_mode' => +{
4354             '_base_type' => FIT_ENUM,
4355             'at_depth' => 0,
4356             'always_on' => 1,
4357             },
4358              
4359             'ccr_setpoint_switch_mode' => +{
4360             '_base_type' => FIT_ENUM,
4361             'manual' => 0, # User switches setpoints manually
4362             'automatic' => 1, # Switch automatically based on depth
4363             },
4364              
4365             'dive_gas_mode' => +{
4366             '_base_type' => FIT_ENUM,
4367             'open_circuit' => 0,
4368             'closed_circuit_diluent' => 1,
4369             },
4370              
4371             'favero_product' => +{
4372             '_base_type' => FIT_UINT16,
4373             'assioma_uno' => 10,
4374             'assioma_duo' => 12,
4375             },
4376              
4377             'split_type' => +{
4378             '_base_type' => FIT_ENUM,
4379             'ascent_split' => 1,
4380             'descent_split' => 2,
4381             'interval_active' => 3,
4382             'interval_rest' => 4,
4383             'interval_warmup' => 5,
4384             'interval_cooldown' => 6,
4385             'interval_recovery' => 7,
4386             'interval_other' => 8,
4387             'climb_active' => 9,
4388             'climb_rest' => 10,
4389             'surf_active' => 11,
4390             'run_active' => 12,
4391             'run_rest' => 13,
4392             'workout_round' => 14,
4393             'rwd_run' => 17, # run/walk detection running
4394             'rwd_walk' => 18, # run/walk detection walking
4395             'windsurf_active' => 21,
4396             'rwd_stand' => 22, # run/walk detection standing
4397             'transition' => 23, # Marks the time going from ascent_split to descent_split/used in backcountry ski
4398             'ski_lift_split' => 28,
4399             'ski_run_split' => 29,
4400             },
4401              
4402             'climb_pro_event' => +{
4403             '_base_type' => FIT_ENUM,
4404             'approach' => 0,
4405             'start' => 1,
4406             'complete' => 2,
4407             },
4408              
4409             'gas_consumption_rate_type' => +{
4410             '_base_type' => FIT_ENUM,
4411             'pressure_sac' => 0, # Pressure-based Surface Air Consumption
4412             'volume_sac' => 1, # Volumetric Surface Air Consumption
4413             'rmv' => 2, # Respiratory Minute Volume
4414             },
4415              
4416             'tap_sensitivity' => +{
4417             '_base_type' => FIT_ENUM,
4418             'high' => 0,
4419             'medium' => 1,
4420             'low' => 2,
4421             },
4422              
4423             'radar_threat_level_type' => +{
4424             '_base_type' => FIT_ENUM,
4425             'threat_unknown' => 0,
4426             'threat_none' => 1,
4427             'threat_approaching' => 2,
4428             'threat_approaching_fast' => 3,
4429             },
4430              
4431             'no_fly_time_mode' => +{
4432             '_base_type' => FIT_ENUM,
4433             'standard' => 0, # Standard Diver Alert Network no-fly guidance
4434             'flat_24_hours' => 1, # Flat 24 hour no-fly guidance
4435             },
4436              
4437             );
4438              
4439             for my $typenam (keys %named_type) { # if a type was _moved_to, copy the new href to $named_type{$typenam}
4440             my $typedesc = $named_type{$typenam};
4441             if (defined $typedesc->{_moved_to} and $typedesc->{_moved_to} ne '') {
4442             if ($typenam ne 'device_type') { $DB::single=1 } # want to know if it applies to others
4443             my $to = $named_type{$typedesc->{_moved_to}};
4444             $named_type{$typenam} = {%$to} if ref $to eq 'HASH'
4445             }
4446             }
4447             while (my ($typenam, $typedesc) = each %named_type) {
4448             # copy and flip key/value pairs of all hrefs in %named_type (except _base_type, _mask, …)
4449             for my $name (grep {!/^_/} keys %$typedesc) {
4450             $typedesc->{$typedesc->{$name}} = $name
4451             }
4452             }
4453              
4454             my %msgtype_by_name = (
4455             'file_id' => +{ # begins === Common messages === section
4456             0 => +{'name' => 'type', 'type_name' => 'file'},
4457             1 => +{'name' => 'manufacturer', 'type_name' => 'manufacturer'},
4458              
4459             2 => +{
4460             'name' => 'product',
4461              
4462             'switch' => +{
4463             '_by' => 'manufacturer',
4464             'garmin' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4465             'dynastream' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4466             'dynastream_oem' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4467             'favero_electronics' => +{'name' => 'favero_product', 'type_name' => 'favero_product'},
4468             'tacx' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4469             },
4470             },
4471              
4472             3 => +{'name' => 'serial_number'},
4473             4 => +{'name' => 'time_created', 'type_name' => 'date_time'},
4474             5 => +{'name' => 'number'},
4475             7 => +{'name' => 'unknown7'}, # unknown UINT32
4476             8 => +{'name' => 'product_name'},
4477             },
4478              
4479             'file_creator' => +{
4480             0 => +{'name' => 'software_version'},
4481             1 => +{'name' => 'hardware_version'},
4482             },
4483              
4484             'timestamp_correlation' => +{
4485             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
4486             0 => +{'name' => 'fractional_timestamp', 'scale' => 32768, 'unit' => 's'},
4487             1 => +{'name' => 'system_timestamp', 'type_name' => 'date_time'},
4488             2 => +{'name' => 'fractional_system_timestamp', 'scale' => 32768, 'unit' => 's'},
4489             3 => +{'name' => 'local_timestamp', 'type_name' => 'local_date_time'},
4490             4 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
4491             5 => +{'name' => 'system_timestamp_ms', 'unit' => 'ms'},
4492             },
4493              
4494             'software' => +{ # begins === Device file messages === section
4495             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4496             3 => +{'name' => 'version', 'scale' => 100},
4497             5 => +{'name' => 'part_number'},
4498             },
4499              
4500             'slave_device' => +{
4501             0 => +{'name' => 'manufacturer', 'type_name' => 'manufacturer'},
4502              
4503             1 => +{
4504             'name' => 'product',
4505              
4506             'switch' => +{
4507             '_by' => 'manufacturer',
4508             'garmin' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4509             'dynastream' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4510             'dynastream_oem' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4511             'favero_electronics' => +{'name' => 'favero_product', 'type_name' => 'favero_product'},
4512             'tacx' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
4513             },
4514             },
4515             },
4516              
4517             'capabilities' => +{
4518             0 => +{'name' => 'languages'},
4519             1 => +{'name' => 'sports', 'type_name' => 'sport_bits_0'},
4520             21 => +{'name' => 'workouts_supported', 'type_name' => 'workout_capabilities'},
4521             22 => +{'name' => 'unknown22'}, # unknown ENUM
4522             23 => +{'name' => 'connectivity_supported', 'type_name' => 'connectivity_capabilities'},
4523             24 => +{'name' => 'unknown24'}, # unknown ENUM
4524             25 => +{'name' => 'unknown25'}, # unknown UINT32Z
4525             },
4526              
4527             'file_capabilities' => +{
4528             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4529             0 => +{'name' => 'type', 'type_name' => 'file'},
4530             1 => +{'name' => 'flags', 'type_name' => 'file_flags'},
4531             2 => +{'name' => 'directory'},
4532             3 => +{'name' => 'max_count'},
4533             4 => +{'name' => 'max_size', 'unit' => 'bytes'},
4534             },
4535              
4536             'mesg_capabilities' => +{
4537             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4538             0 => +{'name' => 'file', 'type_name' => 'file'},
4539             1 => +{'name' => 'mesg_num', 'type_name' => 'mesg_num'},
4540             2 => +{'name' => 'count_type', 'type_name' => 'mesg_count'},
4541              
4542             3 => +{
4543             'name' => 'count',
4544              
4545             'switch' => +{
4546             '_by' => 'count_type',
4547             'num_per_file' => +{'name' => 'num_per_file'},
4548             'max_per_file' => +{'name' => 'max_per_file'},
4549             'max_per_file_type' => +{'name' => 'max_per_file_type'},
4550             },
4551             },
4552             },
4553              
4554             'field_capabilities' => +{
4555             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4556             0 => +{'name' => 'file', 'type_name' => 'file'},
4557             1 => +{'name' => 'mesg_num', 'type_name' => 'mesg_num'},
4558             2 => +{'name' => 'field_num'},
4559             3 => +{'name' => 'count'},
4560             4 => +{'name' => 'bits'}, # not present? PATJOL: I don't see this one in the profile
4561             },
4562              
4563             'device_settings' => +{ # begins === Settings file messages === section
4564             0 => +{'name' => 'active_time_zone'},
4565             1 => +{'name' => 'utc_offset'},
4566             2 => +{'name' => 'time_offset', 'unit' => 's'},
4567             3 => +{'name' => 'unknown3'}, # unknown ENUM
4568             4 => +{'name' => 'time_mode', 'type_name' => 'time_mode'},
4569             5 => +{'name' => 'time_zone_offset', 'scale' => 4, 'unit' => 'hr'},
4570             10 => +{'name' => 'unknown10'}, # unknown ENUM
4571             11 => +{'name' => 'unknown11'}, # unknown ENUM
4572             12 => +{'name' => 'backlight_mode', 'type_name' => 'backlight_mode'},
4573             13 => +{'name' => 'unknown13'}, # unknown UINT8
4574             14 => +{'name' => 'unknown14'}, # unknown UINT8
4575             15 => +{'name' => 'unknown15'}, # unknown UINT8
4576             16 => +{'name' => 'unknown16'}, # unknown ENUM
4577             17 => +{'name' => 'unknown17'}, # unknown ENUM
4578             18 => +{'name' => 'unknown18'}, # unknown ENUM
4579             21 => +{'name' => 'unknown21'}, # unknown ENUM
4580             22 => +{'name' => 'unknown22'}, # unknown ENUM
4581             26 => +{'name' => 'unknown26'}, # unknown ENUM
4582             27 => +{'name' => 'unknown27'}, # unknown ENUM
4583             29 => +{'name' => 'unknown29'}, # unknown ENUM
4584             33 => +{'name' => 'unknown33'}, # unknown UNIT8
4585             36 => +{'name' => 'activity_tracker_enabled', 'type_name' => 'bool'},
4586             38 => +{'name' => 'unknown38'}, # unknown ENUM
4587             39 => +{'name' => 'clock_time', 'type_name' => 'date_time'},
4588             40 => +{'name' => 'pages_enabled'},
4589             41 => +{'name' => 'unknown41'}, # unknown ENUM
4590             46 => +{'name' => 'move_alert_enabled', 'type_name' => 'bool'},
4591             47 => +{'name' => 'date_mode', 'type_name' => 'date_mode'},
4592             48 => +{'name' => 'unknown48'}, # unknown ENUM
4593             49 => +{'name' => 'unknown49'}, # unknown UINT16
4594             52 => +{'name' => 'unknown52'}, # unknown ENUM
4595             53 => +{'name' => 'unknown53'}, # unknown ENUM
4596             54 => +{'name' => 'unknown54'}, # unknown ENUM
4597             55 => +{'name' => 'display_orientation', 'type_name' => 'display_orientation'},
4598             56 => +{'name' => 'mounting_side', 'type_name' => 'side'},
4599             57 => +{'name' => 'default_page'},
4600             58 => +{'name' => 'autosync_min_steps', 'unit' => 'steps'},
4601             59 => +{'name' => 'autosync_min_time', 'unit' => 'minutes'},
4602             75 => +{'name' => 'unknown75'}, # unknown ENUM
4603             80 => +{'name' => 'lactate_threshold_autodetect_enabled', 'type_name' => 'bool'},
4604             85 => +{'name' => 'unknown85'}, # unknown ENUM
4605             86 => +{'name' => 'ble_auto_upload_enabled', 'type_name' => 'bool'},
4606             89 => +{'name' => 'auto_sync_frequency', 'type_name' => 'auto_sync_frequency'},
4607             90 => +{'name' => 'auto_activity_detect', 'type_name' => 'auto_activity_detect'},
4608             94 => +{'name' => 'number_of_screens'},
4609             95 => +{'name' => 'smart_notification_display_orientation', 'type_name' => 'display_orientation'},
4610             97 => +{'name' => 'unknown97'}, # unknown UINT8Z
4611             98 => +{'name' => 'unknown98'}, # unknown ENUM
4612             103 => +{'name' => 'unknown103'}, # unknown ENUM
4613             134 => +{'name' => 'tap_interface', 'type_name' => 'switch'},
4614             174 => +{'name' => 'tap_sensitivity', 'type_name' => 'tap_sensitivity'},
4615             },
4616              
4617             'user_profile' => +{
4618             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4619             0 => +{'name' => 'friendly_name'},
4620             1 => +{'name' => 'gender', 'type_name' => 'gender'},
4621             2 => +{'name' => 'age', 'unit' => 'years'},
4622             3 => +{'name' => 'height', scale => 100, 'unit' => 'm'},
4623             4 => +{'name' => 'weight', scale => 10, 'unit' => 'kg'},
4624             5 => +{'name' => 'language', 'type_name' => 'language'},
4625             6 => +{'name' => 'elev_setting', 'type_name' => 'display_measure'},
4626             7 => +{'name' => 'weight_setting', 'type_name' => 'display_measure'},
4627             8 => +{'name' => 'resting_heart_rate', 'unit' => 'bpm'},
4628             9 => +{'name' => 'default_max_running_heart_rate', 'unit' => 'bpm'},
4629             10 => +{'name' => 'default_max_biking_heart_rate', 'unit' => 'bpm'},
4630             11 => +{'name' => 'default_max_heart_rate', 'unit' => 'bpm'},
4631             12 => +{'name' => 'hr_setting', 'type_name' => 'display_heart'},
4632             13 => +{'name' => 'speed_setting', 'type_name' => 'display_measure'},
4633             14 => +{'name' => 'dist_setting', 'type_name' => 'display_measure'},
4634             16 => +{'name' => 'power_setting', 'type_name' => 'display_power'},
4635             17 => +{'name' => 'activity_class', 'type_name' => 'activity_class'},
4636             18 => +{'name' => 'position_setting', 'type_name' => 'display_position'},
4637             21 => +{'name' => 'temperature_setting', 'type_name' => 'display_measure'},
4638             22 => +{'name' => 'local_id', 'type_name' => 'user_local_id'},
4639             23 => +{'name' => 'global_id'},
4640             24 => +{'name' => 'unknown24'}, # unknown UINT8
4641             28 => +{'name' => 'wake_time', 'type_name' => 'localtime_into_day'},
4642             29 => +{'name' => 'sleep_time', 'type_name' => 'localtime_into_day'},
4643             30 => +{'name' => 'height_setting', 'type_name' => 'display_measure'},
4644             31 => +{'name' => 'user_running_step_length', scale => 1000, 'unit' => 'm'},
4645             32 => +{'name' => 'user_walking_step_length', scale => 1000, 'unit' => 'm'},
4646             33 => +{'name' => 'unknown33'}, # unknown UINT16
4647             34 => +{'name' => 'unknown34'}, # unknown UINT16
4648             35 => +{'name' => 'unknown35'}, # unknown UINT32
4649             36 => +{'name' => 'unknown36'}, # unknown UINT8
4650             38 => +{'name' => 'unknown38'}, # unknown UINT16
4651             40 => +{'name' => 'unknown40'}, # unknown FLOAT32
4652             42 => +{'name' => 'unknown42'}, # unknown UINT32
4653             47 => +{'name' => 'depth_setting', 'type_name' => 'display_measure'},
4654             49 => +{'name' => 'dive_count'},
4655             },
4656              
4657             'hrm_profile' => +{
4658             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4659             0 => +{'name' => 'enabled', 'type_name' => 'bool'},
4660             1 => +{'name' => 'hrm_ant_id'},
4661             2 => +{'name' => 'log_hrv', 'type_name' => 'bool'},
4662             3 => +{'name' => 'hrm_ant_id_trans_type'},
4663             },
4664              
4665             'sdm_profile' => +{
4666             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4667             0 => +{'name' => 'enabled', 'type_name' => 'bool'},
4668             1 => +{'name' => 'sdm_ant_id'},
4669             2 => +{'name' => 'sdm_cal_factor', 'scale' => 10, 'unit' => '%'},
4670             3 => +{'name' => 'odometer', 'scale' => 100, 'unit' => 'm'},
4671             4 => +{'name' => 'speed_source', 'type_name' => 'bool'},
4672             5 => +{'name' => 'sdm_ant_id_trans_type'},
4673             7 => +{'name' => 'odometer_rollover'},
4674             },
4675              
4676             'bike_profile' => +{
4677             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4678             0 => +{'name' => 'name'},
4679             1 => +{'name' => 'sport', 'type_name' => 'sport'},
4680             2 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
4681             3 => +{'name' => 'odometer', 'scale' => 100, 'unit' => 'm'},
4682             4 => +{'name' => 'bike_spd_ant_id'},
4683             5 => +{'name' => 'bike_cad_ant_id'},
4684             6 => +{'name' => 'bike_spdcad_ant_id'},
4685             7 => +{'name' => 'bike_power_ant_id'},
4686             8 => +{'name' => 'custom_wheelsize', 'scale' => 1000, 'unit' => 'm'},
4687             9 => +{'name' => 'auto_wheelsize', 'scale' => 1000, 'unit' => 'm'},
4688             10 => +{'name' => 'bike_weight', 'scale' => 10, 'unit' => 'kg'},
4689             11 => +{'name' => 'power_cal_factor', 'scale' => 10, 'unit' => '%'},
4690             12 => +{'name' => 'auto_wheel_cal', 'type_name' => 'bool'},
4691             13 => +{'name' => 'auto_power_zero', 'type_name' => 'bool'},
4692             14 => +{'name' => 'id'},
4693             15 => +{'name' => 'spd_enabled', 'type_name' => 'bool'},
4694             16 => +{'name' => 'cad_enabled', 'type_name' => 'bool'},
4695             17 => +{'name' => 'spdcad_enabled', 'type_name' => 'bool'},
4696             18 => +{'name' => 'power_enabled', 'type_name' => 'bool'},
4697             19 => +{'name' => 'crank_length', 'scale' => 2, 'offset' => -110, 'unit' => 'mm'},
4698             20 => +{'name' => 'enabled', 'type_name' => 'bool'},
4699             21 => +{'name' => 'bike_spd_ant_id_trans_type'},
4700             22 => +{'name' => 'bike_cad_ant_id_trans_type'},
4701             23 => +{'name' => 'bike_spdcad_ant_id_trans_type'},
4702             24 => +{'name' => 'bike_power_ant_id_trans_type'},
4703             35 => +{'name' => 'unknown35'}, # unknown UINT8 (array[3])
4704             36 => +{'name' => 'unknown36'}, # unknown ENUM
4705             37 => +{'name' => 'odometer_rollover'},
4706             38 => +{'name' => 'front_gear_num'},
4707             39 => +{'name' => 'front_gear'},
4708             40 => +{'name' => 'rear_gear_num'},
4709             41 => +{'name' => 'rear_gear'},
4710             44 => +{'name' => 'shimano_di2_enabled'},
4711             },
4712              
4713             'connectivity' => +{
4714             0 => +{'name' => 'bluetooth_enabled', 'type_name' => 'bool'},
4715             1 => +{'name' => 'bluetooth_le_enabled', 'type_name' => 'bool'},
4716             2 => +{'name' => 'ant_enabled', 'type_name' => 'bool'},
4717             3 => +{'name' => 'name'},
4718             4 => +{'name' => 'live_tracking_enabled', 'type_name' => 'bool'},
4719             5 => +{'name' => 'weather_conditions_enabled', 'type_name' => 'bool'},
4720             6 => +{'name' => 'weather_alerts_enabled', 'type_name' => 'bool'},
4721             7 => +{'name' => 'auto_activity_upload_enabled', 'type_name' => 'bool'},
4722             8 => +{'name' => 'course_download_enabled', 'type_name' => 'bool'},
4723             9 => +{'name' => 'workout_download_enabled', 'type_name' => 'bool'},
4724             10 => +{'name' => 'gps_ephemeris_download_enabled', 'type_name' => 'bool'},
4725             11 => +{'name' => 'incident_detection_enabled', 'type_name' => 'bool'},
4726             12 => +{'name' => 'grouptrack_enabled', 'type_name' => 'bool'},
4727             },
4728              
4729             'watchface_settings' => +{
4730             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4731             0 => +{'name' => 'mode', 'type_name' => 'watchface_mode'},
4732              
4733             1 => +{
4734             'name' => 'layout',
4735              
4736             'switch' => +{
4737             '_by' => 'mode',
4738             'digital' => +{'name' => 'digital_layout', 'type_name' => 'digital_watchface_layout'},
4739             'analog' => +{'name' => 'analog_layout', 'type_name' => 'analog_watchface_layout'},
4740             },
4741             },
4742             },
4743              
4744             'ohr_settings' => +{
4745             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
4746             0 => +{'name' => 'enabled', 'type_name' => 'switch'},
4747             },
4748              
4749             'time_in_zone' => +{
4750             253 => +{ 'name' => 'timestamp', 'type_name' => 'date_time' },
4751             0 => +{ 'name' => 'reference_mesg', 'type_name' => 'mesg_num' },
4752             1 => +{ 'name' => 'reference_index', 'type_name' => 'message_index' },
4753             2 => +{ 'name' => 'time_in_hr_zone', 'scale' => 1000 },
4754             3 => +{ 'name' => 'time_in_speed_zone', 'scale' => 1000 },
4755             4 => +{ 'name' => 'time_in_cadence_zone', 'scale' => 1000 },
4756             5 => +{ 'name' => 'time_in_power_zone', 'scale' => 1000 },
4757             6 => +{ 'name' => 'hr_zone_high_boundary', 'unit' => 'bpm' },
4758             7 => +{ 'name' => 'speed_zone_high_boundary', 'unit' => 'm/s', 'scale' => 1000 },
4759             8 => +{ 'name' => 'cadence_zone_high_boundary', 'unit' => 'rpm' },
4760             9 => +{ 'name' => 'power_zone_high_boundary', 'unit' => 'watts' },
4761             10 => +{ 'name' => 'hr_calc_type', 'type_name' => 'hr_zone_calc' },
4762             11 => +{ 'name' => 'max_heart_rate' },
4763             12 => +{ 'name' => 'resting_heart_rate' },
4764             13 => +{ 'name' => 'threshold_heart_rate' },
4765             14 => +{ 'name' => 'pwr_calc_type', 'type_name' => 'pwr_zone_calc' },
4766             15 => +{ 'name' => 'functional_threshold_power' },
4767             },
4768              
4769             'zones_target' => +{ # begins === Sport settings file messages === section
4770             1 => +{'name' => 'max_heart_rate', 'unit' => 'bpm'},
4771             2 => +{'name' => 'threshold_heart_rate', 'unit' => 'bpm'},
4772             3 => +{'name' => 'functional_threshold_power', 'unit' => 'watts'},
4773             5 => +{'name' => 'hr_calc_type', 'type_name' => 'hr_zone_calc'},
4774             7 => +{'name' => 'pwr_calc_type', 'type_name' => 'power_zone_calc'},
4775             8 => +{'name' => 'unknown8'}, # unknown UINT16
4776             9 => +{'name' => 'unknown9'}, # unknown ENUM
4777             10 => +{'name' => 'unknown10'}, # unknown ENUM
4778             11 => +{'name' => 'unknown11'}, # unknown ENUM
4779             12 => +{'name' => 'unknown12'}, # unknown ENUM
4780             13 => +{'name' => 'unknown13'}, # unknown ENUM
4781             },
4782              
4783             'sport' => +{
4784             0 => +{'name' => 'sport', 'type_name' => 'sport'},
4785             1 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
4786             3 => +{'name' => 'name'},
4787             4 => +{'name' => 'unknown4'}, # unknown UINT16
4788             5 => +{'name' => 'unknown5'}, # unknown ENUM
4789             6 => +{'name' => 'unknown6'}, # unknown ENUM
4790             7 => +{'name' => 'unknown7'}, # unknown UINT8
4791             8 => +{'name' => 'unknown8'}, # unknown UINT8
4792             9 => +{'name' => 'unknown9'}, # unknown UINT8
4793             10 => +{'name' => 'unknown10'}, # unknown UINT8 (array[3])
4794             12 => +{'name' => 'unknown12'}, # unknown UINT8
4795             },
4796              
4797             'hr_zone' => +{
4798             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4799             1 => +{'name' => 'high_bpm', 'unit' => 'bpm'},
4800             2 => +{'name' => 'name'},
4801             },
4802              
4803             'speed_zone' => +{
4804             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4805             0 => +{'name' => 'high_value', 'scale' => 1000, 'unit' => 'm/s'},
4806             1 => +{'name' => 'name'},
4807             },
4808              
4809             'cadence_zone' => +{
4810             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4811             0 => +{'name' => 'high_value', 'unit' => 'rpm'},
4812             1 => +{'name' => 'name'},
4813             },
4814              
4815             'power_zone' => +{
4816             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4817             1 => +{'name' => 'high_value', 'unit' => 'watts'},
4818             2 => +{'name' => 'name'},
4819             },
4820              
4821             'met_zone' => +{
4822             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4823             1 => +{'name' => 'high_bpm', 'unit' => 'bpm'},
4824             2 => +{'name' => 'calories', 'scale' => 10, 'unit' => 'kcal/min'},
4825             3 => +{'name' => 'fat_calories', 'scale' => 10, 'unit' => 'kcal/min'},
4826             },
4827              
4828             'dive_settings' => +{
4829             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
4830             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4831             0 => +{'name' => 'name'},
4832             1 => +{'name' => 'model', 'type_name' => 'tissue_model_type'},
4833             2 => +{'name' => 'gf_low', 'unit' => '%'},
4834             3 => +{'name' => 'gf_high', 'unit' => '%'},
4835             4 => +{'name' => 'water_type', 'type_name' => 'water_type'},
4836             5 => +{'name' => 'water_density', 'unit' => 'kg/m^3'},
4837             6 => +{'name' => 'po2_warn', 'scale' => 100, 'unit' => '%'},
4838             7 => +{'name' => 'po2_critical', 'scale' => 100, 'unit' => '%'},
4839             8 => +{'name' => 'po2_deco', 'scale' => 100, 'unit' => '%'},
4840             9 => +{'name' => 'safety_stop_enabled', 'type_name' => 'bool'},
4841             10 => +{'name' => 'bottom_depth'},
4842             11 => +{'name' => 'bottom_time'},
4843             12 => +{'name' => 'apnea_countdown_enabled', 'type_name' => 'bool'},
4844             13 => +{'name' => 'apnea_countdown_time'},
4845             14 => +{'name' => 'backlight_mode', 'type_name' => 'dive_backlight_mode'},
4846             15 => +{'name' => 'backlight_brightness'},
4847             16 => +{'name' => 'backlight_timeout', 'type_name' => 'backlight_timeout'},
4848             17 => +{'name' => 'repeat_dive_interval', 'unit' => 's'},
4849             18 => +{'name' => 'safety_stop_time', 'unit' => 's'},
4850             19 => +{'name' => 'heart_rate_source_type', 'type_name' => 'source_type'},
4851             20 => +{
4852             'name' => 'heart_rate_source',
4853             'switch' => +{
4854             '_by' => 'heart_rate_source_type',
4855             'antplus' => +{'name' => 'heart_rate_antplus_device_type', 'type_name' => 'antplus_device_type'},
4856             'local' => +{'name' => 'heart_rate_local_device_type', 'type_name' => 'local_device_type'},
4857             },
4858             },
4859             21 => +{ 'name' => 'travel_gas', 'type_name' => 'message_index' }, # Index of travel dive_gas message
4860             22 => +{ 'name' => 'ccr_low_setpoint_switch_mode', 'type_name' => 'ccr_setpoint_switch_mode' }, # If low PO2 should be switched to automatically
4861             23 => +{ 'name' => 'ccr_low_setpoint', 'scale' => 100 }, # Target PO2 when using low setpoint
4862             24 => +{ 'name' => 'ccr_low_setpoint_depth', 'unit' => 'm', 'scale' => 1000 }, # Depth to switch to low setpoint in automatic mode
4863             25 => +{ 'name' => 'ccr_high_setpoint_switch_mode', 'type' => 'ccr_setpoint_switch_mode' }, # If high PO2 should be switched to automatically
4864             26 => +{ 'name' => 'ccr_high_setpoint', 'unit' => '%', 'scale' => 100 }, # Target PO2 when using high setpoint
4865             27 => +{ 'name' => 'ccr_high_setpoint_depth', 'unit' => 'm', 'scale' => 1000 }, # Depth to switch to high setpoint in automatic mode
4866             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.
4867             30 => +{ 'name' => 'up_key_enabled', 'type_name' => 'bool' }, # Indicates whether the up key is enabled during dives
4868             35 => +{ 'name' => 'dive_sounds', 'type_name' => 'tone' }, # Sounds and vibration enabled or disabled in-dive
4869             36 => +{ 'name' => 'last_stop_multiple', 'scale' => 10 }, # Usually 1.0/1.5/2.0 representing 3/4.5/6m or 10/15/20ft
4870             37 => +{ 'name' => 'no_fly_time_mode', 'type_name' => 'no_fly_time_mode' }, # Indicates which guidelines to use for no-fly surface interval.
4871             },
4872              
4873             'dive_alarm' => +{
4874             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4875             0 => +{'name' => 'depth', 'scale' => 1000, 'unit' => 'm'},
4876             1 => +{'name' => 'time', 'unit' => 's'},
4877             2 => +{'name' => 'enabled', 'type_name' => 'bool'},
4878             3 => +{'name' => 'alarm_type', 'type_name' => 'dive_alarm_type'},
4879             4 => +{'name' => 'sound', 'type_name' => 'tone'},
4880             5 => +{'name' => 'dive_types', 'type_name' => 'sub_sport'},
4881             6 => +{ 'name' => 'id' }, # Alarm ID
4882             7 => +{ 'name' => 'popup_enabled', 'type_name' => 'bool' }, # Show a visible pop-up for this alarm
4883             8 => +{ 'name' => 'trigger_on_descent', 'type_name' => 'bool' }, # Trigger the alarm on descent
4884             9 => +{ 'name' => 'trigger_on_ascent', 'type_name' => 'bool' }, # Trigger the alarm on ascent
4885             10 => +{ 'name' => 'repeating', 'type_name' => 'bool' }, # Repeat alarm each time threshold is crossed?
4886             11 => +{ 'name' => 'speed', 'scale' => 1000 }, # Ascent/descent rate (mps) setting for speed type alarms
4887             },
4888              
4889             'dive_apnea_alarm' => +{
4890             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4891             0 => +{ 'name' => 'depth', 'unit' => 'm', 'scale' => 1000 }, # Depth setting (m) for depth type alarms
4892             1 => +{ 'name' => 'time', 'unit' => 's' }, # Time setting (s) for time type alarms
4893             2 => +{ 'name' => 'enabled', 'type_name' => 'bool' }, # Enablement flag
4894             3 => +{ 'name' => 'alarm_type', 'type_name' => 'dive_alarm_type' }, # Alarm type setting
4895             4 => +{ 'name' => 'sound', 'type_name' => 'tone' }, # Tone and Vibe setting for the alarm.
4896             5 => +{ 'name' => 'dive_types', 'type_name' => 'sub_sport' }, # Dive types the alarm will trigger on
4897             6 => +{ 'name' => 'id' }, # Alarm ID
4898             7 => +{ 'name' => 'popup_enabled', 'type_name' => 'bool' }, # Show a visible pop-up for this alarm
4899             8 => +{ 'name' => 'trigger_on_descent', 'type_name' => 'bool' }, # Trigger the alarm on descent
4900             9 => +{ 'name' => 'trigger_on_ascent', 'type_name' => 'bool' }, # Trigger the alarm on ascent
4901             10 => +{ 'name' => 'repeating', 'type_name' => 'bool' }, # Repeat alarm each time threshold is crossed?
4902             11 => +{ 'name' => 'speed', 'unit' => 'mps', 'scale' => 1000 }, # Ascent/descent rate (mps) setting for speed type alarms
4903             },
4904              
4905             'dive_gas' => +{
4906             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4907             0 => +{'name' => 'helium_content', 'unit' => '%'},
4908             1 => +{'name' => 'oxygen_content', 'unit' => '%'},
4909             2 => +{'name' => 'status', 'type_name' => 'dive_gas_status'},
4910             3 => +{'name' => 'mode', 'type_name' => 'dive_gas_mode'},
4911             },
4912              
4913             'goal' => +{ # begins === Goals file messages === section
4914             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4915             0 => +{'name' => 'sport', 'type_name' => 'sport'},
4916             1 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
4917             2 => +{'name' => 'start_date', 'type_name' => 'date_time'},
4918             3 => +{'name' => 'end_date', 'type_name' => 'date_time'},
4919             4 => +{'name' => 'type', 'type_name' => 'goal'},
4920             5 => +{'name' => 'value'},
4921             6 => +{'name' => 'repeat', 'type_name' => 'bool'},
4922             7 => +{'name' => 'target_value'},
4923             8 => +{'name' => 'recurrence', 'type_name' => 'goal_recurrence'},
4924             9 => +{'name' => 'recurrence_value'},
4925             10 => +{'name' => 'enabled', 'type_name' => 'bool'},
4926             11 => +{'name' => 'source', 'type_name' => 'goal_source'},
4927             },
4928              
4929             'activity' => +{ # begins === Activity file messages === section
4930             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
4931             0 => +{'name' => 'total_timer_time', 'scale' => 1000, 'unit' => 's'},
4932             1 => +{'name' => 'num_sessions'},
4933             2 => +{'name' => 'type', 'type_name' => 'activity'},
4934             3 => +{'name' => 'event', 'type_name' => 'event'},
4935             4 => +{'name' => 'event_type', 'type_name' => 'event_type'},
4936             5 => +{'name' => 'local_timestamp', 'type_name' => 'local_date_time'},
4937             6 => +{'name' => 'event_group'},
4938             },
4939              
4940             'session' => +{
4941             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
4942             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
4943             0 => +{'name' => 'event', 'type_name' => 'event'},
4944             1 => +{'name' => 'event_type', 'type_name' => 'event_type'},
4945             2 => +{'name' => 'start_time', 'type_name' => 'date_time'},
4946             3 => +{'name' => 'start_position_lat', 'unit' => 'semicircles'},
4947             4 => +{'name' => 'start_position_long', 'unit' => 'semicircles'},
4948             5 => +{'name' => 'sport', 'type_name' => 'sport'},
4949             6 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
4950             7 => +{'name' => 'total_elapsed_time', 'scale' => 1000, 'unit' => 's'},
4951             8 => +{'name' => 'total_timer_time', 'scale' => 1000, 'unit' => 's'},
4952             9 => +{'name' => 'total_distance', 'scale' => 100, 'unit' => 'm'},
4953              
4954             10 => +{
4955             'name' => 'total_cycles', 'unit' => 'cycles',
4956              
4957             'switch' => +{
4958             '_by' => 'sport',
4959             'walking' => +{'name' => 'total_steps', 'unit' => 'steps'},
4960             'running' => +{'name' => 'total_strides', 'unit' => 'strides'},
4961             'swimming' => +{'name' => 'total_strokes', 'unit' => 'strokes'},
4962             },
4963             },
4964              
4965             11 => +{'name' => 'total_calories', 'unit' => 'kcal'},
4966             13 => +{'name' => 'total_fat_calories', 'unit' => 'kcal'},
4967             14 => +{'name' => 'avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
4968             15 => +{'name' => 'max_speed', 'scale' => 1000, 'unit' => 'm/s'},
4969             16 => +{'name' => 'avg_heart_rate', 'unit' => 'bpm'},
4970             17 => +{'name' => 'max_heart_rate', 'unit' => 'bpm'},
4971              
4972             18 => +{
4973             'name' => 'avg_cadence', 'unit' => 'rpm',
4974              
4975             'switch' => +{
4976             '_by' => 'sport',
4977             'walking' => +{'name' => 'avg_walking_cadence', 'unit' => 'steps/min'},
4978             'running' => +{'name' => 'avg_running_cadence', 'unit' => 'strides/min'},
4979             'swimming' => +{'name' => 'avg_swimming_cadence', 'unit' => 'strokes/min'},
4980             },
4981             },
4982              
4983             19 => +{
4984             'name' => 'max_cadence', 'unit' => 'rpm',
4985              
4986             'switch' => +{
4987             '_by' => 'sport',
4988             'walking' => +{'name' => 'max_walking_cadence', 'unit' => 'steps/min'},
4989             'running' => +{'name' => 'max_running_cadence', 'unit' => 'strides/min'},
4990             'swimming' => +{'name' => 'max_swimming_cadence', 'unit' => 'strokes/min'},
4991             },
4992             },
4993              
4994             20 => +{'name' => 'avg_power', 'unit' => 'watts'},
4995             21 => +{'name' => 'max_power', 'unit' => 'watts'},
4996             22 => +{'name' => 'total_ascent', 'unit' => 'm'},
4997             23 => +{'name' => 'total_descent', 'unit' => 'm'},
4998             24 => +{'name' => 'total_training_effect', 'scale' => 10},
4999             25 => +{'name' => 'first_lap_index'},
5000             26 => +{'name' => 'num_laps'},
5001             27 => +{'name' => 'event_group'},
5002             28 => +{'name' => 'trigger', 'type_name' => 'session_trigger'},
5003             29 => +{'name' => 'nec_lat', 'unit' => 'semicircles'},
5004             30 => +{'name' => 'nec_long', 'unit' => 'semicircles'},
5005             31 => +{'name' => 'swc_lat', 'unit' => 'semicircles'},
5006             32 => +{'name' => 'swc_long', 'unit' => 'semicircles'},
5007             34 => +{'name' => 'normalized_power', 'unit' => 'watts'},
5008             35 => +{'name' => 'training_stress_score', 'scale' => 10, 'unit' => 'tss'},
5009             36 => +{'name' => 'intensity_factor', 'scale' => 1000, 'unit' => 'if'},
5010             37 => +{'name' => 'left_right_balance', 'type_name' => 'left_right_balance_100'},
5011             41 => +{'name' => 'avg_stroke_count', 'scale' => 10, 'unit' => 'strokes/lap'},
5012             42 => +{'name' => 'avg_stroke_distance', 'scale' => 100, 'unit' => 'm'},
5013             43 => +{'name' => 'swim_stroke', 'type_name' => 'swim_stroke'},
5014             44 => +{'name' => 'pool_length', 'scale' => 100, 'unit' => 'm'},
5015             45 => +{'name' => 'threshold_power', 'unit' => 'watts'},
5016             46 => +{'name' => 'pool_length_unit', 'type_name' => 'display_measure'},
5017             47 => +{'name' => 'num_active_lengths', 'unit' => 'lengths'},
5018             48 => +{'name' => 'total_work', 'unit' => 'J'},
5019             49 => +{'name' => 'avg_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5020             50 => +{'name' => 'max_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5021             51 => +{'name' => 'gps_accuracy', 'unit' => 'm'},
5022             52 => +{'name' => 'avg_grade', 'scale' => 100, 'unit' => '%'},
5023             53 => +{'name' => 'avg_pos_grade', 'scale' => 100, 'unit' => '%'},
5024             54 => +{'name' => 'avg_neg_grade', 'scale' => 100, 'unit' => '%'},
5025             55 => +{'name' => 'max_pos_grade', 'scale' => 100, 'unit' => '%'},
5026             56 => +{'name' => 'max_neg_grade', 'scale' => 100, 'unit' => '%'},
5027             57 => +{'name' => 'avg_temperature', 'unit' => 'deg.C'},
5028             58 => +{'name' => 'max_temperature', 'unit' => 'deg.C'},
5029             59 => +{'name' => 'total_moving_time', 'scale' => 1000, 'unit' => 's'},
5030             60 => +{'name' => 'avg_pos_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5031             61 => +{'name' => 'avg_neg_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5032             62 => +{'name' => 'max_pos_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5033             63 => +{'name' => 'max_neg_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5034             64 => +{'name' => 'min_heart_rate', 'unit' => 'bpm'},
5035             65 => +{'name' => 'time_in_hr_zone', 'scale' => 1000, 'unit' => 's'},
5036             66 => +{'name' => 'time_in_speed_zone', 'scale' => 1000, 'unit' => 's'},
5037             67 => +{'name' => 'time_in_cadence_zone', 'scale' => 1000, 'unit' => 's'},
5038             68 => +{'name' => 'time_in_power_zone', 'scale' => 1000, 'unit' => 's'},
5039             69 => +{'name' => 'avg_lap_time', 'scale' => 1000, 'unit' => 's'},
5040             70 => +{'name' => 'best_lap_index'},
5041             71 => +{'name' => 'min_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5042             78 => +{'name' => 'unknown78'}, # unknown UINT32
5043             81 => +{'name' => 'unknown81'}, # unknown ENUM
5044             82 => +{'name' => 'player_score'},
5045             83 => +{'name' => 'opponent_score'},
5046             84 => +{'name' => 'opponent_name'},
5047             85 => +{'name' => 'stroke_count', 'unit' => 'counts'},
5048             86 => +{'name' => 'zone_count', 'unit' => 'counts'},
5049             87 => +{'name' => 'max_ball_speed', 'scale' => 100, 'unit' => 'm/s'},
5050             88 => +{'name' => 'avg_ball_speed', 'scale' => 100, 'unit' => 'm/s'},
5051             89 => +{'name' => 'avg_vertical_oscillation', 'scale' => 10, 'unit' => 'mm'},
5052             90 => +{'name' => 'avg_stance_time_percent', 'scale' => 100, 'unit' => '%'},
5053             91 => +{'name' => 'avg_stance_time', 'scale' => 10, 'unit' => 'ms'},
5054             92 => +{'name' => 'avg_fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
5055             93 => +{'name' => 'max_fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
5056             94 => +{'name' => 'total_fractional_cycles', 'scale' => 128, 'unit' => 'cycles'},
5057             95 => +{'name' => 'avg_total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5058             96 => +{'name' => 'min_total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5059             97 => +{'name' => 'max_total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5060             98 => +{'name' => 'avg_saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5061             99 => +{'name' => 'min_saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5062             100 => +{'name' => 'max_saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5063             101 => +{'name' => 'avg_left_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5064             102 => +{'name' => 'avg_right_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5065             103 => +{'name' => 'avg_left_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5066             104 => +{'name' => 'avg_right_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5067             105 => +{'name' => 'avg_combined_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5068             106 => +{'name' => 'unknown106'}, # unknown UINT16
5069             107 => +{'name' => 'unknown107'}, # unknown UINT16
5070             108 => +{'name' => 'unknown108'}, # unknown UINT16
5071             109 => +{'name' => 'unknown109'}, # unknown UINT8
5072             110 => +{'name' => 'unknown110'}, # unknown STRING
5073             111 => +{'name' => 'sport_index'},
5074             112 => +{'name' => 'time_standing', 'scale' => 1000, 'unit' => 's'},
5075             113 => +{'name' => 'stand_count'},
5076             114 => +{'name' => 'avg_left_pco', 'unit' => 'mm'},
5077             115 => +{'name' => 'avg_right_pco', 'unit' => 'mm'},
5078             116 => +{'name' => 'avg_left_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5079             117 => +{'name' => 'avg_left_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5080             118 => +{'name' => 'avg_right_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5081             119 => +{'name' => 'avg_right_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5082             120 => +{'name' => 'avg_power_position', 'unit' => 'watts'},
5083             121 => +{'name' => 'max_power_position', 'unit' => 'watts'},
5084             122 => +{'name' => 'avg_cadence_position', 'unit' => 'rpm'},
5085             123 => +{'name' => 'max_cadence_position', 'unit' => 'rpm'},
5086             124 => +{'name' => 'enhanced_avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
5087             125 => +{'name' => 'enhanced_max_speed', 'scale' => 1000, 'unit' => 'm/s'},
5088             126 => +{'name' => 'enhanced_avg_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5089             127 => +{'name' => 'enhanced_min_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5090             128 => +{'name' => 'enhanced_max_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5091             129 => +{'name' => 'avg_lev_motor_power', 'unit' => 'watts'},
5092             130 => +{'name' => 'max_lev_motor_power', 'unit' => 'watts'},
5093             131 => +{'name' => 'lev_battery_consumption', 'scale' => 2, 'unit' => '%'},
5094             132 => +{'name' => 'avg_vertical_ratio', 'scale' => 100, 'unit' => '%'},
5095             133 => +{'name' => 'avg_stance_time_balance', 'scale' => 100, 'unit' => '%'},
5096             134 => +{'name' => 'avg_step_length', 'scale' => 10, 'unit' => 'mm'},
5097             137 => +{'name' => 'total_anaerobic_training_effect', 'scale' => 10},
5098             139 => +{'name' => 'avg_vam', 'scale' => 1000, 'unit' => 'm/s'},
5099             140 => +{ 'name' => 'avg_depth', 'unit' => 'm', 'scale' => 1000 }, # 0 if above water
5100             141 => +{ 'name' => 'max_depth', 'unit' => 'm', 'scale' => 1000 }, # 0 if above water
5101             142 => +{ 'name' => 'surface_interval', 'unit' => 's' }, # Time since end of last dive
5102             143 => +{ 'name' => 'start_cns', 'unit' => '%' },
5103             144 => +{ 'name' => 'end_cns', 'unit' => '%' },
5104             145 => +{ 'name' => 'start_n2', 'unit' => '%' },
5105             146 => +{ 'name' => 'end_n2', 'unit' => '%' },
5106             147 => +{ 'name' => 'avg_respiration_rate' },
5107             148 => +{ 'name' => 'max_respiration_rate' },
5108             149 => +{ 'name' => 'min_respiration_rate' },
5109             150 => +{ 'name' => 'min_temperature', 'unit' => 'deg.C' },
5110             155 => +{ 'name' => 'o2_toxicity', 'unit' => 'OTUs' },
5111             156 => +{ 'name' => 'dive_number' },
5112             168 => +{ 'name' => 'training_load_peak', 'scale' => 65536 },
5113             169 => +{ 'name' => 'enhanced_avg_respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 },
5114             170 => +{ 'name' => 'enhanced_max_respiration_rate', 'unit' => 'breaths/min' },
5115             180 => +{ 'name' => 'enhanced_min_respiration_rate', 'scale' => 100 },
5116             181 => +{'name' => 'total_grit', 'unit' => 'kGrit'},
5117             182 => +{'name' => 'total_flow', 'unit' => 'Flow'},
5118             183 => +{'name' => 'jump_count'},
5119             186 => +{'name' => 'avg_grit', 'unit' => 'kGrit'},
5120             187 => +{'name' => 'avg_flow', 'unit' => 'Flow'},
5121             199 => +{'name' => 'total_fractional_ascent', 'unit' => 'm'},
5122             200 => +{'name' => 'total_fractional_descent', 'unit' => 'm'},
5123             208 => +{'name' => 'avg_core_temperature', 'unit' => 'deg.C'},
5124             209 => +{'name' => 'min_core_temperature', 'unit' => 'deg.C'},
5125             210 => +{'name' => 'max_core_temperature', 'unit' => 'deg.C'},
5126             },
5127              
5128             'lap' => +{
5129             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5130             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5131             0 => +{'name' => 'event', 'type_name' => 'event'},
5132             1 => +{'name' => 'event_type', 'type_name' => 'event_type'},
5133             2 => +{'name' => 'start_time', 'type_name' => 'date_time'},
5134             3 => +{'name' => 'start_position_lat', 'unit' => 'semicircles'},
5135             4 => +{'name' => 'start_position_long', 'unit' => 'semicircles'},
5136             5 => +{'name' => 'end_position_lat', 'unit' => 'semicircles'},
5137             6 => +{'name' => 'end_position_long', 'unit' => 'semicircles'},
5138             7 => +{'name' => 'total_elapsed_time', 'scale' => 1000, 'unit' => 's'},
5139             8 => +{'name' => 'total_timer_time', 'scale' => 1000, 'unit' => 's'},
5140             9 => +{'name' => 'total_distance', 'scale' => 100, 'unit' => 'm'},
5141              
5142             10 => +{
5143             'name' => 'total_cycles', 'unit' => 'cycles',
5144              
5145             'switch' => +{
5146             '_by' => 'sport',
5147             'walking' => +{'name' => 'total_steps', 'unit' => 'steps'},
5148             'running' => +{'name' => 'total_strides', 'unit' => 'strides'},
5149             'swimming' => +{'name' => 'total_strokes', 'unit' => 'strokes'},
5150             },
5151             },
5152              
5153             11 => +{'name' => 'total_calories', 'unit' => 'kcal'},
5154             12 => +{'name' => 'total_fat_calories', 'unit' => 'kcal'},
5155             13 => +{'name' => 'avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
5156             14 => +{'name' => 'max_speed', 'scale' => 1000, 'unit' => 'm/s'},
5157             15 => +{'name' => 'avg_heart_rate', 'unit' => 'bpm'},
5158             16 => +{'name' => 'max_heart_rate', 'unit' => 'bpm'},
5159              
5160             17 => +{
5161             'name' => 'avg_cadence', 'unit' => 'rpm',
5162              
5163             'switch' => +{
5164             '_by' => 'sport',
5165             'walking' => +{'name' => 'avg_walking_cadence', 'unit' => 'steps/min'},
5166             'running' => +{'name' => 'avg_running_cadence', 'unit' => 'strides/min'},
5167             'swimming' => +{'name' => 'avg_swimming_cadence', 'unit' => 'strokes/min'},
5168             },
5169             },
5170              
5171             18 => +{
5172             'name' => 'max_cadence', 'unit' => 'rpm',
5173              
5174             'switch' => +{
5175             '_by' => 'sport',
5176             'walking' => +{'name' => 'max_walking_cadence', 'unit' => 'steps/min'},
5177             'running' => +{'name' => 'max_running_cadence', 'unit' => 'strides/min'},
5178             'swimming' => +{'name' => 'max_swimming_cadence', 'unit' => 'strokes/min'},
5179             },
5180             },
5181              
5182             19 => +{'name' => 'avg_power', 'unit' => 'watts'},
5183             20 => +{'name' => 'max_power', 'unit' => 'watts'},
5184             21 => +{'name' => 'total_ascent', 'unit' => 'm'},
5185             22 => +{'name' => 'total_descent', 'unit' => 'm'},
5186             23 => +{'name' => 'intensity', 'type_name' => 'intensity'},
5187             24 => +{'name' => 'lap_trigger', 'type_name' => 'lap_trigger'},
5188             25 => +{'name' => 'sport', 'type_name' => 'sport'},
5189             26 => +{'name' => 'event_group'},
5190             27 => +{'name' => 'nec_lat', 'unit' => 'semicircles'}, # not present?
5191             28 => +{'name' => 'nec_long', 'unit' => 'semicircles'}, # not present?
5192             29 => +{'name' => 'swc_lat', 'unit' => 'semicircles'}, # not present?
5193             30 => +{'name' => 'swc_long', 'unit' => 'semicircles'}, # not present?
5194             32 => +{'name' => 'num_lengths', 'unit' => 'lengths'},
5195             33 => +{'name' => 'normalized_power', 'unit' => 'watts'},
5196             34 => +{'name' => 'left_right_balance', 'type_name' => 'left_right_balance_100'},
5197             35 => +{'name' => 'first_length_index'},
5198             37 => +{'name' => 'avg_stroke_distance', 'scale' => 100, 'unit' => 'm'},
5199             38 => +{'name' => 'swim_stroke', 'type_name' => 'swim_stroke'},
5200             39 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
5201             40 => +{'name' => 'num_active_lengths', 'unit' => 'lengths'},
5202             41 => +{'name' => 'total_work', 'unit' => 'J'},
5203             42 => +{'name' => 'avg_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5204             43 => +{'name' => 'max_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5205             44 => +{'name' => 'gps_accuracy', 'unit' => 'm'},
5206             45 => +{'name' => 'avg_grade', 'scale' => 100, 'unit' => '%'},
5207             46 => +{'name' => 'avg_pos_grade', 'scale' => 100, 'unit' => '%'},
5208             47 => +{'name' => 'avg_neg_grade', 'scale' => 100, 'unit' => '%'},
5209             48 => +{'name' => 'max_pos_grade', 'scale' => 100, 'unit' => '%'},
5210             49 => +{'name' => 'max_neg_grade', 'scale' => 100, 'unit' => '%'},
5211             50 => +{'name' => 'avg_temperature', 'unit' => 'deg.C'},
5212             51 => +{'name' => 'max_temperature', 'unit' => 'deg.C'},
5213             52 => +{'name' => 'total_moving_time', 'scale' => 1000, 'unit' => 's'},
5214             53 => +{'name' => 'avg_pos_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5215             54 => +{'name' => 'avg_neg_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5216             55 => +{'name' => 'max_pos_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5217             56 => +{'name' => 'max_neg_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5218             57 => +{'name' => 'time_in_hr_zone', 'scale' => 1000, 'unit' => 's'},
5219             58 => +{'name' => 'time_in_speed_zone', 'scale' => 1000, 'unit' => 's'},
5220             59 => +{'name' => 'time_in_cadence_zone', 'scale' => 1000, 'unit' => 's'},
5221             60 => +{'name' => 'time_in_power_zone', 'scale' => 1000, 'unit' => 's'},
5222             61 => +{'name' => 'repetition_num'},
5223             62 => +{'name' => 'min_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5224             63 => +{'name' => 'min_heart_rate', 'unit' => 'bpm'},
5225             70 => +{'name' => 'unknown70'}, # unknown UINT32
5226             71 => +{'name' => 'wkt_step_index', 'type_name' => 'message_index'},
5227             72 => +{'name' => 'unknown72'}, # unknown ENUM
5228             74 => +{'name' => 'opponent_score'},
5229             75 => +{'name' => 'stroke_count', 'unit' => 'counts'},
5230             76 => +{'name' => 'zone_count', 'unit' => 'counts'},
5231             77 => +{'name' => 'avg_vertical_oscillation', 'scale' => 10, 'unit' => 'mm'},
5232             78 => +{'name' => 'avg_stance_time_percent', 'scale' => 100, 'unit' => '%'},
5233             79 => +{'name' => 'avg_stance_time', 'scale' => 10, 'unit' => 'ms'},
5234             80 => +{'name' => 'avg_fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
5235             81 => +{'name' => 'max_fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
5236             82 => +{'name' => 'total_fractional_cycles', 'scale' => 128, 'unit' => 'cycles'},
5237             83 => +{'name' => 'player_score'},
5238             84 => +{'name' => 'avg_total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5239             85 => +{'name' => 'min_total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5240             86 => +{'name' => 'max_total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5241             87 => +{'name' => 'avg_saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5242             88 => +{'name' => 'min_saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5243             89 => +{'name' => 'max_saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5244             91 => +{'name' => 'avg_left_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5245             92 => +{'name' => 'avg_right_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5246             93 => +{'name' => 'avg_left_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5247             94 => +{'name' => 'avg_right_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5248             95 => +{'name' => 'avg_combined_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5249             96 => +{'name' => 'unknown96'}, # unknown UINT16
5250             97 => +{'name' => 'unknown97'}, # unknown UINT16
5251             98 => +{'name' => 'time_standing', 'scale' => 1000, 'unit' => 's'},
5252             99 => +{'name' => 'stand_count'},
5253             100 => +{'name' => 'avg_left_pco', 'unit' => 'mm'},
5254             101 => +{'name' => 'avg_right_pco', 'unit' => 'mm'},
5255             102 => +{'name' => 'avg_left_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5256             103 => +{'name' => 'avg_left_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5257             104 => +{'name' => 'avg_right_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5258             105 => +{'name' => 'avg_right_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5259             106 => +{'name' => 'avg_power_position', 'unit' => 'watts'},
5260             107 => +{'name' => 'max_power_position', 'unit' => 'watts'},
5261             108 => +{'name' => 'avg_cadence_position', 'unit' => 'rpm'},
5262             109 => +{'name' => 'max_cadence_position', 'unit' => 'rpm'},
5263             110 => +{'name' => 'enhanced_avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
5264             111 => +{'name' => 'enhanced_max_speed', 'scale' => 1000, 'unit' => 'm/s'},
5265             112 => +{'name' => 'enhanced_avg_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5266             113 => +{'name' => 'enhanced_min_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5267             114 => +{'name' => 'enhanced_max_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5268             115 => +{'name' => 'avg_lev_motor_power', 'unit' => 'watts'},
5269             116 => +{'name' => 'max_lev_motor_power', 'unit' => 'watts'},
5270             117 => +{'name' => 'lev_battery_consumption', 'scale' => 2, 'unit' => '%'},
5271             118 => +{'name' => 'avg_vertical_ratio', 'scale' => 100, 'unit' => '%'},
5272             119 => +{'name' => 'avg_stance_time_balance', 'scale' => 100, 'unit' => '%'},
5273             120 => +{'name' => 'avg_step_length', 'scale' => 10, 'unit' => 'mm'},
5274             121 => +{'name' => 'avg_vam', 'scale' => 1000, 'unit' => 'm/s'},
5275             122 => +{ 'name' => 'avg_depth', 'unit' => 'm', 'scale' => 1000 }, # 0 if above water
5276             123 => +{ 'name' => 'max_depth', 'unit' => 'm', 'scale' => 1000 }, # 0 if above water
5277             124 => +{ 'name' => 'min_temperature', 'unit' => 'deg.C' },
5278             136 => +{ 'name' => 'enhanced_avg_respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 },
5279             137 => +{ 'name' => 'enhanced_max_respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 },
5280             147 => +{ 'name' => 'avg_respiration_rate' },
5281             148 => +{ 'name' => 'max_respiration_rate' },
5282             149 => +{'name' => 'total_grit', 'unit' => 'kGrit'},
5283             150 => +{'name' => 'total_flow', 'unit' => 'Flow'},
5284             151 => +{'name' => 'jump_count'},
5285             153 => +{'name' => 'avg_grit', 'unit' => 'kGrit'},
5286             154 => +{'name' => 'avg_flow', 'unit' => 'Flow'},
5287             156 => +{'name' => 'total_fractional_ascent', 'unit' => 'm'},
5288             157 => +{'name' => 'total_fractional_descent', 'unit' => 'm'},
5289             158 => +{'name' => 'avg_core_temperature', 'unit' => 'deg.C'},
5290             159 => +{'name' => 'min_core_temperature', 'unit' => 'deg.C'},
5291             160 => +{'name' => 'max_core_temperature', 'unit' => 'deg.C'},
5292             },
5293              
5294             'length' => +{
5295             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5296             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5297             0 => +{'name' => 'event', 'type_name' => 'event'},
5298             1 => +{'name' => 'event_type', 'type_name' => 'event_type'},
5299             2 => +{'name' => 'start_time', 'type_name' => 'date_time'},
5300             3 => +{'name' => 'total_elapsed_time', 'scale' => 1000, 'unit' => 's'},
5301             4 => +{'name' => 'total_timer_time', 'scale' => 1000, 'unit' => 's'},
5302             5 => +{'name' => 'total_strokes', 'unit' => 'strokes'},
5303             6 => +{'name' => 'avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
5304             7 => +{'name' => 'swim_stroke', 'type_name' => 'swim_stroke'},
5305             9 => +{'name' => 'avg_swimming_cadence', 'unit' => 'strokes/min'},
5306             10 => +{'name' => 'event_group'},
5307             11 => +{'name' => 'total_calories', 'unit' => 'kcal'},
5308             12 => +{'name' => 'length_type', 'type_name' => 'length_type'},
5309             18 => +{'name' => 'player_score'},
5310             19 => +{'name' => 'opponent_score'},
5311             20 => +{'name' => 'stroke_count', 'unit' => 'counts'},
5312             21 => +{'name' => 'zone_count', 'unit' => 'counts'},
5313             22 => +{ 'name' => 'enhanced_avg_respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 },
5314             23 => +{ 'name' => 'enhanced_max_respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 },
5315             24 => +{ 'name' => 'avg_respiration_rate' },
5316             25 => +{ 'name' => 'max_respiration_rate' },
5317             },
5318              
5319             'record' => +{
5320             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5321             0 => +{'name' => 'position_lat', 'unit' => 'semicircles'},
5322             1 => +{'name' => 'position_long', 'unit' => 'semicircles'},
5323             2 => +{'name' => 'altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5324             3 => +{'name' => 'heart_rate', 'unit' => 'bpm'},
5325             4 => +{'name' => 'cadence', 'unit' => 'rpm'},
5326             5 => +{'name' => 'distance', 'scale' => 100, 'unit' => 'm'},
5327             6 => +{'name' => 'speed', 'scale' => 1000, 'unit' => 'm/s'},
5328             7 => +{'name' => 'power', 'unit' => 'watts'},
5329             8 => +{'name' => 'compressed_speed_distance'}, # complex decoding!
5330             9 => +{'name' => 'grade', 'scale' => 100, 'unit' => '%'},
5331             10 => +{'name' => 'resistance'},
5332             11 => +{'name' => 'time_from_course', 'scale' => 1000, 'unit' => 's'},
5333             12 => +{'name' => 'cycle_length', 'scale' => 100, 'unit' => 'm'},
5334             13 => +{'name' => 'temperature', 'unit' => 'deg.C'},
5335             17 => +{'name' => 'speed_1s', 'scale' => 16, 'unit' => 'm/s'},
5336             18 => +{'name' => 'cycles', 'unit' => 'cycles'},
5337             19 => +{'name' => 'total_cycles', 'unit' => 'cycles'},
5338             28 => +{'name' => 'compressed_accumulated_power', 'unit' => 'watts'},
5339             29 => +{'name' => 'accumulated_power', 'unit' => 'watts'},
5340             30 => +{'name' => 'left_right_balance', 'type_name' => 'left_right_balance'},
5341             31 => +{'name' => 'gps_accuracy', 'unit' => 'm'},
5342             32 => +{'name' => 'vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5343             33 => +{'name' => 'calories', 'unit' => 'kcal'},
5344             39 => +{'name' => 'vertical_oscillation', 'scale' => 10, 'unit' => 'mm'},
5345             40 => +{'name' => 'stance_time_percent', 'scale' => 100, 'unit' => '%'},
5346             41 => +{'name' => 'stance_time', 'scale' => 10, 'unit' => 'ms'},
5347             42 => +{'name' => 'activity_type', 'type_name' => 'activity_type'},
5348             43 => +{'name' => 'left_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5349             44 => +{'name' => 'right_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5350             45 => +{'name' => 'left_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5351             46 => +{'name' => 'right_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5352             47 => +{'name' => 'combined_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5353             48 => +{'name' => 'time128', 'scale' => 128, 'unit' => 's'},
5354             49 => +{'name' => 'stroke_type', 'type_name' => 'stroke_type'},
5355             50 => +{'name' => 'zone'},
5356             51 => +{'name' => 'ball_speed', 'scale' => 100, 'unit' => 'm/s'},
5357             52 => +{'name' => 'cadence256', 'scale' => 256, 'unit' => 'rpm'},
5358             53 => +{'name' => 'fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
5359             54 => +{'name' => 'total_hemoglobin_conc', 'scale' => 100, 'unit' => 'g/dL'},
5360             55 => +{'name' => 'total_hemoglobin_conc_min', 'scale' => 100, 'unit' => 'g/dL'},
5361             56 => +{'name' => 'total_hemoglobin_conc_max', 'scale' => 100, 'unit' => 'g/dL'},
5362             57 => +{'name' => 'saturated_hemoglobin_percent', 'scale' => 10, 'unit' => '%'},
5363             58 => +{'name' => 'saturated_hemoglobin_percent_min', 'scale' => 10, 'unit' => '%'},
5364             59 => +{'name' => 'saturated_hemoglobin_percent_max', 'scale' => 10, 'unit' => '%'},
5365             61 => +{'name' => 'unknown61'}, # unknown UINT16
5366             62 => +{'name' => 'device_index', 'type_name' => 'device_index'},
5367             66 => +{'name' => 'unknown66'}, # unknown SINT16
5368             67 => +{'name' => 'left_pco', 'unit' => 'mm'},
5369             68 => +{'name' => 'right_pco', 'unit' => 'mm'},
5370             69 => +{'name' => 'left_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5371             70 => +{'name' => 'left_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5372             71 => +{'name' => 'right_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5373             72 => +{'name' => 'right_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5374             73 => +{'name' => 'enhanced_speed', 'scale' => 1000, 'unit' => 'm/s'},
5375             78 => +{'name' => 'enhanced_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5376             81 => +{'name' => 'battery_soc', 'scale' => 2, 'unit' => '%'},
5377             82 => +{'name' => 'motor_power', 'unit' => 'watts'},
5378             83 => +{'name' => 'vertical_ratio', 'scale' => 100, 'unit' => '%'},
5379             84 => +{'name' => 'stance_time_balance', 'scale' => 100, 'unit' => '%'},
5380             85 => +{'name' => 'step_length', 'scale' => 10, 'unit' => 'mm'},
5381             91 => +{'name' => 'absolute_pressure', 'unit' => 'Pa'},
5382             92 => +{'name' => 'depth', 'scale' => 1000, 'unit' => 'm'},
5383             93 => +{'name' => 'next_stop_depth', 'scale' => 1000, 'unit' => 'm'},
5384             94 => +{'name' => 'next_stop_time', 'unit' => 's'},
5385             95 => +{'name' => 'time_to_surface', 'unit' => 's'},
5386             96 => +{'name' => 'ndl_time', 'unit' => 's'},
5387             97 => +{'name' => 'cns_load', 'unit' => '%'},
5388             98 => +{'name' => 'n2_load', 'unit' => '%'},
5389             99 => +{ 'name' => 'respiration_rate' },
5390             108 => +{ 'name' => 'enhanced_respiration_rate', 'unit' => 'breaths/min', 'scale' => 100 },
5391             114 => +{'name' => 'grit'},
5392             115 => +{'name' => 'flow'},
5393             117 => +{'name' => 'ebike_travel_range', 'unit' => 'km'},
5394             118 => +{'name' => 'ebike_battery_level', 'unit' => '%'},
5395             119 => +{'name' => 'ebike_assist_mode'},
5396             120 => +{'name' => 'ebike_assist_level_percent', 'unit' => '%'},
5397             123 => +{ 'name' => 'air_time_remaining' },
5398             124 => +{ 'name' => 'pressure_sac', 'unit' => 'bar/min', 'scale' => 100 }, # Pressure-based surface air consumption
5399             125 => +{ 'name' => 'volume_sac', 'unit' => 'l/min', 'scale' => 100 }, # Volumetric surface air consumption
5400             126 => +{ 'name' => 'rmv', 'unit' => 'l/min', 'scale' => 100 }, # Respiratory minute volume
5401             127 => +{ 'name' => 'ascent_rate', 'unit' => 'm/s', 'scale' => 1000 },
5402             129 => +{ 'name' => 'po2', 'unit' => 'percent', 'scale' => 100 }, # Current partial pressure of oxygen
5403             139 => +{'name' => 'core_temperature', 'scale' => 100, 'unit' => 'deg.C'},
5404             },
5405              
5406             'event' => +{
5407             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5408             0 => +{'name' => 'event', 'type_name' => 'event'},
5409             1 => +{'name' => 'event_type', 'type_name' => 'event_type'},
5410             2 => +{'name' => 'data16'}, # no switch?
5411              
5412             3 => +{
5413             'name' => 'data',
5414              
5415             'switch' => +{
5416             '_by' => 'event',
5417             'timer' => +{'name' => 'timer_trigger', 'type_name' => 'timer_trigger'},
5418             'course_point' => +{'name' => 'course_point_index', 'type_name' => 'message_index'},
5419             'battery' => +{'name' => 'battery_level', 'scale' => 1000, 'unit' => 'V'},
5420             'virtual_partner_pace' => +{'name' => 'virtual_partner_speed', 'scale' => 1000, 'unit' => 'm/s'},
5421             'hr_high_alert' => +{'name' => 'hr_high_alert', 'unit' => 'bpm'},
5422             'hr_low_alert' => +{'name' => 'hr_low_alert', 'unit' => 'bpm'},
5423             'speed_high_alert' => +{'name' => 'speed_high_alert', 'scale' => 1000, 'unit' => 'm/s'},
5424             'speed_low_alert' => +{'name' => 'speed_low_alert', 'scale' => 1000, 'unit' => 'm/s'},
5425             'cad_high_alert' => +{'name' => 'cad_high_alert', 'unit' => 'rpm'},
5426             'cad_low_alert' => +{'name' => 'cad_low_alert', 'unit' => 'rpm'},
5427             'power_high_alert' => +{'name' => 'power_high_alert', 'unit' => 'watts'},
5428             'power_low_alert' => +{'name' => 'power_low_alert', 'unit' => 'watts'},
5429             'time_duration_alert' => +{'name' => 'time_duration_alert', 'scale' => 1000, 'unit' => 's'},
5430             'distance_duration_alert' => +{'name' => 'distance_duration_alert', 'scale' => 100, 'unit' => 'm'},
5431             'calorie_duration_alert' => +{'name' => 'calorie_duration_alert', 'unit' => 'kcal'},
5432             # why is this key not the same as the name?
5433             'fitness_equipment' => +{'name' => 'fitness_equipment_state', 'type_name' => 'fitness_equipment_state'},
5434             'sport_point' => +{'name' => 'sport_point', 'scale' => 11}, # complex decoding!
5435             'front_gear_change' => +{'name' => 'gear_change_data', 'scale' => 1111}, # complex decoding!
5436             'rear_gear_change' => +{'name' => 'gear_change_data', 'scale' => 1111}, # complex decoding!
5437             'rider_position_change' => +{'name' => 'rider_position', 'type_name' => 'rider_position_type'},
5438             'comm_timeout' => +{'name' => 'comm_timeout', 'type_name' => 'comm_timeout_type'},
5439             'dive_alert' => +{ 'name' => 'dive_alert', 'type_name' => 'dive_alert' },
5440             'radar_threat_alert' => +{'name' => 'radar_threat_alert'},
5441             },
5442             },
5443              
5444             4 => +{'name' => 'event_group'},
5445             7 => +{'name' => 'score'},
5446             8 => +{'name' => 'opponent_score'},
5447             9 => +{'name' => 'front_gear_num'},
5448             10 => +{'name' => 'front_gear'},
5449             11 => +{'name' => 'rear_gear_num'},
5450             12 => +{'name' => 'rear_gear'},
5451             13 => +{'name' => 'device_index', 'type_name' => 'device_index'},
5452             21 => +{'name' => 'radar_threat_level_max', 'type_name' => 'radar_threat_level_type'},
5453             22 => +{'name' => 'radar_threat_count'},
5454             23 => +{'name' => 'radar_threat_avg_approach_speed', unit => 'm/s'},
5455             24 => +{'name' => 'radar_threat_max_approach_speed', unit => 'm/s'},
5456             },
5457              
5458             'device_info' => +{
5459             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5460             0 => +{'name' => 'device_index', 'type_name' => 'device_index'},
5461              
5462             1 => +{
5463             'name' => 'device_type',
5464              
5465             'switch' => +{
5466             '_by' => 'source_type',
5467             'bluetooth_low_energy' => +{ 'name' => 'ble_device_type', 'type_name' => 'ble_device_type' },
5468             'antplus' => +{'name' => 'antplus_device_type', 'type_name' => 'antplus_device_type'},
5469             'ant' => +{'name' => 'ant_device_type'},
5470             'local' => +{'name' => 'local_device_type', 'type_name' => 'local_device_type'},
5471             },
5472             },
5473              
5474             2 => +{'name' => 'manufacturer', 'type_name' => 'manufacturer'},
5475             3 => +{'name' => 'serial_number'},
5476              
5477             4 => +{
5478             'name' => 'product',
5479              
5480             'switch' => +{
5481             '_by' => 'manufacturer',
5482             'garmin' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5483             'dynastream' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5484             'dynastream_oem' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5485             'favero_electronics' => +{'name' => 'favero_product', 'type_name' => 'favero_product'},
5486             'tacx' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5487             },
5488             },
5489              
5490             5 => +{'name' => 'software_version', 'scale' => 100},
5491             6 => +{'name' => 'hardware_version'},
5492             7 => +{'name' => 'cum_operating_time', 'unit' => 's'},
5493             8 => +{'name' => 'unknown8'}, # unknown UINT32
5494             9 => +{'name' => 'unknown9'}, # unknown UINT8
5495             10 => +{'name' => 'battery_voltage', 'scale' => 256, 'unit' => 'V'},
5496             11 => +{'name' => 'battery_status', 'type_name' => 'battery_status'},
5497             15 => +{'name' => 'unknown15'}, # unknown UINT32
5498             16 => +{'name' => 'unknown16'}, # unknown UINT32
5499             18 => +{'name' => 'sensor_position', 'type_name' => 'body_location'},
5500             19 => +{'name' => 'descriptor'},
5501             20 => +{'name' => 'ant_transmission_type'},
5502             21 => +{'name' => 'ant_device_number'},
5503             22 => +{'name' => 'ant_network', 'type_name' => 'ant_network'},
5504             24 => +{'name' => 'unknown24'}, # unknown UINT32Z
5505             25 => +{'name' => 'source_type', 'type_name' => 'source_type'},
5506             27 => +{'name' => 'product_name'},
5507             32 => +{'name' => 'battery_level', 'unit' => '%'},
5508             },
5509              
5510             'device_aux_battery_info' => +{
5511             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5512             0 => +{'name' => 'device_index', 'type_name' => 'device_index'},
5513             1 => +{'name' => 'battery_voltage', 'scale' => 256, 'unit' => 'V'},
5514             2 => +{'name' => 'battery_status', 'type_name' => 'battery_status'},
5515             },
5516              
5517             'training_file' => +{
5518             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5519             0 => +{'name' => 'type', 'type_name' => 'file'},
5520             1 => +{'name' => 'manufacturer', 'type_name' => 'manufacturer'},
5521              
5522             2 => +{
5523             'name' => 'product',
5524              
5525             'switch' => +{
5526             '_by' => 'manufacturer',
5527             'garmin' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5528             'dynastream' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5529             'dynastream_oem' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5530             'favero_electronics' => +{'name' => 'favero_product', 'type_name' => 'favero_product'},
5531             'tacx' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
5532             },
5533             },
5534              
5535             3 => +{'name' => 'serial_number'},
5536             4 => +{'name' => 'time_created', 'type_name' => 'date_time'},
5537             },
5538              
5539             'weather_conditions' => +{
5540             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5541             0 => +{'name' => 'weather_report', 'type_name' => 'weather_report'},
5542             1 => +{'name' => 'temperature', 'unit' => 'deg.C'},
5543             2 => +{'name' => 'condition', 'type_name' => 'weather_status'},
5544             3 => +{'name' => 'wind_direction', 'unit' => 'degrees'},
5545             4 => +{'name' => 'wind_speed', 'scale' => 1000, 'unit' => 'm/s'},
5546             5 => +{'name' => 'precipitation_probability'},
5547             6 => +{'name' => 'temperature_feels_like', 'unit' => 'deg.C'},
5548             7 => +{'name' => 'relative_humidity', 'unit' => '%'},
5549             8 => +{'name' => 'location'},
5550             9 => +{'name' => 'observed_at_time', 'type_name' => 'date_time'},
5551             10 => +{'name' => 'observed_location_lat', 'unit' => 'semicircles'},
5552             11 => +{'name' => 'observed_location_long', 'unit' => 'semicircles'},
5553             12 => +{'name' => 'day_of_week', 'type_name' => 'day_of_week'},
5554             13 => +{'name' => 'high_temperature', 'unit' => 'deg.C'},
5555             14 => +{'name' => 'low_temperature', 'unit' => 'deg.C'},
5556             },
5557              
5558             'weather_alert' => +{
5559             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5560             0 => +{'name' => 'report_id'},
5561             1 => +{'name' => 'issue_time', 'type_name' => 'date_time'},
5562             2 => +{'name' => 'expire_time', 'type_name' => 'date_time'},
5563             3 => +{'name' => 'severity', 'type_name' => 'weather_severity'},
5564             4 => +{'name' => 'type', 'type_name' => 'weather_severe_type'},
5565             },
5566              
5567             'gps_metadata' => +{
5568             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5569             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5570             1 => +{'name' => 'position_lat', 'unit' => 'semicircles'},
5571             2 => +{'name' => 'position_long', 'unit' => 'semicircles'},
5572             3 => +{'name' => 'enhanced_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5573             4 => +{'name' => 'enhanced_speed', 'scale' => 1000, 'unit' => 'm/s'},
5574             5 => +{'name' => 'heading', 'scale' => 100, 'unit' => 'degrees'},
5575             6 => +{'name' => 'utc_timestamp', 'type_name' => 'date_time'},
5576             7 => +{'name' => 'velocity', 'scale' => 100, 'unit' => 'm/s'},
5577             },
5578              
5579             'camera_event' => +{
5580             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5581             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5582             1 => +{'name' => 'camera_event_type', 'type_name' => 'camera_event_type'},
5583             2 => +{'name' => 'camera_file_uuid'},
5584             3 => +{'name' => 'camera_orientation', 'type_name' => 'camera_orientation_type'},
5585             },
5586              
5587             'gyroscope_data' => +{
5588             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5589             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5590             1 => +{'name' => 'sample_time_offset', 'unit' => 'ms'},
5591             2 => +{'name' => 'gyro_x', 'unit' => 'counts'},
5592             3 => +{'name' => 'gyro_y', 'unit' => 'counts'},
5593             4 => +{'name' => 'gyro_z', 'unit' => 'counts'},
5594             5 => +{'name' => 'calibrated_gyro_x', 'unit' => 'deg/s'},
5595             6 => +{'name' => 'calibrated_gyro_y', 'unit' => 'deg/s'},
5596             7 => +{'name' => 'calibrated_gyro_z', 'unit' => 'deg/s'},
5597             },
5598              
5599             'accelerometer_data' => +{
5600             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5601             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5602             1 => +{'name' => 'sample_time_offset', 'unit' => 'ms'},
5603             2 => +{'name' => 'accel_x', 'unit' => 'counts'},
5604             3 => +{'name' => 'accel_y', 'unit' => 'counts'},
5605             4 => +{'name' => 'accel_z', 'unit' => 'counts'},
5606             5 => +{'name' => 'calibrated_accel_x', 'unit' => 'g'},
5607             6 => +{'name' => 'calibrated_accel_y', 'unit' => 'g'},
5608             7 => +{'name' => 'calibrated_accel_z', 'unit' => 'g'},
5609             8 => +{'name' => 'compressed_calibrated_accel_x', 'unit' => 'mG'},
5610             9 => +{'name' => 'compressed_calibrated_accel_y', 'unit' => 'mG'},
5611             10 => +{'name' => 'compressed_calibrated_accel_z', 'unit' => 'mG'},
5612             },
5613              
5614             'magnetometer_data' => +{
5615             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5616             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5617             1 => +{'name' => 'sample_time_offset', 'unit' => 'ms'},
5618             2 => +{'name' => 'mag_x', 'unit' => 'counts'},
5619             3 => +{'name' => 'mag_y', 'unit' => 'counts'},
5620             4 => +{'name' => 'mag_z', 'unit' => 'counts'},
5621             5 => +{'name' => 'calibrated_mag_x', 'unit' => 'G'},
5622             6 => +{'name' => 'calibrated_mag_y', 'unit' => 'G'},
5623             7 => +{'name' => 'calibrated_mag_z', 'unit' => 'G'},
5624             },
5625              
5626             'barometer_data' => +{
5627             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5628             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5629             1 => +{'name' => 'sample_time_offset', 'unit' => 'ms'},
5630             2 => +{'name' => 'baro_pres', 'unit' => 'Pa'},
5631             },
5632              
5633             'three_d_sensor_calibration' => +{
5634             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5635             0 => +{'name' => 'sensor_type', 'type_name' => 'sensor_type'},
5636              
5637             1 => +{
5638             'name' => 'calibration_factor',
5639              
5640             'switch' => +{
5641             '_by' => 'sensor_type',
5642             'accelerometer' => +{'name' => 'accel_cal_factor'},
5643             'gyroscope' => +{'name' => 'gyro_cal_factor'},
5644             },
5645             },
5646              
5647             2 => +{'name' => 'calibration_divisor', 'unit' => 'counts'},
5648             3 => +{'name' => 'level_shift'},
5649             4 => +{'name' => 'offset_cal'},
5650             5 => +{'name' => 'orientation_matrix', 'scale' => 65535},
5651             },
5652              
5653             'one_d_sensor_calibration' => +{
5654             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5655             0 => +{'name' => 'sensor_type', 'type_name' => 'sensor_type'},
5656              
5657             1 => +{
5658             'name' => 'calibration_factor',
5659              
5660             'switch' => +{
5661             '_by' => 'sensor_type',
5662             'barometer' => +{'name' => 'baro_cal_factor'},
5663             },
5664             },
5665              
5666             2 => +{'name' => 'calibration_divisor', 'unit' => 'counts'},
5667             3 => +{'name' => 'level_shift'},
5668             4 => +{'name' => 'offset_cal'},
5669             },
5670              
5671             'video_frame' => +{
5672             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5673             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5674             1 => +{'name' => 'frame_number'},
5675             },
5676              
5677             'obdii_data' => +{
5678             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5679             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5680             1 => +{'name' => 'time_offset', 'unit' => 'ms'},
5681             2 => +{'name' => 'pid'},
5682             3 => +{'name' => 'raw_data'},
5683             4 => +{'name' => 'pid_data_size'},
5684             5 => +{'name' => 'system_time'},
5685             6 => +{'name' => 'start_timestamp', 'type_name' => 'date_time'},
5686             7 => +{'name' => 'start_timestamp_ms', 'unit' => 'ms'},
5687             },
5688              
5689             'nmea_sentence' => +{
5690             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5691             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5692             1 => +{'name' => 'sentence'},
5693             },
5694              
5695             'aviation_attitude' => +{
5696             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5697             0 => +{'name' => 'timestamp_ms', 'unit' => 'ms'},
5698             1 => +{'name' => 'system_time', 'unit' => 'ms'},
5699             2 => +{'name' => 'pitch', 'scale' => 10430.38, 'unit' => 'radians'},
5700             3 => +{'name' => 'roll', 'scale' => 10430.38, 'unit' => 'radians'},
5701             4 => +{'name' => 'accel_lateral', 'scale' => 100, 'unit' => 'm/s^2'},
5702             5 => +{'name' => 'accel_normal', 'scale' => 100, 'unit' => 'm/s^2'},
5703             6 => +{'name' => 'turn_rate', 'scale' => 1024, 'unit' => 'radians/second'},
5704             7 => +{'name' => 'stage', 'type_name' => 'attitude_stage'},
5705             8 => +{'name' => 'attitude_stage_complete', 'unit' => '%'},
5706             9 => +{'name' => 'track', 'scale' => 10430.38, 'unit' => 'radians'},
5707             10 => +{'name' => 'validity', 'type_name' => 'attitude_validity'},
5708             },
5709              
5710             'video' => +{
5711             0 => +{'name' => 'url'},
5712             1 => +{'name' => 'hosting_provider'},
5713             2 => +{'name' => 'duration', 'unit' => 'ms'},
5714             },
5715              
5716             'video_title' => +{
5717             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5718             0 => +{'name' => 'message_count'},
5719             1 => +{'name' => 'text'},
5720             },
5721              
5722             'video_description' => +{
5723             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5724             0 => +{'name' => 'message_count'},
5725             1 => +{'name' => 'text'},
5726             },
5727              
5728             'video_clip' => +{
5729             0 => +{'name' => 'clip_number'},
5730             1 => +{'name' => 'start_timestamp', 'type_name' => 'date_time'},
5731             2 => +{'name' => 'start_timestamp_ms', 'unit' => 'ms'},
5732             3 => +{'name' => 'end_timestamp', 'type_name' => 'date_time'},
5733             4 => +{'name' => 'end_timestamp_ms', 'unit' => 'ms'},
5734             6 => +{'name' => 'clip_start', 'unit' => 'ms'},
5735             7 => +{'name' => 'clip_end', 'unit' => 'ms'},
5736             },
5737              
5738             'set' => +{
5739             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5740             0 => +{'name' => 'duration', 'scale' => 1000, 'unit' => 's'},
5741             3 => +{'name' => 'repetitions'},
5742             4 => +{'name' => 'weight', 'scale' => 16, 'unit' => 'kg'},
5743             5 => +{'name' => 'set_type', 'type_name' => 'set_type'},
5744             6 => +{'name' => 'start_time', 'type_name' => 'date_time'},
5745             7 => +{'name' => 'category', 'type_name' => 'exercise_category'},
5746             8 => +{'name' => 'category_subtype'},
5747             9 => +{'name' => 'weight_display_unit', 'type_name' => 'fit_base_unit'},
5748             10 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5749             11 => +{'name' => 'wkt_step_index', 'type_name' => 'message_index'},
5750             },
5751              
5752             'jump' => +{
5753             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5754             0 => +{'name' => 'distance', 'unit' => 'm'},
5755             1 => +{'name' => 'height', 'unit' => 'm'},
5756             2 => +{'name' => 'rotations'},
5757             3 => +{'name' => 'hang_time', 'unit' => 's'},
5758             4 => +{'name' => 'score'},
5759             5 => +{'name' => 'position_lat', 'unit' => 'semicircles'},
5760             6 => +{'name' => 'position_long', 'unit' => 'semicircles'},
5761             7 => +{'name' => 'speed', 'scale' => 1000, 'unit' => 'm/s'},
5762             8 => +{'name' => 'enhanced_speed', 'scale' => 1000, 'unit' => 'm/s'},
5763             },
5764              
5765             'split' => +{
5766             0 => +{ 'name' => 'split_type', 'type_name' => 'split_type' },
5767             1 => +{ 'name' => 'total_elapsed_time', 'unit' => 's', 'scale' => 1000 },
5768             2 => +{ 'name' => 'total_timer_time', 'unit' => 's', 'scale' => 1000 },
5769             3 => +{ 'name' => 'total_distance', 'unit' => 'm', 'scale' => 100 },
5770             9 => +{ 'name' => 'start_time', 'type_name' => 'date_time' },
5771             },
5772              
5773             'climb_pro' => +{
5774             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5775             0 => +{'name' => 'position_lat', 'unit' => 'semicircles'},
5776             1 => +{'name' => 'position_long', 'unit' => 'semicircles'},
5777             2 => +{'name' => 'climb_pro_event', 'type_name' => 'climb_pro_event'},
5778             3 => +{'name' => 'climb_number'},
5779             4 => +{'name' => 'climb_category'},
5780             5 => +{'name' => 'current_dist', 'unit' => 'm'},
5781             },
5782              
5783             'field_description' => +{
5784             0 => +{'name' => 'developer_data_index'},
5785             1 => +{'name' => 'field_definition_number'},
5786             2 => +{'name' => 'fit_base_type_id', 'type_name' => 'fit_base_type'},
5787             3 => +{'name' => 'field_name'},
5788             4 => +{'name' => 'array'},
5789             5 => +{'name' => 'components'},
5790             6 => +{'name' => 'scale'},
5791             7 => +{'name' => 'offset'},
5792             8 => +{'name' => 'units'},
5793             9 => +{'name' => 'bits'},
5794             10 => +{'name' => 'accumulate'},
5795             13 => +{'name' => 'fit_base_unit_id', 'type_name' => 'fit_base_unit'},
5796             14 => +{'name' => 'native_mesg_num', 'type_name' => 'mesg_num'},
5797             15 => +{'name' => 'native_field_num'},
5798             },
5799              
5800             'developer_data_id' => +{
5801             0 => +{'name' => 'developer_id'},
5802             1 => +{'name' => 'application_id'},
5803             2 => +{'name' => 'manufacturer_id', 'type_name' => 'manufacturer'},
5804             3 => +{'name' => 'developer_data_index'},
5805             4 => +{'name' => 'application_version'},
5806             },
5807              
5808             'course' => +{ # begins === Course file messages === section
5809             4 => +{'name' => 'sport', 'type_name' => 'sport'},
5810             5 => +{'name' => 'name'},
5811             6 => +{'name' => 'capabilities', 'type_name' => 'course_capabilities'},
5812             7 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
5813             },
5814              
5815             'course_point' => +{
5816             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5817             1 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5818             2 => +{'name' => 'position_lat', 'unit' => 'semicircles'},
5819             3 => +{'name' => 'position_long', 'unit' => 'semicircles'},
5820             4 => +{'name' => 'distance', 'scale' => 100, 'unit' => 'm'},
5821             5 => +{'name' => 'type', 'type_name' => 'course_point'},
5822             6 => +{'name' => 'name'},
5823             8 => +{'name' => 'favorite', 'type_name' => 'bool'},
5824             },
5825              
5826             'segment_id' => +{ # begins === Segment file messages === section
5827             0 => +{'name' => 'name'},
5828             1 => +{'name' => 'uuid'},
5829             2 => +{'name' => 'sport', 'type_name' => 'sport'},
5830             3 => +{'name' => 'enabled', 'type_name' => 'bool'},
5831             4 => +{'name' => 'user_profile_primary_key'},
5832             5 => +{'name' => 'device_id'},
5833             6 => +{'name' => 'default_race_leader'},
5834             7 => +{'name' => 'delete_status', 'type_name' => 'segment_delete_status'},
5835             8 => +{'name' => 'selection_type', 'type_name' => 'segment_selection_type'},
5836             },
5837              
5838             'segment_leaderboard_entry' => +{
5839             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5840             0 => +{'name' => 'name'},
5841             1 => +{'name' => 'type', 'type_name' => 'segment_leaderboard_type'},
5842             2 => +{'name' => 'group_primary_key'},
5843             3 => +{'name' => 'activity_id'},
5844             4 => +{'name' => 'segment_time', 'scale' => 1000, 'unit' => 's'},
5845             5 => +{'name' => 'activity_id_string'},
5846             },
5847              
5848             'segment_point' => +{
5849             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5850             1 => +{'name' => 'position_lat', 'unit' => 'semicircles'},
5851             2 => +{'name' => 'position_long', 'unit' => 'semicircles'},
5852             3 => +{'name' => 'distance', 'scale' => 100, 'unit' => 'm'},
5853             4 => +{'name' => 'altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5854             5 => +{'name' => 'leader_time', 'scale' => 1000, 'unit' => 's'},
5855             6 => +{ 'name' => 'enhanced_altitude', 'unit' => 'm', 'scale' => 5 }, # Accumulated altitude along the segment at the described point
5856             },
5857              
5858             'segment_lap' => +{
5859             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5860             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
5861             0 => +{'name' => 'event', 'type_name' => 'event'},
5862             1 => +{'name' => 'event_type', 'type_name' => 'event_type'},
5863             2 => +{'name' => 'start_time', 'type_name' => 'date_time'},
5864             3 => +{'name' => 'start_position_lat', 'unit' => 'semicircles'},
5865             4 => +{'name' => 'start_position_long', 'unit' => 'semicircles'},
5866             5 => +{'name' => 'end_position_lat', 'unit' => 'semicircles'},
5867             6 => +{'name' => 'end_position_long', 'unit' => 'semicircles'},
5868             7 => +{'name' => 'total_elapsed_time', 'scale' => 1000, 'unit' => 's'},
5869             8 => +{'name' => 'total_timer_time', 'scale' => 1000, 'unit' => 's'},
5870             9 => +{'name' => 'total_distance', 'scale' => 100, 'unit' => 'm'},
5871              
5872             10 => +{
5873             'name' => 'total_cycles', 'unit' => 'cycles',
5874              
5875             'switch' => +{
5876             '_by' => 'sport',
5877             'walking' => +{'name' => 'total_steps', 'unit' => 'steps'},
5878             'running' => +{'name' => 'total_strides', 'unit' => 'strides'},
5879             'swimming' => +{'name' => 'total_strokes', 'unit' => 'strokes'},
5880             },
5881             },
5882              
5883             11 => +{'name' => 'total_calories', 'unit' => 'kcal'},
5884             12 => +{'name' => 'total_fat_calories', 'unit' => 'kcal'},
5885             13 => +{'name' => 'avg_speed', 'scale' => 1000, 'unit' => 'm/s'},
5886             14 => +{'name' => 'max_speed', 'scale' => 1000, 'unit' => 'm/s'},
5887             15 => +{'name' => 'avg_heart_rate', 'unit' => 'bpm'},
5888             16 => +{'name' => 'max_heart_rate', 'unit' => 'bpm'},
5889             17 => +{'name' => 'avg_cadence', 'unit' => 'rpm'},
5890             18 => +{'name' => 'max_cadence', 'unit' => 'rpm'},
5891             19 => +{'name' => 'avg_power', 'unit' => 'watts'},
5892             20 => +{'name' => 'max_power', 'unit' => 'watts'},
5893             21 => +{'name' => 'total_ascent', 'unit' => 'm'},
5894             22 => +{'name' => 'total_descent', 'unit' => 'm'},
5895             23 => +{'name' => 'sport', 'type_name' => 'sport'},
5896             24 => +{'name' => 'event_group'},
5897             25 => +{'name' => 'nec_lat', 'unit' => 'semicircles'},
5898             26 => +{'name' => 'nec_long', 'unit' => 'semicircles'},
5899             27 => +{'name' => 'swc_lat', 'unit' => 'semicircles'},
5900             28 => +{'name' => 'swc_long', 'unit' => 'semicircles'},
5901             29 => +{'name' => 'name'},
5902             30 => +{'name' => 'normalized_power', 'unit' => 'watts'},
5903             31 => +{'name' => 'left_right_balance', 'type_name' => 'left_right_balance_100'},
5904             32 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
5905             33 => +{'name' => 'total_work', 'unit' => 'J'},
5906             34 => +{'name' => 'avg_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5907             35 => +{'name' => 'max_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5908             36 => +{'name' => 'gps_accuracy', 'unit' => 'm'},
5909             37 => +{'name' => 'avg_grade', 'scale' => 100, 'unit' => '%'},
5910             38 => +{'name' => 'avg_pos_grade', 'scale' => 100, 'unit' => '%'},
5911             39 => +{'name' => 'avg_neg_grade', 'scale' => 100, 'unit' => '%'},
5912             40 => +{'name' => 'max_pos_grade', 'scale' => 100, 'unit' => '%'},
5913             41 => +{'name' => 'max_neg_grade', 'scale' => 100, 'unit' => '%'},
5914             42 => +{'name' => 'avg_temperature', 'unit' => 'deg.C'},
5915             43 => +{'name' => 'max_temperature', 'unit' => 'deg.C'},
5916             44 => +{'name' => 'total_moving_time', 'scale' => 1000, 'unit' => 's'},
5917             45 => +{'name' => 'avg_pos_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5918             46 => +{'name' => 'avg_neg_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5919             47 => +{'name' => 'max_pos_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5920             48 => +{'name' => 'max_neg_vertical_speed', 'scale' => 1000, 'unit' => 'm/s'},
5921             49 => +{'name' => 'time_in_hr_zone', 'scale' => 1000, 'unit' => 's'},
5922             50 => +{'name' => 'time_in_speed_zone', 'scale' => 1000, 'unit' => 's'},
5923             51 => +{'name' => 'time_in_cadence_zone', 'scale' => 1000, 'unit' => 's'},
5924             52 => +{'name' => 'time_in_power_zone', 'scale' => 1000, 'unit' => 's'},
5925             53 => +{'name' => 'repetition_num'},
5926             54 => +{'name' => 'min_altitude', 'scale' => 5, 'offset' => 500, 'unit' => 'm'},
5927             55 => +{'name' => 'min_heart_rate', 'unit' => 'bpm'},
5928             56 => +{'name' => 'active_time', 'scale' => 1000, 'unit' => 's'},
5929             57 => +{'name' => 'wkt_step_index', 'type_name' => 'message_index'},
5930             58 => +{'name' => 'sport_event', 'type_name' => 'sport_event'},
5931             59 => +{'name' => 'avg_left_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5932             60 => +{'name' => 'avg_right_torque_effectiveness', 'scale' => 2, 'unit' => '%'},
5933             61 => +{'name' => 'avg_left_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5934             62 => +{'name' => 'avg_right_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5935             63 => +{'name' => 'avg_combined_pedal_smoothness', 'scale' => 2, 'unit' => '%'},
5936             64 => +{'name' => 'status', 'type_name' => 'segment_lap_status'},
5937             65 => +{'name' => 'uuid'},
5938             66 => +{'name' => 'avg_fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
5939             67 => +{'name' => 'max_fractional_cadence', 'scale' => 128, 'unit' => 'rpm'},
5940             68 => +{'name' => 'total_fractional_cycles', 'scale' => 128, 'unit' => 'cycles'},
5941             69 => +{'name' => 'front_gear_shift_count'},
5942             70 => +{'name' => 'rear_gear_shift_count'},
5943             71 => +{'name' => 'time_standing', 'scale' => 1000, 'unit' => 's'},
5944             72 => +{'name' => 'stand_count'},
5945             73 => +{'name' => 'avg_left_pco', 'unit' => 'mm'},
5946             74 => +{'name' => 'avg_right_pco', 'unit' => 'mm'},
5947             75 => +{'name' => 'avg_left_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5948             76 => +{'name' => 'avg_left_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5949             77 => +{'name' => 'avg_right_power_phase', 'scale' => 0.7111111, 'unit' => 'degrees'},
5950             78 => +{'name' => 'avg_right_power_phase_peak', 'scale' => 0.7111111, 'unit' => 'degrees'},
5951             79 => +{'name' => 'avg_power_position', 'unit' => 'watts'},
5952             80 => +{'name' => 'max_power_position', 'unit' => 'watts'},
5953             81 => +{'name' => 'avg_cadence_position', 'unit' => 'rpm'},
5954             82 => +{'name' => 'max_cadence_position', 'unit' => 'rpm'},
5955             83 => +{'name' => 'manufacturer', 'type_name' => 'manufacturer'},
5956             84 => +{'name' => 'total_grit', 'unit' => 'kGrit'},
5957             85 => +{'name' => 'total_flow', 'unit' => 'Flow'},
5958             86 => +{'name' => 'avg_grit', 'unit' => 'kGrit'},
5959             87 => +{'name' => 'avg_flow', 'unit' => 'Flow'},
5960             89 => +{'name' => 'total_fractional_ascent', 'unit' => 'm'},
5961             90 => +{'name' => 'total_fractional_descent', 'unit' => 'm'},
5962             91 => +{ 'name' => 'enhanced_avg_altitude', 'unit' => 'm', 'scale' => 5 },
5963             92 => +{ 'name' => 'enhanced_max_altitude', 'unit' => 'm', 'scale' => 5 },
5964             93 => +{ 'name' => 'enhanced_min_altitude', 'unit' => 'm', 'scale' => 5 },
5965             },
5966              
5967             'segment_file' => +{ # begins === Segment list file messages === section
5968             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5969             1 => +{'name' => 'file_uuid'},
5970             3 => +{'name' => 'enabled', 'type_name' => 'bool'},
5971             4 => +{'name' => 'user_profile_primary_key'},
5972             7 => +{'name' => 'leader_type', 'type_name' => 'segment_leaderboard_type'},
5973             8 => +{'name' => 'leader_group_primary_key'},
5974             9 => +{'name' => 'leader_activity_id'},
5975             10 => +{'name' => 'leader_activity_id_string'},
5976             11 => +{'name' => 'default_race_leader'},
5977             },
5978              
5979             'workout' => +{ # begins === Workout file messages === section
5980             4 => +{'name' => 'sport', 'type_name' => 'sport'},
5981             5 => +{'name' => 'capabilities', 'type_name' => 'workout_capabilities'},
5982             6 => +{'name' => 'num_valid_steps'},
5983             7 => +{'name' => 'protection'}, # not present?
5984             8 => +{'name' => 'wkt_name'},
5985             11 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
5986             14 => +{'name' => 'pool_length', 'scale' => 100, 'unit' => 'm'},
5987             15 => +{'name' => 'pool_length_unit', 'type_name' => 'display_measure'},
5988             },
5989              
5990             'workout_session' => +{
5991             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
5992             0 => +{'name' => 'sport', 'type_name' => 'sport'},
5993             1 => +{'name' => 'sub_sport', 'type_name' => 'sub_sport'},
5994             2 => +{'name' => 'num_valid_steps'},
5995             3 => +{'name' => 'first_step_index'},
5996             4 => +{'name' => 'pool_length', 'scale' => 100, 'unit' => 'm'},
5997             5 => +{'name' => 'pool_length_unit', 'type_name' => 'display_measure'},
5998             },
5999              
6000             'workout_step' => +{
6001             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6002             0 => +{'name' => 'wkt_step_name'},
6003             1 => +{'name' => 'duration_type', 'type_name' => 'wkt_step_duration'},
6004              
6005             2 => +{
6006             'name' => 'duration_value',
6007              
6008             'switch' => +{
6009             '_by' => 'duration_type',
6010             'time' => +{'name' => 'duration_time', 'scale' => 1000, 'unit' => 's'},
6011             'repetition_time' => +{'name' => 'duration_time', 'scale' => 1000, 'unit' => 's'},
6012             'distance' => +{'name' => 'duration_distance', 'scale' => 100, 'unit' => 'm'},
6013             'hr_less_than' => +{'name' => 'duration_hr', 'type_name' => 'workout_hr', 'unit' => 'bpm'},
6014             'hr_greater_than' => +{'name' => 'duration_hr', 'type_name' => 'workout_hr', 'unit' => 'bpm'},
6015             'calories' => +{'name' => 'duration_calories', 'unit' => 'kcal'},
6016             'repeat_until_steps_cmplt' => +{'name' => 'duration_step'},
6017             'repeat_until_time' => +{'name' => 'duration_step'},
6018             'repeat_until_distance' => +{'name' => 'duration_step'},
6019             'repeat_until_calories' => +{'name' => 'duration_step'},
6020             'repeat_until_hr_less_than' => +{'name' => 'duration_step'},
6021             'repeat_until_hr_greater_than' => +{'name' => 'duration_step'},
6022             'repeat_until_power_less_than' => +{'name' => 'duration_step'},
6023             'repeat_until_power_greater_than' => +{'name' => 'duration_step'},
6024             'power_less_than' => +{'name' => 'duration_power', 'type_name' => 'workout_power', 'unit' => 'watts'},
6025             'power_greater_than' => +{'name' => 'duration_power', 'type_name' => 'workout_power', 'unit' => 'watts'},
6026             'reps' => +{'name' => 'duration_reps'},
6027             },
6028             },
6029              
6030             3 => +{'name' => 'target_type', 'type_name' => 'wkt_step_target'},
6031              
6032             4 => +{
6033             'name' => 'target_value',
6034              
6035             'switch' => +{
6036             '_by' => [qw(target_type duration_type)],
6037             'speed' => +{'name' => 'target_speed_zone'},
6038             'heart_rate' => +{'name' => 'target_hr_zone'},
6039             'cadence' => +{'name' => 'target_cadence_zone'},
6040             'power' => +{'name' => 'target_power_zone'},
6041             'repeat_until_steps_cmplt' => +{'name' => 'repeat_steps'},
6042             'repeat_until_time' => +{'name' => 'repeat_time', 'scale' => 1000, 'unit' => 's'},
6043             'repeat_until_distance' => +{'name' => 'repeat_distance', 'scale' => 100, 'unit' => 'm'},
6044             'repeat_until_calories' => +{'name' => 'repeat_calories', 'unit' => 'kcal'},
6045             'repeat_until_hr_less_than' => +{'name' => 'repeat_hr', 'type_name' => 'workout_hr', 'unit' => 'bpm'},
6046             'repeat_until_hr_greater_than' => +{'name' => 'repeat_hr', 'type_name' => 'workout_hr', 'unit' => 'bpm'},
6047             'repeat_until_power_less_than' => +{'name' => 'repeat_power', 'type_name' => 'workout_power', 'unit' => 'watts'},
6048             'repeat_until_power_greater_than' => +{'name' => 'repeat_power', 'type_name' => 'workout_power', 'unit' => 'watts'},
6049             'swim_stroke' => +{'name' => 'target_stroke_type', 'type_name' => 'swim_stroke'},
6050             },
6051             },
6052              
6053             5 => +{
6054             'name' => 'custom_target_value_low',
6055              
6056             'switch' => +{
6057             '_by' => 'target_type',
6058             'speed' => +{'name' => 'custom_target_speed_low', 'scale' => 1000, 'unit' => 'm/s'},
6059             'heart_rate' => +{'name' => 'custom_target_heart_rate_low', 'type_name' => 'workout_hr', 'unit' => 'bpm'},
6060             'cadence' => +{'name' => 'custom_target_cadence_low', 'unit' => 'rpm'},
6061             'power' => +{'name' => 'custom_target_power_low', 'type_name' => 'workout_power', 'unit' => 'watts'},
6062             },
6063             },
6064              
6065             6 => +{
6066             'name' => 'custom_target_value_high',
6067              
6068             'switch' => +{
6069             '_by' => 'target_type',
6070             'speed' => +{'name' => 'custom_target_speed_high', 'scale' => 1000, 'unit' => 'm/s'},
6071             'heart_rate' => +{'name' => 'custom_target_heart_rate_high', 'type_name' => 'workout_hr', 'unit' => 'bpm'},
6072             'cadence' => +{'name' => 'custom_target_cadence_high', 'unit' => 'rpm'},
6073             'power' => +{'name' => 'custom_target_power_high', 'type_name' => 'workout_power', 'unit' => 'watts'},
6074             },
6075             },
6076              
6077             7 => +{'name' => 'intensity', 'type_name' => 'intensity'},
6078             8 => +{'name' => 'notes', 'type_name' => 'string'},
6079             9 => +{'name' => 'equipment', 'type_name' => 'workout_equipment'},
6080             10 => +{'name' => 'exercise_category', 'type_name' => 'exercise_category'},
6081             11 => +{'name' => 'exercise_name'},
6082             12 => +{'name' => 'exercise_weight', 'scale' => 100, 'unit' => 'kg'},
6083             13 => +{'name' => 'weight_display_unit', 'type_name' => 'fit_base_unit'},
6084             19 => +{'name' => 'secondary_target_type', 'type_name' => 'wkt_step_target'},
6085             20 => +{
6086             'name' => 'secondary_target_value',
6087             'switch' => +{
6088             '_by' => 'secondary_target_type',
6089             'speed' => +{'name' => 'secondary_target_speed_zone'},
6090             'heart_rate' => +{'name' => 'secondary_target_hr_zone'},
6091             'cadence' => +{'name' => 'secondary_target_cadence_zone'},
6092             'power' => +{'name' => 'secondary_target_power_zone'},
6093             'swim_stroke' => +{'name' => 'secondary_target_stroke_type', 'type_name' => 'swim_stroke'},
6094             },
6095             },
6096             },
6097              
6098             'exercise_title' => +{
6099             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6100             0 => +{'name' => 'exercise_category', 'type_name' => 'exercise_category'},
6101             1 => +{'name' => 'exercise_name'},
6102             2 => +{'name' => 'wkt_step_name', 'type_name' => 'string'},
6103             },
6104              
6105             'schedule' => +{ # begins === Schedule file messages === section
6106             0 => +{'name' => 'manufacturer', 'type_name' => 'manufacturer'},
6107              
6108             1 => +{
6109             'name' => 'product',
6110              
6111             'switch' => +{
6112             '_by' => 'manufacturer',
6113             'garmin' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
6114             'dynastream' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
6115             'dynastream_oem' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
6116             'favero_electronics' => +{'name' => 'favero_product', 'type_name' => 'favero_product'},
6117             'tacx' => +{'name' => 'garmin_product', 'type_name' => 'garmin_product'},
6118             },
6119             },
6120              
6121             2 => +{'name' => 'serial_number'},
6122             3 => +{'name' => 'time_created', 'type_name' => 'date_time'},
6123             4 => +{'name' => 'completed', 'type_name' => 'bool'},
6124             5 => +{'name' => 'type', 'type_name' => 'schedule'},
6125             6 => +{'name' => 'schedule_time', 'type_name' => 'local_date_time'},
6126             },
6127              
6128             'totals' => +{ # begins === Totals file messages === section
6129             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6130             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6131             0 => +{'name' => 'timer_time', 'unit' => 's'},
6132             1 => +{'name' => 'distance', 'unit' => 'm'},
6133             2 => +{'name' => 'calories', 'unit' => 'kcal'},
6134             3 => +{'name' => 'sport', 'type_name' => 'sport'},
6135             4 => +{'name' => 'elapsed_time', 'unit' => 's'},
6136             5 => +{'name' => 'sessions'},
6137             6 => +{'name' => 'active_time', 'unit' => 's'},
6138             9 => +{'name' => 'sport_index'},
6139             10 => +{'name' => 'profile_name'}, # unknown STRING
6140             },
6141              
6142             'weight_scale' => +{ # begins === Weight scale file messages === section
6143             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6144             0 => +{'name' => 'weight', 'type_name' => 'weight', 'scale' => 100, 'unit' => 'kg'},
6145             1 => +{'name' => 'percent_fat', 'scale' => 100, 'unit' => '%'},
6146             2 => +{'name' => 'percent_hydration', 'scale' => 100, 'unit' => '%'},
6147             3 => +{'name' => 'visceral_fat_mass', 'scale' => 100, 'unit' => 'kg'},
6148             4 => +{'name' => 'bone_mass', 'scale' => 100, 'unit' => 'kg'},
6149             5 => +{'name' => 'muscle_mass', 'scale' => 100, 'unit' => 'kg'},
6150             7 => +{'name' => 'basal_met', 'scale' => 4, 'unit' => 'kcal/day'},
6151             8 => +{'name' => 'physique_rating'},
6152             9 => +{'name' => 'active_met', 'scale' => 4, 'unit' => 'kcal/day'},
6153             10 => +{'name' => 'metabolic_age', 'unit' => 'years'},
6154             11 => +{'name' => 'visceral_fat_rating'},
6155             12 => +{'name' => 'user_profile_index', 'type_name' => 'message_index'},
6156             },
6157              
6158             'blood_pressure' => +{ # begins === Blood pressure file messages === section
6159             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6160             0 => +{'name' => 'systolic_pressure', 'unit' => 'mmHg'},
6161             1 => +{'name' => 'diastolic_pressure', 'unit' => 'mmHg'},
6162             2 => +{'name' => 'mean_arterial_pressure', 'unit' => 'mmHg'},
6163             3 => +{'name' => 'map_3_sample_mean', 'unit' => 'mmHg'},
6164             4 => +{'name' => 'map_morning_values', 'unit' => 'mmHg'},
6165             5 => +{'name' => 'map_evening_values', 'unit' => 'mmHg'},
6166             6 => +{'name' => 'heart_rate', 'unit' => 'bpm'},
6167             7 => +{'name' => 'heart_rate_type', 'type_name' => 'hr_type'},
6168             8 => +{'name' => 'status', 'type_name' => 'bp_status'},
6169             9 => +{'name' => 'user_profile_index', 'type_name' => 'message_index'},
6170             },
6171              
6172             'monitoring_info' => +{ # begins === Monitoring file messages === section
6173             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6174             0 => +{'name' => 'local_timestamp', 'type_name' => 'local_date_time'},
6175             1 => +{'name' => 'activity_type', 'type_name' => 'activity_type'},
6176             3 => +{'name' => 'cycles_to_distance', 'scale' => 5000, 'unit' => 'm/cycle'},
6177             4 => +{'name' => 'cycles_to_calories', 'scale' => 5000, 'unit' => 'kcal/cycle'},
6178             5 => +{'name' => 'resting_metabolic_rate', 'unit' => 'kcal/day'},
6179             },
6180              
6181             'monitoring' => +{
6182             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6183             0 => +{'name' => 'device_index', 'type_name' => 'device_index'},
6184             1 => +{'name' => 'calories', 'unit' => 'kcal'},
6185             2 => +{'name' => 'distance', 'scale' => 100, 'unit' => 'm'},
6186              
6187             3 => +{
6188             'name' => 'cycles', 'scale' => 2, 'unit' => 'cycles',
6189              
6190             'switch' => +{
6191             '_by' => 'activity_type',
6192             'walking' => +{'name' => 'total_steps', 'scale' => 1, 'unit' => 'steps'},
6193             'running' => +{'name' => 'total_strides', 'scale' => 1, 'unit' => 'strides'},
6194             'cycling' => +{'name' => 'total_strokes', 'scale' => 2, 'unit' => 'strokes'},
6195             'swimming' => +{'name' => 'total_strokes', 'scale' => 2, 'unit' => 'strokes'},
6196             },
6197             },
6198              
6199             4 => +{'name' => 'active_time', 'scale' => 1000, 'unit' => 's'},
6200             5 => +{'name' => 'activity_type', 'type_name' => 'activity_type'},
6201             6 => +{'name' => 'activity_subtype', 'type_name' => 'activity_subtype'},
6202             7 => +{'name' => 'activity_level', 'type_name' => 'activity_level'},
6203             8 => +{'name' => 'distance_16', 'scale' => 100, 'unit' => 'm'},
6204             9 => +{'name' => 'cycles_16', 'scale' => 2, 'unit' => 'cycles'},
6205             10 => +{'name' => 'active_time_16', 'unit' => 's'},
6206             11 => +{'name' => 'local_timestamp', 'type_name' => 'local_date_time'},
6207             12 => +{'name' => 'temperature', 'scale' => 100, 'unit' => 'deg.C'},
6208             14 => +{'name' => 'temperature_min', 'scale' => 100, 'unit' => 'deg.C'},
6209             15 => +{'name' => 'temperature_max', 'scale' => 100, 'unit' => 'deg.C'},
6210             16 => +{'name' => 'activity_time', 'unit' => 'min'},
6211             19 => +{'name' => 'active_calories', 'unit' => 'kcal'},
6212             24 => +{'name' => 'current_activity_type_intensity'}, # complex decoding!
6213             25 => +{'name' => 'timestamp_min_8', 'unit' => 'min'},
6214             26 => +{'name' => 'timestamp_16', 'unit' => 's'},
6215             27 => +{'name' => 'heart_rate', 'unit' => 'bpm'},
6216             28 => +{'name' => 'intensity', 'scale' => 10},
6217             29 => +{'name' => 'duration_min', 'unit' => 'min'},
6218             30 => +{'name' => 'duration', 'unit' => 's'},
6219             31 => +{'name' => 'ascent', 'scale' => 1000, 'unit' => 'm'},
6220             32 => +{'name' => 'descent', 'scale' => 1000, 'unit' => 'm'},
6221             33 => +{'name' => 'moderate_activity_minutes', 'unit' => 'min'},
6222             34 => +{'name' => 'vigorous_activity_minutes', 'unit' => 'min'},
6223             },
6224              
6225             'hr' => +{
6226             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6227             0 => +{'name' => 'fractional_timestamp', 'scale' => 32768, 'unit' => 's'},
6228             1 => +{'name' => 'time256', 'scale' => 256, 'unit' => 's'},
6229             6 => +{'name' => 'filtered_bpm', 'unit' => 'bpm'},
6230             9 => +{'name' => 'event_timestamp', 'scale' => 1024, 'unit' => 's'},
6231             10 => +{'name' => 'event_timestamp_12', 'scale' => 1024, 'unit' => 's'},
6232             },
6233              
6234             'stress_level' => +{
6235             0 => +{'name' => 'stress_level_value'},
6236             1 => +{'name' => 'stress_level_time', 'type_name' => 'date_time', 'unit' => 's'},
6237             },
6238              
6239             'memo_glob' => +{ # begins === Other messages === section
6240             250 => +{'name' => 'part_index'},
6241             0 => +{'name' => 'memo'},
6242             1 => +{'name' => 'message_number'},
6243             2 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6244             },
6245              
6246             'ant_channel_id' => +{
6247             0 => +{'name' => 'channel_number'},
6248             1 => +{'name' => 'device_type'},
6249             2 => +{'name' => 'device_number'},
6250             3 => +{'name' => 'transmission_type'},
6251             4 => +{'name' => 'device_index', 'type_name' => 'device_index'},
6252             },
6253              
6254             'ant_rx' => +{
6255             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6256             0 => +{'name' => 'fractional_timestamp', 'scale' => 32768, 'unit' => 's'},
6257             1 => +{'name' => 'mesg_id'},
6258             2 => +{'name' => 'mesg_data'},
6259             3 => +{'name' => 'channel_number'},
6260             4 => +{'name' => 'data'},
6261             },
6262              
6263             'ant_tx' => +{
6264             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6265             0 => +{'name' => 'fractional_timestamp', 'scale' => 32768, 'unit' => 's'},
6266             1 => +{'name' => 'mesg_id'},
6267             2 => +{'name' => 'mesg_data'},
6268             3 => +{'name' => 'channel_number'},
6269             4 => +{'name' => 'data'},
6270             },
6271              
6272             'exd_screen_configuration' => +{
6273             0 => +{'name' => 'screen_index'},
6274             1 => +{'name' => 'field_count'},
6275             2 => +{'name' => 'layout', 'type_name' => 'exd_layout'},
6276             3 => +{'name' => 'screen_enabled', 'type_name' => 'bool'},
6277             },
6278              
6279             'exd_data_field_configuration' => +{
6280             0 => +{'name' => 'screen_index'},
6281             1 => +{'name' => 'concept_field'}, # complex decoding!
6282             2 => +{'name' => 'field_id'},
6283             3 => +{'name' => 'concept_count'},
6284             4 => +{'name' => 'display_type', 'type_name' => 'exd_display_type'},
6285             5 => +{'name' => 'title'},
6286             },
6287              
6288             'exd_data_concept_configuration' => +{
6289             0 => +{'name' => 'screen_index'},
6290             1 => +{'name' => 'concept_field'}, # complex decoding!
6291             2 => +{'name' => 'field_id'},
6292             3 => +{'name' => 'concept_index'},
6293             4 => +{'name' => 'data_page'},
6294             5 => +{'name' => 'concept_key'},
6295             6 => +{'name' => 'scaling'},
6296             7 => +{'name' => 'unknown7'}, # unknown UINT8
6297             8 => +{'name' => 'data_units', 'type_name' => 'exd_data_units'},
6298             9 => +{'name' => 'qualifier', 'type_name' => 'exd_qualifiers'},
6299             10 => +{'name' => 'descriptor', 'type_name' => 'exd_descriptors'},
6300             11 => +{'name' => 'is_signed', 'type_name' => 'bool'},
6301             },
6302              
6303             'dive_summary' => +{
6304             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6305             0 => +{'name' => 'reference_mesg', 'type_name' => 'mesg_num'},
6306             1 => +{'name' => 'reference_index', 'type_name' => 'message_index'},
6307             2 => +{'name' => 'avg_depth', 'scale' => 1000, 'unit' => 'm'},
6308             3 => +{'name' => 'max_depth', 'scale' => 1000, 'unit' => 'm'},
6309             4 => +{'name' => 'surface_interval', 'unit' => 's'},
6310             5 => +{'name' => 'start_cns', 'unit' => '%'},
6311             6 => +{'name' => 'end_cns', 'unit' => '%'},
6312             7 => +{'name' => 'start_n2', 'unit' => '%'},
6313             8 => +{'name' => 'end_n2', 'unit' => '%'},
6314             9 => +{'name' => 'o2_toxicity'},
6315             10 => +{'name' => 'dive_number'},
6316             11 => +{'name' => 'bottom_time', 'scale' => 1000, 'unit' => 's'},
6317             12 => +{ 'name' => 'avg_pressure_sac', 'unit' => 'bar/min', 'scale' => 100 },# Average pressure-based surface air consumption
6318             13 => +{ 'name' => 'avg_volume_sac', 'unit' => 'l/min', 'scale' => 100 }, # Average volumetric surface air consumption
6319             14 => +{ 'name' => 'avg_rmv', 'unit' => 'l/min', 'scale' => 100 }, # Average respiratory minute volume
6320             15 => +{ 'name' => 'descent_time', 'unit' => 's', 'scale' => 1000 }, # Time to reach deepest level stop
6321             16 => +{ 'name' => 'ascent_time', 'unit' => 's', 'scale' => 1000 }, # Time after leaving bottom until reaching surface
6322             17 => +{ 'name' => 'avg_ascent_rate', 'unit' => 'm/s', 'scale' => 1000 }, # Average ascent rate, not including descents or stops
6323             22 => +{ 'name' => 'avg_descent_rate', 'unit' => 'm/s', 'scale' => 1000 }, # Average descent rate, not including ascents or stops
6324             23 => +{ 'name' => 'max_ascent_rate', 'unit' => 'm/s', 'scale' => 1000 }, # Maximum ascent rate
6325             24 => +{ 'name' => 'max_descent_rate', 'unit' => 'm/s', 'scale' => 1000 }, # Maximum descent rate
6326             25 => +{ 'name' => 'hang_time', 'unit' => 's', 'scale' => 1000 }, # Time spent neither ascending nor descending
6327             },
6328              
6329             'hrv' => +{ # heart rate variability
6330             0 => +{'name' => 'time', 'scale' => 1000, 'unit' => 's'},
6331             },
6332              
6333             'tank_update' => +{
6334             253 => +{ 'name' => 'timestamp', 'type_name' => 'date_time' },
6335             0 => +{ 'name' => 'sensor', 'type_name' => 'ant_channel_id' },
6336             1 => +{ 'name' => 'pressure', 'unit' => 'bar', 'scale' => 100 },
6337             },
6338              
6339             'tank_summary' => +{
6340             253 => +{ 'name' => 'timestamp', 'type_name' => 'date_time' },
6341             0 => +{ 'name' => 'sensor', 'type_name' => 'ant_channel_id' },
6342             1 => +{ 'name' => 'start_pressure', 'unit' => 'bar', 'scale' => 100 },
6343             2 => +{ 'name' => 'end_pressure', 'unit' => 'bar', 'scale' => 100 },
6344             3 => +{ 'name' => 'volume_used', 'unit' => 'l', 'scale' => 100 },
6345             },
6346              
6347             'pad' => +{
6348             0 => +{'name' => 'pad'},
6349             },
6350              
6351             'source' => +{ # begins === Undocumented messages === section
6352             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6353             # device_index in device_info
6354             0 => +{'name' => 'unknown0', 'type_name' => 'device_index'}, # unknown UINT8
6355             1 => +{'name' => 'unknown1', 'type_name' => 'device_index'}, # unknown UINT8
6356             2 => +{'name' => 'unknown2', 'type_name' => 'device_index'}, # unknown UINT8
6357             3 => +{'name' => 'unknown3', 'type_name' => 'device_index'}, # unknown UINT8
6358             4 => +{'name' => 'unknown4', 'type_name' => 'device_index'}, # unknown UINT8
6359             5 => +{'name' => 'unknown5'}, # unknown ENUM
6360             6 => +{'name' => 'unknown6'}, # unknown UINT8
6361             7 => +{'name' => 'unknown7'}, # unknown UINT8
6362             8 => +{'name' => 'unknown8'}, # unknown UINT8
6363             9 => +{'name' => 'unknown9'}, # unknown UINT8
6364             },
6365              
6366             'location' => +{
6367             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6368             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6369             0 => +{'name' => 'name'}, # unknown STRING
6370             1 => +{'name' => 'position_lat', 'unit' => 'semicircles'}, # unknown SINT32
6371             2 => +{'name' => 'position_long', 'unit' => 'semicircles'}, # unknown SINT32
6372             3 => +{'name' => 'unknown3'}, # unknown UINT16 (elevation?)
6373             4 => +{'name' => 'unknown4'}, # unknown UINT16
6374             5 => +{'name' => 'unknown5'}, # unknown UINT16
6375             6 => +{'name' => 'unknown6'}, # unknown STRING
6376             },
6377              
6378             'battery' => +{
6379             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6380             0 => +{'name' => 'unknown0'}, # unknown UINT16 (voltage with scale?)
6381             1 => +{'name' => 'unknown1'}, # unknown SINT16
6382             2 => +{'name' => 'charge_level', 'unit' => '%'}, # unknown UINT8
6383             3 => +{'name' => 'temperature', 'unit' => 'deg.C'}, # unknown SINT8
6384             },
6385              
6386             'sensor' => +{
6387             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6388             0 => +{'name' => 'unknown0'}, # unknown UINT32Z
6389             1 => +{'name' => 'unknown1'}, # unknown UINT8
6390             2 => +{'name' => 'sensor_id'}, # unknown STRING
6391             3 => +{'name' => 'unknown3'}, # unknown ENUM
6392             4 => +{'name' => 'unknown4'}, # unknown ENUM
6393             5 => +{'name' => 'unknown5'}, # unknown ENUM
6394             6 => +{'name' => 'unknown6'}, # unknown ENUM
6395             7 => +{'name' => 'unknown7'}, # unknown ENUM
6396             8 => +{'name' => 'unknown8'}, # unknown ENUM
6397             9 => +{'name' => 'unknown9'}, # unknown UINT8
6398             10 => +{'name' => 'wheel_size', 'unit' => 'mm'}, # unknown UINT16
6399             11 => +{'name' => 'unknown11'}, # unknown UINT16
6400             12 => +{'name' => 'unknown12'}, # unknown UINT8
6401             13 => +{'name' => 'unknown13'}, # unknown UINT32
6402             14 => +{'name' => 'unknown14'}, # unknown UINT8
6403             15 => +{'name' => 'unknown15'}, # unknown UINT8
6404             16 => +{'name' => 'unknown16'}, # unknown UINT8
6405             17 => +{'name' => 'unknown17'}, # unknown UINT8Z
6406             18 => +{'name' => 'unknown18'}, # unknown UINT8Z (array[4])
6407             19 => +{'name' => 'unknown19'}, # unknown UINT8Z
6408             20 => +{'name' => 'unknown20'}, # unknown UINT8Z (array[12])
6409             21 => +{'name' => 'unknown21'}, # unknown UINT16
6410             25 => +{'name' => 'unknown25'}, # unknown UINT16
6411             26 => +{'name' => 'unknown26'}, # unknown UINT16
6412             27 => +{'name' => 'unknown27'}, # unknown UINT8
6413             28 => +{'name' => 'unknown28'}, # unknown UINT8 (array[4])
6414             29 => +{'name' => 'unknown29'}, # unknown UINT8 (array[4])
6415             30 => +{'name' => 'unknown30'}, # unknown UINT8 (array[4])
6416             31 => +{'name' => 'unknown31'}, # unknown UINT8
6417             32 => +{'name' => 'unknown32'}, # unknown UINT16
6418             33 => +{'name' => 'unknown33'}, # unknown UINT16
6419             34 => +{'name' => 'unknown34'}, # unknown UINT16
6420             35 => +{'name' => 'unknown35'}, # unknown UINT16
6421             36 => +{'name' => 'unknown36'}, # unknown ENUM
6422             37 => +{'name' => 'unknown37'}, # unknown ENUM (array[7])
6423             38 => +{'name' => 'unknown38'}, # unknown ENUM (array[7])
6424             39 => +{'name' => 'unknown39'}, # unknown ENUM (array[7])
6425             40 => +{'name' => 'unknown40'}, # unknown UINT16Z
6426             41 => +{'name' => 'unknown41'}, # unknown UINT8 (array[7])
6427             42 => +{'name' => 'unknown42'}, # unknown ENUM
6428             43 => +{'name' => 'unknown43'}, # unknown ENUM
6429             44 => +{'name' => 'unknown44'}, # unknown UINT8Z
6430             47 => +{'name' => 'unknown47'}, # unknown ENUM
6431             48 => +{'name' => 'unknown48'}, # unknown ENUM
6432             },
6433              
6434             );
6435              
6436             my %msgtype_by_num = (
6437             13 => +{ # begins === Unknown messages === section
6438             '_number' => 13,
6439             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6440             1 => +{'name' => 'unknown1'}, # unknown ENUM
6441             2 => +{'name' => 'unknown2'}, # unknown UINT16
6442             3 => +{'name' => 'unknown3'}, # unknown ENUM
6443             4 => +{'name' => 'unknown4'}, # unknown UINT32
6444             5 => +{'name' => 'unknown5'}, # unknown SINT32
6445             6 => +{'name' => 'unknown6'}, # unknown SINT32
6446             7 => +{'name' => 'unknown7'}, # unknown ENUM
6447             8 => +{'name' => 'unknown8'}, # unknown UINT16
6448             9 => +{'name' => 'unknown9'}, # unknown ENUM
6449             10 => +{'name' => 'unknown10'}, # unknown UINT16
6450             11 => +{'name' => 'unknown11'}, # unknown UINT8
6451             12 => +{'name' => 'unknown12'}, # unknown ENUM
6452             13 => +{'name' => 'unknown13'}, # unknown ENUM
6453             14 => +{'name' => 'unknown14'}, # unknown ENUM
6454             15 => +{'name' => 'unknown15'}, # unknown ENUM
6455             16 => +{'name' => 'unknown16'}, # unknown ENUM
6456             17 => +{'name' => 'unknown17'}, # unknown ENUM
6457             18 => +{'name' => 'unknown18'}, # unknown ENUM
6458             19 => +{'name' => 'unknown19'}, # unknown UINT16
6459             25 => +{'name' => 'unknown25'}, # unknown ENUM
6460             27 => +{'name' => 'unknown27'}, # unknown ENUM
6461             30 => +{'name' => 'unknown30'}, # unknown ENUM
6462             31 => +{'name' => 'unknown31'}, # unknown UINT32
6463             32 => +{'name' => 'unknown32'}, # unknown UINT16
6464             33 => +{'name' => 'unknown33'}, # unknown UINT32
6465             34 => +{'name' => 'unknown34'}, # unknown ENUM
6466             50 => +{'name' => 'unknown50'}, # unknown ENUM
6467             51 => +{'name' => 'unknown51'}, # unknown ENUM
6468             52 => +{'name' => 'unknown52'}, # unknown UINT16
6469             53 => +{'name' => 'unknown53'}, # unknown ENUM
6470             56 => +{'name' => 'unknown56'}, # unknown ENUM
6471             },
6472              
6473             14 => +{
6474             '_number' => 14,
6475             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6476             1 => +{'name' => 'unknown1'}, # unknown ENUM
6477             3 => +{'name' => 'unknown3'}, # unknown UINT8
6478             4 => +{'name' => 'unknown4'}, # unknown UINT8 (array[10])
6479             5 => +{'name' => 'unknown5'}, # unknown ENUM (array[10])
6480             6 => +{'name' => 'unknown6'}, # unknown STRING
6481             7 => +{'name' => 'unknown7'}, # unknown UINT16 (array[10])
6482             },
6483              
6484             16 => +{
6485             '_number' => 16,
6486             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6487             1 => +{'name' => 'unknown1'}, # unknown ENUM
6488             2 => +{'name' => 'unknown2'}, # unknown UINT32
6489             3 => +{'name' => 'unknown3'}, # unknown ENUM
6490             },
6491              
6492             17 => +{
6493             '_number' => 17,
6494             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6495             1 => +{'name' => 'unknown1'}, # unknown ENUM
6496             2 => +{'name' => 'unknown2'}, # unknown ENUM
6497             3 => +{'name' => 'unknown3'}, # unknown UINT16
6498             4 => +{'name' => 'unknown4'}, # unknown ENUM
6499             5 => +{'name' => 'unknown5'}, # unknown UINT16
6500             },
6501              
6502             70 => +{
6503             '_number' => 70,
6504             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6505             0 => +{'name' => 'unknown0'}, # unknown ENUM
6506             1 => +{'name' => 'unknown1'}, # unknown ENUM
6507             2 => +{'name' => 'unknown2'}, # unknown ENUM
6508             3 => +{'name' => 'unknown3'}, # unknown ENUM
6509             4 => +{'name' => 'unknown4'}, # unknown ENUM
6510             5 => +{'name' => 'unknown5'}, # unknown ENUM
6511             6 => +{'name' => 'unknown6'}, # unknown ENUM
6512             7 => +{'name' => 'unknown7'}, # unknown ENUM
6513             8 => +{'name' => 'unknown8'}, # unknown ENUM
6514             9 => +{'name' => 'unknown9'}, # unknown ENUM
6515             10 => +{'name' => 'unknown10'}, # unknown ENUM
6516             11 => +{'name' => 'unknown11'}, # unknown ENUM
6517             12 => +{'name' => 'unknown12'}, # unknown ENUM
6518             13 => +{'name' => 'unknown13'}, # unknown ENUM
6519             14 => +{'name' => 'unknown14'}, # unknown ENUM
6520             15 => +{'name' => 'unknown15'}, # unknown ENUM
6521             },
6522              
6523             71 => +{
6524             '_number' => 71,
6525             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6526             0 => +{'name' => 'unknown0'}, # unknown ENUM
6527             1 => +{'name' => 'unknown1'}, # unknown ENUM
6528             2 => +{'name' => 'unknown2'}, # unknown ENUM
6529             3 => +{'name' => 'unknown3'}, # unknown UINT16
6530             4 => +{'name' => 'unknown4'}, # unknown ENUM
6531             },
6532              
6533             79 => +{
6534             '_number' => 79,
6535             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6536             0 => +{'name' => 'unknown0'}, # unknown UINT16
6537             1 => +{'name' => 'unknown1'}, # unknown UINT8
6538             2 => +{'name' => 'unknown2'}, # unknown UINT8
6539             3 => +{'name' => 'unknown3'}, # unknown UINT16
6540             4 => +{'name' => 'unknown4'}, # unknown ENUM
6541             5 => +{'name' => 'unknown5'}, # unknown ENUM
6542             6 => +{'name' => 'unknown6'}, # unknown UINT8
6543             7 => +{'name' => 'unknown7'}, # unknown SINT8
6544             8 => +{'name' => 'unknown8'}, # unknown UINT16
6545             9 => +{'name' => 'unknown9'}, # unknown UINT16
6546             10 => +{'name' => 'unknown10'}, # unknown UINT8
6547             11 => +{'name' => 'unknown11'}, # unknown UINT16
6548             12 => +{'name' => 'unknown12'}, # unknown UINT16
6549             13 => +{'name' => 'unknown13'}, # unknown UINT16
6550             14 => +{'name' => 'unknown14'}, # unknown UINT8
6551             },
6552              
6553             113 => +{
6554             '_number' => 113,
6555             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6556             0 => +{'name' => 'unknown0'}, # unknown UINT16
6557             1 => +{'name' => 'unknown1'}, # unknown ENUM
6558             2 => +{'name' => 'unknown2'}, # unknown UINT32
6559             3 => +{'name' => 'unknown3'}, # unknown UINT32
6560             4 => +{'name' => 'unknown4'}, # unknown UINT32
6561             5 => +{'name' => 'unknown5'}, # unknown ENUM
6562             },
6563              
6564             114 => +{
6565             '_number' => 114,
6566             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6567             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6568             0 => +{'name' => 'unknown0'}, # unknown UINT16
6569             1 => +{'name' => 'unknown1'}, # unknown ENUM
6570             2 => +{'name' => 'unknown2'}, # unknown UINT32
6571             3 => +{'name' => 'unknown3'}, # unknown UINT32
6572             4 => +{'name' => 'unknown4'}, # unknown UINT32
6573             5 => +{'name' => 'unknown5'}, # unknown UINT32
6574             6 => +{'name' => 'unknown6'}, # unknown UINT32Z
6575             7 => +{'name' => 'unknown7'}, # unknown UINT32
6576             },
6577              
6578             139 => +{
6579             '_number' => 139,
6580             254 => +{'name' => 'message_index', 'type_name' => 'message_index'},
6581             0 => +{'name' => 'unknown0'}, # unknown ENUM
6582             1 => +{'name' => 'unknown1'}, # unknown UINT16Z
6583             3 => +{'name' => 'unknown3'}, # unknown UINT8Z
6584             4 => +{'name' => 'unknown4'}, # unknown ENUM
6585             5 => +{'name' => 'unknown5'}, # unknown UINT16
6586             },
6587              
6588             140 => +{
6589             '_number' => 140,
6590             253 => +{'name' => 'timestamp', 'type_name' => 'date_time'},
6591             0 => +{'name' => 'unknown0'}, # unknown UINT8
6592             1 => +{'name' => 'unknown1'}, # unknown UINT8
6593             2 => +{'name' => 'unknown2'}, # unknown SINT32
6594             3 => +{'name' => 'unknown3'}, # unknown SINT32
6595             4 => +{'name' => 'unknown4'}, # unknown UINT8
6596             5 => +{'name' => 'unknown5'}, # unknown SINT32
6597             6 => +{'name' => 'unknown6'}, # unknown SINT32
6598             7 => +{'name' => 'unknown7'}, # unknown SINT32
6599             8 => +{'name' => 'unknown8'}, # unknown UINT8
6600             9 => +{'name' => 'unknown9'}, # unknown UINT16
6601             10 => +{'name' => 'unknown10'}, # unknown UINT16
6602             11 => +{'name' => 'unknown11'}, # unknown ENUM
6603             12 => +{'name' => 'unknown12'}, # unknown ENUM
6604             13 => +{'name' => 'unknown13'}, # unknown UINT8
6605             14 => +{'name' => 'unknown14'}, # unknown UINT16
6606             15 => +{'name' => 'unknown15'}, # unknown UINT16
6607             16 => +{'name' => 'unknown16'}, # unknown UINT16
6608             17 => +{'name' => 'unknown17'}, # unknown SINT8
6609             18 => +{'name' => 'unknown18'}, # unknown UINT8
6610             19 => +{'name' => 'unknown19'}, # unknown UINT8
6611             },
6612              
6613             203 => +{
6614             '_number' => 203,
6615             0 => +{'name' => 'unknown0'}, # unknown ENUM
6616             1 => +{'name' => 'unknown1'}, # unknown ENUM
6617             2 => +{'name' => 'unknown2'}, # unknown ENUM
6618             },
6619              
6620             );
6621              
6622             my $mesg_name_vs_num = $named_type{mesg_num};
6623              
6624             for my $msgname (keys %msgtype_by_name) {
6625             my $msgtype = $msgtype_by_name{$msgname};
6626              
6627             $msgtype->{_name} = $msgname;
6628             $msgtype->{_number} = $mesg_name_vs_num->{$msgname};
6629             $msgtype_by_num{$msgtype->{_number}} = $msgtype;
6630              
6631             for my $fldnum (grep {/^\d+$/} keys %$msgtype) {
6632             my $flddesc = $msgtype->{$fldnum};
6633              
6634             $flddesc->{number} = $fldnum;
6635             $msgtype->{$flddesc->{name}} = $flddesc;
6636             }
6637             }
6638              
6639             =head2 Constructor Methods (class)
6640              
6641             =over 4
6642              
6643             =item new()
6644              
6645             creates a new object and returns it.
6646              
6647             =back
6648              
6649             =cut
6650              
6651             sub new {
6652 3     3 1 254 my $class = shift;
6653 3         9 my $self = +{};
6654 3         9 bless $self, $class;
6655 3         16 $self->initialize(@_);
6656             }
6657              
6658             =head2 Class methods
6659              
6660             =over 4
6661              
6662             =item message_name(I)
6663              
6664             returns the message name for I or undef.
6665              
6666             =item message_number(I)
6667              
6668             returns the message number for I or undef.
6669              
6670             =back
6671              
6672             =cut
6673              
6674             sub message_name {
6675 0     0 1 0 my ($self, $mspec) = @_;
6676 0 0       0 my $msgtype = $mspec =~ /^\d+$/ ? $msgtype_by_num{$mspec} : $msgtype_by_name{$mspec};
6677              
6678 0 0       0 if (ref $msgtype eq 'HASH') {
6679 0         0 $msgtype->{_name};
6680             } else {
6681 0         0 undef;
6682             }
6683             }
6684              
6685             sub message_number {
6686 0     0 1 0 my ($self, $mspec) = @_;
6687 0 0       0 my $msgtype = $mspec =~ /^\d+$/ ? $msgtype_by_num{$mspec} : $msgtype_by_name{$mspec};
6688              
6689 0 0       0 if (ref $msgtype eq 'HASH') {
6690 0         0 $msgtype->{_number};
6691             } else {
6692 0         0 undef;
6693             }
6694             }
6695              
6696             =over 4
6697              
6698             =item field_name(I, I)
6699              
6700             returns the field name for I in I or undef.
6701              
6702             =item field_number(I, I)
6703              
6704             returns the field index for I in I or undef.
6705              
6706             =back
6707              
6708             =cut
6709              
6710             sub field_name {
6711 0     0 1 0 my ($self, $mspec, $fspec) = @_;
6712 0 0       0 my $msgtype = $mspec =~ /^\d+$/ ? $msgtype_by_num{$mspec} : $msgtype_by_name{$mspec};
6713              
6714 0 0       0 if (ref $msgtype eq 'HASH') {
6715 0         0 my $flddesc = $msgtype->{$fspec};
6716             ref $flddesc eq 'HASH'
6717 0 0       0 and return $flddesc->{name};
6718             }
6719 0         0 undef;
6720             }
6721              
6722             sub field_number {
6723 0     0 1 0 my ($self, $mspec, $fspec) = @_;
6724 0 0       0 my $msgtype = $mspec =~ /^\d+$/ ? $msgtype_by_num{$mspec} : $msgtype_by_name{$mspec};
6725              
6726 0 0       0 if (ref $msgtype eq 'HASH') {
6727 0         0 my $flddesc = $msgtype->{$fspec};
6728             ref $flddesc eq 'HASH'
6729 0 0       0 and return $flddesc->{number};
6730             }
6731 0         0 undef;
6732             }
6733              
6734             =over 4
6735              
6736             =item protocol_version_string()
6737              
6738             returns a string representing the .FIT protocol version on which this class based.
6739              
6740             =item profile_version_string()
6741              
6742             returns a string representing the .FIT protocol version on which this class based.
6743              
6744             =back
6745              
6746             =cut
6747              
6748             my $profile_current = '21.107';
6749             my $protocol_current = '2.3'; # is there such a thing as current protocol?
6750              
6751             my $protocol_version_major_shift = 4;
6752             my $protocol_version_minor_mask = (1 << $protocol_version_major_shift) - 1;
6753             my $protocol_version_header_crc_started = _protocol_version_from_string("1.0");
6754             my $profile_version_scale = 100;
6755              
6756             sub _protocol_version_from_string {
6757 6     6   13 my $s = shift;
6758 6         38 my ($major, $minor) = split /\./, $s, 2;
6759 6 50       25 return ($major + 0, $minor & $protocol_version_minor_mask) if wantarray;
6760 6         24 return ($major << $protocol_version_major_shift) | ($minor & $protocol_version_minor_mask)
6761             }
6762              
6763             sub protocol_version {
6764 0     0 0 0 my $self = shift;
6765 0         0 my $version;
6766 0 0       0 if (@_) { $version = shift }
  0         0  
6767 0         0 else { $version = _protocol_version_from_string($protocol_current) }
6768 0 0       0 return ($version >> $protocol_version_major_shift, $version & $protocol_version_minor_mask) if wantarray;
6769 0         0 return $version
6770             }
6771              
6772             sub _profile_version_from_string {
6773 15     15   3326 my $str = shift;
6774 15 50       40 croak '_profile_version_from_string() expects a string as argument' unless $str;
6775 15         49 my ($major, $minor) = split /\./, $str, 2;
6776 15 100       45 if ($minor >= 100) { $major += 1 } # kludge to deal with three-digit minor versions
  7         13  
6777 15         43 return $major * $profile_version_scale + $minor % $profile_version_scale
6778             }
6779              
6780             sub profile_version {
6781 19     19 0 7484 my $self = shift;
6782 19         27 my $version;
6783 19 100       39 if (@_) {
6784 16         25 $version = shift;
6785 16 100       65 $version = _profile_version_from_string($version) if $version =~ /\./
6786 3         7 } else { $version = _profile_version_from_string($profile_current) }
6787              
6788 19 100       40 if (wantarray) {
6789 12         28 my $major = int($version / $profile_version_scale);
6790 12         17 my $minor = $version % $profile_version_scale;
6791 12 100       26 if ($version >= 2200) { # kludge to deal with three-digit minor versions
6792 5         7 $major -= 1;
6793 5         7 $minor += 100
6794             }
6795 12         37 return ($major, $minor)
6796             }
6797 7         16 return $version
6798             }
6799              
6800             sub profile_version_string {
6801 5     5 1 2066 my $self = shift;
6802 5         12 my @version;
6803 5 100       21 if (blessed $self) {
6804 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};
6805 1 50       3 croak 'object method expects no arguments' if @_;
6806             @version = profile_version(undef, $self->{profile_version} )
6807 1         4 } else {
6808 4         10 @version = profile_version(undef, @_)
6809             }
6810 5         30 return sprintf '%u.%03u', @version
6811             }
6812              
6813 0     0 0 0 sub profile_version_major { profile_version( @_) };
6814 0     0 1 0 sub protocol_version_string { sprintf '%u.%u', ( protocol_version(@_) ) }
6815 0     0 0 0 sub protocol_version_major { protocol_version(@_) };
6816              
6817             # CRC calculation routine taken from
6818             # Haruhiko Okumura, C gengo ni yoru algorithm dai jiten (1st ed.), GijutsuHyouronsha 1991.
6819              
6820             my $crc_poly = 2 ** 16 + 2 ** 15 + 2 ** 2 + 2 ** 0; # CRC-16
6821             my ($crc_poly_deg, $crc_poly_rev);
6822             my ($x, $y, $i);
6823             for ($crc_poly_deg = 0, $x = $crc_poly ; $x >>= 1 ;) {
6824             ++$crc_poly_deg;
6825             }
6826             my $crc_octets = int($crc_poly_deg / 8 + 0.5);
6827             for ($crc_poly_rev = 0, $y = 1, $x = 2 ** ($crc_poly_deg - 1) ; $x ;) {
6828             $crc_poly_rev |= $y if $x & $crc_poly;
6829             $y <<= 1;
6830             $x >>= 1;
6831             }
6832             my @crc_table = ();
6833             for ($i = 0 ; $i < 2 ** 8 ; ++$i) {
6834             my $r = $i;
6835             my $j;
6836             for ($j = 0 ; $j < 8 ; ++$j) {
6837             if ($r & 1) {
6838             $r = ($r >> 1) ^ $crc_poly_rev;
6839             } else {
6840             $r >>= 1;
6841             }
6842             }
6843             $crc_table[$i] = $r;
6844             }
6845              
6846             sub dump {
6847 0     0 0 0 my ($self, $s, $FH) = @_;
6848 0         0 my ($i, $d);
6849 0         0 for ($i = 0 ; $i < length($s) ;) {
6850 0         0 $FH->printf(' %03u', ord(substr($s, $i++, 1)));
6851             }
6852             }
6853              
6854             sub safe_isa {
6855 4     4 0 7 eval {$_[0]->isa($_[1])};
  4         57  
6856             }
6857              
6858             sub file_read {
6859 12     12 0 16 my $self = shift;
6860 12 100       24 if (@_) {
6861 4         17 $self->{file_read} = $_[0];
6862             } else {
6863 8         31 $self->{file_read};
6864             }
6865             }
6866              
6867             sub file_size {
6868 313     313 0 379 my $self = shift;
6869 313 100       449 if (@_) {
6870 3         10 $self->{file_size} = $_[0];
6871             } else {
6872 310         531 $self->{file_size};
6873             }
6874             }
6875              
6876             sub file_processed {
6877 305     305 0 727 my $self = shift;
6878 305 100       431 if (@_) {
6879 2         5 $self->{file_processed} = $_[0];
6880             } else {
6881 303         429 $self->{file_processed};
6882             }
6883             }
6884              
6885             sub offset {
6886 1651     1651 1 1903 my $self = shift;
6887 1651 100       2297 if (@_) {
6888 345         557 $self->{offset} = $_[0];
6889             } else {
6890 1306         2155 $self->{offset};
6891             }
6892             }
6893              
6894             sub buffer {
6895 1252     1252 0 1494 my $self = shift;
6896 1252 50       1851 if (@_) {
6897 0         0 $self->{buffer} = $_[0];
6898             } else {
6899 1252         1689 $self->{buffer};
6900             }
6901             }
6902              
6903             sub maybe_chained {
6904 2     2 0 4 my $self = shift;
6905 2 50       5 if (@_) {
6906 0         0 $self->{maybe_chained} = $_[0];
6907             } else {
6908 2         11 $self->{maybe_chained};
6909             }
6910             }
6911              
6912             sub really_clear_buffer {
6913 2     2 0 5 my $self = shift;
6914 2         5 my $buffer = $self->{buffer};
6915              
6916 2 50       6 $self->crc_calc(length($$buffer)) if !defined $self->crc;
6917 2         6 $self->file_processed($self->file_processed + $self->offset);
6918 2         5 substr($$buffer, 0, $self->offset) = '';
6919 2         4 $self->offset(0);
6920             }
6921              
6922             sub cp_fit {
6923 2     2 0 4 my $self = shift;
6924 2 50       6 if (@_) {
6925 0         0 $self->{cp_fit} = $_[0];
6926             } else {
6927 2         18 $self->{cp_fit};
6928             }
6929             }
6930              
6931             sub cp_fit_FH {
6932 3     3 0 6 my $self = shift;
6933 3 50       12 if (@_) {
6934 0         0 $self->{cp_fit_FH} = $_[0];
6935             } else {
6936 3         9 $self->{cp_fit_FH};
6937             }
6938             }
6939              
6940             sub clear_buffer {
6941 5     5 0 10 my $self = shift;
6942 5 100       10 if ($self->offset > 0) {
6943 2 50       10 if ($self->cp_fit) {
6944 0         0 my $FH = $self->cp_fit_FH;
6945              
6946 0 0 0     0 if (&safe_isa($FH, 'FileHandle') && $FH->opened) {
6947 0         0 my $buffer = $self->buffer;
6948              
6949 0         0 $FH->print(substr($$buffer, 0, $self->offset));
6950 0         0 $FH->flush;
6951 0         0 $self->really_clear_buffer;
6952             }
6953             } else {
6954 2         9 $self->really_clear_buffer;
6955             }
6956             }
6957             }
6958              
6959             =head2 Object methods
6960              
6961             =over 4
6962              
6963             =item file(I)
6964              
6965             sets the name I of a .FIT file.
6966              
6967             =back
6968              
6969             =cut
6970              
6971             sub file {
6972 7     7 1 1740 my $self = shift;
6973 7 100       21 if (@_) {
6974 3         11 $self->{file} = $_[0];
6975             } else {
6976 4         11 $self->{file};
6977             }
6978             }
6979              
6980             =over 4
6981              
6982             =item open()
6983              
6984             opens the .FIT file.
6985              
6986             =back
6987              
6988             =cut
6989              
6990             sub open {
6991 3     3 1 15 my $self = shift;
6992 3         8 my $fn = $self->file;
6993              
6994 3 50       12 if ($fn ne '') {
6995 3         11 my $FH = $self->FH;
6996              
6997 3 50       19 if ($FH->open("< $fn")) {
6998 3 50       237 if (binmode $FH, ':raw') {
6999 3         10 1;
7000             } else {
7001 0         0 $self->error("binmode \$FH, ':raw': $!");
7002             }
7003             } else {
7004 0         0 $self->error("\$FH->open(\"< $fn\"): $!");
7005             }
7006             } else {
7007 0         0 $self->error('no file name given');
7008             }
7009             }
7010              
7011             sub FH {
7012 11     11 0 22 my $self = shift;
7013 11 50       21 if (@_) {
7014 0         0 $self->{FH} = $_[0];
7015             } else {
7016 11         69 $self->{FH};
7017             }
7018             }
7019              
7020             sub EOF {
7021 1     1   2 my $self = shift;
7022 1 50       3 if (@_) {
7023 1         2 $self->{EOF} = $_[0];
7024             } else {
7025 0         0 $self->{EOF};
7026             }
7027             }
7028              
7029             sub end_of_chunk {
7030 1     1 0 2 my $self = shift;
7031 1 50       3 if (@_) {
7032 1         5 $self->{end_of_chunk} = $_[0];
7033             } else {
7034 0         0 $self->{end_of_chunk};
7035             }
7036             }
7037              
7038             sub fill_buffer {
7039 644     644 0 936 my ($self, $req) = @_;
7040 644         957 my $buffer = $self->buffer;
7041              
7042 644         1150 while (length($$buffer) - $self->offset < $req) {
7043 5         18 $self->clear_buffer;
7044 5         11 my $n = $self->FH->read($$buffer, BUFSIZ, length($$buffer));
7045              
7046 5 100       359 if ($n > 0) {
7047 4         19 $self->file_read($self->file_read + $n);
7048              
7049 4 100       13 if (defined $self->file_size) {
7050 1 50       4 if (defined $self->crc) {
7051 1         5 $self->crc_calc($n);
7052             } else {
7053 0         0 $self->crc_calc(length($$buffer));
7054             }
7055             }
7056             } else {
7057 1 50       55 if (defined $n) {
7058 1         6 $self->error("unexpected EOF");
7059 1         4 $self->EOF(1);
7060             } else {
7061 0         0 $self->error("read(FH): $!");
7062             }
7063             return undef
7064 1         5 }
7065             }
7066 643         1206 return 1
7067             }
7068              
7069             my $header_template = 'C C v V V';
7070             my $header_length = length(pack($header_template));
7071              
7072             sub FIT_HEADER_LENGTH {
7073 0     0 1 0 $header_length;
7074             }
7075              
7076             my $FIT_signature_string = '.FIT';
7077             my $FIT_signature = unpack('V', $FIT_signature_string);
7078              
7079             my $header_crc_template = 'v';
7080             my $header_crc_length = length(pack($header_crc_template));
7081              
7082             =over 4
7083              
7084             =item fetch_header()
7085              
7086             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.
7087              
7088             =back
7089              
7090             =cut
7091              
7092             sub fetch_header {
7093 3     3 1 19 my $self = shift;
7094 3 50       11 $self->fill_buffer($header_length) || return undef;
7095              
7096 3         13 my $buffer = $self->buffer;
7097 3         14 my $h_min = substr($$buffer, $self->offset, $header_length);
7098 3         24 my ($h_len, $proto_ver, $prof_ver, $f_len, $sig) = unpack($header_template, $h_min);
7099              
7100 3         19 $self->offset($self->offset + $header_length);
7101              
7102 3 50       11 if ($h_len < $header_length) {
7103 0         0 $self->error("not a .FIT header ($h_len < $header_length)");
7104 0         0 ();
7105             } else {
7106 3         6 my $extra;
7107              
7108 3 50       12 if ($h_len > $header_length) {
7109 3 50       14 $self->fill_buffer($h_len - $header_length) || return undef;
7110 3         11 $extra = substr($$buffer, $self->offset, $h_len - $header_length);
7111 3         18 $self->offset($self->offset + $h_len - $header_length);
7112             }
7113              
7114 3 50       11 if ($sig != $FIT_signature) {
7115             $self->error("not a .FIT header (" .
7116 0 0 0     0 join('', map {($_ ne "\\" && 0x20 >= ord($_) && ord($_) <= 0x7E) ? $_ : sprintf("\\x%02X", ord($_))} split //, pack('V', $sig))
  0         0  
7117             . " ne '$FIT_signature_string')");
7118 0         0 ();
7119             } else {
7120 3         9 my ($crc_expected, $crc_calculated);
7121              
7122 3 50 33     33 if ($proto_ver >= $protocol_version_header_crc_started && length($extra) >= $header_crc_length) {
7123 3         11 $crc_expected = unpack($header_crc_template, substr($extra, -$header_crc_length));
7124 3         10 substr($extra, -$header_crc_length) = '';
7125 3         17 $crc_calculated = $self->crc_of_string(0, \$h_min, 0, $header_length);
7126             }
7127              
7128 3         7 my $f_size = $f_len + $h_len;
7129              
7130 3         23 $self->file_size($f_size);
7131              
7132 3 50       9 unless (defined $self->crc) {
7133 3         10 $self->crc(0);
7134 3         11 $self->crc_calc(length($$buffer));
7135             }
7136 3         25 $self->{profile_version} = $prof_ver;
7137              
7138 3         35 ($f_size, $proto_ver, $prof_ver, $extra, $crc_expected, $crc_calculated);
7139             }
7140             }
7141             }
7142              
7143             =over 4
7144              
7145             =item fetch()
7146              
7147             reads a message in the .FIT file, and returns C<1> on success, or C on failure or EOF.
7148              
7149             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).
7150              
7151             =back
7152              
7153             =cut
7154              
7155             sub fetch {
7156 302     302 1 31714 my $self = shift;
7157 302 100       533 $self->fill_buffer($crc_octets) or return undef;
7158              
7159 301         465 my $buffer = $self->buffer;
7160 301         445 my $i = $self->offset;
7161 301         473 my $j = $self->file_processed + $i;
7162              
7163 301 100 33     442 if ($j < $self->file_size) {
    50          
7164 300         506 my $record_header = ord(substr($$buffer, $i, 1));
7165 300         378 my $local_msg_type = -1;
7166              
7167 300 50       614 if ($record_header & $rechd_mask_compressed_timestamp_header) {
    100          
7168 0         0 $local_msg_type = ($record_header & $rechd_mask_cth_local_message_type) >> $rechd_offset_cth_local_message_type
7169             } elsif ($record_header & $rechd_mask_definition_message) {
7170 36         127 $self->fetch_definition_message
7171             } else {
7172 264         336 $local_msg_type = $record_header & $rechd_mask_local_message_type
7173             }
7174              
7175 300 100       457 if ($local_msg_type < 0) {
7176 36         101 1;
7177             } else {
7178 264         410 my $desc = $self->data_message_descriptor->[$local_msg_type];
7179              
7180 264 50       447 if (ref $desc eq 'HASH') {
7181 264         426 $self->fetch_data_message($desc);
7182             } else {
7183 0         0 $self->error(sprintf("%d at %ld: not defined", $record_header, $j));
7184             }
7185             }
7186             } elsif (!$self->maybe_chained && $j > $self->file_size) {
7187 0         0 $self->trailing_garbages($self->trailing_garbages + length($$buffer) - $i);
7188 0         0 $self->offset(length($$buffer));
7189 0         0 1;
7190             } else {
7191 1 50       3 $self->crc_calc(length($$buffer)) if !defined $self->crc;
7192              
7193 1         3 my ($crc_expected, $k);
7194              
7195 1         6 for ($crc_expected = 0, $k = $crc_octets ; $k > 0 ;) {
7196 2         6 $crc_expected = ($crc_expected << 8) + ord(substr($$buffer, $i + --$k, 1));
7197             }
7198              
7199 1         6 $self->crc_expected($crc_expected);
7200 1         4 $self->offset($i + $crc_octets);
7201 1         6 $self->end_of_chunk(1);
7202 1         2 !$self->maybe_chained;
7203             }
7204             }
7205              
7206             sub error_callback { # consider making internal (_error_callback)
7207 0     0 0 0 my $self = shift;
7208 0 0       0 if (@_) {
7209 0 0       0 if (&safe_isa($_[0], 'CODE')) {
7210 0         0 $self->{error_callback_argv} = [@_[1 .. $#_]];
7211 0         0 $self->{error_callback} = $_[0];
7212             } else {
7213 0         0 undef;
7214             }
7215             } else {
7216 0         0 $self->{error_callback};
7217             }
7218             }
7219              
7220             =over 4
7221              
7222             =item error()
7223              
7224             returns an error message recorded by a method.
7225              
7226             =back
7227              
7228             =cut
7229              
7230             sub error {
7231 1     1 1 2 my $self = shift;
7232 1 50       5 if (@_) {
7233 1         2 my ($p, $fn, $l, $subr, $fit);
7234              
7235 1         14 (undef, $fn, $l) = caller(0);
7236 1         40 ($p, undef, undef, $subr) = caller(1);
7237 1         7 $fit = $self->file;
7238 1 50       7 $fit .= ': ' if $fit ne '';
7239              
7240 1         7 $self->{error} = "${p}::$subr\#$l\@$fn: $fit$_[0]";
7241              
7242 1 50       8 if (&safe_isa($self->{error_callback}, 'CODE')) {
7243 0 0       0 my $argv = &safe_isa($self->{error_callback_argv}, 'ARRAY') ? $self->{error_callback_argv} : [];
7244              
7245 0         0 $self->{error_callback}->($self, @$argv);
7246             } else {
7247 1         3 undef;
7248             }
7249             } else {
7250 0         0 $self->{error};
7251             }
7252             }
7253              
7254             =over 4
7255              
7256             =item crc()
7257              
7258             CRC-16 calculated from the contents of a .FIT file.
7259              
7260             =back
7261              
7262             =cut
7263              
7264             sub crc {
7265 18     18 1 25 my $self = shift;
7266 18 100       39 if (@_) {
7267 7         18 $self->{crc} = $_[0];
7268             } else {
7269 11         39 $self->{crc};
7270             }
7271             }
7272              
7273             sub crc_of_string {
7274 7     7 0 20 my ($self, $crc, $p, $b, $n) = @_;
7275 7         12 my $e = $b + $n;
7276 7         25 while ($b < $e) {
7277 27494         48296 $crc = ($crc >> 8) ^ $crc_table[($crc & (2 ** 8 - 1)) ^ ord(substr($$p, $b++, 1))];
7278             }
7279 7         42 $crc;
7280             }
7281              
7282             sub crc_calc {
7283 4     4 0 11 my ($self, $m) = @_;
7284 4         17 my $over = $self->file_read - $self->file_size;
7285 4 100       15 $over = 0 if $over < 0;
7286              
7287 4 50       12 if ($m > $over) {
7288 4         9 my $buffer = $self->buffer;
7289 4         12 $self->crc($self->crc_of_string($self->crc, $buffer, length($$buffer) - $m, $m - $over));
7290             }
7291             }
7292              
7293             =over 4
7294              
7295             =item crc_expected()
7296              
7297             CRC-16 attached to the end of a .FIT file. Only available after all contents of the file has been read.
7298              
7299             =back
7300              
7301             =cut
7302              
7303             sub crc_expected {
7304 1     1 1 3 my $self = shift;
7305 1 50       4 if (@_) {
7306 1         2 $self->{crc_expected} = $_[0];
7307             } else {
7308 0         0 $self->{crc_expected};
7309             }
7310             }
7311              
7312             =over 4
7313              
7314             =item trailing_garbages()
7315              
7316             number of octets after CRC-16, 0 usually.
7317              
7318             =back
7319              
7320             =cut
7321              
7322             sub trailing_garbages {
7323 0     0 1 0 my $self = shift;
7324 0 0       0 if (@_) {
7325 0         0 $self->{trailing_garbages} = $_[0];
7326             } else {
7327 0         0 $self->{trailing_garbages};
7328             }
7329             }
7330              
7331             sub numeric_date_time {
7332 0     0 0 0 my $self = shift;
7333 0 0       0 if (@_) {
7334 0         0 $self->{numeric_date_time} = $_[0];
7335             } else {
7336 0         0 $self->{numeric_date_time};
7337             }
7338             }
7339              
7340             sub date_string {
7341 0     0 0 0 my ($self, $time) = @_;
7342 0 0       0 my ($s, $mi, $h, $d, $mo, $y, $gmt) = $self->use_gmtime ? ((gmtime($time))[0 .. 5], 'Z') : (localtime($time))[0 .. 5];
7343 0         0 sprintf('%04u-%02u-%02uT%02u:%02u:%02u%s', $y + 1900, $mo + 1, $d, $h, $mi, $s, $gmt);
7344             }
7345              
7346             sub named_type_value {
7347 0     0 0 0 my ($self, $type_name, $val) = @_;
7348 0         0 my $typedesc = $named_type{$type_name};
7349              
7350 0 0       0 if (ref $typedesc ne 'HASH') {
    0          
    0          
7351 0         0 $self->error("$type_name is not a named type");
7352             } elsif ($typedesc->{_mask}) {
7353 0 0       0 if ($val !~ /^[-+]?\d+$/) {
7354 0         0 my $num = 0;
7355              
7356 0         0 for my $expr (split /,/, $val) {
7357 0         0 $expr =~ s/^.*=//;
7358              
7359 0 0       0 if ($expr =~ s/^0[xX]//) {
7360 0         0 $num |= hex($expr);
7361             } else {
7362 0         0 $num |= $expr + 0;
7363             }
7364             }
7365              
7366 0         0 $num;
7367             } else {
7368 0         0 my $mask = 0;
7369 0         0 my @key;
7370              
7371 0         0 for my $key (sort {$typedesc->{$b} <=> $typedesc->{$a}} grep {/^[A-Za-z]/} keys %$typedesc) {
  0         0  
  0         0  
7372 0         0 push @key, $key . '=' . ($val & $typedesc->{$key});
7373 0         0 $mask |= $typedesc->{$key};
7374             }
7375              
7376 0         0 my $rest = $val & ~$mask & ((1 << ($size[$typedesc->{_base_type}] * 8)) - 1);
7377              
7378 0 0       0 if ($rest) {
    0          
7379 0         0 my $width = $size[$typedesc->{_base_type}] * 2;
7380              
7381 0         0 join(',', @key, sprintf("0x%0${width}X", $rest));
7382             } elsif (@key) {
7383 0         0 join(',', @key);
7384             } else {
7385 0         0 0;
7386             }
7387             }
7388             } elsif ($type_name eq 'date_time') {
7389 0 0 0     0 if ($val !~ /^[-+]?\d+$/) {
    0          
7390 0         0 my ($y, $mo, $d, $h, $mi, $s, $gmt) = $val =~ /(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)\D+(\d+)([zZ]?)/;
7391              
7392 0 0       0 ($gmt ne '' ? timegm($s, $mi, $h, $d, $mo - 1, $y - 1900) : timelocal($s, $mi, $h, $d, $mo - 1, $y - 1900)) + $typedesc->{_offset};
7393             } elsif ($val >= $typedesc->{_min} && $val != $invalid[$typedesc->{_base_type}]) {
7394 0 0       0 if ($self->numeric_date_time) {
7395 0         0 $val - $typedesc->{_offset};
7396             } else {
7397 0         0 $self->date_string($val - $typedesc->{_offset});
7398             }
7399             } else {
7400 0         0 undef;
7401             }
7402             } else {
7403 0         0 $typedesc->{$val};
7404             }
7405             }
7406              
7407             sub data_message_descriptor {
7408 300     300 0 362 my $self = shift;
7409              
7410 300 50       437 if (@_) {
7411 0         0 $self->{data_message_descriptor} = $_[0];
7412             } else {
7413 300 100       591 $self->{data_message_descriptor} = [] if ref $self->{data_message_descriptor} ne 'ARRAY';
7414 300         555 $self->{data_message_descriptor};
7415             }
7416             }
7417              
7418             sub data_message_callback {
7419 46     46 0 75 $_[0]->{data_message_callback};
7420             }
7421              
7422             =over 4
7423              
7424             =item data_message_callback_by_num(I, I[, I, ...])
7425              
7426             register a function I which is called when a data message with the messag number I is fetched.
7427              
7428             =back
7429              
7430             =cut
7431              
7432             my $msgnum_anon = $invalid[FIT_UINT16];
7433             my $msgname_anon = '';
7434              
7435             sub data_message_callback_by_num {
7436 0     0 1 0 my $self = shift;
7437 0         0 my $num = shift;
7438 0         0 my $cbmap = $self->data_message_callback;
7439 0         0 my ($msgtype, $name);
7440              
7441 0 0       0 if ($num == $msgnum_anon) {
    0          
    0          
7442 0 0       0 if (@_) {
7443 0 0       0 if (ref $_[0] eq 'CODE') {
7444 0         0 $cbmap->{$msgname_anon} = $cbmap->{$msgnum_anon} = [@_];
7445             } else {
7446 0         0 $self->error('not a CODE');
7447             }
7448             } else {
7449 0         0 my %res;
7450 0         0 foreach $num (keys %msgtype_by_num) {
7451 0         0 my $cb = $cbmap->{$num};
7452 0 0       0 ref $cb eq 'ARRAY' and $res{$num} = [@$cb];
7453             }
7454 0         0 \%res;
7455             }
7456             } elsif (!defined($msgtype = $msgtype_by_num{$num})) {
7457 0         0 $self->error("$num is not a message type number");
7458             } elsif (@_) {
7459 0 0       0 if (ref $_[0] eq 'CODE') {
7460 0         0 $cbmap->{$num} = [@_];
7461 0 0       0 $msgtype->{_name} ne '' and $cbmap->{$msgtype->{_name}} = $cbmap->{$num};
7462 0         0 $cbmap->{$num};
7463             } else {
7464 0         0 $self->error('not a CODE');
7465             }
7466             } else {
7467 0         0 my $cb = $cbmap->{$num};
7468 0 0       0 ref $cb eq 'ARRAY' ? [@$cb] : [];
7469             }
7470             }
7471              
7472             =over 4
7473              
7474             =item data_message_callback_by_name(I, I[, I, ...])
7475              
7476             register a function I which is called when a data message with the name I is fetched.
7477              
7478             =back
7479              
7480             =cut
7481              
7482             sub data_message_callback_by_name {
7483 10     10 1 45 my $self = shift;
7484 10         17 my $name = shift;
7485 10         21 my $cbmap = $self->data_message_callback;
7486 10         14 my $msgtype;
7487              
7488 10 100       55 if ($name eq $msgname_anon) {
    50          
    50          
7489 2 100       7 if (@_) {
7490 1 50       12 if (ref $_[0] eq 'CODE') {
7491 1         6 $cbmap->{$msgname_anon} = $cbmap->{$msgnum_anon} = [@_];
7492             } else {
7493 0         0 $self->error('not a CODE');
7494             }
7495             } else {
7496 1         2 my %res;
7497 1         26 foreach $name (keys %msgtype_by_name) {
7498 98         113 my $cb = $cbmap->{$name};
7499 98 100       185 ref $cb eq 'ARRAY' and $res{$name} = [@$cb];
7500             }
7501 1         8 \%res;
7502             }
7503             } elsif (!defined($msgtype = $msgtype_by_name{$name})) {
7504 0         0 $self->error("$name is not a message type name");
7505             } elsif (@_) {
7506 8 50       24 if (ref $_[0] eq 'CODE') {
7507 8         41 $cbmap->{$msgtype->{_number}} = $cbmap->{$name} = [@_];
7508             } else {
7509 0         0 $self->error('not a CODE');
7510             }
7511             } else {
7512 0         0 my $cb = $cbmap->{$name};
7513 0 0       0 ref $cb eq 'ARRAY' ? [@$cb] : [];
7514             }
7515             }
7516              
7517             sub undocumented_field_name {
7518 208     208 0 352 my ($self, $index, $size, $type, $i_string) = @_;
7519             # 'xxx' . $i_string . '_' . $index . '_' . $size . '_' . $type;
7520 208         481 'xxx' . $index;
7521             }
7522              
7523             sub syscallback_devdata_id {
7524 0     0 0 0 my ($self, $desc, $v) = @_;
7525 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)};
7526 0         0 my ($emsg, $warn);
7527              
7528 0 0 0     0 if (!defined $i_id) {
    0          
    0          
7529 0         0 $emsg = "no application_id";
7530 0         0 $warn = 1;
7531             } elsif ($T_id != FIT_UINT8 && $T_id != FIT_BYTE) {
7532 0         0 croak "There is a bug here, this should be resolved soon";
7533             # $type_name has not been declared in this scope need to figure out what it should be
7534             # $emsg = "base type of application_id is $type_name [$T_id] ($T_id)";
7535             } elsif (!defined $i_index) {
7536 0         0 $emsg = "no developer_data_index";
7537             }
7538              
7539 0 0       0 if ($emsg ne '') {
7540 0 0       0 if ($warn) {
7541 0         0 $self->error("suspicious developer data id message ($emsg)");
7542             } else {
7543 0         0 $self->error("broken developer data id message ($emsg)");
7544 0         0 return undef;
7545             }
7546             }
7547              
7548 0         0 my $devdata_by_index = $self->{devdata_by_index};
7549 0 0       0 ref $devdata_by_index eq 'HASH' or $devdata_by_index = $self->{devdata_by_index} = +{};
7550              
7551 0         0 my $devdata_by_id = $self->{devdata_by_id};
7552 0 0       0 ref $devdata_by_id eq 'HASH' or $devdata_by_id = $self->{devdata_by_id} = +{};
7553              
7554 0         0 my $id;
7555 0 0       0 if ($T_id == FIT_UINT8) {
7556 0         0 $id = pack('C*', @$v[$i_id .. ($i_id + $c_id - 1)]);
7557             } else {
7558 0         0 $id = $v->[$i_id];
7559             }
7560              
7561 0         0 my %devdata = (id => $id, index => $v->[$i_index]);
7562 0         0 $devdata_by_id->{$devdata{id}} = $devdata_by_index->{$devdata{index}} = \%devdata;
7563             }
7564              
7565             sub syscallback_devdata_field_desc {
7566 0     0 0 0 my ($self, $desc, $v) = @_;
7567              
7568             my ($i_index, $I_index, $i_field_num, $I_field_num,
7569             $i_base_type_id, $T_base_type_id, $I_base_type_id,
7570             $i_field_name, $T_field_name, $c_field_name)
7571 0         0 = @$desc{qw(i_developer_data_index I_developer_data_index i_field_definition_number I_field_definition_number
7572             i_fit_base_type_id T_fit_base_type_id I_fit_base_type_id
7573             i_field_name T_field_name c_field_name)};
7574              
7575 0         0 my ($emsg, $warn, $o_name);
7576              
7577 0 0 0     0 if (!defined $i_index) {
    0          
    0          
    0          
    0          
    0          
7578 0         0 $emsg = 'no developer_data_index';
7579             } elsif (!defined $i_field_num) {
7580 0         0 $emsg = 'no field_num';
7581             } elsif (!defined $i_base_type_id) {
7582 0         0 $emsg = 'no base_type_id';
7583             } elsif ($T_base_type_id != FIT_UINT8) {
7584 0         0 croak "There is a bug here, this should be resolved soon";
7585             # $type_name has not been declared in this scope need to figure out what it should be
7586             # $emsg = "base type of base_type_id is $type_name [$T_base_type_id] ($T_base_type_id)";
7587             } elsif (!defined $i_field_name) {
7588 0         0 $emsg = 'no field_name';
7589 0         0 $warn = 1;
7590             } elsif ($T_field_name != FIT_STRING || $c_field_name <= 0) {
7591 0         0 $emsg = "field_name is not a non-empty string";
7592 0         0 $warn = 1;
7593             } else {
7594 0         0 $o_name = $self->string_value($v, $i_field_name, $c_field_name);
7595             }
7596              
7597 0 0       0 if ($emsg ne '') {
7598 0 0       0 if ($warn) {
7599 0         0 $self->error("suspicious field description message ($emsg)");
7600             } else {
7601 0         0 $self->error("broken field description message ($emsg)");
7602 0         0 return undef;
7603             }
7604             }
7605              
7606 0         0 my $base_type = $v->[$i_base_type_id];
7607              
7608 0 0       0 if ($base_type == $I_base_type_id) {
7609 0         0 $self->error("invalid base type ($base_type)");
7610 0         0 return undef;
7611             }
7612              
7613 0 0       0 if ($base_type < 0) {
7614 0         0 $self->error("unknown base type ($base_type)");
7615 0         0 return undef;
7616             }
7617              
7618 0         0 $base_type &= $deffld_mask_type;
7619              
7620 0 0       0 unless ($base_type <= FIT_BASE_TYPE_MAX) {
7621 0         0 $self->error("unknown base type ($base_type)");
7622 0         0 return undef;
7623             }
7624              
7625 0         0 my $devdata_by_index = $self->{devdata_by_index};
7626              
7627 0 0       0 unless (ref $devdata_by_index eq 'HASH') {
7628 0         0 $self->error('no developer data id message before a field description message');
7629 0         0 return undef;
7630             }
7631              
7632 0         0 my $index = $v->[$i_index];
7633              
7634 0 0       0 if ($index == $I_index) {
7635 0         0 $self->error("invalid developer data index ($index)");
7636 0         0 return undef;
7637             }
7638              
7639 0         0 my $num = $v->[$i_field_num];
7640              
7641 0 0       0 if ($num == $I_field_num) {
7642 0         0 $self->error("invalid field definition number ($num)");
7643 0         0 return undef;
7644             }
7645              
7646 0         0 my $devdata = $devdata_by_index->{$index};
7647              
7648 0 0       0 unless (ref $devdata eq 'HASH') {
7649 0         0 $self->error("No developer data id message with the index $index before a field description message");
7650 0         0 return undef;
7651             }
7652              
7653 0         0 my $field_desc_by_num = $devdata->{field_desc_by_num};
7654 0 0       0 ref $field_desc_by_num eq 'HASH' or $field_desc_by_num = $devdata->{field_desc_by_num} = +{};
7655              
7656 0         0 my $field_desc_by_name = $devdata->{field_desc_by_name};
7657 0 0       0 ref $field_desc_by_name eq 'HASH' or $field_desc_by_name = $devdata->{field_desc_by_name} = +{};
7658              
7659 0         0 my $name = $o_name;
7660              
7661 0 0       0 if (defined $name) {
7662 0         0 $name =~ s/\s+/_/g;
7663 0         0 $name =~ s/\W/sprintf('_%02x_', ord($&))/ge;
  0         0  
7664             }
7665              
7666 0         0 my %fdesc = (
7667             '_index' => $index,
7668             '_num' => $num,
7669             '_name' => $name,
7670             'field_name' => $o_name,
7671             '_type' => $base_type,
7672             );
7673              
7674 0         0 for my $i_aname (grep {/^i_/} keys %$desc) {
  0         0  
7675 0 0       0 if ($i_aname !~ /^i_(developer_data_index|field_definition_number|fit_base_type_id|field_name)$/) {
7676 0         0 my $i = $desc->{$i_aname};
7677 0         0 my $aname = $i_aname;
7678              
7679 0         0 $aname =~ s/^i_//;
7680              
7681 0         0 my $I_aname = 'I_' . $aname;
7682 0         0 my $T_aname = 'T_' . $aname;
7683 0         0 my $c_aname = 'c_' . $aname;
7684              
7685 0 0       0 if ($desc->{$T_aname} == FIT_STRING) {
    0          
7686 0         0 $fdesc{$aname} = $self->string_value($v, $i, $desc->{$c_aname});
7687             } elsif ($v->[$i] != $desc->{$I_aname}) {
7688 0         0 $fdesc{$aname} = $v->[$i];
7689             }
7690             }
7691             }
7692              
7693 0 0       0 defined $name and $field_desc_by_name->{$name} = \%fdesc;
7694 0         0 $field_desc_by_num->{$num} = \%fdesc;
7695             }
7696              
7697             sub add_endian_converter {
7698 858     858 0 1502 my ($self, $endian, $type, $c, $i_string, $cvt) = @_;
7699              
7700 858 50 33     1694 if ($endian != $my_endian && $size[$type] > 1) {
7701 0         0 my ($p, $unp, $n);
7702              
7703 0 0       0 if ($size[$type] == 2) {
    0          
7704 0         0 ($p, $unp) = (qw(n v));
7705             } elsif ($size[$type] == 4) {
7706 0         0 ($p, $unp) = (qw(N V));
7707             } else {
7708 0         0 ($p, $unp, $n) = (qw(N V), 2);
7709             }
7710              
7711 0         0 push @$cvt, $p . $n, $unp . $n, $i_string, $size[$type], $c;
7712 0         0 1;
7713             } else {
7714 858         1126 0;
7715             }
7716             }
7717              
7718             sub fetch_definition_message {
7719 36     36 0 59 my $self = shift;
7720              
7721 36 50       59 $self->fill_buffer($defmsg_min_length) || return undef;
7722              
7723 36         59 my $buffer = $self->buffer;
7724 36         61 my $i = $self->offset;
7725 36         130 my ($record_header, $reserved, $endian, $msgnum, $nfields) = unpack($defmsg_min_template, substr($$buffer, $i, $defmsg_min_length));
7726              
7727 36 50       70 $endian = $endian ? 1 : 0;
7728 36         84 $self->offset($i + $defmsg_min_length);
7729              
7730 36         49 my $len = $nfields * $deffld_length;
7731              
7732 36 50       61 $self->fill_buffer($len) || return undef;
7733 36         61 $i = $self->offset;
7734 36 50       63 $msgnum = unpack('n', pack('v', $msgnum)) if $endian != $my_endian;
7735              
7736 36         135 my $msgtype = $msgtype_by_num{$msgnum};
7737 36         70 my $cbmap = $self->data_message_callback;
7738 36         51 my $e = $i + $len;
7739 36         50 my ($cb, %desc, $i_array, $i_array_t, $i_string, @cvt, @pi);
7740              
7741 36         85 $desc{local_message_type} = $record_header & $rechd_mask_local_message_type;
7742 36         87 $desc{message_number} = $msgnum;
7743 36 100 100     201 $desc{message_name} = $msgtype->{_name} if ref $msgtype eq 'HASH' && exists $msgtype->{_name};
7744 36 50       82 $cb = $cbmap->{$msgnum} if ref $cbmap->{$msgnum} eq 'ARRAY';
7745 36 50       80 $cb = $cbmap->{$msgnum_anon} if ref $cb ne 'ARRAY';
7746 36 100       71 $desc{callback} = $cb if ref $cb eq 'ARRAY';
7747 36         49 $desc{endian} = $endian;
7748 36         59 $desc{template} = 'C';
7749 36         72 $self->data_message_descriptor->[$desc{local_message_type}] = \%desc;
7750              
7751 36         83 for ($i_array = $i_array_t = $i_string = 1 ; $i + $deffld_length <= $e ; $i += $deffld_length) {
7752 858         2103 my ($index, $size, $type) = unpack($deffld_template, substr($$buffer, $i, $deffld_length));
7753 858         1238 my ($name, $tname, %attr, );
7754              
7755 858 100       1492 if (ref $msgtype eq 'HASH') {
7756 832         1748 my $fldtype = $msgtype->{$index};
7757              
7758 832 100       1348 if (ref $fldtype eq 'HASH') {
7759 650         3484 %attr = %$fldtype;
7760 650         1235 ($name, $tname) = @attr{qw(name type_name)};
7761 650         868 delete $attr{name};
7762 650         843 delete $attr{type_name};
7763             }
7764             }
7765              
7766 858 100       1529 $name = $self->undocumented_field_name($index, $size, $type, $i_string) if !defined $name;
7767 858         1476 $desc{$index} = $name;
7768 858         1112 $type &= $deffld_mask_type;
7769              
7770 858         1408 my $c = int($size / $size[$type] + 0.5);
7771              
7772 858         1870 $desc{'i_' . $name} = $i_array;
7773 858         1584 $desc{'o_' . $name} = $i_string;
7774 858         1765 $desc{'c_' . $name} = $c;
7775 858         1867 $desc{'s_' . $name} = $size[$type];
7776 858 100       1935 $desc{'a_' . $name} = \%attr if %attr;
7777 858 100       1549 $desc{'t_' . $name} = $tname if defined $tname;
7778 858         1569 $desc{'T_' . $name} = $type;
7779 858         2026 $desc{'N_' . $name} = $index;
7780 858         1557 $desc{'I_' . $name} = $invalid[$type];
7781              
7782 858         1897 $self->add_endian_converter($endian, $type, $c, $i_string, \@cvt);
7783              
7784 858         1120 $i_array += $c;
7785 858         954 $i_string += $size;
7786 858         1355 $desc{template} .= ' ' . $template[$type];
7787              
7788 858 50       1416 if ($packfactor[$type] > 1) {
7789 0         0 push @pi, $i_array_t, $c, $i_array;
7790 0         0 $c *= $packfactor[$type];
7791             }
7792              
7793 858 100       1284 $desc{template} .= $c if $c > 1;
7794 858         1719 $i_array_t += $c;
7795             }
7796              
7797 36         68 $desc{template_without_devdata} = $desc{template};
7798 36         58 $desc{devdata_first} = $i_array;
7799 36         63 $desc{devdata_nfields} = 0;
7800              
7801 36 50       76 if ($record_header & $rechd_mask_devdata_message) {
7802 0         0 $self->offset($e);
7803 0 0       0 $self->fill_buffer($devdata_min_length) || return undef;
7804 0         0 $i = $self->offset;
7805 0         0 ($nfields) = unpack($devdata_min_template, substr($$buffer, $i, $devdata_min_length));
7806 0         0 $self->offset($i + $devdata_min_length);
7807 0         0 $len = $nfields * $devdata_deffld_length;
7808 0 0       0 $self->fill_buffer($len) || return undef;
7809              
7810 0         0 my $devdata_by_index = $self->{devdata_by_index};
7811 0         0 my @emsg;
7812              
7813 0 0       0 if (ref $devdata_by_index ne 'HASH') {
7814 0         0 push @emsg, 'No developer data id';
7815 0         0 $devdata_by_index = +{};
7816             }
7817              
7818 0         0 for ($i = $self->offset, $e = $i + $len ; $i + $devdata_deffld_length <= $e ; $i += $devdata_deffld_length) {
7819 0         0 my ($fnum, $size, $index) = unpack($devdata_deffld_template, substr($$buffer, $i, $devdata_deffld_length));
7820 0         0 my $devdata = $devdata_by_index->{$index};
7821 0         0 my ($fdesc, $name, $type, %attr);
7822              
7823 0 0       0 if (ref $devdata eq 'HASH') {
7824 0         0 my $fdesc_by_num = $devdata->{field_desc_by_num};
7825              
7826 0 0       0 if (ref $fdesc_by_num eq 'HASH') {
7827 0         0 $fdesc = $fdesc_by_num->{$fnum};
7828             } else {
7829 0         0 push @emsg, "No field description message for developer data with index $index";
7830             }
7831             } else {
7832 0         0 push @emsg, "No developer data id with index $index";
7833             }
7834              
7835 0 0       0 if (ref $fdesc eq 'HASH') {
7836 0         0 %attr = %$fdesc;
7837 0         0 ($type, $name) = @attr{qw(_type _name)};
7838             } else {
7839 0         0 push @emsg, "No field with number $fnum for developer data with index $index";
7840 0         0 $type = FIT_UINT8;
7841             }
7842              
7843 0 0       0 $name = $self->undocumented_field_name($fnum, $size, $type, $i_string) if !defined $name;
7844 0         0 $name = "${index}_${fnum}_$name";
7845              
7846 0         0 my $c = int($size / $size[$type] + 0.5);
7847              
7848 0         0 $desc{'i_' . $name} = $i_array;
7849 0         0 $desc{'o_' . $name} = $i_string;
7850 0         0 $desc{'c_' . $name} = $c;
7851 0         0 $desc{'s_' . $name} = $size[$type];
7852 0 0       0 $desc{'a_' . $name} = \%attr if %attr;
7853 0         0 $desc{'T_' . $name} = $type;
7854 0         0 $desc{'N_' . $name} = $fnum;
7855 0         0 $desc{'I_' . $name} = $invalid[$type];
7856              
7857 0         0 $self->add_endian_converter($endian, $type, $c, $i_string, \@cvt);
7858              
7859 0         0 $i_array += $c;
7860 0         0 $i_string += $size;
7861 0         0 $desc{template} .= ' ' . $template[$type];
7862              
7863 0 0       0 if ($packfactor[$type] > 1) {
7864 0         0 push @pi, $type, $i_array_t, $c, $i_array;
7865 0         0 $c *= $packfactor[$type];
7866             }
7867              
7868 0 0       0 $desc{template} .= $c if $c > 1;
7869 0         0 $i_array_t += $c;
7870             }
7871              
7872 0         0 $desc{devdata_nfields} = $nfields;
7873 0 0       0 $self->error(join(' / ', @emsg)) if (@emsg);
7874             }
7875              
7876 36 50       67 $desc{endian_converter} = \@cvt if @cvt;
7877 36 50       62 $desc{packfilter_index} = \@pi if @pi;
7878 36         59 $desc{message_length} = $i_string;
7879 36         54 $desc{array_length} = $i_array;
7880 36         86 $self->offset($e);
7881 36         78 return 1
7882             }
7883              
7884             sub endian_convert {
7885 0     0 0 0 my ($self, $cvt, $buffer, $i) = @_;
7886 0         0 my $j;
7887              
7888 0         0 for ($j = 4 ; $j < @$cvt ; $j += 5) {
7889 0         0 my ($b, $size, $c) = @$cvt[$j - 2, $j - 1, $j];
7890              
7891 0         0 for ($b += $i ; $c > 0 ; $b += $size, --$c) {
7892 0         0 my @v = unpack($cvt->[$j - 3], substr($$buffer, $b, $size));
7893 0         0 my ($k, $l);
7894              
7895 0         0 for ($k = 0, $l = $#v ; $k < $l ; ++$k, --$l) {
7896 0         0 @v[$k, $l] = @v[$l, $k];
7897             }
7898 0         0 substr($$buffer, $b, $size) = pack($cvt->[$j - 4], @v);
7899             }
7900             }
7901             }
7902              
7903             sub last_timestamp {
7904 0     0 0 0 my $self = shift;
7905 0 0       0 if (@_) {
7906 0         0 $self->{last_timestamp} = $_[0];
7907             } else {
7908 0         0 $self->{last_timestamp};
7909             }
7910             }
7911              
7912             sub fetch_data_message {
7913 264     264 0 377 my ($self, $desc) = @_;
7914              
7915 264 50       416 $self->fill_buffer($desc->{message_length}) || return undef;
7916 264 50       499 $self->endian_convert($desc->{endian_converter}, $self->buffer, $self->offset) if ref $desc->{endian_converter} eq 'ARRAY';
7917              
7918 264         384 my $buffer = $self->buffer;
7919 264         397 my $i = $self->offset;
7920             # unpack('f'/'d', ...) unpacks to NaN
7921 264         1085 my @v = unpack($desc->{template}, substr($$buffer, $i, $desc->{message_length}));
7922              
7923 264 50       556 if (ref $desc->{packfilter_index} eq 'ARRAY') {
7924 0         0 my $piv = $desc->{packfilter_index};
7925 0         0 my ($i, $j);
7926 0         0 my @v_t = @v;
7927              
7928 0         0 @v = ($v_t[0]);
7929              
7930 0         0 for ($i = 1, $j = 3 ; $j < @$piv ; $j += 4) {
7931 0         0 my ($type, $i_array_t, $c, $i_array) = @$piv[($j - 3) .. $j];
7932 0         0 my $delta = $packfactor[$type];
7933              
7934 0 0       0 $i < $i_array_t and push @v, @v_t[$i .. ($i_array_t - 1)];
7935 0         0 $i = $i_array_t + $c * $delta;
7936              
7937 0         0 for (; $i_array_t < $i ; $i_array_t += $delta) {
7938 0         0 push @v, $unpackfilter[$type]->(@v_t[$i_array_t .. ($i_array_t + $delta - 1)]);
7939             }
7940             }
7941             }
7942              
7943 264         565 $self->offset($i + $desc->{message_length});
7944              
7945 264         346 my $cb = $desc->{callback};
7946              
7947 264 100       432 if (ref $cb eq 'ARRAY') {
7948 21 50       44 $v[0] & $rechd_mask_compressed_timestamp_header and push @v, $self->last_timestamp + ($v[0] & $rechd_mask_cth_timestamp);
7949 21         92 $cb->[0]->($self, $desc, \@v, @$cb[1 .. $#$cb]);
7950             } else {
7951 243         571 1;
7952             }
7953             }
7954              
7955             sub pack_data_message {
7956 0     0 0 0 my ($self, $desc, $v) = @_;
7957 0         0 my $drop_devdata = $self->drop_developer_data;
7958              
7959 0 0 0     0 if ($drop_devdata && ($desc->{message_name} eq 'developer_data_id' || $desc->{message_name} eq 'field_description')) {
      0        
7960 0         0 '';
7961             } else {
7962 0         0 my $rv = $v;
7963              
7964 0 0       0 if (ref $desc->{packfilter_index} eq 'ARRAY') {
7965 0         0 my @v = ($v->[0]);
7966 0         0 my $piv = $desc->{packfilter_index};
7967 0         0 my ($i, $j);
7968              
7969 0         0 for ($i = 1, $j = 3 ; $j < @$piv ; $j += 4) {
7970 0         0 my ($type, $i_array_t, $c, $i_array) = @$piv[($j - 3) .. $j];
7971              
7972 0 0       0 $i < $i_array and push @v, @$v[$i .. ($i_array - 1)];
7973 0         0 $i = $i_array + $c;
7974              
7975 0         0 for (; $i_array < $i ; ++$i_array) {
7976 0         0 push @v, $packfilter[$type]->($v->[$i_array]);
7977             }
7978             }
7979 0         0 $rv = \@v;
7980             }
7981              
7982 0 0       0 if ($drop_devdata) {
7983 0 0       0 if ($desc->{devdata_first} > 0) {
7984 0         0 pack($desc->{template_without_devdata}, @$rv[0 .. ($desc->{devdata_first} - 1)]);
7985             } else {
7986 0         0 '';
7987             }
7988             } else {
7989 0         0 pack($desc->{template}, @$rv);
7990             }
7991             }
7992             }
7993              
7994             =over 4
7995              
7996             =item switched(I, I, I)
7997              
7998             returns real data type attributes for a C's union like field.
7999              
8000             =back
8001              
8002             =cut
8003              
8004             sub switched {
8005 0     0 1 0 my ($self, $desc, $v, $sw) = @_;
8006 0         0 my ($keyv, $key, $attr);
8007              
8008 0 0       0 if (ref $sw->{_by} eq 'ARRAY') {
8009 0         0 $keyv = $sw->{_by};
8010             } else {
8011 0         0 $keyv = [$sw->{_by}];
8012             }
8013              
8014 0         0 for $key (@$keyv) {
8015 0         0 my $i_name = 'i_' . $key;
8016 0         0 my $val;
8017              
8018 0 0 0     0 if (defined $desc->{$i_name} && ($val = $v->[$desc->{$i_name}]) != $desc->{'I_' . $key}) {
8019 0         0 my $key_tn = $desc->{'t_' . $key};
8020              
8021 0 0       0 if (defined $key_tn) {
8022 0         0 my $t_val = $self->named_type_value($key_tn, $val);
8023              
8024 0 0       0 $val = $t_val if defined $t_val;
8025             }
8026              
8027 0 0       0 if (ref $sw->{$val} eq 'HASH') {
8028 0         0 $attr = $sw->{$val};
8029 0         0 last;
8030             }
8031             }
8032             }
8033 0         0 $attr;
8034             }
8035              
8036             =over 4
8037              
8038             =item string_value(I, I, I)
8039              
8040             converts an array of character codes to a Perl string.
8041              
8042             =back
8043              
8044             =cut
8045              
8046             sub string_value {
8047 0     0 1 0 my ($self, $v, $i, $n) = @_;
8048 0         0 my $j;
8049              
8050 0         0 for ($j = 0 ; $j < $n ; ++$j) {
8051 0 0       0 $v->[$i + $j] == 0 && last;
8052             }
8053 0         0 pack('C*', @{$v}[$i .. ($i + $j - 1)]);
  0         0  
8054             }
8055              
8056             sub value_processed {
8057 0     0 0 0 my ($self, $num, $attr) = @_;
8058              
8059 0 0       0 if (ref $attr eq 'HASH') {
8060 0         0 my ($unit, $offset, $scale) = @{$attr}{qw(unit offset scale)};
  0         0  
8061              
8062 0 0 0     0 $num /= $scale if defined $scale and $scale > 0;
8063 0 0       0 $num -= $offset if $offset;
8064              
8065 0 0 0     0 if (defined $unit) {
    0          
8066 0         0 my $unit_tab = $self->unit_table($unit);
8067              
8068 0 0       0 if (ref $unit_tab eq 'HASH') {
8069 0         0 my ($unit1, $offset1, $scale1) = @{$unit_tab}{qw(unit offset scale)};
  0         0  
8070              
8071 0 0       0 if ($scale1 > 0) {
8072 0         0 $num /= $scale1;
8073 0         0 $scale += $scale1;
8074             }
8075 0 0       0 $num -= $offset1 if $offset1;
8076 0 0       0 $unit = $unit1 if $unit1 ne '';
8077             }
8078              
8079 0 0       0 if ($scale > 0) {
    0          
8080 0         0 my $below_pt = int(log($scale + 9) / log(10));
8081              
8082 0 0       0 if ($self->without_unit) {
8083 0         0 sprintf("%.${below_pt}f", $num);
8084             } else {
8085 0         0 sprintf("%.${below_pt}f %s", $num, $unit);
8086             }
8087             } elsif ($self->without_unit) {
8088 0         0 $num;
8089             } else {
8090 0         0 $num . " " . $unit;
8091             }
8092             } elsif (defined $scale and $scale > 0) {
8093 0         0 my $below_pt = int(log($scale + 9) / log(10));
8094 0         0 sprintf("%.${below_pt}f", $num);
8095             } else {
8096 0         0 $num;
8097             }
8098             } else {
8099 0         0 $num;
8100             }
8101             }
8102              
8103             sub value_unprocessed {
8104 0     0 0 0 my ($self, $str, $attr) = @_;
8105              
8106 0 0       0 if (ref $attr eq 'HASH') {
8107 0         0 my ($unit, $offset, $scale) = @{$attr}{qw(unit offset scale)};
  0         0  
8108 0         0 my $num = $str;
8109              
8110 0 0       0 if ($unit ne '') {
8111 0         0 my $unit_tab = $self->unit_table($unit);
8112              
8113 0 0       0 if (ref $unit_tab eq 'HASH') {
8114 0         0 my ($unit1, $offset1, $scale1) = @{$unit_tab}{qw(unit offset scale)};
  0         0  
8115              
8116 0 0       0 $scale += $scale1 if $scale1 > 0;
8117 0 0       0 $offset += $offset1 if $offset1;
8118 0 0       0 $unit = $unit1 if $unit1 ne '';
8119             }
8120              
8121 0 0 0     0 length($num) >= length($unit) && substr($num, -length($unit)) eq $unit
8122             and substr($num, -length($unit)) = '';
8123             }
8124              
8125 0 0       0 $num += $offset if $offset;
8126 0 0       0 $num *= $scale if $scale > 0;
8127 0         0 $num;
8128             } else {
8129 0         0 $str;
8130             }
8131             }
8132              
8133             =over 4
8134              
8135             =item value_cooked(I, I, I, I)
8136              
8137             converts I to a (hopefully) human readable form.
8138              
8139             =item value_uncooked(I, I, I, I)
8140              
8141             converts a human readable representation of a datum to an original form.
8142              
8143             =back
8144              
8145             =cut
8146              
8147             sub value_cooked {
8148 0     0 1 0 my ($self, $tname, $attr, $invalid, $val) = @_;
8149              
8150 0 0       0 if ($val == $invalid) {
8151 0         0 $val;
8152             } else {
8153 0 0       0 if (defined $tname) {
8154 0         0 my $vname = $self->named_type_value($tname, $val);
8155              
8156 0 0       0 defined $vname && return $vname;
8157             }
8158              
8159 0 0       0 if (ref $attr eq 'HASH') {
8160 0         0 $self->value_processed($val, $attr);
8161             } else {
8162 0         0 $val;
8163             }
8164             }
8165             }
8166              
8167             sub value_uncooked {
8168 0     0 1 0 my ($self, $tname, $attr, $invalid, $val) = @_;
8169              
8170 0 0       0 if ($val !~ /^[-+]?\d+$/) {
8171 0 0       0 if ($tname ne '') {
8172 0         0 my $vnum = $self->named_type_value($tname, $val);
8173              
8174 0 0       0 defined $vnum && return $vnum;
8175             }
8176              
8177 0 0       0 if (ref $attr eq 'HASH') {
8178 0         0 $self->value_unprocessed($val, $attr);
8179             } else {
8180 0         0 $val;
8181             }
8182             } else {
8183 0         0 $val;
8184             }
8185             }
8186              
8187             sub seconds_to_hms {
8188 0     0 0 0 my ($self, $s) = @_;
8189 0         0 my $sign = 1;
8190              
8191 0 0       0 if ($s < 0) {
8192 0         0 $sign = -1;
8193 0         0 $s = -$s;
8194             }
8195              
8196 0         0 my $h = int($s / 3600);
8197 0         0 my $m = int(($s - $h * 3600) / 60);
8198              
8199 0         0 $s -= $h * 3600 + $m * 60;
8200              
8201 0 0       0 my $hms = sprintf('%s%uh%um%g', $sign < 0 ? '-' : '', $h, $m, $s);
8202              
8203 0 0       0 $hms =~ s/\./s/ or $hms .= 's';
8204 0         0 $hms;
8205             }
8206              
8207             sub drop_developer_data {
8208 0     0 0 0 my $self = shift;
8209 0 0       0 if (@_) {
8210 0         0 $self->{drop_developer_data} = $_[0];
8211             } else {
8212 0         0 $self->{drop_developer_data};
8213             }
8214             }
8215              
8216             sub initialize {
8217 3     3 0 6 my $self = shift;
8218 3         7 my $buffer = '';
8219              
8220 3         47 %$self = (
8221             'error' => undef,
8222             'file_read' => 0,
8223             'file_processed' => 0,
8224             'offset' => 0,
8225             'buffer' => \$buffer,
8226             'FH' => new FileHandle,
8227             'data_message_callback' => +{},
8228             'unit_table' => +{},
8229             'drop_developer_data' => 0,
8230             );
8231              
8232 3         211 $self->data_message_callback_by_name(developer_data_id => \&syscallback_devdata_id);
8233 3         11 $self->data_message_callback_by_name(field_description => \&syscallback_devdata_field_desc);
8234 3         832 $self;
8235             }
8236              
8237             # undocumented (used by fitdump.pl is chained files)
8238             sub reset {
8239 0     0 0 0 my $self = shift;
8240 0         0 $self->clear_buffer;
8241              
8242 0         0 %$self = map {($_ => $self->{$_})} qw(error buffer FH data_message_callback unit_table profile_version
  0         0  
8243             cp_fit cp_fit_FH EOF use_gmtime numeric_date_time without_unit maybe_chained);
8244              
8245 0         0 my $buffer = $self->buffer;
8246 0         0 $self->file_read(length($$buffer));
8247 0         0 $self->file_processed(0);
8248 0         0 $self;
8249             }
8250              
8251             my @type_name = ();
8252              
8253             $type_name[FIT_ENUM] = 'ENUM';
8254             $type_name[FIT_SINT8] = 'SINT8';
8255             $type_name[FIT_UINT8] = 'UINT8';
8256             $type_name[FIT_SINT16] = 'SINT16';
8257             $type_name[FIT_UINT16] = 'UINT16';
8258             $type_name[FIT_SINT32] = 'SINT32';
8259             $type_name[FIT_UINT32] = 'UINT32';
8260             $type_name[FIT_STRING] = 'STRING';
8261             $type_name[FIT_FLOAT32] = 'FLOAT32';
8262             $type_name[FIT_FLOAT64] = 'FLOAT64';
8263             $type_name[FIT_UINT8Z] = 'UINT8Z';
8264             $type_name[FIT_UINT16Z] = 'UINT16Z';
8265             $type_name[FIT_UINT32Z] = 'UINT32Z';
8266             $type_name[FIT_BYTE] = 'BYTE';
8267              
8268             sub isnan {
8269 0     0 0 0 my $ret_val;
8270 6     6   317572 do { no warnings 'numeric'; $ret_val = !defined( $_[0] <=> 9**9**9 ) };
  6         14  
  6         17982  
  0         0  
  0         0  
8271 0         0 return $ret_val
8272             }
8273              
8274             sub print_all_fields {
8275 0     0 0 0 my ($self, $desc, $v, %opt) = @_;
8276 0         0 my ($indent, $FH, $skip_invalid) = @opt{qw(indent FH skip_invalid)};
8277              
8278 0 0       0 $FH=\*STDOUT if !defined $FH;
8279 0 0       0 $FH->print($indent, 'compressed_timestamp: ', $self->named_type_value('date_time', $v->[$#$v]), "\n") if $desc->{array_length} == $#$v;
8280              
8281 0         0 for my $i_name (sort {$desc->{$a} <=> $desc->{$b}} grep {/^i_/} keys %$desc) {
  0         0  
  0         0  
8282 0         0 my $name = $i_name;
8283 0         0 $name =~ s/^i_//;
8284              
8285 0         0 my $attr = $desc->{'a_' . $name};
8286 0         0 my $tname = $desc->{'t_' . $name};
8287 0         0 my $pname = $name;
8288              
8289 0 0       0 if (ref $attr->{switch} eq 'HASH') {
8290 0         0 my $t_attr = $self->switched($desc, $v, $attr->{switch});
8291              
8292 0 0       0 if (ref $t_attr eq 'HASH') {
8293 0         0 $attr = $t_attr;
8294 0         0 $tname = $attr->{type_name};
8295 0         0 $pname = $attr->{name};
8296             }
8297             }
8298              
8299 0         0 my $i = $desc->{$i_name};
8300 0         0 my $c = $desc->{'c_' . $name};
8301 0         0 my $type = $desc->{'T_' . $name};
8302 0         0 my $invalid = $desc->{'I_' . $name};
8303 0         0 my $j;
8304              
8305 0         0 for ($j = 0 ; $j < $c ; ++$j) {
8306 0 0       0 isnan($v->[$i + $j]) && next;
8307 0 0       0 $v->[$i + $j] != $invalid && last;
8308             }
8309              
8310 0 0 0     0 if ($j < $c || !$skip_invalid) {
8311 0 0 0     0 $self->last_timestamp($v->[$i]) if $type == FIT_UINT32 && $tname eq 'date_time' && $pname eq 'timestamp';
      0        
8312 0 0       0 $FH->print($indent, $pname, ' (', $desc->{'N_' . $name}, '-', $c, '-', $type_name[$type] ne '' ? $type_name[$type] : $type);
8313 0 0       0 $FH->print(', original name: ', $name) if $name ne $pname;
8314 0 0       0 $FH->print(', INVALID') if $j >= $c;
8315 0         0 $FH->print('): ');
8316              
8317 0 0       0 if ($type == FIT_STRING) {
8318 0         0 $FH->print("\"", $self->string_value($v, $i, $c), "\"\n");
8319             } else {
8320 0 0       0 $FH->print('{') if $c > 1;
8321              
8322 0         0 my $pval = $self->value_cooked($tname, $attr, $invalid, $v->[$i]);
8323              
8324 0         0 $FH->print($pval);
8325 0 0       0 $FH->print(' (', $v->[$i], ')') if $v->[$i] ne $pval;
8326              
8327 0 0       0 if ($c > 1) {
8328 0         0 my ($j, $k);
8329              
8330 0         0 for ($j = $i + 1, $k = $i + $c ; $j < $k ; ++$j) {
8331 0         0 $pval = $self->value_cooked($tname, $attr, $invalid, $v->[$j]);
8332 0         0 $FH->print(', ', $pval);
8333 0 0       0 $FH->print(' (', $v->[$j], ')') if $v->[$j] ne $pval;
8334             }
8335 0         0 $FH->print('}');
8336             }
8337 0         0 $FH->print("\n");
8338             }
8339             }
8340             }
8341 0         0 1;
8342             }
8343              
8344             sub print_all_json {
8345 0     0 0 0 my ($self, $desc, $v, %opt) = @_;
8346 0         0 my ($indent, $FH, $skip_invalid) = @opt{qw(indent FH skip_invalid)};
8347              
8348 0         0 my $out = 0;
8349              
8350 0 0       0 $FH=\*STDOUT if !defined $FH;
8351 0 0       0 if ($desc->{array_length} == $#$v) {
8352 0         0 $FH->print($indent, '"compressed_timestamp": "', $self->named_type_value('date_time', $v->[$#$v]), '"');
8353 0         0 $out = $out + 1;
8354             }
8355              
8356 0         0 for my $i_name (sort {$desc->{$a} <=> $desc->{$b}} grep {/^i_/} keys %$desc) {
  0         0  
  0         0  
8357 0         0 my $name = $i_name;
8358 0         0 $name =~ s/^i_//;
8359              
8360 0         0 my $attr = $desc->{'a_' . $name};
8361 0         0 my $tname = $desc->{'t_' . $name};
8362 0         0 my $pname = $name;
8363              
8364 0 0       0 if (ref $attr->{switch} eq 'HASH') {
8365 0         0 my $t_attr = $self->switched($desc, $v, $attr->{switch});
8366              
8367 0 0       0 if (ref $t_attr eq 'HASH') {
8368 0         0 $attr = $t_attr;
8369 0         0 $tname = $attr->{type_name};
8370 0         0 $pname = $attr->{name};
8371             }
8372             }
8373              
8374 0         0 my $i = $desc->{$i_name};
8375 0         0 my $c = $desc->{'c_' . $name};
8376 0         0 my $type = $desc->{'T_' . $name};
8377 0         0 my $invalid = $desc->{'I_' . $name};
8378 0         0 my $j;
8379              
8380 0         0 for ($j = 0 ; $j < $c ; ++$j) {
8381 0 0       0 isnan($v->[$i + $j]) && next;
8382 0 0       0 $v->[$i + $j] != $invalid && last;
8383             }
8384              
8385 0 0 0     0 if ($j < $c || !$skip_invalid) {
8386 0 0 0     0 $self->last_timestamp($v->[$i]) if $type == FIT_UINT32 && $tname eq 'date_time' && $pname eq 'timestamp';
      0        
8387 0 0       0 $FH->print(",\n") if $out;
8388 0         0 $FH->print($indent, '"', $pname, '": ');
8389              
8390 0 0       0 if ($type == FIT_STRING) {
8391 0         0 $FH->print("\"", $self->string_value($v, $i, $c), "\"");
8392             } else {
8393 0 0       0 $FH->print('[') if $c > 1;
8394              
8395 0         0 my $pval = $self->value_cooked($tname, $attr, $invalid, $v->[$i]);
8396              
8397 0 0       0 if (looks_like_number($pval)) {
8398 0         0 $FH->print($pval);
8399             } else {
8400 0         0 $FH->print("\"$pval\"");
8401             }
8402              
8403 0 0       0 if ($c > 1) {
8404 0         0 my ($j, $k);
8405              
8406 0         0 for ($j = $i + 1, $k = $i + $c ; $j < $k ; ++$j) {
8407 0         0 $pval = $self->value_cooked($tname, $attr, $invalid, $v->[$j]);
8408 0         0 $FH->print(', ');
8409 0 0       0 if (looks_like_number($pval)) {
8410 0         0 $FH->print($pval);
8411             } else {
8412 0         0 $FH->print("\"$pval\"");
8413             }
8414             }
8415              
8416 0         0 $FH->print(']');
8417             }
8418             }
8419 0         0 $out = $out + 1;
8420             }
8421             }
8422 0         0 1;
8423             }
8424              
8425             =over 4
8426              
8427             =item use_gmtime(I)
8428              
8429             sets the flag which of GMT or local timezone is used for C type value conversion.
8430              
8431             =back
8432              
8433             =cut
8434              
8435             my $use_gmtime = 0;
8436              
8437             sub use_gmtime {
8438 0     0 1 0 my $self = shift;
8439              
8440 0 0       0 if (@_) {
    0          
8441 0 0       0 if (ref $self eq '') {
8442 0         0 $use_gmtime = $_[0];
8443             } else {
8444 0         0 $self->{use_gmtime} = $_[0];
8445             }
8446             } elsif (ref $self eq '') {
8447 0         0 $use_gmtime;
8448             } else {
8449 0         0 $self->{use_gmtime};
8450             }
8451             }
8452              
8453             =over 4
8454              
8455             =item unit_table(I => I)
8456              
8457             sets I for I.
8458              
8459             =back
8460              
8461             =cut
8462              
8463             sub unit_table {
8464 0     0 1 0 my $self = shift;
8465 0         0 my $unit = shift;
8466              
8467 0 0       0 if (@_) {
8468 0         0 $self->{unit_table}->{$unit} = $_[0];
8469             } else {
8470 0         0 $self->{unit_table}->{$unit};
8471             }
8472             }
8473              
8474             sub without_unit {
8475 0     0 0 0 my $self = shift;
8476 0 0       0 if (@_) {
8477 0         0 $self->{without_unit} = $_[0];
8478             } else {
8479 0         0 $self->{without_unit};
8480             }
8481             }
8482              
8483             =over 4
8484              
8485             =item semicircles_to_degree(I)
8486              
8487             =item mps_to_kph(I)
8488              
8489             wrapper methods of C method.
8490              
8491             =back
8492              
8493             =cut
8494              
8495             sub semicircles_to_degree {
8496 0     0 1 0 my ($self, $on) = @_;
8497 0 0       0 $self->unit_table('semicircles' => $on ? +{'unit' => 'deg', 'scale' => 2 ** 31 / 180} : undef);
8498             }
8499              
8500             sub mps_to_kph {
8501 0     0 1 0 my ($self, $on) = @_;
8502 0 0       0 $self->unit_table('m/s' => $on ? +{'unit' => 'km/h', 'scale' => 1 / 3.6} : undef);
8503             }
8504              
8505             =over 4
8506              
8507             =item close()
8508              
8509             closes opened file handles.
8510              
8511             =back
8512              
8513             =cut
8514              
8515             sub close {
8516 3     3 1 1228 my $self = shift;
8517 3         12 my $cp_fit_FH = $self->cp_fit_FH;
8518 3         8 my $FH = $self->FH;
8519              
8520 3 50 33     12 $cp_fit_FH->close if &safe_isa($cp_fit_FH, 'FileHandle') && $cp_fit_FH->opened;
8521 3 50       30 $FH->close if $FH->opened;
8522             }
8523              
8524             =over 4
8525              
8526             =item profile_version_string()
8527              
8528             Returns a string representation of the profile version used by the device or application that created the FIT file opened in the instance.
8529              
8530             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.
8531              
8532             =back
8533              
8534             =head2 Constants
8535              
8536             Following constants are exported: C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C.
8537              
8538             Also exported are:
8539              
8540             =over 4
8541              
8542             =item FIT_BYTE
8543              
8544             numbers representing base types of field values in data messages.
8545              
8546             =item FIT_BASE_TYPE_MAX
8547              
8548             the maximal number representing base types of field values in data messages.
8549              
8550             =item FIT_HEADER_LENGTH
8551              
8552             length of a .FIT file header.
8553              
8554             =back
8555              
8556             =head2 Data message descriptor
8557              
8558             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.
8559              
8560             =over 4
8561              
8562             =item I => I
8563              
8564             in a global .FIT profile.
8565              
8566             =item C => I
8567              
8568             necessarily.
8569              
8570             =item C => I
8571              
8572             necessarily.
8573              
8574             =item C => I
8575              
8576             only if the message is documented.
8577              
8578             =item C => I
8579              
8580             of a callback function and callback data, only if a C is registered.
8581              
8582             =item C => I
8583              
8584             of multi-octets data in this message, where 0 for littel-endian and 1 for big-endian.
8585              
8586             =item C