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 */ 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 767372f..e73a53d 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 @@ -40,6 +41,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)) @@ -52,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); diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c index b316da8..8f9f68d 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 @@ -240,6 +239,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); @@ -252,6 +254,39 @@ 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)); + INFO(abstract->context, "RDBI command completed with %d bytes, evaluated as %08x", (int) 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 = 0xC0000000u; + break; + case 0x90000000: // some firmware versions supported an earlier version of PNF without final record + // use the Predator-Like Format instead + base_addr = 0xC0000000u; + break; + case 0x80000000: // new Petrel Native Format with final record + // that's the correct address + break; + default: // unknown format + // use the defaults for the models + if (model >= TERIC) + base_addr = 0x80000000u; + else + base_addr = 0xC0000000u; + } + + // Read the manifest pages while (1) { // Update the progress state. // Assume the worst case scenario of a full manifest, and adjust the @@ -275,11 +310,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 +334,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 +345,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,13 +360,18 @@ 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); // 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); @@ -343,6 +389,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; diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 46f0ff0..6c2ac18 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."); @@ -411,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; @@ -423,9 +506,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 +523,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 +558,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 +584,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 +602,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 +618,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 +654,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 +684,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 +693,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 +737,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 +765,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 +778,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 +793,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 +829,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 +850,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 +861,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 +876,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 +898,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); } }