1
+ 'use strict' ;
2
+
3
+ Object . defineProperty ( exports , "__esModule" , {
4
+ value : true
5
+ } ) ;
6
+ exports . addEndian = addEndian ;
7
+ exports . readRecord = readRecord ;
8
+ exports . getArrayBuffer = getArrayBuffer ;
9
+ exports . calculateCRC = calculateCRC ;
10
+
11
+ var _fit = require ( './fit' ) ;
12
+
13
+ var _messages = require ( './messages' ) ;
14
+
15
+ function addEndian ( littleEndian , bytes ) {
16
+ var result = 0 ;
17
+ if ( ! littleEndian ) bytes . reverse ( ) ;
18
+ for ( var i = 0 ; i < bytes . length ; i ++ ) {
19
+ result += bytes [ i ] << ( i << 3 ) >>> 0 ;
20
+ }
21
+
22
+ return result ;
23
+ }
24
+
25
+ function readData ( blob , fDef , startIndex ) {
26
+ if ( fDef . endianAbility === true ) {
27
+ var temp = [ ] ;
28
+ for ( var i = 0 ; i < fDef . size ; i ++ ) {
29
+ temp . push ( blob [ startIndex + i ] ) ;
30
+ }
31
+ var uint32Rep = addEndian ( fDef . littleEndian , temp ) ;
32
+
33
+ if ( fDef . dataType === 'sint32' ) {
34
+ return uint32Rep >> 0 ;
35
+ }
36
+
37
+ return uint32Rep ;
38
+ }
39
+ return blob [ startIndex ] ;
40
+ }
41
+
42
+ function formatByType ( data , type , scale , offset ) {
43
+ switch ( type ) {
44
+ case 'date_time' :
45
+ return new Date ( data * 1000 + 631062000000 ) ;
46
+ case 'sint32' :
47
+ case 'sint16' :
48
+ return data * _fit . FIT . scConst ;
49
+ case 'uint32' :
50
+ case 'uint16' :
51
+ return scale ? data / scale + offset : data ;
52
+ default :
53
+ return data ;
54
+ }
55
+ }
56
+
57
+ function convertTo ( data , unitsList , speedUnit ) {
58
+ var unitObj = _fit . FIT . options [ unitsList ] [ speedUnit ] ;
59
+ return unitObj ? data * unitObj . multiplier + unitObj . offset : data ;
60
+ }
61
+
62
+ function applyOptions ( data , field , options ) {
63
+ switch ( field ) {
64
+ case 'speed' :
65
+ case 'enhanced_speed' :
66
+ case 'vertical_speed' :
67
+ case 'avg_speed' :
68
+ case 'max_speed' :
69
+ case 'speed_1s' :
70
+ case 'ball_speed' :
71
+ case 'enhanced_avg_speed' :
72
+ case 'enhanced_max_speed' :
73
+ case 'avg_pos_vertical_speed' :
74
+ case 'max_pos_vertical_speed' :
75
+ case 'avg_neg_vertical_speed' :
76
+ case 'max_neg_vertical_speed' :
77
+ return convertTo ( data , 'speedUnits' , options . speedUnit ) ;
78
+ case 'distance' :
79
+ case 'total_distance' :
80
+ case 'enhanced_avg_altitude' :
81
+ case 'enhanced_min_altitude' :
82
+ case 'enhanced_max_altitude' :
83
+ case 'enhanced_altitude' :
84
+ case 'height' :
85
+ case 'odometer' :
86
+ case 'avg_stroke_distance' :
87
+ case 'min_altitude' :
88
+ case 'avg_altitude' :
89
+ case 'max_altitude' :
90
+ case 'total_ascent' :
91
+ case 'total_descent' :
92
+ case 'altitude' :
93
+ case 'cycle_length' :
94
+ case 'auto_wheelsize' :
95
+ case 'custom_wheelsize' :
96
+ case 'gps_accuracy' :
97
+ return convertTo ( data , 'lengthUnits' , options . lengthUnit ) ;
98
+ case 'temperature' :
99
+ case 'avg_temperature' :
100
+ case 'max_temperature' :
101
+ return convertTo ( data , 'temperatureUnits' , options . temperatureUnit ) ;
102
+ default :
103
+ return data ;
104
+ }
105
+ }
106
+
107
+ function readRecord ( blob , messageTypes , startIndex , options ) {
108
+ var recordHeader = blob [ startIndex ] ;
109
+ var localMessageType = recordHeader & 15 ;
110
+
111
+ if ( ( recordHeader & 64 ) === 64 ) {
112
+ // is definition message
113
+ // startIndex + 1 is reserved
114
+
115
+ var lEnd = blob [ startIndex + 2 ] === 0 ;
116
+ var mTypeDef = {
117
+ littleEndian : lEnd ,
118
+ globalMessageNumber : addEndian ( lEnd , [ blob [ startIndex + 3 ] , blob [ startIndex + 4 ] ] ) ,
119
+ numberOfFields : blob [ startIndex + 5 ] ,
120
+ fieldDefs : [ ]
121
+ } ;
122
+
123
+ var _message = ( 0 , _messages . getFitMessage ) ( mTypeDef . globalMessageNumber ) ;
124
+
125
+ for ( var i = 0 ; i < mTypeDef . numberOfFields ; i ++ ) {
126
+ var fDefIndex = startIndex + 6 + i * 3 ;
127
+ var baseType = blob [ fDefIndex + 2 ] ;
128
+
129
+ var _message$getAttribute = _message . getAttributes ( blob [ fDefIndex ] ) ;
130
+
131
+ var field = _message$getAttribute . field ;
132
+ var type = _message$getAttribute . type ;
133
+
134
+ var fDef = {
135
+ type : type ,
136
+ fDefNo : blob [ fDefIndex ] ,
137
+ size : blob [ fDefIndex + 1 ] ,
138
+ endianAbility : ( baseType & 128 ) === 128 ,
139
+ littleEndian : lEnd ,
140
+ baseTypeNo : baseType & 15 ,
141
+ name : field ,
142
+ dataType : ( 0 , _messages . getFitMessageBaseType ) ( baseType & 15 )
143
+ } ;
144
+
145
+ mTypeDef . fieldDefs . push ( fDef ) ;
146
+ }
147
+ messageTypes [ localMessageType ] = mTypeDef ;
148
+
149
+ return {
150
+ messageType : 'fieldDescription' ,
151
+ nextIndex : startIndex + 6 + mTypeDef . numberOfFields * 3
152
+ } ;
153
+ }
154
+
155
+ var messageType = void 0 ;
156
+
157
+ if ( messageTypes [ localMessageType ] ) {
158
+ messageType = messageTypes [ localMessageType ] ;
159
+ } else {
160
+ messageType = messageTypes [ 0 ] ;
161
+ }
162
+
163
+ // TODO: handle compressed header ((recordHeader & 128) == 128)
164
+
165
+ // uncompressed header
166
+ var messageSize = 0 ;
167
+ var readDataFromIndex = startIndex + 1 ;
168
+ var fields = { } ;
169
+ var message = ( 0 , _messages . getFitMessage ) ( messageType . globalMessageNumber ) ;
170
+
171
+ for ( var _i = 0 ; _i < messageType . fieldDefs . length ; _i ++ ) {
172
+ var _fDef = messageType . fieldDefs [ _i ] ;
173
+ var data = readData ( blob , _fDef , readDataFromIndex ) ;
174
+
175
+ var _message$getAttribute2 = message . getAttributes ( _fDef . fDefNo ) ;
176
+
177
+ var field = _message$getAttribute2 . field ;
178
+ var type = _message$getAttribute2 . type ;
179
+ var scale = _message$getAttribute2 . scale ;
180
+ var offset = _message$getAttribute2 . offset ;
181
+
182
+ if ( field !== 'unknown' && field !== '' ) {
183
+ fields [ field ] = applyOptions ( formatByType ( data , type , scale , offset ) , field , options ) ;
184
+ }
185
+ readDataFromIndex += _fDef . size ;
186
+ messageSize += _fDef . size ;
187
+ }
188
+
189
+ var result = {
190
+ messageType : message . name ,
191
+ nextIndex : startIndex + messageSize + 1 ,
192
+ message : fields
193
+ } ;
194
+
195
+ switch ( messageType . globalMessageNumber ) {
196
+ case _fit . FIT . TYPE . RECORD :
197
+ break ;
198
+ case _fit . FIT . TYPE . LAP :
199
+ break ;
200
+ case _fit . FIT . TYPE . SESSION :
201
+ break ;
202
+ default :
203
+ break ;
204
+ }
205
+
206
+ return result ;
207
+ }
208
+
209
+ function getArrayBuffer ( buffer ) {
210
+ var ab = new ArrayBuffer ( buffer . length ) ;
211
+ var view = new Uint8Array ( ab ) ;
212
+ for ( var i = 0 ; i < buffer . length ; ++ i ) {
213
+ view [ i ] = buffer [ i ] ;
214
+ }
215
+ return ab ;
216
+ }
217
+
218
+ function calculateCRC ( blob , start , end ) {
219
+ var crcTable = [ 0x0000 , 0xCC01 , 0xD801 , 0x1400 , 0xF001 , 0x3C00 , 0x2800 , 0xE401 , 0xA001 , 0x6C00 , 0x7800 , 0xB401 , 0x5000 , 0x9C01 , 0x8801 , 0x4400 ] ;
220
+
221
+ var crc = 0 ;
222
+ for ( var i = start ; i < end ; i ++ ) {
223
+ var byte = blob [ i ] ;
224
+ var tmp = crcTable [ crc & 0xF ] ;
225
+ crc = crc >> 4 & 0x0FFF ;
226
+ crc = crc ^ tmp ^ crcTable [ byte & 0xF ] ;
227
+ tmp = crcTable [ crc & 0xF ] ;
228
+ crc = crc >> 4 & 0x0FFF ;
229
+ crc = crc ^ tmp ^ crcTable [ byte >> 4 & 0xF ] ;
230
+ }
231
+
232
+ return crc ;
233
+ }
0 commit comments