From 48e46cf7775452740f67ba72d9c5a0960ebf3718 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 7 May 2020 15:14:10 -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/shearwater_predator_parser.c | 125 +++++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 7 deletions(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 7ad1c54..1be4383 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -28,6 +28,7 @@ #include "context-private.h" #include "parser-private.h" #include "array.h" +#include "field-cache.h" #define ISINSTANCE(parser) ( \ dc_parser_isinstance((parser), &shearwater_predator_parser_vtable) || \ @@ -69,6 +70,7 @@ #define IMPERIAL 1 #define NGASMIXES 10 +#define MAXSTRINGS 32 #define NTANKS 2 #define NRECORDS 8 @@ -90,6 +92,7 @@ typedef struct shearwater_predator_tank_t { unsigned int enabled; unsigned int beginpressure; unsigned int endpressure; + unsigned int battery; } shearwater_predator_tank_t; struct shearwater_predator_parser_t { @@ -113,10 +116,13 @@ struct shearwater_predator_parser_t { unsigned int tankidx[NTANKS]; unsigned int calibrated; double calibration[3]; - dc_divemode_t mode; + unsigned int serial; unsigned int units; unsigned int atmospheric; unsigned int density; + + /* Generic field cache */ + struct dc_field_cache cache; }; static dc_status_t shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); @@ -190,6 +196,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->pnf = 0; parser->logversion = 0; @@ -210,17 +219,19 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig parser->tank[i].enabled = 0; parser->tank[i].beginpressure = 0; parser->tank[i].endpressure = 0; + parser->tank[i].battery = 0; parser->tankidx[i] = i; } parser->calibrated = 0; for (unsigned int i = 0; i < 3; ++i) { parser->calibration[i] = 0.0; } - parser->mode = DC_DIVEMODE_OC; parser->units = METRIC; parser->density = 1025; parser->atmospheric = ATM / (BAR / 1000); + DC_ASSIGN_FIELD(parser->cache, DIVEMODE, DC_DIVEMODE_OC); + *out = (dc_parser_t *) parser; return DC_STATUS_SUCCESS; @@ -273,11 +284,12 @@ shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char for (unsigned int i = 0; i < 3; ++i) { parser->calibration[i] = 0.0; } - parser->mode = DC_DIVEMODE_OC; parser->units = METRIC; parser->density = 1025; parser->atmospheric = ATM / (BAR / 1000); + DC_ASSIGN_FIELD(parser->cache, DIVEMODE, DC_DIVEMODE_OC); + return DC_STATUS_SUCCESS; } @@ -303,6 +315,84 @@ shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *d return DC_STATUS_SUCCESS; } +// 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) +{ + // We don't know what other state bits than 0-2 mean + state &= 7; + 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 + }; + dc_field_add_string(&parser->cache, desc, states[state]); + } +} + +static void +add_deco_model(shearwater_predator_parser_t *parser, const unsigned char *data) +{ + unsigned int idx_deco_model = parser->pnf ? parser->opening[2] + 18 : 67; + unsigned int idx_gfs = parser->pnf ? parser->opening[3] + 5 : 85; + + switch (data[idx_deco_model]) { + case 0: + dc_field_add_string_fmt(&parser->cache, "Deco model", "GF %u/%u", data[4], data[5]); + break; + case 1: + dc_field_add_string_fmt(&parser->cache, "Deco model", "VPM-B +%u", data[idx_deco_model + 1]); + break; + case 2: + dc_field_add_string_fmt(&parser->cache, "Deco model", "VPM-B/GFS +%u %u%%", data[idx_deco_model + 1], data[idx_gfs]); + break; + default: + dc_field_add_string_fmt(&parser->cache, "Deco model", "Unknown model %d", data[idx_deco_model]); + } +} + +static void +add_battery_type(shearwater_predator_parser_t *parser, const unsigned char *data) +{ + if (parser->logversion < 7) + return; + + unsigned int idx_battery_type = parser->pnf ? parser->opening[4] + 9 : 120; + switch (data[idx_battery_type]) { + case 1: + dc_field_add_string(&parser->cache, "Battery type", "1.5V Alkaline"); + break; + case 2: + dc_field_add_string(&parser->cache, "Battery type", "1.5V Lithium"); + break; + case 3: + dc_field_add_string(&parser->cache, "Battery type", "1.2V NiMH"); + break; + case 4: + dc_field_add_string(&parser->cache, "Battery type", "3.6V Saft"); + break; + case 5: + dc_field_add_string(&parser->cache, "Battery type", "3.7V Li-Ion"); + break; + default: + dc_field_add_string_fmt(&parser->cache, "Battery type", "unknown type %d", data[idx_battery_type]); + break; + } +} static dc_status_t shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) @@ -314,6 +404,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) if (parser->cached) { return DC_STATUS_SUCCESS; } + memset(&parser->cache, 0, sizeof(parser->cache)); // Log versions before 6 weren't reliably stored in the data, but // 6 is also the oldest version that we assume in our code @@ -435,13 +526,16 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) // bits the tank pressure in units of 2 psi. unsigned int pressure = array_uint16_be (data + offset + pnf + idx[i]); if (pressure < 0xFFF0) { + unsigned int battery = 1u << (pressure >> 12); pressure &= 0x0FFF; if (!tank[i].enabled) { tank[i].enabled = 1; tank[i].beginpressure = pressure; tank[i].endpressure = pressure; + tank[i].battery = 0; } tank[i].endpressure = pressure; + tank[i].battery |= battery; } } } @@ -475,6 +569,8 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) } } + dc_field_add_string_fmt(&parser->cache, "Logversion", "%d%s", logversion, pnf ? "(PNF)" : ""); + // Cache sensor calibration for later use unsigned int nsensors = 0, ndefaults = 0; unsigned int base = parser->opening[3] + (pnf ? 6 : 86); @@ -503,8 +599,12 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) // uncalibrated). WARNING (abstract->context, "Disabled all O2 sensors due to a default calibration value."); parser->calibrated = 0; + if (mode != DC_DIVEMODE_OC) + dc_field_add_string(&parser->cache, "PPO2 source", "voted/averaged"); } else { parser->calibrated = data[base]; + if (mode != DC_DIVEMODE_OC) + dc_field_add_string(&parser->cache, "PPO2 source", "cells"); } // Cache the data for later use. @@ -526,12 +626,22 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) parser->tankidx[i] = UNDEFINED; } } - parser->mode = mode; parser->units = data[parser->opening[0] + 8]; parser->atmospheric = array_uint16_be (data + parser->opening[1] + (parser->pnf ? 16 : 47)); parser->density = array_uint16_be (data + parser->opening[3] + (parser->pnf ? 3 : 83)); parser->cached = 1; + DC_ASSIGN_FIELD(parser->cache, DIVEMODE, mode); + + dc_field_add_string_fmt(&parser->cache, "Serial", "%08x", parser->serial); + // bytes 1-31 are identical in all formats + dc_field_add_string_fmt(&parser->cache, "FW Version", "%2x", data[19]); + add_deco_model(parser, data); + add_battery_type(parser, data); + dc_field_add_string_fmt(&parser->cache, "Battery at end", "%.1f V", data[9] / 10.0); + add_battery_info(parser, "T1 battery", tank[0].battery); + add_battery_info(parser, "T2 battery", tank[1].battery); + return DC_STATUS_SUCCESS; } @@ -550,6 +660,7 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ 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_field_string_t *string = (dc_field_string_t *) value; if (value) { switch (type) { @@ -597,8 +708,9 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ *((double *) value) = parser->atmospheric / 1000.0; break; case DC_FIELD_DIVEMODE: - *((dc_divemode_t *) value) = parser->mode; - break; + return DC_FIELD_VALUE(parser->cache, value, DIVEMODE); + case DC_FIELD_STRING: + return dc_field_get_string(&parser->cache, flags, string); default: return DC_STATUS_UNSUPPORTED; } @@ -853,6 +965,5 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal offset += parser->samplesize; } - return DC_STATUS_SUCCESS; }