Merge branch 'teric' of git://github.com/Subsurface-divelog/libdc into Subsurface-NG

Merge Dirk's Shearwater Teric branch.

The Teric only supports Shearwater's new download format (PFN - Petrel
Native Format), and the old legacy format no longer works.

I'm sure there are issues, and Dirk just ended up ignoring an
unexplained format difference on Android, but without this you get
nothing.

[ Fixed up a format warning in the merge commit  - Linus ]

* 'teric' of git://github.com/Subsurface-divelog/libdc:
  Shearwater PNF support: fall back to default logbook style
  Shearwater: report error when parsing freedive
  Shearwater: add Teric to list of supported dive computers
  Shearwater: try to gracefully shut down the Bluetooth connection
  Shearwater: add helper to send bytes to the dive computer
  Shearwater Petrel Native Format parsing
  Shearwater: use the correct address to download dives
  Shearwater: detect which logbook format is support
  Detect Sherwater Teric
  Shearwater: skip deleted dives
This commit is contained in:
Linus Torvalds 2018-09-12 09:12:10 -10:00
commit 8f2ac8f61e
5 changed files with 251 additions and 48 deletions

View File

@ -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 */

View File

@ -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)
{

View File

@ -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);

View File

@ -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;

View File

@ -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 havent 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);
}
}