File Coverage

blib/lib/Geo/FIT.pm
Criterion Covered Total %
statement 516 997 51.7
branch 227 574 39.5
condition 20 78 25.6
subroutine 61 97 62.8
pod 32 83 38.5
total 856 1829 46.8


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