From bcb2925a258d7ed86fd77d9d0888c9a06e9ed673 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 7 Mar 2015 10:07:35 +0100 Subject: [PATCH 1/6] Fix the Galileo trimix detection. Originally, I assumed that the trimix firmware update changed the model number from 0x11 (Galileo) to 0x19 (Galileo Trimix). But that assumption appears to be wrong because I received data from a Galileo with model number 0x11, but with the trimix data format. Another explanation might be that the trimix data format is not specific to the trimix firmware. Anyway, this is easily fixed by treating both models identically. --- src/uwatec_smart_parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index c2a09cf..770fdb4 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -375,7 +375,7 @@ uwatec_smart_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi unsigned int header = parser->headersize; unsigned int trimix = 0; - if (parser->model == GALILEOTRIMIX) { + if (parser->model == GALILEO || parser->model == GALILEOTRIMIX) { if (size < 44) return DC_STATUS_DATAFORMAT; @@ -501,7 +501,7 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t unsigned int header = parser->headersize; unsigned int trimix = 0; - if (parser->model == GALILEOTRIMIX) { + if (parser->model == GALILEO || parser->model == GALILEOTRIMIX) { if (size < 44) return DC_STATUS_DATAFORMAT; From fb417ee5b5c090203810a63ae9753464f753829e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 7 Mar 2015 10:58:37 +0100 Subject: [PATCH 2/6] No gas mix support for the Galileo Trimix yet. For the Galileo Trimix we don't know yet where and how the gas mixes are stored. Right now, we just pretend there are no gas mixes available, which is misleading. --- src/uwatec_smart_parser.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index 770fdb4..8a40b4b 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -400,11 +400,12 @@ uwatec_smart_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi break; case DC_FIELD_GASMIX_COUNT: if (trimix) - *((unsigned int *) value) = 0; - else - *((unsigned int *) value) = table->ngases; + return DC_STATUS_UNSUPPORTED; + *((unsigned int *) value) = table->ngases; break; case DC_FIELD_GASMIX: + if (trimix) + return DC_STATUS_UNSUPPORTED; gasmix->helium = 0.0; gasmix->oxygen = data[table->gasmix + flags * 2] / 100.0; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; @@ -704,7 +705,7 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t sample.time = time; if (callback) callback (DC_SAMPLE_TIME, sample, userdata); - if (gasmix != gasmix_previous && !trimix) { + if (ngasmix && gasmix != gasmix_previous) { if (gasmix >= ngasmix) return DC_STATUS_DATAFORMAT; sample.event.type = SAMPLE_EVENT_GASCHANGE; From 8b64ce0edccae9099480a190e8daeb199db424d4 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 7 Mar 2015 11:50:24 +0100 Subject: [PATCH 3/6] Cache the parser data internally. --- src/uwatec_smart_parser.c | 125 +++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 41 deletions(-) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index 8a40b4b..ef0e682 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -49,6 +49,8 @@ #define UNSUPPORTED 0xFFFFFFFF +#define NGASMIXES 3 + typedef enum { PRESSURE_DEPTH, RBT, @@ -93,6 +95,11 @@ struct uwatec_smart_parser_t { const uwatec_smart_header_info_t *header; unsigned int headersize; unsigned int nsamples; + // Cached fields. + unsigned int cached; + unsigned int trimix; + unsigned int ngasmixes; + unsigned int oxygen[NGASMIXES]; }; static dc_status_t uwatec_smart_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); @@ -254,6 +261,45 @@ uwatec_smart_sample_info_t uwatec_smart_tec_samples[] = { }; +static dc_status_t +uwatec_smart_parser_cache (uwatec_smart_parser_t *parser) +{ + const unsigned char *data = parser->base.data; + unsigned int size = parser->base.size; + + 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; + } + } + + // Get the gas mixes. + unsigned int oxygen[NGASMIXES] = {0}; + unsigned int ngasmixes = (trimix ? 0 : parser->header->ngases); + for (unsigned int i = 0; i < ngasmixes; ++i) { + oxygen[i] = data[parser->header->gasmix + i * 2]; + } + + // Cache the data for later use. + parser->trimix = trimix; + parser->ngasmixes = ngasmixes; + for (unsigned int i = 0; i < ngasmixes; ++i) { + parser->oxygen[i] = oxygen[i]; + } + parser->cached = 1; + + return DC_STATUS_SUCCESS; +} + + dc_status_t uwatec_smart_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int devtime, dc_ticks_t systime) { @@ -321,6 +367,13 @@ uwatec_smart_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i return DC_STATUS_INVALIDARGS; } + parser->cached = 0; + parser->trimix = 0; + parser->ngasmixes = 0; + for (unsigned int i = 0; i < NGASMIXES; ++i) { + parser->oxygen[i] = 0; + } + *out = (dc_parser_t*) parser; return DC_STATUS_SUCCESS; @@ -340,6 +393,16 @@ uwatec_smart_parser_destroy (dc_parser_t *abstract) static dc_status_t uwatec_smart_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size) { + uwatec_smart_parser_t *parser = (uwatec_smart_parser_t *) abstract; + + // Reset the cache. + parser->cached = 0; + parser->trimix = 0; + parser->ngasmixes = 0; + for (unsigned int i = 0; i < NGASMIXES; ++i) { + parser->oxygen[i] = 0; + } + return DC_STATUS_SUCCESS; } @@ -369,24 +432,12 @@ 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; - unsigned int size = abstract->size; - const uwatec_smart_header_info_t *table = parser->header; - unsigned int header = parser->headersize; - unsigned int trimix = 0; - if (parser->model == GALILEO || parser->model == GALILEOTRIMIX) { - if (size < 44) - return DC_STATUS_DATAFORMAT; - - if (data[43] & 0x80) { - header = 0xB1; - trimix = 1; - } - } - - if (size < header) - return DC_STATUS_DATAFORMAT; + // Cache the parser data. + dc_status_t rc = uwatec_smart_parser_cache (parser); + if (rc != DC_STATUS_SUCCESS) + return rc; dc_gasmix_t *gasmix = (dc_gasmix_t *) value; @@ -399,15 +450,15 @@ uwatec_smart_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi *((double *) value) = array_uint16_le (data + table->maxdepth) / 100.0; break; case DC_FIELD_GASMIX_COUNT: - if (trimix) + if (parser->trimix) return DC_STATUS_UNSUPPORTED; - *((unsigned int *) value) = table->ngases; + *((unsigned int *) value) = parser->ngasmixes; break; case DC_FIELD_GASMIX: - if (trimix) + if (parser->trimix) return DC_STATUS_UNSUPPORTED; gasmix->helium = 0.0; - gasmix->oxygen = data[table->gasmix + flags * 2] / 100.0; + gasmix->oxygen = parser->oxygen[flags] / 100.0; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; break; case DC_FIELD_TEMPERATURE_MINIMUM: @@ -497,19 +548,16 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t 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; unsigned int header = parser->headersize; - unsigned int trimix = 0; - - if (parser->model == GALILEO || parser->model == GALILEOTRIMIX) { - if (size < 44) - return DC_STATUS_DATAFORMAT; - - if (data[43] & 0x80) { - header = 0xB1; - trimix = 1; - } + if (parser->trimix) { + header = 0xB1; } // Get the maximum number of alarm bytes. @@ -522,13 +570,6 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t } } - // Get all the gas mixes. - unsigned int oxygen[3] = {0}; - unsigned int ngasmix = (trimix ? 0 : parser->header->ngases); - for (unsigned int i = 0; i < ngasmix; ++i) { - oxygen[i] = data[parser->header->gasmix + i * 2]; - } - int complete = 0; int calibrated = 0; @@ -630,7 +671,7 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t break; case PRESSURE: if (table[id].absolute) { - if (trimix) { + if (parser->trimix) { tank = (value & 0xF000) >> 24; pressure = (value & 0x0FFF) / 4.0; } else { @@ -705,13 +746,15 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t sample.time = time; if (callback) callback (DC_SAMPLE_TIME, sample, userdata); - if (ngasmix && gasmix != gasmix_previous) { - if (gasmix >= ngasmix) + if (parser->ngasmixes && gasmix != gasmix_previous) { + if (gasmix >= parser->ngasmixes) { + ERROR (abstract->context, "Invalid gas mix index."); return DC_STATUS_DATAFORMAT; + } sample.event.type = SAMPLE_EVENT_GASCHANGE; sample.event.time = 0; sample.event.flags = 0; - sample.event.value = oxygen[gasmix]; + sample.event.value = parser->oxygen[gasmix]; if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); gasmix_previous = gasmix; } From 4fd825cdac341a2d4a1366c6deb7d4a71bbdf94d Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 7 Mar 2015 23:27:26 +0100 Subject: [PATCH 4/6] Uwatec Aladin Tec 2G supports maximum 2 gas mixes. According to the technical specifications, the Uwatec Aladin Tec 2G supports maximum two gas mixes. The data appears to confirm this, because the extra third gas mix always contains unrealistic oxygen percentages. However, I came across some data containing gas switches to the third gas mix. The interesting part is that according to the Uwatec application, this is actually a switch to the second gas mix in the header. One possible explanation is that for models with up to 3 gas mixes, they are labelled respectively "bottom", "travel" and "deco" mix. But the documentation for the Aladin Tec 2G only refers to the bottom and deco mix. So it might be that internally the index of the deco mix is always the 3th mix, regardless of whether a travel mix is supported or not. If the only allowed values for the gas mix index are 0 (for the bottom mix) or 2 (for the deco mix), then manually remapping the deco mix is equivalent with ignoring the lowest bit. This has the advantage that the required bitmasks and shifts are no longer different from those for the other models. --- src/uwatec_smart_parser.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index ef0e682..a4c002f 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -151,7 +151,7 @@ static const uwatec_smart_header_info_t uwatec_smart_aladin_tec2g_header = { 22, 26, - 34, 3, + 34, 2, 30, /* temp_minimum */ 28, /* temp_maximum */ 32, /* temp_surface */ @@ -713,11 +713,7 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t alarms[table[id].index] = value; have_alarms = 1; if (table[id].index == 1) { - if (parser->model == ALADINTEC || parser->model == ALADINTEC2G) { - gasmix = (value & 0x18) >> 3; - } else { - gasmix = (value & 0x30) >> 4; - } + gasmix = (value & 0x30) >> 4; } break; case TIME: From 76f93d3fe53127157223574db7456f65b4bde418 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 7 Mar 2015 23:30:02 +0100 Subject: [PATCH 5/6] Ignore disabled gas mixes. Gas mixes are disabled by setting their oxygen percentage byte to 0x00. This is clearly an invalid gas mix, and it makes no sense to return it back to the application. It seems the device doesn't allow you to enable a gas mix if the previous gas mix has already been disabled. Therefore we can simply stop parsing the gas mixes once the first disabled gas mix has been found. --- src/uwatec_smart_parser.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index a4c002f..cf2b2c7 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -282,10 +282,16 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser) } // Get the gas mixes. + unsigned int ngasmixes = 0; unsigned int oxygen[NGASMIXES] = {0}; - unsigned int ngasmixes = (trimix ? 0 : parser->header->ngases); - for (unsigned int i = 0; i < ngasmixes; ++i) { - oxygen[i] = data[parser->header->gasmix + i * 2]; + if (!trimix) { + for (unsigned int i = 0; i < parser->header->ngases; ++i) { + unsigned int o2 = data[parser->header->gasmix + i * 2]; + if (o2 == 0) + break; // Skip disabled gas mixes. + oxygen[ngasmixes] = o2; + ngasmixes++; + } } // Cache the data for later use. From c135eb75de747500a9aef06f3b4323f7b94d59df Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 9 Mar 2015 10:42:16 +0100 Subject: [PATCH 6/6] Implement the dive mode field. Dives with all gas mixes disabled are flagged as gauge dives in the Uwatec application. --- src/uwatec_smart_parser.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index cf2b2c7..3cd19bf 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -480,6 +480,14 @@ uwatec_smart_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi return DC_STATUS_UNSUPPORTED; *((double *) value) = (signed short) array_uint16_le (data + table->temp_surface) / 10.0; break; + case DC_FIELD_DIVEMODE: + if (parser->trimix) + return DC_STATUS_UNSUPPORTED; + if (parser->ngasmixes) + *((dc_divemode_t *) value) = DC_DIVEMODE_OC; + else + *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE; + break; default: return DC_STATUS_UNSUPPORTED; }