From 6b88bc1b8b18059cd570dd023c21aec4cb48087c Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 31 Aug 2017 19:44:38 +0200 Subject: [PATCH 1/3] Fix the Uwatec trimix data format The trimix dive header is only 84 bytes large, instead of 0xB1 bytes. The difference is quite hard to notice, because compared to the normal Galileo data format, the majority of the fields are located at exactly the same offset. But there are also some subtle differences, like the settings field containing the freedive and gauge bits. To fix this bug, a new header table is added. The rest of the code is updated to use this new table instead of the old trimix flag. The only place where the old flag is still used is for the decoding of the tank and pressure sample. --- src/uwatec_smart_parser.c | 67 ++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index 54bdee5..c1311ee 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -142,9 +142,9 @@ struct uwatec_smart_parser_t { unsigned int nsamples; const uwatec_smart_event_info_t *events[NEVENTS]; unsigned int nevents[NEVENTS]; + unsigned int trimix; // Cached fields. unsigned int cached; - unsigned int trimix; unsigned int ngasmixes; uwatec_smart_gasmix_t gasmix[NGASMIXES]; unsigned int ntanks; @@ -194,6 +194,19 @@ uwatec_smart_header_info_t uwatec_smart_galileo_header = { 92, /* settings */ }; +static const +uwatec_smart_header_info_t uwatec_smart_trimix_header = { + 22, /* maxdepth */ + 26, /* divetime */ + UNSUPPORTED, 0, /* gasmixes */ + 30, /* temp_minimum */ + 28, /* temp_maximum */ + 32, /* temp_surface */ + UNSUPPORTED, /* tankpressure */ + 16, /* timezone */ + 68, /* settings */ +}; + static const uwatec_smart_header_info_t uwatec_smart_aladin_tec_header = { 22, @@ -418,32 +431,32 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser) { const unsigned char *data = parser->base.data; unsigned int size = parser->base.size; - const uwatec_smart_header_info_t *header = parser->header; if (parser->cached) { return DC_STATUS_SUCCESS; } - unsigned int trimix = 0; if (parser->model == GALILEO || parser->model == GALILEOTRIMIX) { if (size < 44) return DC_STATUS_DATAFORMAT; if (data[43] & 0x80) { - trimix = 1; - } - - if (trimix) { + parser->trimix = 1; + parser->headersize = 84; + parser->header = &uwatec_smart_trimix_header; parser->events[2] = uwatec_smart_trimix_events_2; parser->nevents[2] = C_ARRAY_SIZE (uwatec_smart_trimix_events_2); } else { + parser->trimix = 0; + parser->headersize = 152; + parser->header = &uwatec_smart_galileo_header; parser->events[2] = uwatec_smart_galileo_events_2; parser->nevents[2] = C_ARRAY_SIZE (uwatec_smart_galileo_events_2); } - } else if (parser->model == G2) { - trimix = 1; } + const uwatec_smart_header_info_t *header = parser->header; + // Get the settings. dc_divemode_t divemode = DC_DIVEMODE_OC; dc_water_t watertype = DC_WATER_FRESH; @@ -479,7 +492,7 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser) unsigned int ngasmixes = 0; uwatec_smart_tank_t tank[NGASMIXES] = {{0}}; uwatec_smart_gasmix_t gasmix[NGASMIXES] = {{0}}; - if (!trimix) { + if (header->gasmix != UNSUPPORTED) { for (unsigned int i = 0; i < header->ngases; ++i) { unsigned int idx = DC_GASMIX_UNKNOWN; unsigned int o2 = 0; @@ -526,7 +539,6 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser) } // Cache the data for later use. - parser->trimix = trimix; parser->ngasmixes = ngasmixes; for (unsigned int i = 0; i < ngasmixes; ++i) { parser->gasmix[i] = gasmix[i]; @@ -563,6 +575,7 @@ uwatec_smart_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i parser->model = model; parser->devtime = devtime; parser->systime = systime; + parser->trimix = 0; for (unsigned int i = 0; i < NEVENTS; ++i) { parser->events[i] = NULL; parser->nevents[i] = 0; @@ -582,7 +595,6 @@ uwatec_smart_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i case MERIDIAN: case CHROMIS: case MANTIS2: - case G2: parser->headersize = 152; parser->header = &uwatec_smart_galileo_header; parser->samples = uwatec_smart_galileo_samples; @@ -594,6 +606,19 @@ uwatec_smart_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i parser->nevents[1] = C_ARRAY_SIZE (uwatec_smart_galileo_events_1); parser->nevents[2] = C_ARRAY_SIZE (uwatec_smart_galileo_events_2); break; + case G2: + parser->headersize = 84; + parser->header = &uwatec_smart_trimix_header; + parser->samples = uwatec_smart_galileo_samples; + parser->nsamples = C_ARRAY_SIZE (uwatec_smart_galileo_samples); + parser->events[0] = uwatec_smart_galileo_events_0; + parser->events[1] = uwatec_smart_galileo_events_1; + parser->events[2] = uwatec_smart_trimix_events_2; + parser->nevents[0] = C_ARRAY_SIZE (uwatec_smart_galileo_events_0); + parser->nevents[1] = C_ARRAY_SIZE (uwatec_smart_galileo_events_1); + parser->nevents[2] = C_ARRAY_SIZE (uwatec_smart_trimix_events_2); + parser->trimix = 1; + break; case ALADINTEC: parser->headersize = 108; parser->header = &uwatec_smart_aladin_tec_header; @@ -635,7 +660,6 @@ uwatec_smart_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i } parser->cached = 0; - parser->trimix = 0; parser->ngasmixes = 0; parser->ntanks = 0; for (unsigned int i = 0; i < NGASMIXES; ++i) { @@ -667,7 +691,6 @@ uwatec_smart_parser_set_data (dc_parser_t *abstract, const unsigned char *data, // Reset the cache. parser->cached = 0; - parser->trimix = 0; parser->ngasmixes = 0; parser->ntanks = 0; for (unsigned int i = 0; i < NGASMIXES; ++i) { @@ -728,9 +751,6 @@ uwatec_smart_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi { uwatec_smart_parser_t *parser = (uwatec_smart_parser_t *) abstract; - const unsigned char *data = abstract->data; - const uwatec_smart_header_info_t *table = parser->header; - // Cache the parser data. dc_status_t rc = uwatec_smart_parser_cache (parser); if (rc != DC_STATUS_SUCCESS) @@ -743,6 +763,9 @@ uwatec_smart_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi return rc; } + const uwatec_smart_header_info_t *table = parser->header; + const unsigned char *data = abstract->data; + double salinity = (parser->watertype == DC_WATER_SALT ? SALT : FRESH); dc_gasmix_t *gasmix = (dc_gasmix_t *) value; @@ -766,13 +789,9 @@ uwatec_smart_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; break; case DC_FIELD_TANK_COUNT: - if (table->tankpressure == UNSUPPORTED) - return DC_STATUS_UNSUPPORTED; *((unsigned int *) value) = parser->ntanks; break; case DC_FIELD_TANK: - if (table->tankpressure == UNSUPPORTED) - return DC_STATUS_UNSUPPORTED; tank->type = DC_TANKVOLUME_NONE; tank->volume = 0.0; tank->workpressure = 0.0; @@ -885,10 +904,6 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t const uwatec_smart_sample_info_t *table = parser->samples; unsigned int entries = parser->nsamples; - unsigned int header = parser->headersize; - if (parser->trimix) { - header = 0xB1; - } // Get the maximum number of alarm bytes. unsigned int nalarms = 0; @@ -927,7 +942,7 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t int have_depth = 0, have_temperature = 0, have_pressure = 0, have_rbt = 0, have_heartrate = 0, have_bearing = 0; - unsigned int offset = header; + unsigned int offset = parser->headersize; while (offset < size) { dc_sample_value_t sample = {0}; From ce590b5bc92ad4595de10d08efde299e001c45a5 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 2 Sep 2017 09:47:49 +0200 Subject: [PATCH 2/3] Use two passes to parse the profile data In the trimix data format, the tank and gas mix information is no longer stored in the header, but in a special sample. Because this sample is usually located at the end of the profile, the info isn't available yet during the first pass. Hence the need for a second pass. Without this change, the tank and gas mix samples will be missing unless the caller calls the dc_parser_get_field() function before calling the dc_parser_samples_foreach() function. --- src/uwatec_smart_parser.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index c1311ee..78f68bd 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -158,6 +158,8 @@ static dc_status_t uwatec_smart_parser_get_datetime (dc_parser_t *abstract, dc_d static dc_status_t uwatec_smart_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); +static dc_status_t uwatec_smart_parse (uwatec_smart_parser_t *parser, dc_sample_callback_t callback, void *userdata); + static const dc_parser_vtable_t uwatec_smart_parser_vtable = { sizeof(uwatec_smart_parser_t), DC_FAMILY_UWATEC_SMART, @@ -758,7 +760,7 @@ uwatec_smart_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi // Cache the profile data. if (parser->cached < PROFILE) { - rc = uwatec_smart_parser_samples_foreach (abstract, NULL, NULL); + rc = uwatec_smart_parse (parser, NULL, NULL); if (rc != DC_STATUS_SUCCESS) return rc; } @@ -890,18 +892,13 @@ uwatec_smart_fixsignbit (unsigned int x, unsigned int n) static dc_status_t -uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +uwatec_smart_parse (uwatec_smart_parser_t *parser, dc_sample_callback_t callback, void *userdata) { - uwatec_smart_parser_t *parser = (uwatec_smart_parser_t*) abstract; + dc_parser_t *abstract = (dc_parser_t *) parser; const unsigned char *data = abstract->data; unsigned int size = abstract->size; - // Cache the parser data. - dc_status_t rc = uwatec_smart_parser_cache (parser); - if (rc != DC_STATUS_SUCCESS) - return rc; - const uwatec_smart_sample_info_t *table = parser->samples; unsigned int entries = parser->nsamples; @@ -1226,3 +1223,24 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t return DC_STATUS_SUCCESS; } + + +static dc_status_t +uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + uwatec_smart_parser_t *parser = (uwatec_smart_parser_t *) abstract; + + // Cache the parser data. + dc_status_t rc = uwatec_smart_parser_cache (parser); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Cache the profile data. + if (parser->cached < PROFILE) { + rc = uwatec_smart_parse (parser, NULL, NULL); + if (rc != DC_STATUS_SUCCESS) + return rc; + } + + return uwatec_smart_parse (parser, callback, userdata); +} From cb2a4f4a72367f21c7cbc708fd96c96cef62ca2a Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 2 Sep 2017 10:31:19 +0200 Subject: [PATCH 3/3] Use more descriptive names Those two samples are no longer unknown. The first one contains some freedive related data, and the second one contains some additional data with several sub types. At the moment only the tank and gas mix info is used. --- src/uwatec_smart_parser.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index 78f68bd..639d0e4 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -74,8 +74,8 @@ typedef enum { BEARING, ALARMS, TIME, - UNKNOWN1, - UNKNOWN2, + APNEA, + MISC, } uwatec_smart_sample_t; typedef enum { @@ -292,8 +292,8 @@ uwatec_smart_sample_info_t uwatec_smart_galileo_samples[] = { {HEARTRATE, 1, 0, 8, 0, 1}, // 1111 0111 dddddddd {BEARING, 1, 0, 8, 0, 2}, // 1111 1000 dddddddd dddddddd {ALARMS, 1, 2, 8, 0, 1}, // 1111 1001 dddddddd - {UNKNOWN1, 1, 0, 8, 0, 0}, // 1111 1010 (8 bytes) - {UNKNOWN2, 1, 0, 8, 0, 1}, // 1111 1011 dddddddd (n-1 bytes) + {APNEA, 1, 0, 8, 0, 0}, // 1111 1010 (8 bytes) + {MISC, 1, 0, 8, 0, 1}, // 1111 1011 dddddddd (n-1 bytes) }; @@ -1091,14 +1091,14 @@ uwatec_smart_parse (uwatec_smart_parser_t *parser, dc_sample_callback_t callback case TIME: complete = value; break; - case UNKNOWN1: + case APNEA: if (offset + 8 > size) { ERROR (abstract->context, "Incomplete sample data."); return DC_STATUS_DATAFORMAT; } offset += 8; break; - case UNKNOWN2: + case MISC: if (value < 1 || offset + value - 1 > size) { ERROR (abstract->context, "Incomplete sample data."); return DC_STATUS_DATAFORMAT;