diff --git a/src/suunto_d9_parser.c b/src/suunto_d9_parser.c index 4e34f08..f982902 100644 --- a/src/suunto_d9_parser.c +++ b/src/suunto_d9_parser.c @@ -31,6 +31,7 @@ #define ISINSTANCE(parser) dc_parser_isinstance((parser), &suunto_d9_parser_vtable) #define MAXPARAMS 3 +#define NGASMIXES 11 #define D9 0x0E #define D6 0x0F @@ -60,6 +61,13 @@ typedef struct suunto_d9_parser_t suunto_d9_parser_t; struct suunto_d9_parser_t { dc_parser_t base; unsigned int model; + // Cached fields. + unsigned int cached; + unsigned int mode; + unsigned int ngasmixes; + unsigned int oxygen[NGASMIXES]; + unsigned int helium[NGASMIXES]; + unsigned int config; }; typedef struct sample_info_t { @@ -85,6 +93,77 @@ static const dc_parser_vtable_t suunto_d9_parser_vtable = { }; +static dc_status_t +suunto_d9_parser_cache (suunto_d9_parser_t *parser) +{ + const unsigned char *data = parser->base.data; + unsigned int size = parser->base.size; + + if (parser->cached) { + return DC_STATUS_SUCCESS; + } + + // Gasmix information. + unsigned int gasmode_offset = 0x19; + unsigned int gasmix_offset = 0x21; + unsigned int gasmix_count = 3; + if (parser->model == HELO2) { + gasmode_offset = 0x1F; + gasmix_offset = 0x54; + gasmix_count = 8; + } else if (parser->model == D4i) { + gasmode_offset = 0x1D; + gasmix_offset = 0x5F; + gasmix_count = 1; + } else if (parser->model == D6i) { + gasmode_offset = 0x1D; + gasmix_offset = 0x5F; + if (data[1] == 0x63) + gasmix_count = 3; + else + gasmix_count = 2; + } else if (parser->model == D9tx) { + gasmode_offset = 0x1D; + gasmix_offset = 0x87; + gasmix_count = 8; + } else if (parser->model == DX) { + gasmode_offset = 0x21; + gasmix_offset = 0xC1; + gasmix_count = 11; + } + + // Offset to the configuration data. + unsigned int config = 0x3A; + if (parser->model == D4) { + config += 1; + } else if (parser->model == HELO2 || parser->model == D4i || + parser->model == D6i || parser->model == D9tx || + parser->model == DX) { + config = gasmix_offset + gasmix_count * 6; + } + if (config + 1 > size) + return DC_STATUS_DATAFORMAT; + + // Cache the data for later use. + parser->mode = data[gasmode_offset]; + parser->ngasmixes = gasmix_count; + for (unsigned int i = 0; i < gasmix_count; ++i) { + if (parser->model == HELO2 || parser->model == D4i || + parser->model == D6i || parser->model == D9tx || + parser->model == DX) { + parser->oxygen[i] = data[gasmix_offset + 6 * i + 1]; + parser->helium[i] = data[gasmix_offset + 6 * i + 2]; + } else { + parser->oxygen[i] = data[gasmix_offset + i]; + parser->helium[i] = 0.0; + } + } + parser->config = config; + parser->cached = 1; + + return DC_STATUS_SUCCESS; +} + dc_status_t suunto_d9_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) { @@ -103,6 +182,14 @@ suunto_d9_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int // Set the default values. parser->model = model; + parser->cached = 0; + parser->mode = AIR; + parser->ngasmixes = 0; + for (unsigned int i = 0; i < NGASMIXES; ++i) { + parser->oxygen[i] = 0; + parser->helium[i] = 0; + } + parser->config = 0; *out = (dc_parser_t*) parser; @@ -123,6 +210,18 @@ suunto_d9_parser_destroy (dc_parser_t *abstract) static dc_status_t suunto_d9_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size) { + suunto_d9_parser_t *parser = (suunto_d9_parser_t *) abstract; + + // Reset the cache. + parser->cached = 0; + parser->mode = AIR; + parser->ngasmixes = 0; + for (unsigned int i = 0; i < NGASMIXES; ++i) { + parser->oxygen[i] = 0; + parser->helium[i] = 0; + } + parser->config = 0; + return DC_STATUS_SUCCESS; } @@ -174,49 +273,10 @@ suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigne const unsigned char *data = abstract->data; unsigned int size = abstract->size; - // Gasmix information. - unsigned int gasmix_offset = 0x21, gasmix_count = 3; - if (parser->model == HELO2) { - gasmix_offset = 0x54; - gasmix_count = 8; - } else if (parser->model == D4i) { - gasmix_offset = 0x5F; - gasmix_count = 1; - } else if (parser->model == D6i) { - gasmix_offset = 0x5F; - if (data[1] == 0x63) - gasmix_count = 3; - else - gasmix_count = 2; - } else if (parser->model == D9tx) { - gasmix_offset = 0x87; - gasmix_count = 8; - } else if (parser->model == DX) { - gasmix_offset = 0xC1; - gasmix_count = 11; - } - - // Offset to the configuration data. - unsigned int config = 0x3A; - if (parser->model == D4) { - config += 1; - } else if (parser->model == HELO2 || parser->model == D4i || - parser->model == D6i || parser->model == D9tx || - parser->model == DX) { - config = gasmix_offset + gasmix_count * 6; - } - if (config + 1 > size) - return DC_STATUS_DATAFORMAT; - - // Gas model - unsigned int gasmodel_offset = 0x19; - if (parser->model == HELO2) - gasmodel_offset = 0x1F; - else if (parser->model == D4i || parser->model == D6i || parser->model == D9tx) - gasmodel_offset = 0x1D; - else if (parser->model == DX) - gasmodel_offset = 0x21; - unsigned int gasmodel = data[gasmodel_offset]; + // Cache the gas mix data. + dc_status_t rc = suunto_d9_parser_cache (parser); + if (rc != DC_STATUS_SUCCESS) + return rc; dc_gasmix_t *gasmix = (dc_gasmix_t *) value; @@ -237,24 +297,19 @@ suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigne *((double *) value) = array_uint16_le (data + 0x09) / 100.0; break; case DC_FIELD_GASMIX_COUNT: - if (gasmodel == AIR) { + if (parser->mode == AIR) { *((unsigned int *) value) = 1; } else { - *((unsigned int *) value) = gasmix_count; + *((unsigned int *) value) = parser->ngasmixes; } break; case DC_FIELD_GASMIX: - if (gasmodel == AIR) { + if (parser->mode == AIR) { gasmix->helium = 0.0; gasmix->oxygen = 0.21; - } else if (parser->model == HELO2 || parser->model == D4i || - parser->model == D6i || parser->model == D9tx || - parser->model == DX) { - gasmix->helium = data[gasmix_offset + 6 * flags + 2] / 100.0; - gasmix->oxygen = data[gasmix_offset + 6 * flags + 1] / 100.0; } else { - gasmix->helium = 0.0; - gasmix->oxygen = data[gasmix_offset + flags] / 100.0; + gasmix->helium = parser->helium[flags] / 100.0; + gasmix->oxygen = parser->oxygen[flags] / 100.0; } gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; break; @@ -275,42 +330,13 @@ suunto_d9_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t ca const unsigned char *data = abstract->data; unsigned int size = abstract->size; - // Gasmix information. - unsigned int gasmix_offset = 0x21, gasmix_count = 3; - if (parser->model == HELO2) { - gasmix_offset = 0x54; - gasmix_count = 8; - } else if (parser->model == D4i) { - gasmix_offset = 0x5F; - gasmix_count = 1; - } else if (parser->model == D6i) { - gasmix_offset = 0x5F; - if (data[1] == 0x63) - gasmix_count = 3; - else - gasmix_count = 2; - } else if (parser->model == D9tx) { - gasmix_offset = 0x87; - gasmix_count = 8; - } else if (parser->model == DX) { - gasmix_offset = 0xC1; - gasmix_count = 11; - } - - // Offset to the configuration data. - unsigned int config = 0x3A; - if (parser->model == D4) { - config += 1; - } else if (parser->model == HELO2 || parser->model == D4i || - parser->model == D6i || parser->model == D9tx || - parser->model == DX) { - config = gasmix_offset + gasmix_count * 6; - } - if (config + 1 > size) - return DC_STATUS_DATAFORMAT; + // Cache the gas mix data. + dc_status_t rc = suunto_d9_parser_cache (parser); + if (rc != DC_STATUS_SUCCESS) + return rc; // Number of parameters in the configuration data. - unsigned int nparams = data[config]; + unsigned int nparams = data[parser->config]; if (nparams == 0 || nparams > MAXPARAMS) return DC_STATUS_DATAFORMAT; @@ -320,7 +346,7 @@ suunto_d9_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t ca // Get the sample configuration. sample_info_t info[MAXPARAMS] = {{0}}; for (unsigned int i = 0; i < nparams; ++i) { - unsigned int idx = config + 2 + i * 3; + unsigned int idx = parser->config + 2 + i * 3; info[i].type = data[idx + 0]; info[i].interval = data[idx + 1]; info[i].divisor = divisors[(data[idx + 2] & 0x1C) >> 2]; @@ -338,7 +364,7 @@ suunto_d9_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t ca } // Offset to the profile data. - unsigned int profile = config + 2 + nparams * 3; + unsigned int profile = parser->config + 2 + nparams * 3; if (profile + 5 > size) return DC_STATUS_DATAFORMAT;