From 811a9b4f89498a337301429b8e8578e61f34a6a9 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 7 Feb 2022 19:03:46 +0100 Subject: [PATCH 1/7] Report the correct dive mode for SCR dives The status field contains an extra bit for SCR dives, so there is no reason to report them as CCR dives. --- src/shearwater_predator_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 290001a..9fd9656 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -391,7 +391,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) // Status flags. unsigned int status = data[offset + 11 + pnf]; if ((status & OC) == 0) { - mode = DC_DIVEMODE_CCR; + mode = status & SC ? DC_DIVEMODE_SCR : DC_DIVEMODE_CCR; } // Gaschange. From ccd37d4fa310ef0b598300f7935a1c29e454afa7 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 7 Feb 2022 20:15:41 +0100 Subject: [PATCH 2/7] Use the dive mode stored in the header Starting with log version 8, the dive mode is stored in one of the opening records. For backwards compatibility with older firmware versions, the autodetection based on the status field in the sample data is kept as a fallback mechanism. --- src/shearwater_predator_parser.c | 51 +++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 9fd9656..b6f223b 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -67,6 +67,15 @@ #define SC 0x08 #define OC 0x10 +#define M_CC 0 +#define M_OC_TEC 1 +#define M_GAUGE 2 +#define M_PPO2 3 +#define M_SC 4 +#define M_CC2 5 +#define M_OC_REC 6 +#define M_FREEDIVE 7 + #define METRIC 0 #define IMPERIAL 1 @@ -113,7 +122,7 @@ struct shearwater_predator_parser_t { unsigned int tankidx[NTANKS]; unsigned int calibrated; double calibration[3]; - dc_divemode_t mode; + unsigned int divemode; unsigned int units; unsigned int atmospheric; unsigned int density; @@ -216,7 +225,7 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig for (unsigned int i = 0; i < 3; ++i) { parser->calibration[i] = 0.0; } - parser->mode = DC_DIVEMODE_OC; + parser->divemode = M_OC_TEC; parser->units = METRIC; parser->density = 1025; parser->atmospheric = ATM / (BAR / 1000); @@ -273,7 +282,7 @@ 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->divemode = M_OC_TEC; parser->units = METRIC; parser->density = 1025; parser->atmospheric = ATM / (BAR / 1000); @@ -367,7 +376,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) } // Default dive mode. - dc_divemode_t mode = DC_DIVEMODE_OC; + unsigned int divemode = M_OC_TEC; // Get the gas mixes. unsigned int ngasmixes = 0; @@ -391,7 +400,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) // Status flags. unsigned int status = data[offset + 11 + pnf]; if ((status & OC) == 0) { - mode = status & SC ? DC_DIVEMODE_SCR : DC_DIVEMODE_CCR; + divemode = status & SC ? M_SC : M_CC; } // Gaschange. @@ -447,7 +456,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) } } else if (type == LOG_RECORD_FREEDIVE_SAMPLE) { // Freedive record - mode = DC_DIVEMODE_FREEDIVE; + divemode = M_FREEDIVE; } else if (type >= LOG_RECORD_OPENING_0 && type <= LOG_RECORD_OPENING_7) { // Opening record parser->opening[type - LOG_RECORD_OPENING_0] = offset; @@ -510,6 +519,11 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) parser->calibrated = data[base]; } + // Get the dive mode from the header (if available). + if (logversion >= 8) { + divemode = data[parser->opening[4] + (pnf ? 1 : 112)]; + } + // Cache the data for later use. parser->pnf = pnf; parser->logversion = logversion; @@ -529,7 +543,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) parser->tankidx[i] = UNDEFINED; } } - parser->mode = mode; + parser->divemode = divemode; 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)); @@ -600,7 +614,28 @@ 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; + switch (parser->divemode) { + case M_CC: + case M_CC2: + *((dc_divemode_t *) value) = DC_DIVEMODE_CCR; + break; + case M_SC: + *((dc_divemode_t *) value) = DC_DIVEMODE_SCR; + break; + case M_OC_TEC: + case M_OC_REC: + *((dc_divemode_t *) value) = DC_DIVEMODE_OC; + break; + case M_GAUGE: + case M_PPO2: + *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE; + break; + case M_FREEDIVE: + *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE; + break; + default: + return DC_STATUS_DATAFORMAT; + } break; default: return DC_STATUS_UNSUPPORTED; From d1242a28cfb2f69115486ce491c5165e1fe9cde8 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 7 Feb 2022 21:36:59 +0100 Subject: [PATCH 3/7] Use the correct model number from the final block During the download, the model number is obtained from the hardware type because the model number isn't available before downloading the first dive. Since the list with available hardware types is incomplete, the correct model number is not always available. However, during parsing the correct model number is available in the final block. --- src/shearwater_predator_parser.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index b6f223b..d7a8ad8 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -524,6 +524,11 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) divemode = data[parser->opening[4] + (pnf ? 1 : 112)]; } + // Get the correct model number from the final block. + if (parser->final != UNDEFINED) { + parser->model = data[parser->final + 13]; + } + // Cache the data for later use. parser->pnf = pnf; parser->logversion = logversion; From 54fa676e75a5b7df804468a2dca72ca018bd577c Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 7 Feb 2022 21:41:34 +0100 Subject: [PATCH 4/7] Report the timezone offset for the Teric The Shearwater Teric supports a UTC offset and DST setting. --- src/shearwater_predator_parser.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index d7a8ad8..a79677f 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -85,6 +85,7 @@ #define PREDATOR 2 #define PETREL 3 +#define TERIC 8 #define UNDEFINED 0xFFFFFFFF @@ -307,7 +308,13 @@ shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *d if (!dc_datetime_gmtime (datetime, ticks)) return DC_STATUS_DATAFORMAT; - datetime->timezone = DC_TIMEZONE_NONE; + if (parser->model == TERIC && parser->logversion >= 9 && parser->opening[5] != UNDEFINED) { + int utc_offset = (int) array_uint32_be (data + parser->opening[5] + 26); + int dst = data[parser->opening[5] + 30]; + datetime->timezone = utc_offset * 60 + dst * 3600; + } else { + datetime->timezone = DC_TIMEZONE_NONE; + } return DC_STATUS_SUCCESS; } From 86e1d59a6ad1dcf3965de917520e541844c6bd56 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 7 Feb 2022 22:12:25 +0100 Subject: [PATCH 5/7] Limit the number of records for the Predator In the older Predator-like data format, the 4th opening/closing record is the last one. To avoid accidental use of the higher ones, leave them undefined. --- src/shearwater_predator_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index a79677f..360b978 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -373,7 +373,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) // byte opening and closing block. To minimize the differences // with the PNF format, all record offsets are assigned the same // value here. - for (unsigned int i = 0; i < NRECORDS; ++i) { + for (unsigned int i = 0; i <= 4; ++i) { parser->opening[i] = 0; parser->closing[i] = size - footersize; } From 7a650f940cab0f575dd9ae5f98402e247f58ce1e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 8 Feb 2022 09:13:47 +0100 Subject: [PATCH 6/7] Add support for transmitter T3 and T4 In recent firmware versions (log version 13), Shearwater added support for two more tank transmittors: T3 and T4. --- src/shearwater_predator_parser.c | 40 ++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 360b978..b7e7b32 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -52,6 +52,7 @@ #define LOG_RECORD_CLOSING_6 0x26 #define LOG_RECORD_CLOSING_7 0x27 #define LOG_RECORD_INFO_EVENT 0x30 +#define LOG_RECORD_DIVE_SAMPLE_EXT 0xE1 #define LOG_RECORD_FINAL 0xFF #define INFO_EVENT_TAG_LOG 38 @@ -80,7 +81,7 @@ #define IMPERIAL 1 #define NGASMIXES 10 -#define NTANKS 2 +#define NTANKS 4 #define NRECORDS 8 #define PREDATOR 2 @@ -439,8 +440,8 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) // Tank pressure if (logversion >= 7) { - const unsigned int idx[NTANKS] = {27, 19}; - for (unsigned int i = 0; i < NTANKS; ++i) { + const unsigned int idx[2] = {27, 19}; + for (unsigned int i = 0; i < 2; ++i) { // Values above 0xFFF0 are special codes: // 0xFFFF AI is off // 0xFFFE No comms for 90 seconds+ @@ -461,6 +462,22 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) } } } + } else if (type == LOG_RECORD_DIVE_SAMPLE_EXT) { + // Tank pressure + if (logversion >= 13) { + for (unsigned int i = 0; i < 2; ++i) { + unsigned int pressure = array_uint16_be (data + offset + pnf + i * 2); + if (pressure < 0xFFF0) { + pressure &= 0x0FFF; + if (!tank[i + 2].enabled) { + tank[i + 2].enabled = 1; + tank[i + 2].beginpressure = pressure; + tank[i + 2].endpressure = pressure; + } + tank[i + 2].endpressure = pressure; + } + } + } } else if (type == LOG_RECORD_FREEDIVE_SAMPLE) { // Freedive record divemode = M_FREEDIVE; @@ -805,8 +822,8 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal // for logversion 7 and newer (introduced for Perdix AI) // detect tank pressure if (parser->logversion >= 7) { - const unsigned int idx[NTANKS] = {27, 19}; - for (unsigned int i = 0; i < NTANKS; ++i) { + const unsigned int idx[2] = {27, 19}; + for (unsigned int i = 0; i < 2; ++i) { // Tank pressure // Values above 0xFFF0 are special codes: // 0xFFFF AI is off @@ -837,6 +854,19 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal if (callback) callback (DC_SAMPLE_RBT, sample, userdata); } } + } else if (type == LOG_RECORD_DIVE_SAMPLE_EXT) { + // Tank pressure + if (parser->logversion >= 13) { + for (unsigned int i = 0; i < 2; ++i) { + unsigned int pressure = array_uint16_be (data + offset + pnf + i * 2); + if (pressure < 0xFFF0) { + pressure &= 0x0FFF; + sample.pressure.tank = parser->tankidx[i + 2]; + sample.pressure.value = pressure * 2 * PSI / BAR; + if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); + } + } + } } else if (type == LOG_RECORD_FREEDIVE_SAMPLE) { // A freedive record is actually 4 samples, each 8-bytes, // packed into a standard 32-byte sized record. At the end From c6640aa7d319506b3ece645361e6f1d9fb1ac0fb Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 8 Feb 2022 20:24:59 +0100 Subject: [PATCH 7/7] Read the extra tank information The latest firmware does store some additional information for each tank. Right now it's not really used for anything yet, but it's available for future use. --- src/array.c | 12 +++++ src/array.h | 3 ++ src/shearwater_predator_parser.c | 88 +++++++++++++++++++++++++++++--- 3 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/array.c b/src/array.c index de15bc1..1777a01 100644 --- a/src/array.c +++ b/src/array.c @@ -160,6 +160,18 @@ array_convert_str2num (const unsigned char data[], unsigned int size) return value; } +unsigned int +array_convert_bcd2dec (const unsigned char data[], unsigned int size) +{ + unsigned int value = 0; + for (unsigned int i = 0; i < size; ++i) { + value *= 100; + value += bcd2dec(data[i]); + } + + return value; +} + unsigned int array_uint_be (const unsigned char data[], unsigned int n) { diff --git a/src/array.h b/src/array.h index da70efa..d69fa32 100644 --- a/src/array.h +++ b/src/array.h @@ -52,6 +52,9 @@ array_convert_hex2bin (const unsigned char input[], unsigned int isize, unsigned unsigned int array_convert_str2num (const unsigned char data[], unsigned int size); +unsigned int +array_convert_bcd2dec (const unsigned char data[], unsigned int size); + unsigned int array_uint_be (const unsigned char data[], unsigned int n); diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index b7e7b32..28c4915 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -20,6 +20,7 @@ */ #include +#include #include @@ -99,8 +100,13 @@ typedef struct shearwater_predator_gasmix_t { typedef struct shearwater_predator_tank_t { unsigned int enabled; + unsigned int active; unsigned int beginpressure; unsigned int endpressure; + unsigned int pressure_max; + unsigned int pressure_reserve; + unsigned int serial; + char name[2]; } shearwater_predator_tank_t; struct shearwater_predator_parser_t { @@ -219,8 +225,13 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig parser->ntanks = 0; for (unsigned int i = 0; i < NTANKS; ++i) { parser->tank[i].enabled = 0; + parser->tank[i].active = 0; parser->tank[i].beginpressure = 0; parser->tank[i].endpressure = 0; + parser->tank[i].pressure_max = 0; + parser->tank[i].pressure_reserve = 0; + parser->tank[i].serial = 0; + memset (parser->tank[i].name, 0, sizeof (parser->tank[i].name)); parser->tankidx[i] = i; } parser->calibrated = 0; @@ -276,8 +287,13 @@ shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char parser->ntanks = 0; for (unsigned int i = 0; i < NTANKS; ++i) { parser->tank[i].enabled = 0; + parser->tank[i].active = 0; parser->tank[i].beginpressure = 0; parser->tank[i].endpressure = 0; + parser->tank[i].pressure_max = 0; + parser->tank[i].pressure_reserve = 0; + parser->tank[i].serial = 0; + memset (parser->tank[i].name, 0, sizeof (parser->tank[i].name)); parser->tankidx[i] = i; } parser->calibrated = 0; @@ -453,8 +469,8 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) unsigned int pressure = array_uint16_be (data + offset + pnf + idx[i]); if (pressure < 0xFFF0) { pressure &= 0x0FFF; - if (!tank[i].enabled) { - tank[i].enabled = 1; + if (!tank[i].active) { + tank[i].active = 1; tank[i].beginpressure = pressure; tank[i].endpressure = pressure; } @@ -469,8 +485,8 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) unsigned int pressure = array_uint16_be (data + offset + pnf + i * 2); if (pressure < 0xFFF0) { pressure &= 0x0FFF; - if (!tank[i + 2].enabled) { - tank[i + 2].enabled = 1; + if (!tank[i + 2].active) { + tank[i + 2].active = 1; tank[i + 2].beginpressure = pressure; tank[i + 2].endpressure = pressure; } @@ -485,9 +501,59 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) // Opening record parser->opening[type - LOG_RECORD_OPENING_0] = offset; - // Log version if (type == LOG_RECORD_OPENING_4) { + // Log version logversion = data[offset + 16]; + + // Air integration mode + if (logversion >= 7) { + unsigned int airmode = data[offset + 28]; + if (logversion < 13) { + if (airmode == 1 || airmode == 2) { + tank[airmode - 1].enabled = 1; + } else if (airmode == 3) { + tank[0].enabled = 1; + tank[1].enabled = 1; + } + } + if (airmode == 4) { + tank[0].enabled = 1; + tank[1].enabled = 1; + } + } + } else if (type == LOG_RECORD_OPENING_5) { + if (logversion >= 9) { + tank[0].serial = array_convert_bcd2dec (data + offset + 1, 3); + tank[0].pressure_max = array_uint16_be(data + offset + 6); + tank[0].pressure_reserve = array_uint16_be(data + offset + 8); + + tank[1].serial = array_convert_bcd2dec(data + offset + 10, 3); + tank[1].pressure_max = array_uint16_be(data + offset + 15); + tank[1].pressure_reserve = array_uint16_be(data + offset + 17); + } + } else if (type == LOG_RECORD_OPENING_6) { + if (logversion >= 13) { + tank[0].enabled = data[offset + 19]; + memcpy (tank[0].name, data + offset + 20, sizeof (tank[0].name)); + + tank[1].enabled = data[offset + 22]; + memcpy (tank[1].name, data + offset + 23, sizeof (tank[1].name)); + + tank[2].serial = array_convert_bcd2dec(data + offset + 25, 3); + tank[2].pressure_max = array_uint16_be(data + offset + 28); + tank[2].pressure_reserve = array_uint16_be(data + offset + 30); + } + } else if (type == LOG_RECORD_OPENING_7) { + if (logversion >= 13) { + tank[2].enabled = data[offset + 1]; + memcpy (tank[2].name, data + offset + 2, sizeof (tank[2].name)); + + tank[3].serial = array_convert_bcd2dec(data + offset + 4, 3); + tank[3].pressure_max = array_uint16_be(data + offset + 7); + tank[3].pressure_reserve = array_uint16_be(data + offset + 9); + tank[3].enabled = data[offset + 11]; + memcpy (tank[3].name, data + offset + 12, sizeof (tank[3].name)); + } } } else if (type >= LOG_RECORD_CLOSING_0 && type <= LOG_RECORD_CLOSING_7) { // Closing record @@ -553,6 +619,16 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) parser->model = data[parser->final + 13]; } + // Fix the Teric tank serial number. + if (parser->model == TERIC) { + for (unsigned int i = 0; i < NTANKS; ++i) { + tank[i].serial = + ((tank[i].serial / 10000) % 100) + + ((tank[i].serial / 100) % 100) * 100 + + ((tank[i].serial ) % 100) * 10000; + } + } + // Cache the data for later use. parser->pnf = pnf; parser->logversion = logversion; @@ -564,7 +640,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) } parser->ntanks = 0; for (unsigned int i = 0; i < NTANKS; ++i) { - if (tank[i].enabled) { + if (tank[i].active) { parser->tankidx[i] = parser->ntanks; parser->tank[parser->ntanks] = tank[i]; parser->ntanks++;