rubyfit 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,590 @@
1
+ ////////////////////////////////////////////////////////////////////////////////
2
+ // The following .FIT software provided may be used with .FIT devices only and
3
+ // remains the copyrighted property of Dynastream Innovations Inc.
4
+ // The software is being provided on an "as-is" basis and as an accommodation,
5
+ // and therefore all warranties, representations, or guarantees of any kind
6
+ // (whether express, implied or statutory) including, without limitation,
7
+ // warranties of merchantability, non-infringement, or fitness for a particular
8
+ // purpose, are specifically disclaimed.
9
+ //
10
+ // Copyright 2008 Dynastream Innovations Inc.
11
+ // All rights reserved. This software may not be reproduced by any means
12
+ // without express written approval of Dynastream Innovations Inc.
13
+ ////////////////////////////////////////////////////////////////////////////////
14
+
15
+ #include "stdio.h"
16
+ #include "string.h"
17
+ #include "ruby.h"
18
+ #include "math.h"
19
+
20
+ #include "fit_convert.h"
21
+
22
+ VALUE mRubyFit;
23
+ VALUE cFitParser;
24
+ VALUE cFitHandler;
25
+ VALUE cFitHandlerPrintFun;
26
+ VALUE cFitHandlerActivityFun;
27
+ VALUE cFitHandlerRecordFun;
28
+ VALUE cFitHandlerLapFun;
29
+ VALUE cFitHandlerSessionFun;
30
+ VALUE cFitHandlerDeviceInfoFun;
31
+ VALUE cFitHandlerUserProfileFun;
32
+ VALUE cFitHandlerEventFun;
33
+ VALUE cFitHandlerWeightScaleInfoFun;
34
+ static ID HANDLER_ATTR;
35
+ //garmin/dynastream, in their infinite wisdom, decided to pinch pennies on bits
36
+ //by tinkering with well established time offsets. This is the magic number of
37
+ //seconds needed to add to their number to get the true number of seconds since
38
+ //the epoch. For those math challenged, this is 20 years of seconds.
39
+ const GARMIN_SUCKS_OFFSET = 631065600;
40
+
41
+
42
+ void pass_message(char *msg) {
43
+ rb_funcall(cFitHandler, cFitHandlerPrintFun, 1, rb_str_new2(msg));
44
+ }
45
+
46
+ static VALUE fit_pos_to_rb(FIT_SINT32 pos) {
47
+ float tmp = pos * (180.0 / pow(2,31));
48
+ tmp -= (tmp > 180.0 ? 360.0 : 0.0);
49
+ return rb_float_new(tmp);
50
+ }
51
+
52
+
53
+ static VALUE init(VALUE self, VALUE handler) {
54
+ cFitHandler = handler;
55
+ rb_ivar_set(self, HANDLER_ATTR, handler);
56
+
57
+ //callbacks
58
+ cFitHandlerPrintFun = rb_intern("print_msg");
59
+ cFitHandlerActivityFun = rb_intern("on_activity");
60
+ cFitHandlerSessionFun = rb_intern("on_session");
61
+ cFitHandlerLapFun = rb_intern("on_lap");
62
+ cFitHandlerRecordFun = rb_intern("on_record");
63
+ cFitHandlerEventFun = rb_intern("on_event");
64
+ cFitHandlerDeviceInfoFun = rb_intern("on_device_info");
65
+ cFitHandlerUserProfileFun = rb_intern("on_user_profile");
66
+ cFitHandlerWeightScaleInfoFun = rb_intern("on_weight_scale_info");
67
+
68
+ return Qnil;
69
+ }
70
+
71
+ static pass_activity(const FIT_ACTIVITY_MESG *mesg) {
72
+ VALUE rh = rb_hash_new();
73
+
74
+ if(mesg->timestamp != FIT_DATE_TIME_INVALID)
75
+ rb_hash_aset(rh, rb_str_new2("timestamp"), UINT2NUM(mesg->timestamp + GARMIN_SUCKS_OFFSET));
76
+ if(mesg->total_timer_time != FIT_UINT32_INVALID)
77
+ rb_hash_aset(rh, rb_str_new2("total_timer_time"), rb_float_new(mesg->total_timer_time / 1000.0));
78
+ if(mesg->local_timestamp != FIT_DATE_TIME_INVALID)
79
+ rb_hash_aset(rh, rb_str_new2("local_timestamp"), rb_float_new(mesg->local_timestamp + GARMIN_SUCKS_OFFSET));
80
+ if(mesg->num_sessions != FIT_UINT16_INVALID)
81
+ rb_hash_aset(rh, rb_str_new2("num_sessions"), UINT2NUM(mesg->num_sessions));
82
+ if(mesg->type != FIT_ENUM_INVALID)
83
+ rb_hash_aset(rh, rb_str_new2("type"), CHR2FIX(mesg->type));
84
+ if(mesg->event != FIT_ENUM_INVALID)
85
+ rb_hash_aset(rh, rb_str_new2("event"), CHR2FIX(mesg->event));
86
+ if(mesg->event_type != FIT_ENUM_INVALID)
87
+ rb_hash_aset(rh, rb_str_new2("event_type"), CHR2FIX(mesg->event_type));
88
+ if(mesg->event_group != FIT_UINT8_INVALID)
89
+ rb_hash_aset(rh, rb_str_new2("event_group"), UINT2NUM(mesg->event_group));
90
+
91
+ rb_funcall(cFitHandler, cFitHandlerActivityFun, 1, rh);
92
+ }
93
+
94
+ static pass_record(const FIT_RECORD_MESG *mesg) {
95
+ VALUE rh = rb_hash_new();
96
+
97
+ if(mesg->timestamp != FIT_DATE_TIME_INVALID)
98
+ rb_hash_aset(rh, rb_str_new2("timestamp"), UINT2NUM(mesg->timestamp + GARMIN_SUCKS_OFFSET));
99
+ if(mesg->position_lat != FIT_SINT32_INVALID)
100
+ rb_hash_aset(rh, rb_str_new2("position_lat"), fit_pos_to_rb(mesg->position_lat));
101
+ if(mesg->position_long != FIT_SINT32_INVALID)
102
+ rb_hash_aset(rh, rb_str_new2("position_long"), fit_pos_to_rb(mesg->position_long));
103
+ if(mesg->distance != FIT_UINT32_INVALID)
104
+ rb_hash_aset(rh, rb_str_new2("distance"), rb_float_new(mesg->distance / 100.0));
105
+ if(mesg->time_from_course != FIT_SINT32_INVALID)
106
+ rb_hash_aset(rh, rb_str_new2("time_from_course"), rb_float_new(mesg->time_from_course / 1000.0));
107
+ if(mesg->heart_rate != FIT_UINT8_INVALID)
108
+ rb_hash_aset(rh, rb_str_new2("heart_rate"), UINT2NUM(mesg->heart_rate));
109
+ if(mesg->altitude != FIT_UINT16_INVALID)
110
+ rb_hash_aset(rh, rb_str_new2("altitude"), rb_float_new(mesg->altitude / 5.0 - 500));
111
+ if(mesg->speed != FIT_UINT16_INVALID)
112
+ rb_hash_aset(rh, rb_str_new2("speed"), rb_float_new(mesg->speed / 1000.0));
113
+ if(mesg->grade != FIT_SINT16_INVALID)
114
+ rb_hash_aset(rh, rb_str_new2("grade"), rb_float_new(mesg->grade / 100.0));
115
+ if(mesg->power != FIT_UINT16_INVALID)
116
+ rb_hash_aset(rh, rb_str_new2("power"), UINT2NUM(mesg->power));
117
+ if(mesg->cadence != FIT_UINT8_INVALID)
118
+ rb_hash_aset(rh, rb_str_new2("cadence"), UINT2NUM(mesg->cadence));
119
+ if(mesg->resistance != FIT_UINT8_INVALID)
120
+ rb_hash_aset(rh, rb_str_new2("resistance"), UINT2NUM(mesg->resistance));
121
+ if(mesg->cycle_length != FIT_UINT8_INVALID)
122
+ rb_hash_aset(rh, rb_str_new2("cycle_length"), UINT2NUM(mesg->cycle_length));
123
+ if(mesg->temperature != FIT_SINT8_INVALID)
124
+ rb_hash_aset(rh, rb_str_new2("temperature"), INT2FIX(mesg->temperature));
125
+
126
+ rb_funcall(cFitHandler, cFitHandlerRecordFun, 1, rh);
127
+ }
128
+
129
+ static pass_lap(const FIT_LAP_MESG *mesg) {
130
+ VALUE rh = rb_hash_new();
131
+
132
+ if(mesg->timestamp != FIT_DATE_TIME_INVALID)
133
+ rb_hash_aset(rh, rb_str_new2("timestamp"), UINT2NUM(mesg->timestamp + GARMIN_SUCKS_OFFSET));
134
+ if(mesg->start_time != FIT_DATE_TIME_INVALID)
135
+ rb_hash_aset(rh, rb_str_new2("start_time"), UINT2NUM(mesg->start_time + GARMIN_SUCKS_OFFSET));
136
+ if(mesg->start_position_lat != FIT_SINT32_INVALID)
137
+ rb_hash_aset(rh, rb_str_new2("start_position_lat"), fit_pos_to_rb(mesg->start_position_lat));
138
+ if(mesg->start_position_long != FIT_SINT32_INVALID)
139
+ rb_hash_aset(rh, rb_str_new2("start_position_long"), fit_pos_to_rb(mesg->start_position_long));
140
+ if(mesg->end_position_lat != FIT_SINT32_INVALID)
141
+ rb_hash_aset(rh, rb_str_new2("end_position_lat"), fit_pos_to_rb(mesg->end_position_lat));
142
+ if(mesg->end_position_long != FIT_SINT32_INVALID)
143
+ rb_hash_aset(rh, rb_str_new2("end_position_long"), fit_pos_to_rb(mesg->end_position_long));
144
+ if(mesg->total_elapsed_time != FIT_UINT32_INVALID)
145
+ rb_hash_aset(rh, rb_str_new2("total_elapsed_time"), UINT2NUM(mesg->total_elapsed_time));
146
+ if(mesg->total_timer_time != FIT_UINT32_INVALID)
147
+ rb_hash_aset(rh, rb_str_new2("total_timer_time"), rb_float_new(mesg->total_timer_time / 1000.0));
148
+ if(mesg->total_distance != FIT_UINT32_INVALID)
149
+ rb_hash_aset(rh, rb_str_new2("total_distance"), rb_float_new(mesg->total_distance / 100.0));
150
+ if(mesg->total_cycles != FIT_UINT32_INVALID)
151
+ rb_hash_aset(rh, rb_str_new2("total_cycles"), UINT2NUM(mesg->total_cycles));
152
+ if(mesg->nec_lat != FIT_SINT32_INVALID)
153
+ rb_hash_aset(rh, rb_str_new2("nec_lat"), fit_pos_to_rb(mesg->nec_lat));
154
+ if(mesg->nec_long != FIT_SINT32_INVALID)
155
+ rb_hash_aset(rh, rb_str_new2("nec_long"), fit_pos_to_rb(mesg->nec_long));
156
+ if(mesg->swc_lat != FIT_SINT32_INVALID)
157
+ rb_hash_aset(rh, rb_str_new2("swc_lat"), fit_pos_to_rb(mesg->swc_lat));
158
+ if(mesg->swc_long != FIT_SINT32_INVALID)
159
+ rb_hash_aset(rh, rb_str_new2("swc_long"), fit_pos_to_rb(mesg->swc_long));
160
+ if(mesg->message_index != FIT_UINT16_INVALID)
161
+ rb_hash_aset(rh, rb_str_new2("message_index"), UINT2NUM(mesg->message_index));
162
+ if(mesg->total_calories != FIT_UINT16_INVALID)
163
+ rb_hash_aset(rh, rb_str_new2("total_calories"), UINT2NUM(mesg->total_calories));
164
+ if(mesg->total_fat_calories != FIT_UINT16_INVALID)
165
+ rb_hash_aset(rh, rb_str_new2("total_fat_calories"), UINT2NUM(mesg->total_fat_calories));
166
+ if(mesg->avg_speed != FIT_UINT16_INVALID)
167
+ rb_hash_aset(rh, rb_str_new2("avg_speed"), rb_float_new(mesg->avg_speed / 1000.0));
168
+ if(mesg->max_speed != FIT_UINT16_INVALID)
169
+ rb_hash_aset(rh, rb_str_new2("max_speed"), rb_float_new(mesg->max_speed / 1000.0));
170
+ if(mesg->avg_power != FIT_UINT16_INVALID)
171
+ rb_hash_aset(rh, rb_str_new2("avg_power"), UINT2NUM(mesg->avg_power));
172
+ if(mesg->max_power != FIT_UINT16_INVALID)
173
+ rb_hash_aset(rh, rb_str_new2("max_power"), UINT2NUM(mesg->max_power));
174
+ if(mesg->total_ascent != FIT_UINT16_INVALID)
175
+ rb_hash_aset(rh, rb_str_new2("total_ascent"), UINT2NUM(mesg->total_ascent));
176
+ if(mesg->total_descent != FIT_UINT16_INVALID)
177
+ rb_hash_aset(rh, rb_str_new2("total_descent"), UINT2NUM(mesg->total_descent));
178
+ if(mesg->event != FIT_EVENT_INVALID)
179
+ rb_hash_aset(rh, rb_str_new2("event"), CHR2FIX(mesg->event));
180
+ if(mesg->event_type != FIT_EVENT_INVALID)
181
+ rb_hash_aset(rh, rb_str_new2("event_type"), CHR2FIX(mesg->event_type));
182
+ if(mesg->avg_heart_rate != FIT_UINT8_INVALID)
183
+ rb_hash_aset(rh, rb_str_new2("avg_heart_rate"), UINT2NUM(mesg->avg_heart_rate));
184
+ if(mesg->max_heart_rate != FIT_UINT8_INVALID)
185
+ rb_hash_aset(rh, rb_str_new2("max_heart_rate"), UINT2NUM(mesg->max_heart_rate));
186
+ if(mesg->avg_cadence != FIT_UINT8_INVALID)
187
+ rb_hash_aset(rh, rb_str_new2("avg_cadence"), UINT2NUM(mesg->avg_cadence));
188
+ if(mesg->max_cadence != FIT_UINT8_INVALID)
189
+ rb_hash_aset(rh, rb_str_new2("max_cadence"), UINT2NUM(mesg->max_cadence));
190
+ if(mesg->intensity != FIT_INTENSITY_INVALID)
191
+ rb_hash_aset(rh, rb_str_new2("intensity"), CHR2FIX(mesg->intensity));
192
+ if(mesg->lap_trigger != FIT_LAP_TRIGGER_INVALID)
193
+ rb_hash_aset(rh, rb_str_new2("lap_trigger"), CHR2FIX(mesg->lap_trigger));
194
+ if(mesg->sport != FIT_SPORT_INVALID)
195
+ rb_hash_aset(rh, rb_str_new2("sport"), CHR2FIX(mesg->sport));
196
+ if(mesg->event_group != FIT_UINT8_INVALID)
197
+ rb_hash_aset(rh, rb_str_new2("event_group"), UINT2NUM(mesg->event_group));
198
+
199
+ rb_funcall(cFitHandler, cFitHandlerLapFun, 1, rh);
200
+ }
201
+
202
+ static pass_session(const FIT_SESSION_MESG *mesg) {
203
+ VALUE rh = rb_hash_new();
204
+
205
+ if(mesg->timestamp != FIT_DATE_TIME_INVALID)
206
+ rb_hash_aset(rh, rb_str_new2("timestamp"), UINT2NUM(mesg->timestamp + GARMIN_SUCKS_OFFSET));
207
+ if(mesg->start_time != FIT_DATE_TIME_INVALID)
208
+ rb_hash_aset(rh, rb_str_new2("start_time"), UINT2NUM(mesg->start_time + GARMIN_SUCKS_OFFSET));
209
+ if(mesg->start_position_lat != FIT_SINT32_INVALID)
210
+ rb_hash_aset(rh, rb_str_new2("start_position_lat"), fit_pos_to_rb(mesg->start_position_lat));
211
+ if(mesg->start_position_long != FIT_SINT32_INVALID)
212
+ rb_hash_aset(rh, rb_str_new2("start_position_long"), fit_pos_to_rb(mesg->start_position_long));
213
+ if(mesg->total_elapsed_time != FIT_UINT32_INVALID)
214
+ rb_hash_aset(rh, rb_str_new2("total_elapsed_time"), rb_float_new(mesg->total_elapsed_time / 1000.0));
215
+ if(mesg->total_timer_time != FIT_UINT32_INVALID)
216
+ rb_hash_aset(rh, rb_str_new2("total_timer_time"), rb_float_new(mesg->total_timer_time / 1000.0));
217
+ if(mesg->total_distance != FIT_UINT32_INVALID)
218
+ rb_hash_aset(rh, rb_str_new2("total_distance"), rb_float_new(mesg->total_distance / 100.0));
219
+ if(mesg->total_cycles != FIT_UINT32_INVALID)
220
+ rb_hash_aset(rh, rb_str_new2("total_cycles"), UINT2NUM(mesg->total_cycles));
221
+ if(mesg->nec_lat != FIT_SINT32_INVALID)
222
+ rb_hash_aset(rh, rb_str_new2("nec_lat"), fit_pos_to_rb(mesg->nec_lat));
223
+ if(mesg->nec_long != FIT_SINT32_INVALID)
224
+ rb_hash_aset(rh, rb_str_new2("nec_long"), fit_pos_to_rb(mesg->nec_long));
225
+ if(mesg->swc_lat != FIT_SINT32_INVALID)
226
+ rb_hash_aset(rh, rb_str_new2("swc_lat"), fit_pos_to_rb(mesg->swc_lat));
227
+ if(mesg->swc_long != FIT_SINT32_INVALID)
228
+ rb_hash_aset(rh, rb_str_new2("swc_long"), fit_pos_to_rb(mesg->swc_long));
229
+ if(mesg->message_index != FIT_MESSAGE_INDEX_INVALID)
230
+ rb_hash_aset(rh, rb_str_new2("message_index"), UINT2NUM(mesg->message_index));
231
+ if(mesg->total_calories != FIT_UINT16_INVALID)
232
+ rb_hash_aset(rh, rb_str_new2("total_calories"), UINT2NUM(mesg->total_calories));
233
+ if(mesg->total_fat_calories != FIT_UINT16_INVALID)
234
+ rb_hash_aset(rh, rb_str_new2("total_fat_calories"), UINT2NUM(mesg->total_fat_calories));
235
+ if(mesg->avg_speed != FIT_UINT16_INVALID)
236
+ rb_hash_aset(rh, rb_str_new2("avg_speed"), rb_float_new(mesg->avg_speed / 1000.0));
237
+ if(mesg->max_speed != FIT_UINT16_INVALID)
238
+ rb_hash_aset(rh, rb_str_new2("max_speed"), rb_float_new(mesg->max_speed / 1000.0));
239
+ if(mesg->avg_power != FIT_UINT16_INVALID)
240
+ rb_hash_aset(rh, rb_str_new2("avg_power"), UINT2NUM(mesg->avg_power));
241
+ if(mesg->max_power != FIT_UINT16_INVALID)
242
+ rb_hash_aset(rh, rb_str_new2("max_power"), UINT2NUM(mesg->max_power));
243
+ if(mesg->total_ascent != FIT_UINT16_INVALID)
244
+ rb_hash_aset(rh, rb_str_new2("total_ascent"), UINT2NUM(mesg->total_ascent));
245
+ if(mesg->total_descent != FIT_UINT16_INVALID)
246
+ rb_hash_aset(rh, rb_str_new2("total_descent"), UINT2NUM(mesg->total_descent));
247
+ if(mesg->first_lap_index != FIT_UINT16_INVALID)
248
+ rb_hash_aset(rh, rb_str_new2("first_lap_index"), UINT2NUM(mesg->first_lap_index));
249
+ if(mesg->num_laps != FIT_UINT16_INVALID)
250
+ rb_hash_aset(rh, rb_str_new2("num_laps"), UINT2NUM(mesg->num_laps));
251
+ if(mesg->event != FIT_EVENT_INVALID)
252
+ rb_hash_aset(rh, rb_str_new2("event"), CHR2FIX(mesg->event));
253
+ if(mesg->event_type != FIT_EVENT_INVALID)
254
+ rb_hash_aset(rh, rb_str_new2("event_type"), CHR2FIX(mesg->event_type));
255
+ if(mesg->avg_heart_rate != FIT_UINT8_INVALID)
256
+ rb_hash_aset(rh, rb_str_new2("avg_heart_rate"), UINT2NUM(mesg->avg_heart_rate));
257
+ if(mesg->max_heart_rate != FIT_UINT8_INVALID)
258
+ rb_hash_aset(rh, rb_str_new2("max_heart_rate"), UINT2NUM(mesg->max_heart_rate));
259
+ if(mesg->avg_cadence != FIT_UINT8_INVALID)
260
+ rb_hash_aset(rh, rb_str_new2("avg_cadence"), UINT2NUM(mesg->avg_cadence));
261
+ if(mesg->max_cadence != FIT_UINT8_INVALID)
262
+ rb_hash_aset(rh, rb_str_new2("max_cadence"), UINT2NUM(mesg->max_cadence));
263
+ if(mesg->sport != FIT_SPORT_INVALID)
264
+ rb_hash_aset(rh, rb_str_new2("sport"), CHR2FIX(mesg->sport));
265
+ if(mesg->sub_sport != FIT_SUB_SPORT_INVALID)
266
+ rb_hash_aset(rh, rb_str_new2("sub_sport"), CHR2FIX(mesg->sub_sport));
267
+ if(mesg->event_group != FIT_UINT8_INVALID)
268
+ rb_hash_aset(rh, rb_str_new2("event_group"), UINT2NUM(mesg->event_group));
269
+ if(mesg->total_training_effect != FIT_UINT8_INVALID)
270
+ rb_hash_aset(rh, rb_str_new2("total_training_effect"), UINT2NUM(mesg->total_training_effect));
271
+
272
+ rb_funcall(cFitHandler, cFitHandlerSessionFun, 1, rh);
273
+ }
274
+
275
+ static pass_user_profile(const FIT_USER_PROFILE_MESG *mesg) {
276
+ VALUE rh = rb_hash_new();
277
+
278
+ if(mesg->friendly_name != FIT_STRING_INVALID)
279
+ rb_hash_aset(rh, rb_str_new2("friendly_name"), rb_str_new2(mesg->friendly_name));
280
+ if(mesg->message_index != FIT_MESSAGE_INDEX_INVALID)
281
+ rb_hash_aset(rh, rb_str_new2("message_index"), UINT2NUM(mesg->message_index));
282
+ if(mesg->weight != FIT_UINT16_INVALID)
283
+ rb_hash_aset(rh, rb_str_new2("weight"), rb_float_new(mesg->weight / 10.0));
284
+ if(mesg->gender != FIT_GENDER_INVALID)
285
+ rb_hash_aset(rh, rb_str_new2("gender"), UINT2NUM(mesg->gender));
286
+ if(mesg->age != FIT_UINT8_INVALID)
287
+ rb_hash_aset(rh, rb_str_new2("age"), UINT2NUM(mesg->age));
288
+ if(mesg->height != FIT_UINT8_INVALID)
289
+ rb_hash_aset(rh, rb_str_new2("height"), rb_float_new(mesg->height / 100.0));
290
+ if(mesg->language != FIT_LANGUAGE_INVALID)
291
+ rb_hash_aset(rh, rb_str_new2("language"), UINT2NUM(mesg->language));
292
+ if(mesg->elev_setting != FIT_DISPLAY_MEASURE_INVALID)
293
+ rb_hash_aset(rh, rb_str_new2("elev_setting"), UINT2NUM(mesg->elev_setting));
294
+ if(mesg->weight_setting != FIT_DISPLAY_MEASURE_INVALID)
295
+ rb_hash_aset(rh, rb_str_new2("weight_setting"), UINT2NUM(mesg->weight_setting));
296
+ if(mesg->resting_heart_rate != FIT_UINT8_INVALID)
297
+ rb_hash_aset(rh, rb_str_new2("resting_heart_rate"), UINT2NUM(mesg->resting_heart_rate));
298
+ if(mesg->default_max_running_heart_rate != FIT_UINT8_INVALID)
299
+ rb_hash_aset(rh, rb_str_new2("default_max_running_heart_rate"), UINT2NUM(mesg->default_max_running_heart_rate));
300
+ if(mesg->default_max_biking_heart_rate != FIT_UINT8_INVALID)
301
+ rb_hash_aset(rh, rb_str_new2("default_max_biking_heart_rate"), UINT2NUM(mesg->default_max_biking_heart_rate));
302
+ if(mesg->default_max_heart_rate != FIT_UINT8_INVALID)
303
+ rb_hash_aset(rh, rb_str_new2("default_max_heart_rate"), UINT2NUM(mesg->default_max_heart_rate));
304
+ if(mesg->hr_setting != FIT_DISPLAY_HEART_INVALID)
305
+ rb_hash_aset(rh, rb_str_new2("hr_setting"), UINT2NUM(mesg->hr_setting));
306
+ if(mesg->speed_setting != FIT_DISPLAY_MEASURE_INVALID)
307
+ rb_hash_aset(rh, rb_str_new2("speed_setting"), UINT2NUM(mesg->speed_setting));
308
+ if(mesg->dist_setting != FIT_DISPLAY_MEASURE_INVALID)
309
+ rb_hash_aset(rh, rb_str_new2("dist_setting"), UINT2NUM(mesg->dist_setting));
310
+ if(mesg->power_setting != FIT_DISPLAY_POWER_INVALID)
311
+ rb_hash_aset(rh, rb_str_new2("power_setting"), UINT2NUM(mesg->power_setting));
312
+ if(mesg->activity_class != FIT_ACTIVITY_CLASS_INVALID)
313
+ rb_hash_aset(rh, rb_str_new2("activity_class"), UINT2NUM(mesg->activity_class));
314
+ if(mesg->position_setting != FIT_DISPLAY_POSITION_INVALID)
315
+ rb_hash_aset(rh, rb_str_new2("position_setting"), UINT2NUM(mesg->position_setting));
316
+
317
+ rb_funcall(cFitHandler, cFitHandlerUserProfileFun, 1, rh);
318
+ }
319
+
320
+ static pass_event(const FIT_EVENT_MESG *mesg) {
321
+ VALUE rh = rb_hash_new();
322
+
323
+ if(mesg->timestamp != FIT_DATE_TIME_INVALID)
324
+ rb_hash_aset(rh, rb_str_new2("timestamp"), UINT2NUM(mesg->timestamp + GARMIN_SUCKS_OFFSET));
325
+ if(mesg->data != FIT_UINT32_INVALID)
326
+ rb_hash_aset(rh, rb_str_new2("data"), UINT2NUM(mesg->data));
327
+ if(mesg->data16 != FIT_UINT16_INVALID)
328
+ rb_hash_aset(rh, rb_str_new2("data16"), UINT2NUM(mesg->data16));
329
+ if(mesg->timestamp != FIT_EVENT_INVALID)
330
+ rb_hash_aset(rh, rb_str_new2("event"), CHR2FIX(mesg->event));
331
+ if(mesg->timestamp != FIT_EVENT_TYPE_INVALID)
332
+ rb_hash_aset(rh, rb_str_new2("event_type"), CHR2FIX(mesg->event_type));
333
+ if(mesg->event_group != FIT_UINT8_INVALID)
334
+ rb_hash_aset(rh, rb_str_new2("event_group"), UINT2NUM(mesg->event_group));
335
+
336
+ rb_funcall(cFitHandler, cFitHandlerEventFun, 1, rh);
337
+ }
338
+
339
+ static pass_device_info(const FIT_DEVICE_INFO_MESG *mesg) {
340
+ VALUE rh = rb_hash_new();
341
+
342
+ if(mesg->timestamp != FIT_DATE_TIME_INVALID)
343
+ rb_hash_aset(rh, rb_str_new2("timestamp"), UINT2NUM(mesg->timestamp + GARMIN_SUCKS_OFFSET));
344
+ if(mesg->serial_number != FIT_UINT32Z_INVALID)
345
+ rb_hash_aset(rh, rb_str_new2("serial_number"), UINT2NUM(mesg->serial_number));
346
+ if(mesg->manufacturer != FIT_MANUFACTURER_INVALID)
347
+ rb_hash_aset(rh, rb_str_new2("manufacturer"), UINT2NUM(mesg->manufacturer));
348
+ if(mesg->product != FIT_UINT16_INVALID)
349
+ rb_hash_aset(rh, rb_str_new2("product"), UINT2NUM(mesg->product));
350
+ if(mesg->software_version != FIT_UINT16_INVALID)
351
+ rb_hash_aset(rh, rb_str_new2("software_version"), UINT2NUM(mesg->software_version));
352
+ if(mesg->battery_voltage != FIT_UINT16_INVALID)
353
+ rb_hash_aset(rh, rb_str_new2("battery_voltage"), UINT2NUM(mesg->battery_voltage));
354
+ if(mesg->device_index != FIT_DEVICE_INDEX_INVALID)
355
+ rb_hash_aset(rh, rb_str_new2("device_index"), UINT2NUM(mesg->device_index));
356
+ if(mesg->device_type != FIT_DEVICE_TYPE_INVALID)
357
+ rb_hash_aset(rh, rb_str_new2("device_type"), UINT2NUM(mesg->device_type));
358
+ if(mesg->hardware_version != FIT_UINT8_INVALID)
359
+ rb_hash_aset(rh, rb_str_new2("hardware_version"), UINT2NUM(mesg->hardware_version));
360
+ if(mesg->battery_status != FIT_BATTERY_STATUS_INVALID)
361
+ rb_hash_aset(rh, rb_str_new2("battery_status"), UINT2NUM(mesg->battery_status));
362
+
363
+ rb_funcall(cFitHandler, cFitHandlerDeviceInfoFun, 1, rh);
364
+ }
365
+
366
+ static pass_weight_scale_info(const FIT_WEIGHT_SCALE_MESG *mesg) {
367
+ VALUE rh = rb_hash_new();
368
+
369
+ if(mesg->timestamp != FIT_DATE_TIME_INVALID)
370
+ rb_hash_aset(rh, rb_str_new2("timestamp"), UINT2NUM(mesg->timestamp + GARMIN_SUCKS_OFFSET));
371
+ if(mesg->weight != FIT_WEIGHT_INVALID)
372
+ rb_hash_aset(rh, rb_str_new2("weight"), rb_float_new(mesg->weight / 100.0));
373
+ if(mesg->percent_fat != FIT_UINT16_INVALID)
374
+ rb_hash_aset(rh, rb_str_new2("percent_fat"), rb_float_new(mesg->percent_fat / 100.0));
375
+ if(mesg->percent_hydration != FIT_UINT16_INVALID)
376
+ rb_hash_aset(rh, rb_str_new2("percent_hydration"), rb_float_new(mesg->percent_hydration / 100.0));
377
+ if(mesg->visceral_fat_mass != FIT_UINT16_INVALID)
378
+ rb_hash_aset(rh, rb_str_new2("visceral_fat_mass"), rb_float_new(mesg->visceral_fat_mass / 100.0));
379
+ if(mesg->bone_mass != FIT_UINT16_INVALID)
380
+ rb_hash_aset(rh, rb_str_new2("bone_mass"), rb_float_new(mesg->bone_mass / 100.0));
381
+ if(mesg->muscle_mass != FIT_UINT16_INVALID)
382
+ rb_hash_aset(rh, rb_str_new2("muscle_mass"), rb_float_new(mesg->muscle_mass / 100.0));
383
+ if(mesg->basal_met != FIT_UINT16_INVALID)
384
+ rb_hash_aset(rh, rb_str_new2("basal_met"), rb_float_new(mesg->basal_met / 4.0));
385
+ if(mesg->active_met != FIT_UINT16_INVALID)
386
+ rb_hash_aset(rh, rb_str_new2("active_met"), rb_float_new(mesg->active_met / 4.0));
387
+ if(mesg->physique_rating != FIT_UINT8_INVALID)
388
+ rb_hash_aset(rh, rb_str_new2("physique_rating"), rb_float_new(mesg->physique_rating));
389
+ if(mesg->metabolic_age != FIT_UINT8_INVALID)
390
+ rb_hash_aset(rh, rb_str_new2("metabolic_age"), rb_float_new(mesg->metabolic_age));
391
+ if(mesg->visceral_fat_rating != FIT_UINT8_INVALID)
392
+ rb_hash_aset(rh, rb_str_new2("visceral_fat_rating"), rb_float_new(mesg->visceral_fat_rating));
393
+
394
+ rb_funcall(cFitHandler, cFitHandlerWeightScaleInfoFun, 1, rh);
395
+ }
396
+
397
+ static VALUE parse(VALUE self, VALUE original_str) {
398
+ int i = 0;
399
+ VALUE str = StringValue(original_str);
400
+ char *p = RSTRING_PTR(str);
401
+ char err_msg[128];
402
+
403
+ FIT_UINT8 buf[8];
404
+ FIT_CONVERT_RETURN convert_return = FIT_CONVERT_CONTINUE;
405
+ FIT_UINT32 buf_size;
406
+ FIT_UINT32 mesg_index = 0;
407
+ #if defined(FIT_CONVERT_MULTI_THREAD)
408
+ FIT_CONVERT_STATE state;
409
+ #endif
410
+
411
+ #if defined(FIT_CONVERT_MULTI_THREAD)
412
+ FitConvert_Init(&state, FIT_TRUE);
413
+ #else
414
+ FitConvert_Init(FIT_TRUE);
415
+ #endif
416
+
417
+ if(RSTRING_LEN(str) == 0) {
418
+ //sprintf(err_msg, "Passed in string with length of 0!");
419
+ pass_message(err_msg);
420
+ return Qnil;
421
+ }
422
+
423
+ while(i < RSTRING_LEN(str) && (convert_return == FIT_CONVERT_CONTINUE)) {
424
+ for(buf_size=0;(buf_size < sizeof(buf)) && (p != NULL); buf_size++) {
425
+ buf[buf_size] = *p;
426
+ p++;
427
+ i++;
428
+ }
429
+
430
+ do {
431
+ #if defined(FIT_CONVERT_MULTI_THREAD)
432
+ convert_return = FitConvert_Read(&state, buf, buf_size);
433
+ #else
434
+ convert_return = FitConvert_Read(buf, buf_size);
435
+ #endif
436
+
437
+ switch(convert_return) {
438
+ case FIT_CONVERT_MESSAGE_AVAILABLE: {
439
+ #if defined(FIT_CONVERT_MULTI_THREAD)
440
+ const FIT_UINT8 *mesg = FitConvert_GetMessageData(&state);
441
+ FIT_UINT16 mesg_num = FitConvert_GetMessageNumber(&state);
442
+ #else
443
+ const FIT_UINT8 *mesg = FitConvert_GetMessageData();
444
+ FIT_UINT16 mesg_num = FitConvert_GetMessageNumber();
445
+ #endif
446
+
447
+ //sprintf(err_msg, "Mesg %d (%d) - ", mesg_index++, mesg_num);
448
+ //pass_message(err_msg);
449
+
450
+ switch(mesg_num) {
451
+ case FIT_MESG_NUM_FILE_ID: {
452
+ const FIT_FILE_ID_MESG *id = (FIT_FILE_ID_MESG *) mesg;
453
+ //sprintf(err_msg, "File ID: type=%u, number=%u\n", id->type, id->number);
454
+ //pass_message(err_msg);
455
+ break;
456
+ }
457
+
458
+ case FIT_MESG_NUM_USER_PROFILE: {
459
+ const FIT_USER_PROFILE_MESG *user_profile = (FIT_USER_PROFILE_MESG *) mesg;
460
+ //sprintf(err_msg, "User Profile: weight=%0.1fkg\n", user_profile->weight / 10.0f);
461
+ //pass_message(err_msg);
462
+ pass_user_profile(user_profile);
463
+ break;
464
+ }
465
+
466
+ case FIT_MESG_NUM_ACTIVITY: {
467
+ const FIT_ACTIVITY_MESG *activity = (FIT_ACTIVITY_MESG *) mesg;
468
+ //sprintf(err_msg, "Activity: timestamp=%u, type=%u, event=%u, event_type=%u, num_sessions=%u\n", activity->timestamp, activity->type, activity->event, activity->event_type, activity->num_sessions);
469
+ //pass_message(err_msg);
470
+ pass_activity(activity);
471
+
472
+ {
473
+ FIT_ACTIVITY_MESG old_mesg;
474
+ old_mesg.num_sessions = 1;
475
+ #if defined(FIT_CONVERT_MULTI_THREAD)
476
+ FitConvert_RestoreFields(&state, &old_mesg);
477
+ #else
478
+ FitConvert_RestoreFields(&old_mesg);
479
+ #endif
480
+ //sprintf(err_msg, "Restored num_sessions=1 - Activity: timestamp=%u, type=%u, event=%u, event_type=%u, num_sessions=%u\n", activity->timestamp, activity->type, activity->event, activity->event_type, activity->num_sessions);
481
+ //pass_message(err_msg);
482
+ }
483
+ break;
484
+ }
485
+
486
+ case FIT_MESG_NUM_SESSION: {
487
+ const FIT_SESSION_MESG *session = (FIT_SESSION_MESG *) mesg;
488
+ //sprintf(err_msg, "Session: timestamp=%u\n", session->timestamp);
489
+ //pass_message(err_msg);
490
+ pass_session(session);
491
+ break;
492
+ }
493
+
494
+ case FIT_MESG_NUM_LAP: {
495
+ const FIT_LAP_MESG *lap = (FIT_LAP_MESG *) mesg;
496
+ //sprintf(err_msg, "Lap: timestamp=%u, total_ascent=%u, total_distance=%f\n", lap->timestamp, lap->total_ascent, (lap->total_distance / 100.0) / 1000.0 * 0.621371192);
497
+ //pass_message(err_msg);
498
+ pass_lap(lap);
499
+ break;
500
+ }
501
+
502
+ case FIT_MESG_NUM_RECORD: {
503
+ const FIT_RECORD_MESG *record = (FIT_RECORD_MESG *) mesg;
504
+
505
+ //sprintf(err_msg, "Record: timestamp=%u", record->timestamp);
506
+ //pass_message(err_msg);
507
+ pass_record(record);
508
+ break;
509
+ }
510
+
511
+ case FIT_MESG_NUM_EVENT: {
512
+ const FIT_EVENT_MESG *event = (FIT_EVENT_MESG *) mesg;
513
+ //sprintf(err_msg, "Event: timestamp=%u, event_type = %i\n", event->timestamp, event->event_type);
514
+ //pass_message(err_msg);
515
+ pass_event(event);
516
+ break;
517
+ }
518
+
519
+ case FIT_MESG_NUM_DEVICE_INFO: {
520
+ const FIT_DEVICE_INFO_MESG *device_info = (FIT_DEVICE_INFO_MESG *) mesg;
521
+ //sprintf(err_msg, "Device Info: timestamp=%u, battery_status=%u\n", (unsigned int)device_info->timestamp, device_info->battery_voltage);
522
+ //pass_message(err_msg);
523
+ pass_device_info(device_info);
524
+ break;
525
+ }
526
+
527
+ case FIT_MESG_NUM_WEIGHT_SCALE: {
528
+ const FIT_WEIGHT_SCALE_MESG *weight_scale_info = (FIT_WEIGHT_SCALE_MESG *) mesg;
529
+ //sprintf(err_msg, "Device Info: timestamp=%u, battery_status=%u\n", (unsigned int)device_info->timestamp, device_info->battery_voltage);
530
+ //pass_message(err_msg);
531
+ pass_weight_scale_info(weight_scale_info);
532
+ break;
533
+ }
534
+
535
+ default: {
536
+ //sprintf(err_msg, "Unknown\n");
537
+ //pass_message(err_msg);
538
+ break;
539
+ }
540
+ }
541
+ break;
542
+ }
543
+ default:
544
+ break;
545
+ }
546
+ } while (convert_return == FIT_CONVERT_MESSAGE_AVAILABLE);
547
+ }
548
+
549
+ if (convert_return == FIT_CONVERT_ERROR) {
550
+ //sprintf(err_msg, "Error decoding file.\n");
551
+ pass_message(err_msg);
552
+ //fclose(file);
553
+ return Qnil;
554
+ }
555
+
556
+ if (convert_return == FIT_CONVERT_CONTINUE) {
557
+ //sprintf(err_msg, "Unexpected end of file.\n");
558
+ //pass_message(err_msg);
559
+ //fclose(file);
560
+ //return Qnil;
561
+ }
562
+
563
+ if (convert_return == FIT_CONVERT_PROTOCOL_VERSION_NOT_SUPPORTED) {
564
+ //sprintf(err_msg, "Protocol version not supported.\n");
565
+ pass_message(err_msg);
566
+ //fclose(file);
567
+ return Qnil;
568
+ }
569
+
570
+ if (convert_return == FIT_CONVERT_END_OF_FILE) {
571
+ //sprintf(err_msg, "File converted successfully.\n");
572
+ pass_message(err_msg);
573
+ }
574
+
575
+ return Qnil;
576
+ }
577
+
578
+ void Init_rubyfit() {
579
+ mRubyFit = rb_define_module("RubyFit");
580
+ cFitParser = rb_define_class_under(mRubyFit, "FitParser", rb_cObject);
581
+ //cFitParser = rb_define_class("FitParser", rb_cObject);
582
+
583
+ //instance methods
584
+ rb_define_method(cFitParser, "initialize", init, 1);
585
+ rb_define_method(cFitParser, "parse", parse, 1);
586
+
587
+ //attributes
588
+ HANDLER_ATTR = rb_intern("@handler");
589
+ rb_define_attr(cFitParser, "handler", 1, 1);
590
+ }
@@ -0,0 +1,3 @@
1
+ module Rubyfit
2
+ VERSION = "0.0.2"
3
+ end
data/lib/rubyfit.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "rubyfit/version"
2
+ require 'rubyfit/rubyfit'
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubyfit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Cullen King
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-23 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: FIT files are binary, and as a result, are a pain to parse. This is
15
+ a wrapper around the FIT SDK, which makes creating a stream based parser simple.
16
+ email:
17
+ - cullen@ridewithgps.com
18
+ executables: []
19
+ extensions:
20
+ - ext/rubyfit/extconf.rb
21
+ extra_rdoc_files: []
22
+ files:
23
+ - lib/rubyfit/version.rb
24
+ - lib/rubyfit.rb
25
+ - ext/rubyfit/fit_convert.c
26
+ - ext/rubyfit/rubyfit.c
27
+ - ext/rubyfit/fit.c
28
+ - ext/rubyfit/fit_crc.c
29
+ - ext/rubyfit/fit_sdk.c
30
+ - ext/rubyfit/fit_product.c
31
+ - ext/rubyfit/fit.h
32
+ - ext/rubyfit/fit_convert.h
33
+ - ext/rubyfit/fit_crc.h
34
+ - ext/rubyfit/fit_config.h
35
+ - ext/rubyfit/fit_sdk.h
36
+ - ext/rubyfit/fit_product.h
37
+ - ext/rubyfit/extconf.rb
38
+ homepage: http://cullenking.com
39
+ licenses: []
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ - ext
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project: rubyfit
59
+ rubygems_version: 1.8.10
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: A stream based parser for FIT files.
63
+ test_files: []