diff --git a/examples/output_xml.c b/examples/output_xml.c index 854646b..7d76cb1 100644 --- a/examples/output_xml.c +++ b/examples/output_xml.c @@ -386,6 +386,30 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u names[divemode]); } + // Parse the deco model. + message ("Parsing the deco model.\n"); + dc_decomodel_t decomodel = {DC_DECOMODEL_NONE}; + status = dc_parser_get_field (parser, DC_FIELD_DECOMODEL, 0, &decomodel); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the deco model."); + goto cleanup; + } + + if (status != DC_STATUS_UNSUPPORTED) { + const char *names[] = {"none", "buhlmann", "vpm", "rgbm", "dciem"}; + fprintf (output->ostream, "%s\n", + names[decomodel.type]); + if (decomodel.type == DC_DECOMODEL_BUHLMANN && + (decomodel.params.gf.low != 0 || decomodel.params.gf.high != 0)) { + fprintf (output->ostream, "%u/%u\n", + decomodel.params.gf.low, decomodel.params.gf.high); + } + if (decomodel.conservatism) { + fprintf (output->ostream, "%d\n", + decomodel.conservatism); + } + } + // Parse the salinity. message ("Parsing the salinity.\n"); dc_salinity_t salinity = {DC_WATER_FRESH, 0.0}; diff --git a/include/libdivecomputer/parser.h b/include/libdivecomputer/parser.h index 1b53fd3..dc81317 100644 --- a/include/libdivecomputer/parser.h +++ b/include/libdivecomputer/parser.h @@ -62,7 +62,8 @@ typedef enum dc_field_type_t { DC_FIELD_TEMPERATURE_MAXIMUM, DC_FIELD_TANK_COUNT, DC_FIELD_TANK, - DC_FIELD_DIVEMODE + DC_FIELD_DIVEMODE, + DC_FIELD_DECOMODEL, } dc_field_type_t; typedef enum parser_sample_event_t { @@ -186,6 +187,43 @@ typedef struct dc_tank_t { double endpressure; /* End pressure (bar) */ } dc_tank_t; +typedef enum dc_decomodel_type_t { + DC_DECOMODEL_NONE, + DC_DECOMODEL_BUHLMANN, + DC_DECOMODEL_VPM, + DC_DECOMODEL_RGBM, + DC_DECOMODEL_DCIEM, +} dc_decomodel_type_t; + +/* + * Decompression model + * + * The type field contains the decompression algorithm. + * + * The (optional) conservatism field contains the personal adjustment + * setting of the algorithm. The exact interpretation depends on the + * dive computer, but the default value (zero) will typically correspond + * to the neutral setting, while a positive value is more conservative + * and a negative value more aggressive. + * + * The (optional) params field contains the parameters of the algorithm: + * + * DC_DECOMODEL_BUHLMANN: The Gradient Factor (GF) parameters low and + * high. For a pure Buhlmann algorithm (e.g. without GF enabled), both + * values are 100. If GF are enabled, but the actual parameter values + * are not available from the dive computer, both values are zero. + */ +typedef struct dc_decomodel_t { + dc_decomodel_type_t type; + int conservatism; + union { + struct { + unsigned int high; + unsigned int low; + } gf; + } params; +} dc_decomodel_t; + typedef union dc_sample_value_t { unsigned int time; double depth; diff --git a/src/divesystem_idive_parser.c b/src/divesystem_idive_parser.c index 5ace8f4..f0d7700 100644 --- a/src/divesystem_idive_parser.c +++ b/src/divesystem_idive_parser.c @@ -48,6 +48,9 @@ #define FREEDIVE 4 #define INVALID 0xFFFFFFFF +#define BUHLMANN 0 +#define VPM 1 + typedef struct divesystem_idive_parser_t divesystem_idive_parser_t; typedef struct divesystem_idive_gasmix_t { @@ -74,6 +77,9 @@ struct divesystem_idive_parser_t { unsigned int ntanks; divesystem_idive_gasmix_t gasmix[NGASMIXES]; divesystem_idive_tank_t tank[NTANKS]; + unsigned int algorithm; + unsigned int gf_low; + unsigned int gf_high; }; static dc_status_t divesystem_idive_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); @@ -129,6 +135,10 @@ divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, unsign parser->tank[i].beginpressure = 0; parser->tank[i].endpressure = 0; } + parser->algorithm = INVALID; + parser->gf_low = INVALID; + parser->gf_high = INVALID; + *out = (dc_parser_t*) parser; @@ -157,6 +167,9 @@ divesystem_idive_parser_set_data (dc_parser_t *abstract, const unsigned char *da parser->tank[i].beginpressure = 0; parser->tank[i].endpressure = 0; } + parser->algorithm = INVALID; + parser->gf_low = INVALID; + parser->gf_high = INVALID; return DC_STATUS_SUCCESS; } @@ -280,6 +293,7 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_tank_t *tank = (dc_tank_t *) value; dc_salinity_t *water = (dc_salinity_t *) value; + dc_decomodel_t *decomodel = (dc_decomodel_t *) value; if (value) { switch (type) { @@ -343,6 +357,25 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, return DC_STATUS_DATAFORMAT; } break; + case DC_FIELD_DECOMODEL: + if (parser->algorithm == INVALID) + return DC_STATUS_UNSUPPORTED; + switch (parser->algorithm) { + case BUHLMANN: + decomodel->type = DC_DECOMODEL_BUHLMANN; + decomodel->conservatism = 0; + decomodel->params.gf.low = parser->gf_low; + decomodel->params.gf.high = parser->gf_high; + break; + case VPM: + decomodel->type = DC_DECOMODEL_VPM; + decomodel->conservatism = 0; + break; + default: + ERROR (abstract->context, "Unknown deco algorithm %02x.", parser->algorithm); + return DC_STATUS_DATAFORMAT; + } + break; default: return DC_STATUS_UNSUPPORTED; } @@ -371,6 +404,10 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba unsigned int divemode = INVALID; unsigned int tank_previous = INVALID; unsigned int tank_idx = INVALID; + unsigned int algorithm = INVALID; + unsigned int algorithm_previous = INVALID; + unsigned int gf_low = INVALID; + unsigned int gf_high = INVALID; unsigned int firmware = 0; unsigned int apos4 = 0; @@ -433,6 +470,22 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba divemode = mode; } + // Deco model + unsigned int s_algorithm = data[offset + 14]; + unsigned int s_gf_high = data[offset + 15]; + unsigned int s_gf_low = data[offset + 16]; + if (s_algorithm != algorithm_previous) { + if (algorithm_previous != INVALID) { + WARNING (abstract->context, "Deco algorithm changed from %02x to %02x.", algorithm_previous, s_algorithm); + } + algorithm_previous = s_algorithm; + } + if (algorithm == INVALID) { + algorithm = s_algorithm; + gf_low = s_gf_low; + gf_high = s_gf_high; + } + // Setpoint if (mode == SCR || mode == CCR) { unsigned int setpoint = array_uint16_le (data + offset + 19); @@ -560,6 +613,9 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba parser->maxdepth = maxdepth; parser->divetime = time; parser->divemode = divemode; + parser->algorithm = algorithm; + parser->gf_low = gf_low; + parser->gf_high = gf_high; parser->cached = 1; return DC_STATUS_SUCCESS; diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index f1092c0..06470d7 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -69,6 +69,10 @@ #define OSTC3_APNEA 3 #define OSTC3_PSCR 4 +#define OSTC3_ZHL16 0 +#define OSTC3_ZHL16_GF 1 +#define OSTC4_VPM 2 + #define OSTC4 0x3B #define OSTC3FW(major,minor) ( \ @@ -97,6 +101,8 @@ typedef struct hw_ostc_layout_t { unsigned int salinity; unsigned int avgdepth; unsigned int duration; + unsigned int gf; + unsigned int decomodel; unsigned int divemode; } hw_ostc_layout_t; @@ -147,6 +153,8 @@ static const hw_ostc_layout_t hw_ostc_layout_ostc = { 43, /* salinity */ 45, /* avgdepth */ 47, /* duration */ + 49, /* gf */ + UNDEFINED, /* decomodel */ 51, /* divemode */ }; @@ -160,6 +168,8 @@ static const hw_ostc_layout_t hw_ostc_layout_frog = { 43, /* salinity */ 45, /* avgdepth */ 47, /* duration */ + 49, /* gf */ + UNDEFINED, /* decomodel */ 51, /* divemode */ }; @@ -173,6 +183,8 @@ static const hw_ostc_layout_t hw_ostc_layout_ostc3 = { 70, /* salinity */ 73, /* avgdepth */ 75, /* duration */ + 77, /* gf */ + 79, /* decomodel */ 82, /* divemode */ }; @@ -472,6 +484,8 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_salinity_t *water = (dc_salinity_t *) value; + dc_decomodel_t *decomodel = (dc_decomodel_t *) value; + unsigned int salinity = data[layout->salinity]; if (version == 0x23 || version == 0x24) salinity += 100; @@ -572,6 +586,70 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned return DC_STATUS_UNSUPPORTED; } break; + case DC_FIELD_DECOMODEL: + if (version == 0x21) { + switch (data[layout->divemode]) { + case OSTC_APNEA: + case OSTC_GAUGE: + decomodel->type = DC_DECOMODEL_NONE; + break; + case OSTC_ZHL16_OC: + case OSTC_ZHL16_CC: + decomodel->type = DC_DECOMODEL_BUHLMANN; + decomodel->params.gf.low = 100; + decomodel->params.gf.high = 100; + break; + case OSTC_ZHL16_OC_GF: + case OSTC_ZHL16_CC_GF: + case OSTC_PSCR_GF: + decomodel->type = DC_DECOMODEL_BUHLMANN; + decomodel->params.gf.low = data[layout->gf + 0]; + decomodel->params.gf.high = data[layout->gf + 1]; + break; + default: + return DC_STATUS_DATAFORMAT; + } + } else if (version == 0x22) { + switch (data[layout->divemode]) { + case FROG_ZHL16: + decomodel->type = DC_DECOMODEL_BUHLMANN; + decomodel->params.gf.low = 100; + decomodel->params.gf.high = 100; + break; + case FROG_ZHL16_GF: + decomodel->type = DC_DECOMODEL_BUHLMANN; + decomodel->params.gf.low = data[layout->gf + 0]; + decomodel->params.gf.high = data[layout->gf + 1]; + break; + case FROG_APNEA: + decomodel->type = DC_DECOMODEL_NONE; + break; + default: + return DC_STATUS_DATAFORMAT; + } + } else if (version == 0x23 || version == 0x24) { + switch (data[layout->decomodel]) { + case OSTC3_ZHL16: + decomodel->type = DC_DECOMODEL_BUHLMANN; + decomodel->params.gf.low = 100; + decomodel->params.gf.high = 100; + break; + case OSTC3_ZHL16_GF: + decomodel->type = DC_DECOMODEL_BUHLMANN; + decomodel->params.gf.low = data[layout->gf + 0]; + decomodel->params.gf.high = data[layout->gf + 1]; + break; + case OSTC4_VPM: + decomodel->type = DC_DECOMODEL_VPM; + break; + default: + return DC_STATUS_DATAFORMAT; + } + } else { + return DC_STATUS_UNSUPPORTED; + } + decomodel->conservatism = 0; + break; default: return DC_STATUS_UNSUPPORTED; } diff --git a/src/liquivision_lynx_parser.c b/src/liquivision_lynx_parser.c index c4272d8..2ff52cb 100644 --- a/src/liquivision_lynx_parser.c +++ b/src/liquivision_lynx_parser.c @@ -66,6 +66,9 @@ #define TEC 2 #define REC 3 +#define ZHL16GF 0 +#define RGBM 1 + #define NORMAL 0 #define BOOKMARK 1 #define ALARM_DEPTH 2 @@ -231,6 +234,7 @@ liquivision_lynx_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_tank_t *tank = (dc_tank_t *) value; dc_salinity_t *water = (dc_salinity_t *) value; + dc_decomodel_t *decomodel = (dc_decomodel_t *) value; if (value) { switch (type) { @@ -288,6 +292,22 @@ liquivision_lynx_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, } } break; + case DC_FIELD_DECOMODEL: + switch (abstract->data[93]) { + case ZHL16GF: + decomodel->type = DC_DECOMODEL_BUHLMANN; + decomodel->conservatism = 0; + decomodel->params.gf.low = 0; + decomodel->params.gf.high = 0; + break; + case RGBM: + decomodel->type = DC_DECOMODEL_RGBM; + decomodel->conservatism = 0; + break; + default: + return DC_STATUS_DATAFORMAT; + } + break; case DC_FIELD_GASMIX_COUNT: *((unsigned int *) value) = parser->ngasmixes; break; diff --git a/src/seac_screen_parser.c b/src/seac_screen_parser.c index 28988e4..46e3303 100644 --- a/src/seac_screen_parser.c +++ b/src/seac_screen_parser.c @@ -44,6 +44,8 @@ struct seac_screen_parser_t { unsigned int cached; unsigned int ngasmixes; unsigned int oxygen[NGASMIXES]; + unsigned int gf_low; + unsigned int gf_high; }; static dc_status_t seac_screen_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); @@ -82,6 +84,8 @@ seac_screen_parser_create (dc_parser_t **out, dc_context_t *context) for (unsigned int i = 0; i < NGASMIXES; ++i) { parser->oxygen[i] = 0; } + parser->gf_low = 0; + parser->gf_high = 0; *out = (dc_parser_t *) parser; @@ -99,6 +103,8 @@ seac_screen_parser_set_data (dc_parser_t *abstract, const unsigned char *data, u for (unsigned int i = 0; i < NGASMIXES; ++i) { parser->oxygen[i] = 0; } + parser->gf_low = 0; + parser->gf_high = 0; return DC_STATUS_SUCCESS; } @@ -207,6 +213,7 @@ seac_screen_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsig } dc_gasmix_t *gasmix = (dc_gasmix_t *) value; + dc_decomodel_t *decomodel = (dc_decomodel_t *) value; if (value) { switch (type) { @@ -249,6 +256,12 @@ seac_screen_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsig return DC_STATUS_DATAFORMAT; } break; + case DC_FIELD_DECOMODEL: + decomodel->type = DC_DECOMODEL_BUHLMANN; + decomodel->conservatism = 0; + decomodel->params.gf.low = parser->gf_low; + decomodel->params.gf.high = parser->gf_high; + break; default: return DC_STATUS_UNSUPPORTED; } @@ -279,6 +292,9 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t unsigned int oxygen[NGASMIXES] = {0}; unsigned int o2_previous = INVALID; + unsigned int gf_low = 0; + unsigned int gf_high = 0; + unsigned int time = 0; unsigned int offset = SZ_HEADER; while (offset + SZ_SAMPLE <= size) { @@ -298,6 +314,8 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t unsigned int decotime = array_uint16_le (data + offset + 0x10); unsigned int ndl_tts = array_uint16_le (data + offset + 0x12); unsigned int cns = array_uint16_le (data + offset + 0x16); + unsigned int gf_hi = data[offset + 0x3B]; + unsigned int gf_lo = data[offset + 0x3C]; if (id != dive_id) { ERROR (abstract->context, "Unexpected sample id (%u %u).", dive_id, id); @@ -362,6 +380,12 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t sample.cns = cns / 100.0; if (callback) callback (DC_SAMPLE_CNS, sample, userdata); + // Deco model + if (gf_low == 0 && gf_high == 0) { + gf_low = gf_lo; + gf_high = gf_hi; + } + offset += SZ_SAMPLE; } @@ -370,6 +394,8 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t parser->oxygen[i] = oxygen[i]; } parser->ngasmixes = ngasmixes; + parser->gf_low = gf_low; + parser->gf_high = gf_high; parser->cached = 1; return DC_STATUS_SUCCESS; diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 28c4915..5113f75 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -78,6 +78,11 @@ #define M_OC_REC 6 #define M_FREEDIVE 7 +#define GF 0 +#define VPMB 1 +#define VPMB_GFS 2 +#define DCIEM 3 + #define METRIC 0 #define IMPERIAL 1 @@ -669,9 +674,13 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ if (rc != DC_STATUS_SUCCESS) return rc; + unsigned int decomodel_idx = parser->pnf ? parser->opening[2] + 18 : 67; + unsigned int gf_idx = parser->pnf ? parser->opening[0] + 4 : 4; + dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_tank_t *tank = (dc_tank_t *) value; dc_salinity_t *water = (dc_salinity_t *) value; + dc_decomodel_t *decomodel = (dc_decomodel_t *) value; if (value) { switch (type) { @@ -742,6 +751,27 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ return DC_STATUS_DATAFORMAT; } break; + case DC_FIELD_DECOMODEL: + switch (data[decomodel_idx]) { + case GF: + decomodel->type = DC_DECOMODEL_BUHLMANN; + decomodel->conservatism = 0; + decomodel->params.gf.low = data[gf_idx + 0]; + decomodel->params.gf.high = data[gf_idx + 1]; + break; + case VPMB: + case VPMB_GFS: + decomodel->type = DC_DECOMODEL_VPM; + decomodel->conservatism = data[decomodel_idx + 1]; + break; + case DCIEM: + decomodel->type = DC_DECOMODEL_DCIEM; + decomodel->conservatism = 0; + break; + default: + return DC_STATUS_DATAFORMAT; + } + break; default: return DC_STATUS_UNSUPPORTED; } diff --git a/src/suunto_d9_parser.c b/src/suunto_d9_parser.c index 7fc05fd..32e28ea 100644 --- a/src/suunto_d9_parser.c +++ b/src/suunto_d9_parser.c @@ -361,6 +361,7 @@ suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigne return rc; dc_gasmix_t *gasmix = (dc_gasmix_t *) value; + dc_decomodel_t *decomodel = (dc_decomodel_t *) value; if (value) { switch (type) { @@ -408,6 +409,20 @@ suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigne return DC_STATUS_DATAFORMAT; } break; + case DC_FIELD_DECOMODEL: + decomodel->type = DC_DECOMODEL_RGBM; + if (parser->model == D4i ||parser->model == D6i || + parser->model == D9tx || parser->model == ZOOPNOVO_A || + parser->model == ZOOPNOVO_B || parser->model == VYPERNOVO || + parser->model == D4F) + decomodel->conservatism = data[0x21] - 2; + else if (parser->model == HELO2) + decomodel->conservatism = data[0x23] - 2; + else if (parser->model == DX) + decomodel->conservatism = data[0x25] - 2; + else + decomodel->conservatism = data[0x1E]; + break; default: return DC_STATUS_UNSUPPORTED; } diff --git a/src/suunto_eonsteel_parser.c b/src/suunto_eonsteel_parser.c index de00001..cc9276a 100644 --- a/src/suunto_eonsteel_parser.c +++ b/src/suunto_eonsteel_parser.c @@ -90,6 +90,7 @@ typedef struct suunto_eonsteel_parser_t { dc_tankvolume_t tankinfo[MAXGASES]; double tanksize[MAXGASES]; double tankworkingpressure[MAXGASES]; + dc_decomodel_t decomodel; } cache; } suunto_eonsteel_parser_t; @@ -1095,6 +1096,9 @@ suunto_eonsteel_parser_get_field(dc_parser_t *parser, dc_field_type_t type, unsi tank->type = DC_TANKVOLUME_IMPERIAL; } break; + case DC_FIELD_DECOMODEL: + field_value(value, eon->cache.decomodel); + break; default: return DC_STATUS_UNSUPPORTED; } @@ -1341,6 +1345,20 @@ static int traverse_diving_fields(suunto_eonsteel_parser_t *eon, const struct ty return 0; } + if (!strcmp(name, "Algorithm")) { + if (!strcmp((const char *)data, "Suunto Fused RGBM")) { + eon->cache.decomodel.type = DC_DECOMODEL_RGBM; + eon->cache.initialized |= 1 << DC_FIELD_DECOMODEL; + } + return 0; + } + + if (!strcmp(name, "Conservatism")) { + eon->cache.decomodel.conservatism = *(const signed char *)data; + eon->cache.initialized |= 1 << DC_FIELD_DECOMODEL; + return 0; + } + if (!strcmp(name, "LowSetPoint")) { unsigned int pressure = array_uint32_le(data); // in SI units - Pascal eon->cache.lowsetpoint = pressure / 100000.0; // bar diff --git a/src/suunto_vyper_parser.c b/src/suunto_vyper_parser.c index 369ad5c..d7af05c 100644 --- a/src/suunto_vyper_parser.c +++ b/src/suunto_vyper_parser.c @@ -238,6 +238,7 @@ suunto_vyper_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi dc_gasmix_t *gas = (dc_gasmix_t *) value; dc_tank_t *tank = (dc_tank_t *) value; + dc_decomodel_t *decomodel = (dc_decomodel_t *) value; // Cache the data. dc_status_t rc = suunto_vyper_parser_cache (parser); @@ -297,6 +298,10 @@ suunto_vyper_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi *((dc_divemode_t *) value) = DC_DIVEMODE_OC; } break; + case DC_FIELD_DECOMODEL: + decomodel->type = DC_DECOMODEL_RGBM; + decomodel->conservatism = (data[4] & 0x0F) / 3; + break; default: return DC_STATUS_UNSUPPORTED; }