diff --git a/src/suunto_d9_parser.c b/src/suunto_d9_parser.c index 3c3f783..86e9411 100644 --- a/src/suunto_d9_parser.c +++ b/src/suunto_d9_parser.c @@ -38,6 +38,7 @@ struct suunto_d9_parser_t { static parser_status_t suunto_d9_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size); static parser_status_t suunto_d9_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime); +static parser_status_t suunto_d9_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned int flags, void *value); static parser_status_t suunto_d9_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata); static parser_status_t suunto_d9_parser_destroy (parser_t *abstract); @@ -45,7 +46,7 @@ static const parser_backend_t suunto_d9_parser_backend = { PARSER_TYPE_SUUNTO_D9, suunto_d9_parser_set_data, /* set_data */ suunto_d9_parser_get_datetime, /* datetime */ - NULL, /* fields */ + suunto_d9_parser_get_field, /* fields */ suunto_d9_parser_samples_foreach, /* samples_foreach */ suunto_d9_parser_destroy /* destroy */ }; @@ -136,6 +137,64 @@ suunto_d9_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime) } +static parser_status_t +suunto_d9_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned int flags, void *value) +{ + suunto_d9_parser_t *parser = (suunto_d9_parser_t*) abstract; + + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + // Offset to the configuration data. + unsigned int config = 0x3E - SKIP; + if (parser->model == 0x12) + config += 1; // D4 + if (parser->model == 0x15) + config += 74; // HelO2 + if (size < config) + return PARSER_STATUS_ERROR; + + gasmix_t *gasmix = (gasmix_t *) value; + + if (value) { + switch (type) { + case FIELD_TYPE_DIVETIME: + if (parser->model == 0x12) + *((unsigned int *) value) = array_uint16_le (data + 0x0F - SKIP); + else if (parser->model == 0x15) + *((unsigned int *) value) = array_uint16_le (data + 0x0F - SKIP + 2) * 60; + else + *((unsigned int *) value) = array_uint16_le (data + 0x0F - SKIP) * 60; + break; + case FIELD_TYPE_MAXDEPTH: + *((double *) value) = array_uint16_le (data + 0x0D - SKIP) / 100.0; + break; + case FIELD_TYPE_GASMIX_COUNT: + if (parser->model == 0x15) { + *((unsigned int *) value) = 8; + } else { + *((unsigned int *) value) = 3; + } + break; + case FIELD_TYPE_GASMIX: + if (parser->model == 0x15) { + gasmix->helium = data[0x58 - SKIP + 6 * flags + 2] / 100.0; + gasmix->oxygen = data[0x58 - SKIP + 6 * flags + 1] / 100.0; + } else { + gasmix->helium = 0.0; + gasmix->oxygen = data[0x25 - SKIP + flags] / 100.0; + } + gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; + break; + default: + return PARSER_STATUS_UNSUPPORTED; + } + } + + return PARSER_STATUS_SUCCESS; +} + + static parser_status_t suunto_d9_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata) { diff --git a/src/suunto_eon_parser.c b/src/suunto_eon_parser.c index 06717d0..25d0a87 100644 --- a/src/suunto_eon_parser.c +++ b/src/suunto_eon_parser.c @@ -32,10 +32,15 @@ typedef struct suunto_eon_parser_t suunto_eon_parser_t; struct suunto_eon_parser_t { parser_t base; int spyder; + // Cached fields. + unsigned int cached; + unsigned int divetime; + unsigned int maxdepth; }; static parser_status_t suunto_eon_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size); static parser_status_t suunto_eon_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime); +static parser_status_t suunto_eon_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned int flags, void *value); static parser_status_t suunto_eon_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata); static parser_status_t suunto_eon_parser_destroy (parser_t *abstract); @@ -43,7 +48,7 @@ static const parser_backend_t suunto_eon_parser_backend = { PARSER_TYPE_SUUNTO_EON, suunto_eon_parser_set_data, /* set_data */ suunto_eon_parser_get_datetime, /* datetime */ - NULL, /* fields */ + suunto_eon_parser_get_field, /* fields */ suunto_eon_parser_samples_foreach, /* samples_foreach */ suunto_eon_parser_destroy /* destroy */ }; @@ -77,6 +82,9 @@ suunto_eon_parser_create (parser_t **out, int spyder) // Set the default values. parser->spyder = spyder; + parser->cached = 0; + parser->divetime = 0; + parser->maxdepth = 0; *out = (parser_t*) parser; @@ -100,9 +108,16 @@ suunto_eon_parser_destroy (parser_t *abstract) static parser_status_t suunto_eon_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size) { + suunto_eon_parser_t *parser = (suunto_eon_parser_t *) abstract; + if (! parser_is_suunto_eon (abstract)) return PARSER_STATUS_TYPE_MISMATCH; + // Reset the cache. + parser->cached = 0; + parser->divetime = 0; + parser->maxdepth = 0; + return PARSER_STATUS_SUCCESS; } @@ -138,6 +153,72 @@ suunto_eon_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime) } +static parser_status_t +suunto_eon_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned int flags, void *value) +{ + suunto_eon_parser_t *parser = (suunto_eon_parser_t *) abstract; + + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + if (size < 13) + return PARSER_STATUS_ERROR; + + if (!parser->cached) { + unsigned int interval = data[3]; + unsigned int nsamples = 0; + unsigned int depth = 0, maxdepth = 0; + unsigned int offset = 11; + while (offset < size && data[offset] != 0x80) { + unsigned char value = data[offset++]; + if (value < 0x7d || value > 0x82) { + depth += (signed char) value; + if (depth > maxdepth) + maxdepth = depth; + nsamples++; + } + } + + // Store the offset to the end marker. + unsigned int marker = offset; + if (marker + 2 >= size || data[marker] != 0x80) + return PARSER_STATUS_ERROR; + + parser->cached = 1; + parser->divetime = nsamples * interval; + parser->maxdepth = maxdepth; + } + + gasmix_t *gasmix = (gasmix_t *) value; + + if (value) { + switch (type) { + case FIELD_TYPE_DIVETIME: + *((unsigned int *) value) = parser->divetime; + break; + case FIELD_TYPE_MAXDEPTH: + *((double *) value) = parser->maxdepth * FEET; + break; + case FIELD_TYPE_GASMIX_COUNT: + *((unsigned int *) value) = 1; + break; + case FIELD_TYPE_GASMIX: + gasmix->helium = 0.0; + if ((data[4] & 0x80) && !parser->spyder) + gasmix->oxygen = data[0x05] / 100.0; + else + gasmix->oxygen = 0.21; + gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; + break; + default: + return PARSER_STATUS_UNSUPPORTED; + } + } + + return PARSER_STATUS_SUCCESS; +} + + static parser_status_t suunto_eon_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata) { diff --git a/src/suunto_solution_parser.c b/src/suunto_solution_parser.c index 41c04fd..2bc61f4 100644 --- a/src/suunto_solution_parser.c +++ b/src/suunto_solution_parser.c @@ -30,9 +30,14 @@ typedef struct suunto_solution_parser_t suunto_solution_parser_t; struct suunto_solution_parser_t { parser_t base; + // Cached fields. + unsigned int cached; + unsigned int divetime; + unsigned int maxdepth; }; static parser_status_t suunto_solution_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size); +static parser_status_t suunto_solution_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned int flags, void *value); static parser_status_t suunto_solution_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata); static parser_status_t suunto_solution_parser_destroy (parser_t *abstract); @@ -40,7 +45,7 @@ static const parser_backend_t suunto_solution_parser_backend = { PARSER_TYPE_SUUNTO_SOLUTION, suunto_solution_parser_set_data, /* set_data */ NULL, /* datetime */ - NULL, /* fields */ + suunto_solution_parser_get_field, /* fields */ suunto_solution_parser_samples_foreach, /* samples_foreach */ suunto_solution_parser_destroy /* destroy */ }; @@ -72,6 +77,11 @@ suunto_solution_parser_create (parser_t **out) // Initialize the base class. parser_init (&parser->base, &suunto_solution_parser_backend); + // Set the default values. + parser->cached = 0; + parser->divetime = 0; + parser->maxdepth = 0; + *out = (parser_t*) parser; return PARSER_STATUS_SUCCESS; @@ -94,13 +104,86 @@ suunto_solution_parser_destroy (parser_t *abstract) static parser_status_t suunto_solution_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size) { + suunto_solution_parser_t *parser = (suunto_solution_parser_t *) abstract; + if (! parser_is_suunto_solution (abstract)) return PARSER_STATUS_TYPE_MISMATCH; + // Reset the cache. + parser->cached = 0; + parser->divetime = 0; + parser->maxdepth = 0; + return PARSER_STATUS_SUCCESS; } +static parser_status_t +suunto_solution_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned int flags, void *value) +{ + suunto_solution_parser_t *parser = (suunto_solution_parser_t *) abstract; + + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + if (size < 4) + return PARSER_STATUS_ERROR; + + if (!parser->cached) { + unsigned int nsamples = 0; + unsigned int depth = 0, maxdepth = 0; + unsigned int offset = 3; + while (offset < size && data[offset] != 0x80) { + unsigned char value = data[offset++]; + if (value < 0x7e || value > 0x82) { + depth += (signed char) value; + if (value == 0x7D || value == 0x83) { + if (offset + 1 > size) + return PARSER_STATUS_ERROR; + depth += (signed char) data[offset++]; + } + if (depth > maxdepth) + maxdepth = depth; + nsamples++; + } + } + + // Store the offset to the end marker. + unsigned int marker = offset; + if (marker + 1 >= size || data[marker] != 0x80) + return PARSER_STATUS_ERROR; + + parser->cached = 1; + parser->divetime = (nsamples * 3 + data[marker + 1]) * 60; + parser->maxdepth = maxdepth; + } + + gasmix_t *gasmix = (gasmix_t *) value; + + if (value) { + switch (type) { + case FIELD_TYPE_DIVETIME: + *((unsigned int *) value) = parser->divetime; + break; + case FIELD_TYPE_MAXDEPTH: + *((double *) value) = parser->maxdepth * FEET; + break; + case FIELD_TYPE_GASMIX_COUNT: + *((unsigned int *) value) = 1; + break; + case FIELD_TYPE_GASMIX: + gasmix->helium = 0.0; + gasmix->oxygen = 0.21; + gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; + break; + default: + return PARSER_STATUS_UNSUPPORTED; + } + } + + return PARSER_STATUS_SUCCESS; +} + static parser_status_t suunto_solution_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata) { diff --git a/src/suunto_vyper_parser.c b/src/suunto_vyper_parser.c index ddbf309..8a3ebde 100644 --- a/src/suunto_vyper_parser.c +++ b/src/suunto_vyper_parser.c @@ -30,10 +30,15 @@ typedef struct suunto_vyper_parser_t suunto_vyper_parser_t; struct suunto_vyper_parser_t { parser_t base; + // Cached fields. + unsigned int cached; + unsigned int divetime; + unsigned int maxdepth; }; static parser_status_t suunto_vyper_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size); static parser_status_t suunto_vyper_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime); +static parser_status_t suunto_vyper_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned int flags, void *value); static parser_status_t suunto_vyper_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata); static parser_status_t suunto_vyper_parser_destroy (parser_t *abstract); @@ -41,7 +46,7 @@ static const parser_backend_t suunto_vyper_parser_backend = { PARSER_TYPE_SUUNTO_VYPER, suunto_vyper_parser_set_data, /* set_data */ suunto_vyper_parser_get_datetime, /* datetime */ - NULL, /* fields */ + suunto_vyper_parser_get_field, /* fields */ suunto_vyper_parser_samples_foreach, /* samples_foreach */ suunto_vyper_parser_destroy /* destroy */ }; @@ -73,6 +78,11 @@ suunto_vyper_parser_create (parser_t **out) // Initialize the base class. parser_init (&parser->base, &suunto_vyper_parser_backend); + // Set the default values. + parser->cached = 0; + parser->divetime = 0; + parser->maxdepth = 0; + *out = (parser_t*) parser; return PARSER_STATUS_SUCCESS; @@ -95,9 +105,16 @@ suunto_vyper_parser_destroy (parser_t *abstract) static parser_status_t suunto_vyper_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size) { + suunto_vyper_parser_t *parser = (suunto_vyper_parser_t *) abstract; + if (! parser_is_suunto_vyper (abstract)) return PARSER_STATUS_TYPE_MISMATCH; + // Reset the cache. + parser->cached = 0; + parser->divetime = 0; + parser->maxdepth = 0; + return PARSER_STATUS_SUCCESS; } @@ -123,6 +140,71 @@ suunto_vyper_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime) } +static parser_status_t +suunto_vyper_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned int flags, void *value) +{ + suunto_vyper_parser_t *parser = (suunto_vyper_parser_t *) abstract; + + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + if (size < 18) + return PARSER_STATUS_ERROR; + + if (!parser->cached) { + unsigned int interval = data[3]; + unsigned int nsamples = 0; + unsigned int depth = 0, maxdepth = 0; + unsigned int offset = 14; + while (offset < size && data[offset] != 0x80) { + unsigned char value = data[offset++]; + if (value < 0x79 || value > 0x87) { + depth += (signed char) value; + if (depth > maxdepth) + maxdepth = depth; + nsamples++; + } + } + + // Store the offset to the end marker. + unsigned int marker = offset; + if (marker + 4 >= size || data[marker] != 0x80) + return PARSER_STATUS_ERROR; + + parser->cached = 1; + parser->divetime = nsamples * interval; + parser->maxdepth = maxdepth; + } + + gasmix_t *gas = (gasmix_t *) value; + + if (value) { + switch (type) { + case FIELD_TYPE_DIVETIME: + *((unsigned int *) value) = parser->divetime; + break; + case FIELD_TYPE_MAXDEPTH: + *((double *) value) = parser->maxdepth * FEET; + break; + case FIELD_TYPE_GASMIX_COUNT: + *((unsigned int *) value) = 1; + break; + case FIELD_TYPE_GASMIX: + gas->helium = 0.0; + if (data[6]) + gas->oxygen = data[6] / 100.0; + else + gas->oxygen = 0.21; + gas->nitrogen = 1.0 - gas->oxygen; + break; + default: + return PARSER_STATUS_UNSUPPORTED; + } + } + + return PARSER_STATUS_SUCCESS; +} + static parser_status_t suunto_vyper_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata) {