Add support for parsing the decompression model

Report the decompression algorithm (Buhlmann, VPM, RGBM or DCIEM), and
if available also the parameters. For now only the conservatism setting
is supported, and for the Buhlmann algorithm also the Gradient Factors
(GF).
This commit is contained in:
Jef Driesen 2022-06-30 22:03:28 +02:00
parent 95f309a1c9
commit 7fb943ae7f
10 changed files with 311 additions and 1 deletions

View File

@ -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, "<decomodel>%s</decomodel>\n",
names[decomodel.type]);
if (decomodel.type == DC_DECOMODEL_BUHLMANN &&
(decomodel.params.gf.low != 0 || decomodel.params.gf.high != 0)) {
fprintf (output->ostream, "<gf>%u/%u</gf>\n",
decomodel.params.gf.low, decomodel.params.gf.high);
}
if (decomodel.conservatism) {
fprintf (output->ostream, "<conservatism>%d</conservatism>\n",
decomodel.conservatism);
}
}
// Parse the salinity.
message ("Parsing the salinity.\n");
dc_salinity_t salinity = {DC_WATER_FRESH, 0.0};

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}