From ec0029c4ce5b9858500c637bea3d9524123e330e Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 7 Sep 2018 15:12:12 -0700 Subject: [PATCH 01/10] Shearwater: skip deleted dives Without this change a deleted dive on device is treated like the end of the dive list. Signed-off-by: Dirk Hohndel --- src/shearwater_petrel.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index b316da8..36e47c5 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -275,11 +275,17 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call unsigned int size = dc_buffer_get_size (buffer); // Process the records in the manifest. - unsigned int count = 0; + unsigned int count = 0, deleted = 0; unsigned int offset = 0; while (offset < size) { // Check for a valid dive header. unsigned int header = array_uint16_be (data + offset); + if (header == 0x5A23) { + // this is a deleted dive; keep looking + offset += RECORD_SIZE; + deleted++; + continue; + } if (header != 0xA5C4) break; @@ -293,7 +299,7 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Update the progress state. current += 1; - maximum -= RECORD_COUNT - count; + maximum -= RECORD_COUNT - count - deleted; // Append the manifest records to the main buffer. if (!dc_buffer_append (manifests, data, count * RECORD_SIZE)) { @@ -304,7 +310,7 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call } // Stop downloading manifest if there are no more records. - if (count != RECORD_COUNT) + if (count + deleted != RECORD_COUNT) break; } @@ -319,6 +325,11 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call unsigned int offset = 0; while (offset < size) { + // skip deleted dives + if (array_uint16_be(data + offset) == 0x5A23) { + offset += RECORD_SIZE; + continue; + } // Get the address of the dive. unsigned int address = array_uint32_be (data + offset + 20); From 12b90c693a27527d83be08342056e1e28e3b01a2 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 7 Sep 2018 17:06:05 -0700 Subject: [PATCH 02/10] Detect Sherwater Teric Signed-off-by: Dirk Hohndel --- src/shearwater_common.h | 1 + src/shearwater_petrel.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/shearwater_common.h b/src/shearwater_common.h index 767372f..8798853 100644 --- a/src/shearwater_common.h +++ b/src/shearwater_common.h @@ -40,6 +40,7 @@ extern "C" { #define PERDIX 5 #define PERDIXAI 6 #define NERD2 7 +#define TERIC 8 #define NSTEPS 10000 #define STEP(i,n) ((NSTEPS * (i) + (n) / 2) / (n)) diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index 36e47c5..e000ec2 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -240,6 +240,9 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call case 0x0D0D: model = PERDIXAI; break; + case 0x0F0F: + model = TERIC; + break; default: model = PETREL; WARNING (abstract->context, "Unknown hardware type %04x. Assuming Petrel.", hardware); From f80024ed59068bab35e8f0ed321ca1b134a6714e Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 7 Sep 2018 16:15:21 -0700 Subject: [PATCH 03/10] Shearwater: detect which logbook format is support RDBI response tells us which format the dive computer supports. Shearwater recommends to use the 'Petrel Native Format' for all dive computers which support it, even those pre-Teric models which (depending on firmware) might support both PNF and the older 'Predator-Like Format'. They also recommend to ignore the 0x90...... format which is very similar to PNF but without the final record and to use the older Predator Like Format in that case. Which format we use is determined by the base address used to download the logbook entries. Signed-off-by: Dirk Hohndel --- src/shearwater_common.h | 1 + src/shearwater_petrel.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/shearwater_common.h b/src/shearwater_common.h index 8798853..73911a5 100644 --- a/src/shearwater_common.h +++ b/src/shearwater_common.h @@ -32,6 +32,7 @@ extern "C" { #define ID_SERIAL 0x8010 #define ID_FIRMWARE 0x8011 +#define ID_RDBI 0x8021 #define ID_HARDWARE 0x8050 #define PREDATOR 2 diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index e000ec2..4307eac 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -255,6 +255,37 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call devinfo.serial = array_uint32_be (serial); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + // Read the logbook type + rc = shearwater_common_identifier (&device->base, buffer, ID_RDBI); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the logbook type."); + dc_buffer_free (buffer); + dc_buffer_free (manifests); + return rc; + } + unsigned int base_addr = array_uint_be (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer)); + base_addr &= 0xFF000000u; + switch (base_addr) { + case 0xDD000000: // Predator or Predator-Like Format + // on a Predator, use the old format, otherwise use the Predator-Like Format (what we called Petrel so far) + if (model != PREDATOR) + base_addr = 0xC0000000; + break; + case 0x90000000: // some firmware versions supported an earlier version of PNF without final record + // use the Predator-Like Format instead + base_addr = 0xC0000000; + break; + case 0x80000000: // new Petrel Native Format with final record + // that's the correct address + break; + default: // unknown format + ERROR (abstract->context, "Unknown logbook format %08x", base_addr); + dc_buffer_free (buffer); + dc_buffer_free (manifests); + return DC_STATUS_UNSUPPORTED; + } + + // Read the manifest pages while (1) { // Update the progress state. // Assume the worst case scenario of a full manifest, and adjust the From 0cbcc0518c2884975d8530d28f2a6ffbaa5eda2d Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Sep 2018 17:19:34 -0700 Subject: [PATCH 04/10] Shearwater: use the correct address to download dives Replace the hardcoded address which the one we determined based on the logbook type available. Signed-off-by: Dirk Hohndel --- src/shearwater_petrel.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index 4307eac..4b5eca9 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -33,7 +33,6 @@ #define MANIFEST_ADDR 0xE0000000 #define MANIFEST_SIZE 0x600 -#define DIVE_ADDR 0xC0000000 #define DIVE_SIZE 0xFFFFFF #define RECORD_SIZE 0x20 @@ -370,7 +369,7 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Download the dive. progress.current = NSTEPS * current; progress.maximum = NSTEPS * maximum; - rc = shearwater_common_download (&device->base, buffer, DIVE_ADDR + address, DIVE_SIZE, 1, &progress); + rc = shearwater_common_download (&device->base, buffer, base_addr + address, DIVE_SIZE, 1, &progress); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to download the dive."); dc_buffer_free (buffer); From 34785f55ff40553f096307cc68ac82c92935e19f Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Sep 2018 09:58:08 -0700 Subject: [PATCH 05/10] Shearwater Petrel Native Format parsing This will allow parsing dives from the Shearwater Teric, but depending on the firmware could also be used on older models. Signed-off-by: Dirk Hohndel --- src/shearwater_predator_parser.c | 202 ++++++++++++++++++++++++------- 1 file changed, 160 insertions(+), 42 deletions(-) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 46f0ff0..86f2310 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -40,6 +40,30 @@ dc_parser_isinstance((parser), &shearwater_predator_parser_vtable) || \ dc_parser_isinstance((parser), &shearwater_petrel_parser_vtable)) +// Petrel Native Format constants +#define PNF_BLOCKSIZE 0x20 +#define LOG_RECORD_DIVE_SAMPLE 0x01 +#define LOG_RECORD_FREEDIVE_SAMPLE 0x02 +#define LOG_RECORD_OPENING_0 0x10 +#define LOG_RECORD_OPENING_1 0x11 +#define LOG_RECORD_OPENING_2 0x12 +#define LOG_RECORD_OPENING_3 0x13 +#define LOG_RECORD_OPENING_4 0x14 +#define LOG_RECORD_OPENING_5 0x15 +#define LOG_RECORD_OPENING_6 0x16 +#define LOG_RECORD_OPENING_7 0x17 +#define LOG_RECORD_CLOSING_0 0x20 +#define LOG_RECORD_CLOSING_1 0x21 +#define LOG_RECORD_CLOSING_2 0x22 +#define LOG_RECORD_CLOSING_3 0x23 +#define LOG_RECORD_CLOSING_4 0x24 +#define LOG_RECORD_CLOSING_5 0x25 +#define LOG_RECORD_CLOSING_6 0x26 +#define LOG_RECORD_CLOSING_7 0x27 +#define LOG_RECORD_FINAL 0xFF +#define NUM_BLOCK_IDS 0x28 + +// constant for the older Predator and Predator-like formats #define SZ_BLOCK 0x80 #define SZ_SAMPLE_PREDATOR 0x10 #define SZ_SAMPLE_PETREL 0x20 @@ -65,6 +89,7 @@ struct shearwater_predator_parser_t { dc_parser_t base; unsigned int model; unsigned int petrel; + unsigned int pnf; unsigned int samplesize; // Cached fields. unsigned int cached; @@ -79,6 +104,9 @@ struct shearwater_predator_parser_t { unsigned int serial; dc_divemode_t mode; + /* Block addresses for PNF */ + unsigned int block_offset[NUM_BLOCK_IDS]; + /* String fields */ dc_field_string_t strings[MAXSTRINGS]; }; @@ -220,6 +248,7 @@ shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *d { const unsigned char *data = abstract->data; unsigned int size = abstract->size; + shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract; if (size < 2 * SZ_BLOCK) return DC_STATUS_DATAFORMAT; @@ -332,18 +361,21 @@ add_battery_info(shearwater_predator_parser_t *parser, const char *desc, unsigne static void add_deco_model(shearwater_predator_parser_t *parser, const unsigned char *data) { - switch (data[67]) { + unsigned int idx_deco_model = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_2] + 18 : 67; + unsigned int idx_gfs = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_3] + 5 : 85; + + switch (data[idx_deco_model]) { 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]); + add_string_fmt(parser, "Deco model", "VPM-B +%u", data[idx_deco_model + 1]); break; case 2: - add_string_fmt(parser, "Deco model", "VPM-B/GFS +%u %u%%", data[68], data[85]); + add_string_fmt(parser, "Deco model", "VPM-B/GFS +%u %u%%", data[idx_deco_model + 1], data[idx_gfs]); break; default: - add_string_fmt(parser, "Deco model", "Unknown model %d", data[67]); + add_string_fmt(parser, "Deco model", "Unknown model %d", data[idx_deco_model]); } } @@ -353,7 +385,8 @@ add_battery_type(shearwater_predator_parser_t *parser, const unsigned char *data if (parser->logversion < 7) return; - switch (data[120]) { + unsigned int idx_battery_type = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_4] + 9 : 120; + switch (data[idx_battery_type]) { case 1: add_string(parser, "Battery type", "1.5V Alkaline"); break; @@ -370,7 +403,7 @@ add_battery_type(shearwater_predator_parser_t *parser, const unsigned char *data add_string(parser, "Battery type", "3.7V Li-Ion"); break; default: - add_string_fmt(parser, "Battery type", "unknown type %d", data[120]); + add_string_fmt(parser, "Battery type", "unknown type %d", data[idx_battery_type]); break; } } @@ -386,24 +419,66 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) return DC_STATUS_SUCCESS; } + // the log formats are very similar - but the Petrel Native Format (PNF) + // is organized differently. There everything is in 32 byte (PNF_BLOCKSIZE) blocks + // and the offsets of various fields are different. It still seems to make sense + // to just all parse it in one place + + // header and footer are concepts of the Predator and Predator-like formats unsigned int headersize = SZ_BLOCK; unsigned int footersize = SZ_BLOCK; + if (size < headersize + footersize) { ERROR (abstract->context, "Invalid data length."); return DC_STATUS_DATAFORMAT; } + // remember if this is a Petrel Native Format download + // if yes, we need different ways to access the various data fields + // for samples it's simple, they are just offset by one (so we can use pnf as offset) + // for header and footer data it's more complicated because of the new block structure + unsigned int pnf = parser->pnf = data[0] == 0x10 ? 1 : 0; + + // sanity check on the log format + // is this a Predator-like or Petrel-native (or Teric style) log? + if (parser->petrel == 0 && pnf) { + ERROR (abstract->context, "This is a Petrel-native log, but we claim this is a Predator"); + return DC_STATUS_DATAFORMAT; + } + + memset (parser->block_offset, 0, NUM_BLOCK_IDS * sizeof(unsigned int)); + if (pnf) { + // find the offsets of the various header and footer blocks + int i = 0, j = 0; + while (i < size) { + for (j = LOG_RECORD_OPENING_0; j < NUM_BLOCK_IDS; j++) { + if (data[i] == j) + parser->block_offset[j] = i; + if (j == LOG_RECORD_OPENING_7) + j = LOG_RECORD_CLOSING_0 - 1; + } + i += PNF_BLOCKSIZE; + } + } + // there is a small risk we are taking here... if the log were damaged and one or + // more of the blocks were missing, we'll default to looking into block 0 and + // report bogus data. This may be worth testing for? + // Log versions before 6 weren't reliably stored in the data, but // 6 is also the oldest version that we assume in our code unsigned int logversion = 6; - if (data[127] > 6) + if (!pnf && data[127] > 6) logversion = data[127]; + if (pnf) + logversion = data[parser->block_offset[LOG_RECORD_OPENING_4] + 16]; + INFO(abstract->context, "Shearwater log version %u\n", logversion); memset(parser->strings, 0, sizeof(parser->strings)); + add_string_fmt(parser, "Logversion", "%d%s", logversion, pnf ? "(PNF)" : ""); // Adjust the footersize for the final block. - if (parser->petrel || array_uint16_be (data + size - footersize) == 0xFFFD) { + if (parser->petrel == 1 || array_uint16_be (data + size - footersize) == 0xFFFD) { footersize += SZ_BLOCK; if (size < headersize + footersize) { ERROR (abstract->context, "Invalid data length."); @@ -423,9 +498,16 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) // Transmitter battery levels unsigned int t1_battery = 0, t2_battery = 0; - unsigned int offset = headersize; - unsigned int length = size - footersize; + // the indices in the sample block are offset by 1 in PNF + unsigned int offset = pnf ? 0 : headersize; + unsigned int length = pnf ? size : size - footersize; + while (offset < length) { + // Ignore blocks that aren't dive samples + if (pnf && data[offset] != LOG_RECORD_DIVE_SAMPLE) { + offset += parser->samplesize; + continue; + } // Ignore empty samples. if (array_isequal (data + offset, parser->samplesize, 0x00)) { offset += parser->samplesize; @@ -433,14 +515,14 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) } // Status flags. - unsigned int status = data[offset + 11]; + unsigned int status = data[offset + 11 + pnf]; if ((status & OC) == 0) { mode = DC_DIVEMODE_CCR; } // Gaschange. - unsigned int o2 = data[offset + 7]; - unsigned int he = data[offset + 8]; + unsigned int o2 = data[offset + 7 + pnf]; + unsigned int he = data[offset + 8 + pnf]; if (o2 != o2_previous || he != he_previous) { // Find the gasmix in the list. unsigned int idx = 0; @@ -468,17 +550,24 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) // 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); + t1_battery |= battery_state(data + offset + 27 + pnf); + t2_battery |= battery_state(data + offset + 19 + pnf); } offset += parser->samplesize; } + // for header and footer indices we use a variable base that is set to the + // correct value based on the log type + unsigned int base = 0; + // Cache sensor calibration for later use unsigned int nsensors = 0, ndefaults = 0; + + // calibration value for sensors + base = pnf ? parser->block_offset[LOG_RECORD_OPENING_3] + 7 : 87; for (size_t i = 0; i < 3; ++i) { - unsigned int calibration = array_uint16_be(data + 87 + i * 2); + unsigned int calibration = array_uint16_be(data + base + i * 2); parser->calibration[i] = calibration / 100000.0; if (parser->model == PREDATOR) { // The Predator expects the mV output of the cells to be @@ -487,7 +576,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) // sensors lines up and matches the average. parser->calibration[i] *= 2.2; } - if (data[86] & (1 << i)) { + if (data[base - 1] & (1 << i)) { if (calibration == 2100) { ndefaults++; } @@ -505,7 +594,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) if (mode != DC_DIVEMODE_OC) add_string(parser, "PPO2 source", "voted/averaged"); } else { - parser->calibrated = data[86]; + parser->calibrated = data[base - 1]; if (mode != DC_DIVEMODE_OC) add_string(parser, "PPO2 source", "cells"); } @@ -521,6 +610,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) } parser->mode = mode; add_string_fmt(parser, "Serial", "%08x", parser->serial); + // bytes 1-31 are identical in all formats add_string_fmt(parser, "FW Version", "%2x", data[19]); add_deco_model(parser, data); add_battery_type(parser, data); @@ -556,16 +646,26 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ dc_field_string_t *string = (dc_field_string_t *) value; unsigned int density = 0; + // the first 32 bytes of the footer and closing block 0 are identical + unsigned int block_start = parser->pnf ? parser->block_offset[LOG_RECORD_CLOSING_0] : footer; if (value) { + unsigned int idx; switch (type) { case DC_FIELD_DIVETIME: - *((unsigned int *) value) = array_uint16_be (data + footer + 6) * 60; + // FIXME: this is wrong based on the documentation I received + // it should be a 3 byte value in offsets 6-8 that is dive length in seconds + *((unsigned int *) value) = array_uint16_be (data + block_start + 6) * 60; break; case DC_FIELD_MAXDEPTH: if (units == IMPERIAL) - *((double *) value) = array_uint16_be (data + footer + 4) * FEET; + *((double *) value) = array_uint16_be (data + block_start + 4) * FEET; else - *((double *) value) = array_uint16_be (data + footer + 4); + *((double *) value) = array_uint16_be (data + block_start + 4); + // according to the documentation this should have been in tenth of a meter + // before, but the existing code for the Predator-like format didn't have + // that adjustment, so let's just do that for PNF (where we definitely need it). + if (parser->pnf) + *((double *)value) /= 10.0; break; case DC_FIELD_GASMIX_COUNT: *((unsigned int *) value) = parser->ngasmixes; @@ -576,7 +676,8 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; break; case DC_FIELD_SALINITY: - density = array_uint16_be (data + 83); + idx = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_3] + 3 : 83; + density = array_uint16_be (data + idx); if (density == 1000) water->type = DC_WATER_FRESH; else @@ -584,6 +685,7 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ water->density = density; break; case DC_FIELD_ATMOSPHERIC: + idx = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_1] + 16 : 47; *((double *) value) = array_uint16_be (data + 47) / 1000.0; break; case DC_FIELD_DIVEMODE: @@ -627,12 +729,27 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal unsigned int o2_previous = 0, he_previous = 0; unsigned int time = 0; - unsigned int offset = parser->headersize; - unsigned int length = size - parser->footersize; + unsigned int pnf = parser->pnf; + unsigned int offset = pnf ? 0 : parser->headersize; + unsigned int length = pnf ? size : size - parser->footersize; + unsigned int time_increment = 10; + + // the time increment is now given in ms. not sure how we'll deal with that since all we do is full seconds + if (pnf && parser->logversion >= 9) + time_increment = array_uint16_be (data + parser->block_offset[LOG_RECORD_OPENING_5] + 23) / 1000; while (offset < length) { dc_sample_value_t sample = {0}; + // stop parsing if we see the end block + if (pnf && data[offset] == LOG_RECORD_FINAL && data[offset + 1] == 0xFD) + break; + + // Ignore blocks that aren't dive samples + if (pnf && data[offset] != LOG_RECORD_DIVE_SAMPLE) { + offset += parser->samplesize; + continue; + } // Ignore empty samples. if (array_isequal (data + offset, parser->samplesize, 0x00)) { offset += parser->samplesize; @@ -640,12 +757,12 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal } // Time (seconds). - time += 10; + time += time_increment; sample.time = time; if (callback) callback (DC_SAMPLE_TIME, sample, userdata); // Depth (1/10 m or ft). - unsigned int depth = array_uint16_be (data + offset); + unsigned int depth = array_uint16_be (data + pnf + offset); if (units == IMPERIAL) sample.depth = depth * FEET / 10.0; else @@ -653,7 +770,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); // Temperature (°C or °F). - int temperature = (signed char) data[offset + 13]; + int temperature = (signed char) data[offset + pnf + 13]; if (temperature < 0) { // Fix negative temperatures. temperature += 102; @@ -668,30 +785,31 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); // Status flags. - unsigned int status = data[offset + 11]; + unsigned int status = data[offset + pnf + 11]; if ((status & OC) == 0) { // PPO2 if ((status & PPO2_EXTERNAL) == 0) { if (!parser->calibrated) { - sample.ppo2 = data[offset + 6] / 100.0; + sample.ppo2 = data[offset + pnf + 6] / 100.0; if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); } else { - sample.ppo2 = data[offset + 12] * parser->calibration[0]; + sample.ppo2 = data[offset + pnf + 12] * parser->calibration[0]; if (callback && (parser->calibrated & 0x01)) callback (DC_SAMPLE_PPO2, sample, userdata); - sample.ppo2 = data[offset + 14] * parser->calibration[1]; + sample.ppo2 = data[offset + pnf + 14] * parser->calibration[1]; if (callback && (parser->calibrated & 0x02)) callback (DC_SAMPLE_PPO2, sample, userdata); - sample.ppo2 = data[offset + 15] * parser->calibration[2]; + sample.ppo2 = data[offset + pnf + 15] * parser->calibration[2]; if (callback && (parser->calibrated & 0x04)) callback (DC_SAMPLE_PPO2, sample, userdata); } } // Setpoint if (parser->petrel) { - sample.setpoint = data[offset + 18] / 100.0; + sample.setpoint = data[offset + pnf + 18] / 100.0; } else { + // this will only ever be called for the actual Predator, so no adjustment needed for PNF if (status & SETPOINT_HIGH) { sample.setpoint = data[18] / 100.0; } else { @@ -703,13 +821,13 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal // CNS if (parser->petrel) { - sample.cns = data[offset + 22] / 100.0; + sample.cns = data[offset + pnf + 22] / 100.0; if (callback) callback (DC_SAMPLE_CNS, sample, userdata); } // Gaschange. - unsigned int o2 = data[offset + 7]; - unsigned int he = data[offset + 8]; + unsigned int o2 = data[offset + pnf + 7]; + unsigned int he = data[offset + pnf + 8]; if (o2 != o2_previous || he != he_previous) { unsigned int idx = shearwater_predator_find_gasmix (parser, o2, he); if (idx >= parser->ngasmixes) { @@ -724,7 +842,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal } // Deco stop / NDL. - unsigned int decostop = array_uint16_be (data + offset + 2); + unsigned int decostop = array_uint16_be (data + offset + pnf + 2); if (decostop) { sample.deco.type = DC_DECO_DECOSTOP; if (units == IMPERIAL) @@ -735,7 +853,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal sample.deco.type = DC_DECO_NDL; sample.deco.depth = 0.0; } - sample.deco.time = data[offset + 9] * 60; + sample.deco.time = data[offset + pnf + 9] * 60; if (callback) callback (DC_SAMPLE_DECO, sample, userdata); // for logversion 7 and newer (introduced for Perdix AI) @@ -750,14 +868,14 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal // For regular values, the top 4 bits contain the battery // level (0=normal, 1=critical, 2=warning), and the lower 12 // bits the tank pressure in units of 2 psi. - unsigned int pressure = array_uint16_be (data + offset + 27); + unsigned int pressure = array_uint16_be (data + offset + pnf + 27); if (pressure < 0xFFF0) { pressure &= 0x0FFF; sample.pressure.tank = 0; sample.pressure.value = pressure * 2 * PSI / BAR; if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); } - pressure = array_uint16_be (data + offset + 19); + pressure = array_uint16_be (data + offset + pnf + 19); if (pressure < 0xFFF0) { pressure &= 0x0FFF; sample.pressure.tank = 1; @@ -772,8 +890,8 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal // 0xFD Not available in current mode // 0xFC Not available because of DECO // 0xFB Tank size or max pressure haven’t been set up - if (data[offset + 21] < 0xF0) { - sample.rbt = data[offset + 21]; + if (data[offset + pnf + 21] < 0xF0) { + sample.rbt = data[offset + pnf + 21]; if (callback) callback (DC_SAMPLE_RBT, sample, userdata); } } From 9379004b2df57c87ca414570766d5d8f2bb52594 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Sep 2018 17:14:55 -0700 Subject: [PATCH 06/10] Shearwater: add helper to send bytes to the dive computer This should allow us to gracefully shut down the communication with BLE devices. Signed-off-by: Dirk Hohndel --- src/shearwater_common.c | 22 +++++++++++++++++++++- src/shearwater_common.h | 3 +++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/shearwater_common.c b/src/shearwater_common.c index a7f67f6..fd58488 100644 --- a/src/shearwater_common.c +++ b/src/shearwater_common.c @@ -316,6 +316,27 @@ done: return status; } +dc_status_t +shearwater_common_command (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + if (isize > SZ_PACKET) + return DC_STATUS_INVALIDARGS; + + if (device_is_cancelled (abstract)) + return DC_STATUS_CANCELLED; + + // Send the command packet. + status = shearwater_common_slip_write (device, input, isize); + if (status != DC_STATUS_SUCCESS) + ERROR (abstract->context, "Failed to send the command packet."); + + return status; +} + + dc_status_t shearwater_common_transfer (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize, unsigned int *actual) @@ -379,7 +400,6 @@ shearwater_common_transfer (shearwater_common_device_t *device, const unsigned c return DC_STATUS_SUCCESS; } - dc_status_t shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buffer, unsigned int address, unsigned int size, unsigned int compression, dc_event_progress_t *progress) { diff --git a/src/shearwater_common.h b/src/shearwater_common.h index 73911a5..e73a53d 100644 --- a/src/shearwater_common.h +++ b/src/shearwater_common.h @@ -54,6 +54,9 @@ typedef struct shearwater_common_device_t { dc_status_t shearwater_common_setup (shearwater_common_device_t *device, dc_context_t *context, dc_iostream_t *iostream); +dc_status_t +shearwater_common_command (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize); + dc_status_t shearwater_common_transfer (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize, unsigned int *actual); From 437cc3e0cc9f88eedf27657c485cd55f23a4f2df Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Sep 2018 17:15:59 -0700 Subject: [PATCH 07/10] Shearwater: try to gracefully shut down the Bluetooth connection Signed-off-by: Dirk Hohndel --- src/shearwater_petrel.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index 4b5eca9..67a3bbc 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -387,6 +387,11 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call offset += RECORD_SIZE; } + // send the "graceful exit" instruction + // I don't think we care about the return value of this call - this is just trying to be nice to the device + unsigned char command[4] = { 0x2e, 0x90, 0x20, 0x00 }; + rc = shearwater_common_command(&device->base, command, 4); + DEBUG(abstract->context, "Sent graceful exit command, rc=%d", rc); // Update and emit a progress event. progress.current = NSTEPS * current; From d0a3336c82ab7e5ed6e41a4c5d1a7d514b2676bc Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Sep 2018 17:17:47 -0700 Subject: [PATCH 08/10] Shearwater: add Teric to list of supported dive computers Signed-off-by: Dirk Hohndel --- src/descriptor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/descriptor.c b/src/descriptor.c index cc0d4bd..c40fb60 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -292,6 +292,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_shearwater}, {"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6, DC_TRANSPORT_BLE, dc_filter_shearwater}, {"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7, DC_TRANSPORT_BLE, dc_filter_shearwater}, + {"Shearwater", "Teric", DC_FAMILY_SHEARWATER_PETREL, 8, DC_TRANSPORT_BLE, dc_filter_shearwater}, /* Dive Rite NiTek Q */ {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, DC_TRANSPORT_SERIAL, NULL}, /* Citizen Hyper Aqualand */ From ee7c14ecc3a3a2486f77d2098ded6638b4c969a9 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 8 Sep 2018 18:44:00 -0700 Subject: [PATCH 09/10] Shearwater: report error when parsing freedive Support for the 8 byte freedive samples has yet to be added. For now bail out. Signed-off-by: Dirk Hohndel --- src/shearwater_predator_parser.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 86f2310..6c2ac18 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -486,6 +486,14 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser) } } + // if this is logversion 9 or higher, make sure this isn't a freedive, as we can't parse that + if (logversion > 9 && pnf) { + if (data[parser->block_offset[LOG_RECORD_OPENING_5] + 25] == LOG_RECORD_FREEDIVE_SAMPLE) { + ERROR (abstract->context, "Cannot parse freedive samples"); + return DC_STATUS_DATAFORMAT; + } + } + // Default dive mode. dc_divemode_t mode = DC_DIVEMODE_OC; From 348387c6f68c6299facd97aa6d76fe6b6a749e4a Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 11 Sep 2018 21:30:45 -0700 Subject: [PATCH 10/10] Shearwater PNF support: fall back to default logbook style On Android we appear to mis-interpret the response to the RDBI command. Instead of failing, fall back to the default value (PNF for Teric, Predator-like for everything else). With this I can successfully download dive data from my Teric on Android. Signed-off-by: Dirk Hohndel --- src/shearwater_petrel.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index 67a3bbc..f71a89f 100644 --- a/src/shearwater_petrel.c +++ b/src/shearwater_petrel.c @@ -263,25 +263,27 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call return rc; } unsigned int base_addr = array_uint_be (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer)); + INFO(abstract->context, "RDBI command completed with %d bytes, evaluated as %08x", dc_buffer_get_size (buffer), base_addr); base_addr &= 0xFF000000u; switch (base_addr) { case 0xDD000000: // Predator or Predator-Like Format // on a Predator, use the old format, otherwise use the Predator-Like Format (what we called Petrel so far) if (model != PREDATOR) - base_addr = 0xC0000000; + base_addr = 0xC0000000u; break; case 0x90000000: // some firmware versions supported an earlier version of PNF without final record // use the Predator-Like Format instead - base_addr = 0xC0000000; + base_addr = 0xC0000000u; break; case 0x80000000: // new Petrel Native Format with final record // that's the correct address break; default: // unknown format - ERROR (abstract->context, "Unknown logbook format %08x", base_addr); - dc_buffer_free (buffer); - dc_buffer_free (manifests); - return DC_STATUS_UNSUPPORTED; + // use the defaults for the models + if (model >= TERIC) + base_addr = 0x80000000u; + else + base_addr = 0xC0000000u; } // Read the manifest pages