From ecc9e0b09bc18604acc6974cd58eebd9d92e3ff0 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 31 Jul 2023 19:42:13 +0200 Subject: [PATCH 1/4] Limit the lookup function to the manual gas mixes Looking up the gasmix by oxygen and helium content is only needed for the manual gas mixes. For gas switches to a fixed gas mix, the index is stored directly in the data. --- src/hw_ostc_parser.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index 288d28a..8798216 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -37,10 +37,6 @@ #define UNDEFINED 0xFFFFFFFF -#define ALL 0 -#define FIXED 1 -#define MANUAL 2 - #define HEADER 1 #define PROFILE 2 @@ -194,15 +190,10 @@ static const hw_ostc_layout_t hw_ostc_layout_ostc3 = { }; static unsigned int -hw_ostc_find_gasmix (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he, unsigned int dil, unsigned int type) +hw_ostc_find_gasmix_manual (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he, unsigned int dil) { - unsigned int offset = 0; + unsigned int offset = parser->nfixed; unsigned int count = parser->ngasmixes; - if (type == FIXED) { - count = parser->nfixed; - } else if (type == MANUAL) { - offset = parser->nfixed; - } unsigned int i = offset; while (i < count) { @@ -899,7 +890,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call } unsigned int o2 = data[offset]; unsigned int he = data[offset + 1]; - unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, ccr, MANUAL); + unsigned int idx = hw_ostc_find_gasmix_manual (parser, o2, he, ccr); if (idx >= parser->ngasmixes) { if (idx >= NGASMIXES) { ERROR (abstract->context, "Maximum number of gas mixes reached."); @@ -964,7 +955,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call unsigned int o2 = data[offset]; unsigned int he = data[offset + 1]; - unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, 0, MANUAL); + unsigned int idx = hw_ostc_find_gasmix_manual (parser, o2, he, 0); if (idx >= parser->ngasmixes) { if (idx >= NGASMIXES) { ERROR (abstract->context, "Maximum number of gas mixes reached."); @@ -1099,7 +1090,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call unsigned int o2 = data[offset]; unsigned int he = data[offset + 1]; - unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, 0, MANUAL); + unsigned int idx = hw_ostc_find_gasmix_manual (parser, o2, he, 0); if (idx >= parser->ngasmixes) { if (idx >= NGASMIXES) { ERROR (abstract->context, "Maximum number of gas mixes reached."); From 323804d5e68d1f5e075be29f479ef50bc72bc326 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 31 Jul 2023 20:17:06 +0200 Subject: [PATCH 2/4] Keep track of the actively used gas mixes The hwOS models support switching to a disabled gas mix. Therefore, the disabled state is not always a good indication whether a gas mix is used or not. Look for gas switches during the parsing step instead to keep track of the actively used gas mixes. --- src/hw_ostc_parser.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index 8798216..7d23964 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -108,6 +108,7 @@ typedef struct hw_ostc_gasmix_t { unsigned int helium; unsigned int type; unsigned int enabled; + unsigned int active; unsigned int diluent; } hw_ostc_gasmix_t; @@ -290,6 +291,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser) gasmix[i].helium = 0; gasmix[i].type = 0; gasmix[i].enabled = 1; + gasmix[i].active = 0; gasmix[i].diluent = 0; } } else if (version == 0x23 || version == 0x24) { @@ -299,6 +301,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser) gasmix[i].helium = data[28 + 4 * i + 1]; gasmix[i].type = data[28 + 4 * i + 3]; gasmix[i].enabled = gasmix[i].type != 0; + gasmix[i].active = 0; gasmix[i].diluent = ccr; // Find the first gas marked as the initial gas. if (initial == UNDEFINED && data[28 + 4 * i + 3] == 1) { @@ -325,6 +328,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser) } else { gasmix[i].enabled = 1; } + gasmix[i].active = 0; gasmix[i].diluent = ccr; } } @@ -387,6 +391,7 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, const parser->gasmix[i].helium = 0; parser->gasmix[i].type = 0; parser->gasmix[i].enabled = 0; + parser->gasmix[i].active = 0; parser->gasmix[i].diluent = 0; } @@ -802,6 +807,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call // Initial gas mix. if (time == samplerate && parser->initial != UNDEFINED) { + parser->gasmix[parser->initial].active = 1; sample.gasmix = parser->initial; if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata); } @@ -900,6 +906,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call parser->gasmix[idx].helium = he; parser->gasmix[idx].type = 0; parser->gasmix[idx].enabled = 1; + parser->gasmix[idx].active = 1; parser->gasmix[idx].diluent = ccr; parser->ngasmixes = idx + 1; } @@ -926,6 +933,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call return DC_STATUS_DATAFORMAT; } idx--; /* Convert to a zero based index. */ + parser->gasmix[idx].active = 1; sample.gasmix = idx; if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata); tank = idx; @@ -965,6 +973,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call parser->gasmix[idx].helium = he; parser->gasmix[idx].type = 0; parser->gasmix[idx].enabled = 1; + parser->gasmix[idx].active = 1; parser->gasmix[idx].diluent = 0; parser->ngasmixes = idx + 1; } @@ -1100,6 +1109,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call parser->gasmix[idx].helium = he; parser->gasmix[idx].type = 0; parser->gasmix[idx].enabled = 1; + parser->gasmix[idx].active = 1; parser->gasmix[idx].diluent = 0; parser->ngasmixes = idx + 1; } From 993283d1c8c36f20249b2065c3af3afd1b1f3f28 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 31 Jul 2023 21:08:50 +0200 Subject: [PATCH 3/4] Store the original one based gas mix index --- src/hw_ostc_parser.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index 7d23964..59e06ae 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -104,6 +104,7 @@ typedef struct hw_ostc_layout_t { } hw_ostc_layout_t; typedef struct hw_ostc_gasmix_t { + unsigned int id; unsigned int oxygen; unsigned int helium; unsigned int type; @@ -287,6 +288,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser) initial = data[31]; } for (unsigned int i = 0; i < ngasmixes; ++i) { + gasmix[i].id = i + 1; gasmix[i].oxygen = data[25 + 2 * i]; gasmix[i].helium = 0; gasmix[i].type = 0; @@ -297,6 +299,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser) } else if (version == 0x23 || version == 0x24) { ngasmixes = 5; for (unsigned int i = 0; i < ngasmixes; ++i) { + gasmix[i].id = i + 1; gasmix[i].oxygen = data[28 + 4 * i + 0]; gasmix[i].helium = data[28 + 4 * i + 1]; gasmix[i].type = data[28 + 4 * i + 3]; @@ -320,6 +323,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser) initial = data[31]; } for (unsigned int i = 0; i < ngasmixes; ++i) { + gasmix[i].id = i + 1; gasmix[i].oxygen = data[19 + 2 * i + 0]; gasmix[i].helium = data[19 + 2 * i + 1]; gasmix[i].type = 0; @@ -387,6 +391,7 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, const parser->initial_setpoint = 0; parser->initial_cns = 0; for (unsigned int i = 0; i < NGASMIXES; ++i) { + parser->gasmix[i].id = 0; parser->gasmix[i].oxygen = 0; parser->gasmix[i].helium = 0; parser->gasmix[i].type = 0; @@ -902,6 +907,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call ERROR (abstract->context, "Maximum number of gas mixes reached."); return DC_STATUS_NOMEMORY; } + parser->gasmix[idx].id = 0; parser->gasmix[idx].oxygen = o2; parser->gasmix[idx].helium = he; parser->gasmix[idx].type = 0; @@ -969,6 +975,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call ERROR (abstract->context, "Maximum number of gas mixes reached."); return DC_STATUS_NOMEMORY; } + parser->gasmix[idx].id = 0; parser->gasmix[idx].oxygen = o2; parser->gasmix[idx].helium = he; parser->gasmix[idx].type = 0; @@ -1105,6 +1112,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call ERROR (abstract->context, "Maximum number of gas mixes reached."); return DC_STATUS_NOMEMORY; } + parser->gasmix[idx].id = 0; parser->gasmix[idx].oxygen = o2; parser->gasmix[idx].helium = he; parser->gasmix[idx].type = 0; From 5d36cc079878672d3669db91c44f05fa2905e1dd Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 31 Jul 2023 21:08:50 +0200 Subject: [PATCH 4/4] Remove disabled gas mixes Returning disabled gas mixes to the application mainly results in lots of unnecessary information. Therefore, remove all disabled gas mixes, unless they are actively used. Many other dive computers do not even include disabled gas mixes in the data. The removal of the disabled gas mixes requires a two pass approach for parsing the profile data. The first pass is only used to discover which gas mixes are actively used during the dive. Next, all disabled and not actively used gas mixes are removed from the list. Since removing one or more gas mixes also invalidates the index of the remaining gas mixes, the profile needs to be parsed again to report the new index in the gas switch samples. The original one based index is used as the stable gas mix id, used for looking up the new gas mix index. --- src/hw_ostc_parser.c | 99 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index 59e06ae..f1e306d 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -124,6 +124,7 @@ typedef struct hw_ostc_parser_t { const hw_ostc_layout_t *layout; unsigned int ngasmixes; unsigned int nfixed; + unsigned int ndisabled; unsigned int initial; unsigned int initial_setpoint; unsigned int initial_cns; @@ -134,6 +135,8 @@ static dc_status_t hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_dateti static dc_status_t hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); +static dc_status_t hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t callback, void *userdata); + static const dc_parser_vtable_t hw_ostc_parser_vtable = { sizeof(hw_ostc_parser_t), DC_FAMILY_HW_OSTC, @@ -194,7 +197,7 @@ static const hw_ostc_layout_t hw_ostc_layout_ostc3 = { static unsigned int hw_ostc_find_gasmix_manual (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he, unsigned int dil) { - unsigned int offset = parser->nfixed; + unsigned int offset = parser->nfixed - parser->ndisabled; unsigned int count = parser->ngasmixes; unsigned int i = offset; @@ -207,6 +210,22 @@ hw_ostc_find_gasmix_manual (hw_ostc_parser_t *parser, unsigned int o2, unsigned return i; } +static unsigned int +hw_ostc_find_gasmix_fixed (hw_ostc_parser_t *parser, unsigned int id) +{ + unsigned int offset = 0; + unsigned int count = parser->nfixed - parser->ndisabled; + + unsigned int i = offset; + while (i < count) { + if (id == parser->gasmix[i].id) + break; + i++; + } + + return i; +} + static unsigned int hw_ostc_is_ccr (unsigned int divemode, unsigned int version) { @@ -341,7 +360,6 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser) ERROR(abstract->context, "Invalid initial gas mix."); return DC_STATUS_DATAFORMAT; } - initial--; /* Convert to a zero based index. */ } else { WARNING(abstract->context, "No initial gas mix available."); } @@ -352,6 +370,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser) parser->layout = layout; parser->ngasmixes = ngasmixes; parser->nfixed = ngasmixes; + parser->ndisabled = 0; parser->initial = initial; parser->initial_setpoint = initial_setpoint; parser->initial_cns = initial_cns; @@ -387,6 +406,7 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, const parser->layout = NULL; parser->ngasmixes = 0; parser->nfixed = 0; + parser->ndisabled = 0; parser->initial = 0; parser->initial_setpoint = 0; parser->initial_cns = 0; @@ -491,7 +511,7 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned // Cache the profile data. if (parser->cached < PROFILE) { - rc = hw_ostc_parser_samples_foreach (abstract, NULL, NULL); + rc = hw_ostc_parser_internal_foreach (parser, NULL, NULL); if (rc != DC_STATUS_SUCCESS) return rc; } @@ -679,17 +699,12 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned static dc_status_t -hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t callback, void *userdata) { - hw_ostc_parser_t *parser = (hw_ostc_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 = hw_ostc_parser_cache (parser); - if (rc != DC_STATUS_SUCCESS) - return rc; - unsigned int version = parser->version; unsigned int header = parser->header; const hw_ostc_layout_t *layout = parser->layout; @@ -795,7 +810,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call unsigned int time = 0; unsigned int nsamples = 0; - unsigned int tank = parser->initial != UNDEFINED ? parser->initial : 0; + unsigned int tank = parser->initial != UNDEFINED ? parser->initial - 1 : 0; unsigned int offset = header; if (version == 0x23 || version == 0x24) @@ -812,8 +827,9 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call // Initial gas mix. if (time == samplerate && parser->initial != UNDEFINED) { - parser->gasmix[parser->initial].active = 1; - sample.gasmix = parser->initial; + unsigned int idx = hw_ostc_find_gasmix_fixed (parser, parser->initial); + parser->gasmix[idx].active = 1; + sample.gasmix = idx; if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata); } @@ -929,20 +945,20 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call ERROR (abstract->context, "Buffer overflow detected!"); return DC_STATUS_DATAFORMAT; } - unsigned int idx = data[offset]; - if (parser->model == OSTC4 && ccr && idx > parser->nfixed) { + unsigned int id = data[offset]; + if (parser->model == OSTC4 && ccr && id > parser->nfixed) { // Fix the OSTC4 diluent index. - idx -= parser->nfixed; + id -= parser->nfixed; } - if (idx < 1 || idx > parser->nfixed) { - ERROR(abstract->context, "Invalid gas mix (%u).", idx); + if (id < 1 || id > parser->nfixed) { + ERROR(abstract->context, "Invalid gas mix (%u).", id); return DC_STATUS_DATAFORMAT; } - idx--; /* Convert to a zero based index. */ + unsigned int idx = hw_ostc_find_gasmix_fixed (parser, id); parser->gasmix[idx].active = 1; sample.gasmix = idx; if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata); - tank = idx; + tank = id - 1; offset++; length--; } @@ -1141,7 +1157,50 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call return DC_STATUS_DATAFORMAT; } + // Remove the disabled gas mixes from the fixed gas mixes. + unsigned int ndisabled = 0, nenabled = 0; + unsigned int count = parser->nfixed - parser->ndisabled; + for (unsigned int i = 0; i < count; ++i) { + if (parser->gasmix[i].enabled || parser->gasmix[i].active) { + // Move the fixed gas mix. + parser->gasmix[nenabled] = parser->gasmix[i]; + nenabled++; + } else { + ndisabled++; + } + } + + // Move all the manual gas mixes. + memmove (parser->gasmix + nenabled, parser->gasmix + count, + (parser->ngasmixes - count) * sizeof (hw_ostc_gasmix_t)); + memset (parser->gasmix + parser->ngasmixes - ndisabled, 0, + ndisabled * sizeof (hw_ostc_gasmix_t)); + + // Adjust the counts. + parser->ngasmixes -= ndisabled; + parser->ndisabled += ndisabled; + parser->cached = PROFILE; return DC_STATUS_SUCCESS; } + +static dc_status_t +hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + hw_ostc_parser_t *parser = (hw_ostc_parser_t *) abstract; + + // Cache the header data. + dc_status_t rc = hw_ostc_parser_cache (parser); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Cache the profile data. + if (parser->cached < PROFILE) { + rc = hw_ostc_parser_internal_foreach (parser, NULL, NULL); + if (rc != DC_STATUS_SUCCESS) + return rc; + } + + return hw_ostc_parser_internal_foreach (parser, callback, userdata); +}