From e97a47cca55973199715df0f818b4955e60d3a31 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 17 Apr 2018 11:30:26 -0700 Subject: [PATCH] Shearwater: add extended information parsing This adds the string field interface to the Shearwater family of dive computers. That includes proper serial number formatting, but it also has a lot of new fields for battery information (both the dive computer itself and the transmitter) but also deco model information. Much of the deco model cases come from Anton Lundin in the original subsurface branch, and Dirk Hohndel added the battery type and serial number and firmware version data. And I ended up massaging it even in that original branch, so it blamed me for all these lines even back there. The sign-offs from Dirk and Anton are from the original commits. Signed-off-by: Anton Lundin Signed-off-by: Dirk Hohndel Signed-off-by: Linus Torvalds --- src/parser.c | 4 +- src/shearwater_petrel.h | 2 +- src/shearwater_predator.h | 2 +- src/shearwater_predator_parser.c | 197 ++++++++++++++++++++++++++++++- 4 files changed, 195 insertions(+), 10 deletions(-) diff --git a/src/parser.c b/src/parser.c index 39789cd..f5f55e3 100644 --- a/src/parser.c +++ b/src/parser.c @@ -150,10 +150,10 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa rc = atomics_cobalt_parser_create (&parser, context); break; case DC_FAMILY_SHEARWATER_PREDATOR: - rc = shearwater_predator_parser_create (&parser, context, model); + rc = shearwater_predator_parser_create (&parser, context, model, serial); break; case DC_FAMILY_SHEARWATER_PETREL: - rc = shearwater_petrel_parser_create (&parser, context, model); + rc = shearwater_petrel_parser_create (&parser, context, model, serial); break; case DC_FAMILY_DIVERITE_NITEKQ: rc = diverite_nitekq_parser_create (&parser, context); diff --git a/src/shearwater_petrel.h b/src/shearwater_petrel.h index 0a4e756..0d8593c 100644 --- a/src/shearwater_petrel.h +++ b/src/shearwater_petrel.h @@ -35,7 +35,7 @@ dc_status_t shearwater_petrel_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); dc_status_t -shearwater_petrel_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model); +shearwater_petrel_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model, unsigned int serial); #ifdef __cplusplus } diff --git a/src/shearwater_predator.h b/src/shearwater_predator.h index 5d1fccb..2682e0a 100644 --- a/src/shearwater_predator.h +++ b/src/shearwater_predator.h @@ -35,7 +35,7 @@ dc_status_t shearwater_predator_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); dc_status_t -shearwater_predator_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model); +shearwater_predator_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model, unsigned int serial); #ifdef __cplusplus } diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index dd9992a..dda042c 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -20,6 +20,13 @@ */ #include +#include +#include +#include + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif #include @@ -47,6 +54,7 @@ #define IMPERIAL 1 #define NGASMIXES 10 +#define MAXSTRINGS 32 #define PREDATOR 2 #define PETREL 3 @@ -68,7 +76,11 @@ struct shearwater_predator_parser_t { unsigned int helium[NGASMIXES]; unsigned int calibrated; double calibration[3]; + unsigned int serial; dc_divemode_t mode; + + /* String fields */ + dc_field_string_t strings[MAXSTRINGS]; }; static dc_status_t shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); @@ -112,7 +124,7 @@ shearwater_predator_find_gasmix (shearwater_predator_parser_t *parser, unsigned static dc_status_t -shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int petrel) +shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial, unsigned int petrel) { shearwater_predator_parser_t *parser = NULL; const dc_parser_vtable_t *vtable = NULL; @@ -140,6 +152,9 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig parser->model = model; parser->petrel = petrel; parser->samplesize = samplesize; + parser->serial = serial; + + // Set the default values. parser->cached = 0; parser->logversion = 0; parser->headersize = 0; @@ -162,16 +177,16 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig dc_status_t -shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) +shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial) { - return shearwater_common_parser_create (out, context, model, 0); + return shearwater_common_parser_create (out, context, model, serial, 0); } dc_status_t -shearwater_petrel_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) +shearwater_petrel_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial) { - return shearwater_common_parser_create (out, context, model, 1); + return shearwater_common_parser_create (out, context, model, serial, 1); } @@ -219,6 +234,146 @@ shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *d return DC_STATUS_SUCCESS; } +/* + * These string cache interfaces should be some generic + * library rather than copied for all the dive computers. + * + * This is just copied from the EON Steel code. + */ +static void +add_string(shearwater_predator_parser_t *parser, const char *desc, const char *value) +{ + int i; + + for (i = 0; i < MAXSTRINGS; i++) { + dc_field_string_t *str = parser->strings+i; + if (str->desc) + continue; + str->desc = desc; + str->value = strdup(value); + break; + } +} + +static void +add_string_fmt(shearwater_predator_parser_t *parser, const char *desc, const char *fmt, ...) +{ + char buffer[256]; + va_list ap; + + /* + * We ignore the return value from vsnprintf, and we + * always NUL-terminate the destination buffer ourselves. + * + * That way we don't have to worry about random bad legacy + * implementations. + */ + va_start(ap, fmt); + buffer[sizeof(buffer)-1] = 0; + (void) vsnprintf(buffer, sizeof(buffer)-1, fmt, ap); + va_end(ap); + + return add_string(parser, desc, buffer); +} + +// The Battery state is a big-endian word: +// +// ffff = not paired / no comms for 90 s +// fffe = no comms for 30 s +// +// Otherwise: +// - top four bits are battery state (0 - normal, 1 - critical, 2 - warning) +// - bottom 12 bits are pressure in 2 psi increments (0..8k psi) +// +// This returns the state as a bitmask (so you can see all states it had +// during the dive). Note that we currently do not report pairing and +// communication lapses. Todo? +static unsigned int +battery_state(const unsigned char *data) +{ + unsigned int pressure = array_uint16_be(data); + unsigned int state; + + if ((pressure & 0xFFF0) == 0xFFF0) + return 0; + state = pressure >> 12; + if (state > 2) + return 0; + return 1u << state; +} + +// Show the battery state +// +// NOTE! Right now it only shows the most serious bit +// but the code is set up so that we could perhaps +// indicate that the battery is on the edge (ie it +// reported both "normal" _and_ "warning" during the +// dive - maybe that would be a "starting to warn") +// +// We could also report unpaired and comm errors. +static void +add_battery_info(shearwater_predator_parser_t *parser, const char *desc, unsigned int state) +{ + if (state >= 1 && state <= 7) { + static const char *states[8] = { + "", // 000 - No state bits, not used + "normal", // 001 - only normal + "critical", // 010 - only critical + "critical", // 011 - both normal and critical + "warning", // 100 - only warning + "warning", // 101 - normal and warning + "critical", // 110 - warning and critical + "critical", // 111 - normal, warning and critical + }; + add_string(parser, desc, states[state]); + } +} + +static void +add_deco_model(shearwater_predator_parser_t *parser, const unsigned char *data) +{ + switch (data[67]) { + case 0: + add_string_fmt(parser, "Deco model", "GF %u/%u", data[4], data[5]); + break; + case 1: + add_string_fmt(parser, "Deco model", "VPM-B +%u", data[68]); + break; + case 2: + add_string_fmt(parser, "Deco model", "VPM-B/GFS +%u %u%%", data[68], data[85]); + break; + default: + add_string_fmt(parser, "Deco model", "Unknown model %d", data[67]); + } +} + +static void +add_battery_type(shearwater_predator_parser_t *parser, const unsigned char *data) +{ + if (parser->logversion < 7) + return; + + switch (data[120]) { + case 1: + add_string(parser, "Battery type", "1.5V Alkaline"); + break; + case 2: + add_string(parser, "Battery type", "1.5V Lithium"); + break; + case 3: + add_string(parser, "Battery type", "1.2V NiMH"); + break; + case 4: + add_string(parser, "Battery type", "3.6V Saft"); + break; + case 5: + add_string(parser, "Battery type", "3.7V Li-Ion"); + break; + default: + add_string_fmt(parser, "Battery type", "unknown type %d", data[120]); + break; + } +} static dc_status_t shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) @@ -243,6 +398,9 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) unsigned int logversion = 6; if (data[127] > 6) logversion = data[127]; + INFO(abstract->context, "Shearwater log version %u\n", logversion); + + memset(parser->strings, 0, sizeof(parser->strings)); // Adjust the footersize for the final block. if (parser->petrel || array_uint16_be (data + size - footersize) == 0xFFFD) { @@ -262,6 +420,9 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) unsigned int helium[NGASMIXES] = {0}; unsigned int o2_previous = 0, he_previous = 0; + // Transmitter battery levels + unsigned int t1_battery = 0, t2_battery = 0; + unsigned int offset = headersize; unsigned int length = size - footersize; while (offset < length) { @@ -304,6 +465,13 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) he_previous = he; } + // Transmitter battery levels + if (logversion >= 7) { + // T1 at offset 27, T2 at offset 19 + t1_battery |= battery_state(data + offset + 27); + t2_battery |= battery_state(data + offset + 19); + } + offset += parser->samplesize; } @@ -348,6 +516,13 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) parser->helium[i] = helium[i]; } parser->mode = mode; + add_string_fmt(parser, "Serial", "%08x", parser->serial); + add_string_fmt(parser, "FW Version", "%2x", data[19]); + add_deco_model(parser, data); + add_battery_type(parser, data); + add_string_fmt(parser, "Battery at end", "%.1f V", data[9] / 10.0); + add_battery_info(parser, "T1 battery", t1_battery); + add_battery_info(parser, "T2 battery", t2_battery); parser->cached = 1; return DC_STATUS_SUCCESS; @@ -374,6 +549,7 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_salinity_t *water = (dc_salinity_t *) value; + dc_field_string_t *string = (dc_field_string_t *) value; unsigned int density = 0; if (value) { @@ -409,6 +585,15 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ case DC_FIELD_DIVEMODE: *((dc_divemode_t *) value) = parser->mode; break; + case DC_FIELD_STRING: + if (flags < MAXSTRINGS) { + dc_field_string_t *p = parser->strings + flags; + if (p->desc) { + *string = *p; + break; + } + } + return DC_STATUS_UNSUPPORTED; default: return DC_STATUS_UNSUPPORTED; } @@ -440,6 +625,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal unsigned int time = 0; unsigned int offset = parser->headersize; unsigned int length = size - parser->footersize; + while (offset < length) { dc_sample_value_t sample = {0}; @@ -590,6 +776,5 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal offset += parser->samplesize; } - return DC_STATUS_SUCCESS; }