rubyfit 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []