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