]> git.tdb.fi Git - ext/subsurface.git/blob - libdivecomputer.c
Make subsurface compile with current libdivecomputer git tree
[ext/subsurface.git] / libdivecomputer.c
1 #include <stdio.h>
2 #include <pthread.h>
3 #include <unistd.h>
4 #include <inttypes.h>
5
6 #include "dive.h"
7 #include "divelist.h"
8 #include "display.h"
9 #include "display-gtk.h"
10
11 #include "libdivecomputer.h"
12
13 /* Christ. Libdivecomputer has the worst configuration system ever. */
14 #ifdef HW_FROG_H
15   #define NOT_FROG , 0
16   #define LIBDIVECOMPUTER_SUPPORTS_FROG
17 #else
18   #define NOT_FROG
19 #endif
20
21 static void error(const char *fmt, ...)
22 {
23         va_list args;
24         GError *error;
25
26         va_start(args, fmt);
27         error = g_error_new_valist(
28                 g_quark_from_string("subsurface"),
29                 DIVE_ERROR_PARSE, fmt, args);
30         va_end(args);
31         report_error(error);
32         g_error_free(error);
33 }
34
35 static parser_status_t create_parser(device_data_t *devdata, parser_t **parser)
36 {
37         switch (devdata->type) {
38         case DEVICE_TYPE_SUUNTO_SOLUTION:
39                 return suunto_solution_parser_create(parser);
40
41         case DEVICE_TYPE_SUUNTO_EON:
42                 return suunto_eon_parser_create(parser, 0);
43
44         case DEVICE_TYPE_SUUNTO_VYPER:
45                 if (devdata->devinfo.model == 0x01)
46                         return suunto_eon_parser_create(parser, 1);
47                 return suunto_vyper_parser_create(parser);
48
49         case DEVICE_TYPE_SUUNTO_VYPER2:
50         case DEVICE_TYPE_SUUNTO_D9:
51                 return suunto_d9_parser_create(parser, devdata->devinfo.model);
52
53         case DEVICE_TYPE_UWATEC_ALADIN:
54         case DEVICE_TYPE_UWATEC_MEMOMOUSE:
55                 return uwatec_memomouse_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
56
57         case DEVICE_TYPE_UWATEC_SMART:
58                 return uwatec_smart_parser_create(parser, devdata->devinfo.model, devdata->clock.devtime, devdata->clock.systime);
59
60         case DEVICE_TYPE_REEFNET_SENSUS:
61                 return reefnet_sensus_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
62
63         case DEVICE_TYPE_REEFNET_SENSUSPRO:
64                 return reefnet_sensuspro_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
65
66         case DEVICE_TYPE_REEFNET_SENSUSULTRA:
67                 return reefnet_sensusultra_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
68
69         case DEVICE_TYPE_OCEANIC_VTPRO:
70                 return oceanic_vtpro_parser_create(parser);
71
72         case DEVICE_TYPE_OCEANIC_VEO250:
73                 return oceanic_veo250_parser_create(parser, devdata->devinfo.model);
74
75         case DEVICE_TYPE_OCEANIC_ATOM2:
76                 return oceanic_atom2_parser_create(parser, devdata->devinfo.model);
77
78         case DEVICE_TYPE_MARES_DARWIN:
79                 return mares_darwin_parser_create(parser, devdata->devinfo.model);
80
81         case DEVICE_TYPE_MARES_NEMO:
82         case DEVICE_TYPE_MARES_PUCK:
83                 return mares_nemo_parser_create(parser, devdata->devinfo.model);
84
85         case DEVICE_TYPE_MARES_ICONHD:
86                 return mares_iconhd_parser_create(parser, devdata->devinfo.model);
87
88         case DEVICE_TYPE_HW_OSTC:
89                 return hw_ostc_parser_create(parser NOT_FROG);
90
91 #ifdef LIBDIVECOMPUTER_SUPPORTS_FROG
92         case DEVICE_TYPE_HW_FROG:
93                 return hw_ostc_parser_create(parser, 1);
94 #endif
95
96         case DEVICE_TYPE_CRESSI_EDY:
97         case DEVICE_TYPE_ZEAGLE_N2ITION3:
98                 return cressi_edy_parser_create(parser, devdata->devinfo.model);
99
100         case DEVICE_TYPE_ATOMICS_COBALT:
101                 return atomics_cobalt_parser_create(parser);
102
103         default:
104                 return PARSER_STATUS_ERROR;
105         }
106 }
107
108 static int parse_gasmixes(struct dive *dive, parser_t *parser, int ngases)
109 {
110         int i;
111
112         for (i = 0; i < ngases; i++) {
113                 int rc;
114                 gasmix_t gasmix = {0};
115                 int o2, he;
116
117                 rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
118                 if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
119                         return rc;
120
121                 if (i >= MAX_CYLINDERS)
122                         continue;
123
124                 o2 = gasmix.oxygen * 1000 + 0.5;
125                 he = gasmix.helium * 1000 + 0.5;
126
127                 /* Ignore bogus data - libdivecomputer does some crazy stuff */
128                 if (o2 <= AIR_PERMILLE || o2 >= 1000)
129                         o2 = 0;
130                 if (he < 0 || he >= 800 || o2+he >= 1000)
131                         he = 0;
132
133                 dive->cylinder[i].gasmix.o2.permille = o2;
134                 dive->cylinder[i].gasmix.he.permille = he;
135         }
136         return PARSER_STATUS_SUCCESS;
137 }
138
139 static void handle_event(struct dive *dive, struct sample *sample, parser_sample_value_t value)
140 {
141         int type, time;
142         static const char *events[] = {
143                 "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
144                 "violation", "bookmark", "surface", "safety stop", "gaschange",
145                 "safety stop (voluntary)", "safety stop (mandatory)", "deepstop",
146                 "ceiling (safety stop)", "unknown", "divetime", "maxdepth",
147                 "OLF", "PO2", "airtime", "rgbm", "heading", "tissue level warning"
148         };
149         const int nr_events = sizeof(events) / sizeof(const char *);
150         const char *name;
151
152         /*
153          * Just ignore surface events.  They are pointless.  What "surface"
154          * means depends on the dive computer (and possibly even settings
155          * in the dive computer). It does *not* necessarily mean "depth 0",
156          * so don't even turn it into that.
157          */
158         if (value.event.type == SAMPLE_EVENT_SURFACE)
159                 return;
160
161         /*
162          * Other evens might be more interesting, but for now we just print them out.
163          */
164         type = value.event.type;
165         name = "invalid event number";
166         if (type < nr_events)
167                 name = events[type];
168
169         time = value.event.time;
170         if (sample)
171                 time += sample->time.seconds;
172
173         add_event(dive, time, type, value.event.flags, value.event.value, name);
174 }
175
176 void
177 sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
178 {
179         int i;
180         struct dive **divep = userdata;
181         struct dive *dive = *divep;
182         struct sample *sample;
183
184         /*
185          * We fill in the "previous" sample - except for SAMPLE_TYPE_TIME,
186          * which creates a new one.
187          */
188         sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
189
190         switch (type) {
191         case SAMPLE_TYPE_TIME:
192                 sample = prepare_sample(divep);
193                 sample->time.seconds = value.time;
194                 finish_sample(*divep);
195                 break;
196         case SAMPLE_TYPE_DEPTH:
197                 sample->depth.mm = value.depth * 1000 + 0.5;
198                 break;
199         case SAMPLE_TYPE_PRESSURE:
200                 sample->cylinderindex = value.pressure.tank;
201                 sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
202                 break;
203         case SAMPLE_TYPE_TEMPERATURE:
204                 sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
205                 break;
206         case SAMPLE_TYPE_EVENT:
207                 handle_event(dive, sample, value);
208                 break;
209         case SAMPLE_TYPE_RBT:
210                 printf("   <rbt>%u</rbt>\n", value.rbt);
211                 break;
212         case SAMPLE_TYPE_HEARTBEAT:
213                 printf("   <heartbeat>%u</heartbeat>\n", value.heartbeat);
214                 break;
215         case SAMPLE_TYPE_BEARING:
216                 printf("   <bearing>%u</bearing>\n", value.bearing);
217                 break;
218         case SAMPLE_TYPE_VENDOR:
219                 printf("   <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
220                 for (i = 0; i < value.vendor.size; ++i)
221                         printf("%02X", ((unsigned char *) value.vendor.data)[i]);
222                 printf("</vendor>\n");
223                 break;
224         default:
225                 break;
226         }
227 }
228
229
230 static int parse_samples(struct dive **divep, parser_t *parser)
231 {
232         // Parse the sample data.
233         printf("Parsing the sample data.\n");
234         return parser_samples_foreach(parser, sample_cb, divep);
235 }
236
237 /*
238  * Check if this dive already existed before the import
239  */
240 static int find_dive(struct dive *dive, device_data_t *devdata)
241 {
242         int i;
243
244         for (i = 0; i < dive_table.preexisting; i++) {
245                 struct dive *old = dive_table.dives[i];
246
247                 if (dive->when != old->when)
248                         continue;
249                 return 1;
250         }
251         return 0;
252 }
253
254 static int dive_cb(const unsigned char *data, unsigned int size,
255         const unsigned char *fingerprint, unsigned int fsize,
256         void *userdata)
257 {
258         int rc;
259         parser_t *parser = NULL;
260         device_data_t *devdata = userdata;
261         dc_datetime_t dt = {0};
262         struct tm tm;
263         struct dive *dive;
264
265         rc = create_parser(devdata, &parser);
266         if (rc != PARSER_STATUS_SUCCESS) {
267                 fprintf(stderr, "Unable to create parser for %s", devdata->name);
268                 return rc;
269         }
270
271         rc = parser_set_data(parser, data, size);
272         if (rc != PARSER_STATUS_SUCCESS) {
273                 fprintf(stderr, "Error registering the data.");
274                 parser_destroy(parser);
275                 return rc;
276         }
277
278         dive = alloc_dive();
279         rc = parser_get_datetime(parser, &dt);
280         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
281                 fprintf(stderr, "Error parsing the datetime.");
282                 parser_destroy (parser);
283                 return rc;
284         }
285
286         tm.tm_year = dt.year;
287         tm.tm_mon = dt.month-1;
288         tm.tm_mday = dt.day;
289         tm.tm_hour = dt.hour;
290         tm.tm_min = dt.minute;
291         tm.tm_sec = dt.second;
292         dive->when = utc_mktime(&tm);
293
294         // Parse the divetime.
295         printf("Parsing the divetime.\n");
296         unsigned int divetime = 0;
297         rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
298         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
299                 fprintf(stderr, "Error parsing the divetime.");
300                 parser_destroy(parser);
301                 return rc;
302         }
303         dive->duration.seconds = divetime;
304
305         // Parse the maxdepth.
306         printf("Parsing the maxdepth.\n");
307         double maxdepth = 0.0;
308         rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
309         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
310                 fprintf(stderr, "Error parsing the maxdepth.");
311                 parser_destroy(parser);
312                 return rc;
313         }
314         dive->maxdepth.mm = maxdepth * 1000 + 0.5;
315
316         // Parse the gas mixes.
317         printf("Parsing the gas mixes.\n");
318         unsigned int ngases = 0;
319         rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
320         if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
321                 fprintf(stderr, "Error parsing the gas mix count.");
322                 parser_destroy(parser);
323                 return rc;
324         }
325
326         rc = parse_gasmixes(dive, parser, ngases);
327         if (rc != PARSER_STATUS_SUCCESS) {
328                 fprintf(stderr, "Error parsing the gas mix.");
329                 parser_destroy(parser);
330                 return rc;
331         }
332
333         // Initialize the sample data.
334         rc = parse_samples(&dive, parser);
335         if (rc != PARSER_STATUS_SUCCESS) {
336                 fprintf(stderr, "Error parsing the samples.");
337                 parser_destroy(parser);
338                 return rc;
339         }
340
341         parser_destroy(parser);
342
343         /* If we already saw this dive, abort. */
344         if (find_dive(dive, devdata))
345                 return 0;
346
347         record_dive(dive);
348         return 1;
349 }
350
351
352 static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
353 {
354         return device_foreach(device, dive_cb, devicedata);
355 }
356
357 static device_status_t device_open(const char *devname,
358         device_type_t type,
359         device_t **device)
360 {
361         switch (type) {
362         case DEVICE_TYPE_SUUNTO_SOLUTION:
363                 return suunto_solution_device_open(device, devname);
364
365         case DEVICE_TYPE_SUUNTO_EON:
366                 return suunto_eon_device_open(device, devname);
367
368         case DEVICE_TYPE_SUUNTO_VYPER:
369                 return suunto_vyper_device_open(device, devname);
370
371         case DEVICE_TYPE_SUUNTO_VYPER2:
372                 return suunto_vyper2_device_open(device, devname);
373
374         case DEVICE_TYPE_SUUNTO_D9:
375                 return suunto_d9_device_open(device, devname);
376
377         case DEVICE_TYPE_UWATEC_ALADIN:
378                 return uwatec_aladin_device_open(device, devname);
379
380         case DEVICE_TYPE_UWATEC_MEMOMOUSE:
381                 return uwatec_memomouse_device_open(device, devname);
382
383         case DEVICE_TYPE_UWATEC_SMART:
384                 return uwatec_smart_device_open(device);
385
386         case DEVICE_TYPE_REEFNET_SENSUS:
387                 return reefnet_sensus_device_open(device, devname);
388
389         case DEVICE_TYPE_REEFNET_SENSUSPRO:
390                 return reefnet_sensuspro_device_open(device, devname);
391
392         case DEVICE_TYPE_REEFNET_SENSUSULTRA:
393                 return reefnet_sensusultra_device_open(device, devname);
394
395         case DEVICE_TYPE_OCEANIC_VTPRO:
396                 return oceanic_vtpro_device_open(device, devname);
397
398         case DEVICE_TYPE_OCEANIC_VEO250:
399                 return oceanic_veo250_device_open(device, devname);
400
401         case DEVICE_TYPE_OCEANIC_ATOM2:
402                 return oceanic_atom2_device_open(device, devname);
403
404         case DEVICE_TYPE_MARES_DARWIN:
405                 return mares_darwin_device_open(device, devname, 0); /// last parameter is model type (taken from example), 0 seems to be standard, 1 is DARWIN_AIR => Darwin Air wont work if this is fixed here?
406
407         case DEVICE_TYPE_MARES_NEMO:
408                 return mares_nemo_device_open(device, devname);
409
410         case DEVICE_TYPE_MARES_PUCK:
411                 return mares_puck_device_open(device, devname);
412
413         case DEVICE_TYPE_MARES_ICONHD:
414                 return mares_iconhd_device_open(device, devname);
415
416         case DEVICE_TYPE_HW_OSTC:
417                 return hw_ostc_device_open(device, devname);
418
419         case DEVICE_TYPE_CRESSI_EDY:
420                 return cressi_edy_device_open(device, devname);
421
422         case DEVICE_TYPE_ZEAGLE_N2ITION3:
423                 return zeagle_n2ition3_device_open(device, devname);
424
425         case DEVICE_TYPE_ATOMICS_COBALT:
426                 return atomics_cobalt_device_open(device);
427
428         default:
429                 return DEVICE_STATUS_ERROR;
430         }
431 }
432
433 static void event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
434 {
435         const device_progress_t *progress = data;
436         const device_devinfo_t *devinfo = data;
437         const device_clock_t *clock = data;
438         device_data_t *devdata = userdata;
439
440         switch (event) {
441         case DEVICE_EVENT_WAITING:
442                 printf("Event: waiting for user action\n");
443                 break;
444         case DEVICE_EVENT_PROGRESS:
445                 update_progressbar(&devdata->progress,
446                         (double) progress->current / (double) progress->maximum);
447                 break;
448         case DEVICE_EVENT_DEVINFO:
449                 devdata->devinfo = *devinfo;
450                 printf("Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
451                         devinfo->model, devinfo->model,
452                         devinfo->firmware, devinfo->firmware,
453                         devinfo->serial, devinfo->serial);
454                 break;
455         case DEVICE_EVENT_CLOCK:
456                 devdata->clock = *clock;
457                 printf("Event: systime=%"PRId64", devtime=%u\n",
458                         (uint64_t)clock->systime, clock->devtime);
459                 break;
460         default:
461                 break;
462         }
463 }
464
465 static int import_thread_done = 0, import_thread_cancelled;
466
467 static int
468 cancel_cb(void *userdata)
469 {
470         return import_thread_cancelled;
471 }
472
473 static const char *do_libdivecomputer_import(device_data_t *data)
474 {
475         device_t *device = NULL;
476         device_status_t rc;
477
478         rc = device_open(data->devname, data->type, &device);
479         if (rc != DEVICE_STATUS_SUCCESS)
480                 return "Unable to open %s (%s)";
481
482         // Register the event handler.
483         int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
484         rc = device_set_events(device, events, event_cb, data);
485         if (rc != DEVICE_STATUS_SUCCESS) {
486                 device_close(device);
487                 return "Error registering the event handler.";
488         }
489
490         // Register the cancellation handler.
491         rc = device_set_cancel(device, cancel_cb, data);
492         if (rc != DEVICE_STATUS_SUCCESS) {
493                 device_close(device);
494                 return "Error registering the cancellation handler.";
495         }
496
497         rc = import_device_data(device, data);
498         if (rc != DEVICE_STATUS_SUCCESS) {
499                 device_close(device);
500                 return "Dive data import error";
501         }
502
503         device_close(device);
504         return NULL;
505 }
506
507 static void *pthread_wrapper(void *_data)
508 {
509         device_data_t *data = _data;
510         const char *err_string = do_libdivecomputer_import(data);
511         import_thread_done = 1;
512         return (void *)err_string;
513 }
514
515 void do_import(device_data_t *data)
516 {
517         pthread_t pthread;
518         void *retval;
519
520         /* I'm sure there is some better interface for waiting on a thread in a UI main loop */
521         import_thread_done = 0;
522         pthread_create(&pthread, NULL, pthread_wrapper, data);
523         while (!import_thread_done) {
524                 import_thread_cancelled = process_ui_events();
525                 usleep(100000);
526         }
527         if (pthread_join(pthread, &retval) < 0)
528                 retval = "Odd pthread error return";
529         if (retval)
530                 error(retval, data->name, data->devname);
531 }
532
533 /*
534  * Taken from 'example.c' in libdivecomputer.
535  *
536  * I really wish there was some way to just have
537  * libdivecomputer tell us what devices it supports,
538  * rather than have the application have to know..
539  */
540 struct device_list device_list[] = {
541         { "Suunto Solution",    DEVICE_TYPE_SUUNTO_SOLUTION },
542         { "Suunto Eon",         DEVICE_TYPE_SUUNTO_EON },
543         { "Suunto Vyper",       DEVICE_TYPE_SUUNTO_VYPER },
544         { "Suunto Vyper Air",   DEVICE_TYPE_SUUNTO_VYPER2 },
545         { "Suunto D9",          DEVICE_TYPE_SUUNTO_D9 },
546         { "Uwatec Aladin",      DEVICE_TYPE_UWATEC_ALADIN },
547         { "Uwatec Memo Mouse",  DEVICE_TYPE_UWATEC_MEMOMOUSE },
548         { "Uwatec Smart",       DEVICE_TYPE_UWATEC_SMART },
549         { "ReefNet Sensus",     DEVICE_TYPE_REEFNET_SENSUS },
550         { "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
551         { "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
552         { "Oceanic VT Pro",     DEVICE_TYPE_OCEANIC_VTPRO },
553         { "Oceanic Veo250",     DEVICE_TYPE_OCEANIC_VEO250 },
554         { "Oceanic Atom 2",     DEVICE_TYPE_OCEANIC_ATOM2 },
555         { "Mares Darwin, M1, M2, Airlab",       DEVICE_TYPE_MARES_DARWIN },
556         { "Mares Nemo, Excel, Apneist", DEVICE_TYPE_MARES_NEMO },
557         { "Mares Puck, Nemo Air, Nemo Wide",    DEVICE_TYPE_MARES_PUCK },
558         { "Mares Icon HD",      DEVICE_TYPE_MARES_ICONHD },
559         { "OSTC",               DEVICE_TYPE_HW_OSTC },
560 #ifdef LIBDIVECOMPUTER_SUPPORTS_FROG
561         { "OSTC Frog",          DEVICE_TYPE_HW_FROG },
562 #endif
563         { "Cressi Edy",         DEVICE_TYPE_CRESSI_EDY },
564         { "Zeagle N2iTiON 3",   DEVICE_TYPE_ZEAGLE_N2ITION3 },
565         { "Atomics Cobalt",     DEVICE_TYPE_ATOMICS_COBALT },
566         { NULL }
567 };