#include <stdlib.h>
#include <string.h>
#include <libdivecomputer/units.h>
#include "shearwater_predator.h"
#include "shearwater_petrel.h"
#include "context-private.h"
#include "parser-private.h"
#include "array.h"
#define ISINSTANCE(parser) ( \
dc_parser_isinstance((parser), &shearwater_predator_parser_vtable) || \
dc_parser_isinstance((parser), &shearwater_petrel_parser_vtable))
#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_INFO_EVENT 0x30
#define LOG_RECORD_DIVE_SAMPLE_EXT 0xE1
#define LOG_RECORD_FINAL 0xFF
#define INFO_EVENT_TAG_LOG 38
#define SZ_BLOCK 0x80
#define SZ_SAMPLE_PREDATOR 0x10
#define SZ_SAMPLE_PETREL 0x20
#define SZ_SAMPLE_FREEDIVE 0x08
#define GASSWITCH 0x01
#define PPO2_EXTERNAL 0x02
#define SETPOINT_HIGH 0x04
#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 AI_OFF 0
#define AI_HPCCR 4
#define AI_ON 5
#define GF 0
#define VPMB 1
#define VPMB_GFS 2
#define DCIEM 3
#define METRIC 0
#define IMPERIAL 1
#define NGASMIXES 20
#define NFIXED 10
#define NTANKS 6
#define NRECORDS 8
#define PREDATOR 2
#define PETREL 3
#define TERIC 8
#define UNDEFINED 0xFFFFFFFF
typedef struct shearwater_predator_parser_t shearwater_predator_parser_t;
typedef struct shearwater_predator_gasmix_t {
unsigned int oxygen;
unsigned int helium;
unsigned int diluent;
} 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 {
dc_parser_t base;
unsigned int model;
unsigned int petrel;
unsigned int samplesize;
unsigned int cached;
unsigned int pnf;
unsigned int logversion;
unsigned int headersize;
unsigned int footersize;
unsigned int opening[NRECORDS];
unsigned int closing[NRECORDS];
unsigned int final;
unsigned int ngasmixes;
unsigned int ntanks;
shearwater_predator_gasmix_t gasmix[NGASMIXES];
shearwater_predator_tank_t tank[NTANKS];
unsigned int tankidx[NTANKS];
unsigned int aimode;
unsigned int calibrated;
double calibration[3];
unsigned int divemode;
unsigned int units;
unsigned int atmospheric;
unsigned int density;
};
static dc_status_t shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
static dc_status_t shearwater_predator_parser_cache (shearwater_predator_parser_t *parser);
static const dc_parser_vtable_t shearwater_predator_parser_vtable = {
sizeof(shearwater_predator_parser_t),
DC_FAMILY_SHEARWATER_PREDATOR,
shearwater_predator_parser_set_data,
NULL,
NULL,
NULL,
shearwater_predator_parser_get_datetime,
shearwater_predator_parser_get_field,
shearwater_predator_parser_samples_foreach,
NULL
};
static const dc_parser_vtable_t shearwater_petrel_parser_vtable = {
sizeof(shearwater_predator_parser_t),
DC_FAMILY_SHEARWATER_PETREL,
shearwater_predator_parser_set_data,
NULL,
NULL,
NULL,
shearwater_predator_parser_get_datetime,
shearwater_predator_parser_get_field,
shearwater_predator_parser_samples_foreach,
NULL
};
static unsigned int
shearwater_predator_find_gasmix (shearwater_predator_parser_t *parser, unsigned int o2, unsigned int he, unsigned int dil)
{
unsigned int i = 0;
while (i < parser->ngasmixes) {
if (o2 == parser->gasmix[i].oxygen && he == parser->gasmix[i].helium && dil == parser->gasmix[i].diluent)
break;
i++;
}
return i;
}
static dc_status_t
shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int petrel)
{
shearwater_predator_parser_t *parser = NULL;
const dc_parser_vtable_t *vtable = NULL;
unsigned int samplesize = 0;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
if (petrel) {
vtable = &shearwater_petrel_parser_vtable;
samplesize = SZ_SAMPLE_PETREL;
} else {
vtable = &shearwater_predator_parser_vtable;
samplesize = SZ_SAMPLE_PREDATOR;
}
parser = (shearwater_predator_parser_t *) dc_parser_allocate (context, vtable);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
parser->model = model;
parser->petrel = petrel;
parser->samplesize = samplesize;
parser->cached = 0;
parser->pnf = 0;
parser->logversion = 0;
parser->headersize = 0;
parser->footersize = 0;
for (unsigned int i = 0; i < NRECORDS; ++i) {
parser->opening[i] = UNDEFINED;
parser->closing[i] = UNDEFINED;
}
parser->final = UNDEFINED;
parser->ngasmixes = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->gasmix[i].oxygen = 0;
parser->gasmix[i].helium = 0;
parser->gasmix[i].diluent = 0;
}
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->aimode = AI_OFF;
parser->calibrated = 0;
for (unsigned int i = 0; i < 3; ++i) {
parser->calibration[i] = 0.0;
}
parser->divemode = M_OC_TEC;
parser->units = METRIC;
parser->density = DEF_DENSITY_SALT;
parser->atmospheric = DEF_ATMOSPHERIC / (BAR / 1000);
*out = (dc_parser_t *) parser;
return DC_STATUS_SUCCESS;
}
dc_status_t
shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
{
return shearwater_common_parser_create (out, context, model, 0);
}
dc_status_t
shearwater_petrel_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
{
return shearwater_common_parser_create (out, context, model, 1);
}
static dc_status_t
shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract;
parser->cached = 0;
parser->pnf = 0;
parser->logversion = 0;
parser->headersize = 0;
parser->footersize = 0;
for (unsigned int i = 0; i < NRECORDS; ++i) {
parser->opening[i] = UNDEFINED;
parser->closing[i] = UNDEFINED;
}
parser->final = UNDEFINED;
parser->ngasmixes = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->gasmix[i].oxygen = 0;
parser->gasmix[i].helium = 0;
parser->gasmix[i].diluent = 0;
}
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->aimode = AI_OFF;
parser->calibrated = 0;
for (unsigned int i = 0; i < 3; ++i) {
parser->calibration[i] = 0.0;
}
parser->divemode = M_OC_TEC;
parser->units = METRIC;
parser->density = DEF_DENSITY_SALT;
parser->atmospheric = DEF_ATMOSPHERIC / (BAR / 1000);
return DC_STATUS_SUCCESS;
}
static dc_status_t
shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract;
const unsigned char *data = abstract->data;
dc_status_t rc = shearwater_predator_parser_cache (parser);
if (rc != DC_STATUS_SUCCESS)
return rc;
unsigned int ticks = array_uint32_be (data + parser->opening[0] + 12);
if (!dc_datetime_gmtime (datetime, ticks))
return DC_STATUS_DATAFORMAT;
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;
}
static dc_status_t
shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
{
dc_parser_t *abstract = (dc_parser_t *) parser;
const unsigned char *data = parser->base.data;
unsigned int size = parser->base.size;
if (parser->cached) {
return DC_STATUS_SUCCESS;
}
unsigned int logversion = 0;
if (size < 2) {
ERROR (abstract->context, "Invalid data length.");
return DC_STATUS_DATAFORMAT;
}
unsigned int pnf = parser->petrel ? array_uint16_be (data) != 0xFFFF : 0;
unsigned int headersize = 0;
unsigned int footersize = 0;
if (!pnf) {
headersize = SZ_BLOCK;
footersize = SZ_BLOCK;
if (size < headersize + footersize) {
ERROR (abstract->context, "Invalid data length.");
return DC_STATUS_DATAFORMAT;
}
if (parser->petrel || array_uint16_be (data + size - footersize) == 0xFFFD) {
footersize += SZ_BLOCK;
if (size < headersize + footersize) {
ERROR (abstract->context, "Invalid data length.");
return DC_STATUS_DATAFORMAT;
}
parser->final = size - SZ_BLOCK;
}
for (unsigned int i = 0; i <= 4; ++i) {
parser->opening[i] = 0;
parser->closing[i] = size - footersize;
}
logversion = data[127];
}
unsigned int divemode = M_OC_TEC;
unsigned int ngasmixes = NFIXED;
shearwater_predator_gasmix_t gasmix[NGASMIXES] = {0};
shearwater_predator_tank_t tank[NTANKS] = {0};
unsigned int o2_previous = UNDEFINED, he_previous = UNDEFINED, dil_previous = UNDEFINED;
unsigned int aimode = AI_OFF;
if (!pnf) {
for (unsigned int i = 0; i < NFIXED; ++i) {
gasmix[i].oxygen = data[20 + i];
gasmix[i].helium = data[30 + i];
gasmix[i].diluent = i >= 5;
}
}
unsigned int offset = headersize;
unsigned int length = size - footersize;
while (offset + parser->samplesize <= length) {
if (array_isequal (data + offset, parser->samplesize, 0x00)) {
offset += parser->samplesize;
continue;
}
unsigned int type = pnf ? data[offset] : LOG_RECORD_DIVE_SAMPLE;
if (type == LOG_RECORD_DIVE_SAMPLE) {
unsigned int status = data[offset + 11 + pnf];
unsigned int ccr = (status & OC) == 0;
if (ccr) {
divemode = status & SC ? M_SC : M_CC;
}
unsigned int o2 = data[offset + 7 + pnf];
unsigned int he = data[offset + 8 + pnf];
if ((o2 != o2_previous || he != he_previous || ccr != dil_previous) &&
(o2 != 0 || he != 0)) {
unsigned int idx = 0;
while (idx < ngasmixes) {
if (o2 == gasmix[idx].oxygen && he == gasmix[idx].helium && ccr == gasmix[idx].diluent)
break;
idx++;
}
if (idx >= ngasmixes) {
if (idx >= NGASMIXES) {
ERROR (abstract->context, "Maximum number of gas mixes reached.");
return DC_STATUS_NOMEMORY;
}
gasmix[idx].oxygen = o2;
gasmix[idx].helium = he;
gasmix[idx].diluent = ccr;
ngasmixes = idx + 1;
}
o2_previous = o2;
he_previous = he;
dil_previous = ccr;
}
if (logversion >= 7) {
const unsigned int idx[2] = {27, 19};
for (unsigned int i = 0; i < 2; ++i) {
unsigned int pressure = array_uint16_be (data + offset + pnf + idx[i]);
unsigned int id = (aimode == AI_HPCCR ? 4 : 0) + i;
if (pressure < 0xFFF0) {
pressure &= 0x0FFF;
if (!tank[id].active) {
tank[id].active = 1;
tank[id].beginpressure = pressure;
tank[id].endpressure = pressure;
}
tank[id].endpressure = pressure;
}
}
}
} else if (type == LOG_RECORD_DIVE_SAMPLE_EXT) {
if (logversion >= 13) {
for (unsigned int i = 0; i < 2; ++i) {
unsigned int pressure = array_uint16_be (data + offset + pnf + i * 2);
unsigned int id = 2 + i;
if (pressure < 0xFFF0) {
pressure &= 0x0FFF;
if (!tank[id].active) {
tank[id].active = 1;
tank[id].beginpressure = pressure;
tank[id].endpressure = pressure;
}
tank[id].endpressure = pressure;
}
}
}
if (logversion >= 14) {
for (unsigned int i = 0; i < 2; ++i) {
unsigned int pressure = array_uint16_be (data + offset + pnf + 4 + i * 2);
unsigned int id = 4 + i;
if (pressure) {
if (!tank[id].active) {
tank[id].active = 1;
tank[id].enabled = 1;
tank[id].beginpressure = pressure;
tank[id].endpressure = pressure;
tank[id].name[0] = i == 0 ? 'D': 'O';
tank[id].name[1] = 0;
}
tank[id].endpressure = pressure;
}
}
}
} else if (type == LOG_RECORD_FREEDIVE_SAMPLE) {
divemode = M_FREEDIVE;
} else if (type >= LOG_RECORD_OPENING_0 && type <= LOG_RECORD_OPENING_7) {
parser->opening[type - LOG_RECORD_OPENING_0] = offset;
if (type == LOG_RECORD_OPENING_0) {
for (unsigned int i = 0; i < NFIXED; ++i) {
gasmix[i].oxygen = data[offset + 20 + i];
gasmix[i].diluent = i >= 5;
}
for (unsigned int i = 0; i < 2; ++i) {
gasmix[i].helium = data[offset + 30 + i];
}
} else if (type == LOG_RECORD_OPENING_1) {
for (unsigned int i = 2; i < NFIXED; ++i) {
gasmix[i].helium = data[offset + 1 + i - 2];
}
} else if (type == LOG_RECORD_OPENING_4) {
logversion = data[offset + 16];
if (logversion >= 7) {
aimode = data[offset + 28];
if (logversion < 13) {
if (aimode == 1 || aimode == 2) {
tank[aimode - 1].enabled = 1;
} else if (aimode == 3) {
tank[0].enabled = 1;
tank[1].enabled = 1;
}
}
if (logversion < 14) {
if (aimode == AI_HPCCR) {
for (unsigned int i = 0; i < 2; ++i) {
tank[4 + i].enabled = 1;
tank[4 + i].name[0] = i == 0 ? 'D': 'O';
tank[4 + i].name[1] = 0;
}
}
}
}
} 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) {
parser->closing[type - LOG_RECORD_CLOSING_0] = offset;
} else if (type == LOG_RECORD_FINAL) {
parser->final = offset;
}
offset += parser->samplesize;
}
for (unsigned int i = 0; i <= 4; ++i) {
if (parser->opening[i] == UNDEFINED || parser->closing[i] == UNDEFINED) {
ERROR (abstract->context, "Opening or closing record %u not found.", i);
return DC_STATUS_DATAFORMAT;
}
}
unsigned int nsensors = 0, ndefaults = 0;
unsigned int base = parser->opening[3] + (pnf ? 6 : 86);
for (size_t i = 0; i < 3; ++i) {
unsigned int calibration = array_uint16_be(data + base + 1 + i * 2);
parser->calibration[i] = calibration / 100000.0;
if (parser->model == PREDATOR) {
parser->calibration[i] *= 2.2;
}
if (data[base] & (1 << i)) {
if (calibration == 2100) {
ndefaults++;
}
nsensors++;
}
}
if (nsensors && nsensors == ndefaults) {
WARNING (abstract->context, "Disabled all O2 sensors due to a default calibration value.");
parser->calibrated = 0;
} else {
parser->calibrated = data[base];
}
if (logversion >= 8) {
divemode = data[parser->opening[4] + (pnf ? 1 : 112)];
}
if (parser->final != UNDEFINED) {
parser->model = data[parser->final + 13];
}
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;
}
}
parser->pnf = pnf;
parser->logversion = logversion;
parser->headersize = headersize;
parser->footersize = footersize;
parser->ngasmixes = 0;
if (divemode != M_FREEDIVE) {
for (unsigned int i = 0; i < ngasmixes; ++i) {
if (gasmix[i].oxygen == 0 && gasmix[i].helium == 0)
continue;
parser->gasmix[parser->ngasmixes] = gasmix[i];
parser->ngasmixes++;
}
}
parser->ntanks = 0;
for (unsigned int i = 0; i < NTANKS; ++i) {
if (tank[i].active) {
parser->tankidx[i] = parser->ntanks;
parser->tank[parser->ntanks] = tank[i];
parser->ntanks++;
} else {
parser->tankidx[i] = UNDEFINED;
}
}
parser->aimode = aimode;
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));
parser->cached = 1;
return DC_STATUS_SUCCESS;
}
static dc_status_t
shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
{
shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract;
const unsigned char *data = abstract->data;
dc_status_t rc = shearwater_predator_parser_cache (parser);
if (rc != DC_STATUS_SUCCESS)
return rc;
unsigned int decomodel_idx = parser->pnf ? parser->opening[2] + 18 : 67;
unsigned int gf_idx = parser->pnf ? parser->opening[0] + 4 : 4;
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
dc_tank_t *tank = (dc_tank_t *) value;
dc_salinity_t *water = (dc_salinity_t *) value;
dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
if (value) {
switch (type) {
case DC_FIELD_DIVETIME:
if (parser->pnf)
*((unsigned int *) value) = array_uint24_be (data + parser->closing[0] + 6);
else
*((unsigned int *) value) = array_uint16_be (data + parser->closing[0] + 6) * 60;
break;
case DC_FIELD_MAXDEPTH:
if (parser->units == IMPERIAL)
*((double *) value) = array_uint16_be (data + parser->closing[0] + 4) * FEET;
else
*((double *) value) = array_uint16_be (data + parser->closing[0] + 4);
if (parser->pnf)
*((double *)value) /= 10.0;
break;
case DC_FIELD_GASMIX_COUNT:
*((unsigned int *) value) = parser->ngasmixes;
break;
case DC_FIELD_GASMIX:
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
case DC_FIELD_TANK_COUNT:
*((unsigned int *) value) = parser->ntanks;
break;
case DC_FIELD_TANK:
tank->type = DC_TANKVOLUME_NONE;
tank->volume = 0.0;
tank->workpressure = 0.0;
tank->beginpressure = parser->tank[flags].beginpressure * 2 * PSI / BAR;
tank->endpressure = parser->tank[flags].endpressure * 2 * PSI / BAR;
tank->gasmix = DC_GASMIX_UNKNOWN;
break;
case DC_FIELD_SALINITY:
if (parser->density == 1000)
water->type = DC_WATER_FRESH;
else
water->type = DC_WATER_SALT;
water->density = parser->density;
break;
case DC_FIELD_ATMOSPHERIC:
*((double *) value) = parser->atmospheric / 1000.0;
break;
case DC_FIELD_DIVEMODE:
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;
case DC_FIELD_DECOMODEL:
switch (data[decomodel_idx]) {
case GF:
decomodel->type = DC_DECOMODEL_BUHLMANN;
decomodel->conservatism = 0;
decomodel->params.gf.low = data[gf_idx + 0];
decomodel->params.gf.high = data[gf_idx + 1];
break;
case VPMB:
case VPMB_GFS:
decomodel->type = DC_DECOMODEL_VPM;
decomodel->conservatism = data[decomodel_idx + 1];
break;
case DCIEM:
decomodel->type = DC_DECOMODEL_DCIEM;
decomodel->conservatism = 0;
break;
default:
return DC_STATUS_DATAFORMAT;
}
break;
default:
return DC_STATUS_UNSUPPORTED;
}
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract;
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
dc_status_t rc = shearwater_predator_parser_cache (parser);
if (rc != DC_STATUS_SUCCESS)
return rc;
unsigned int o2_previous = UNDEFINED, he_previous = UNDEFINED, dil_previous = UNDEFINED;
unsigned int time = 0;
unsigned int interval = 10;
if (parser->pnf && parser->logversion >= 9 && parser->opening[5] != UNDEFINED) {
interval = array_uint16_be (data + parser->opening[5] + 23);
if (interval % 1000 != 0) {
ERROR (abstract->context, "Unsupported sample interval (%u ms).", interval);
return DC_STATUS_DATAFORMAT;
}
interval /= 1000;
}
unsigned int pnf = parser->pnf;
unsigned int offset = parser->headersize;
unsigned int length = size - parser->footersize;
while (offset + parser->samplesize <= length) {
dc_sample_value_t sample = {0};
if (array_isequal (data + offset, parser->samplesize, 0x00)) {
offset += parser->samplesize;
continue;
}
unsigned int type = pnf ? data[offset] : LOG_RECORD_DIVE_SAMPLE;
if (type == LOG_RECORD_DIVE_SAMPLE) {
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
unsigned int depth = array_uint16_be (data + pnf + offset);
if (parser->units == IMPERIAL)
sample.depth = depth * FEET / 10.0;
else
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
int temperature = (signed char) data[offset + pnf + 13];
if (temperature < 0) {
temperature += 102;
if (temperature > 0) {
temperature = 0;
}
}
if (parser->units == IMPERIAL)
sample.temperature = (temperature - 32.0) * (5.0 / 9.0);
else
sample.temperature = temperature;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
unsigned int status = data[offset + pnf + 11];
unsigned int ccr = (status & OC) == 0;
if (ccr) {
if ((status & PPO2_EXTERNAL) == 0) {
#ifdef SENSOR_AVERAGE
sample.ppo2 = data[offset + pnf + 6] / 100.0;
if (callback) callback (DC_SAMPLE_PPO2, sample, userdata);
#else
sample.ppo2 = data[offset + pnf + 12] * parser->calibration[0];
if (callback && (parser->calibrated & 0x01)) callback (DC_SAMPLE_PPO2, sample, userdata);
sample.ppo2 = data[offset + pnf + 14] * parser->calibration[1];
if (callback && (parser->calibrated & 0x02)) callback (DC_SAMPLE_PPO2, sample, userdata);
sample.ppo2 = data[offset + pnf + 15] * parser->calibration[2];
if (callback && (parser->calibrated & 0x04)) callback (DC_SAMPLE_PPO2, sample, userdata);
#endif
}
if (parser->petrel) {
sample.setpoint = data[offset + pnf + 18] / 100.0;
} else {
if (status & SETPOINT_HIGH) {
sample.setpoint = data[18] / 100.0;
} else {
sample.setpoint = data[17] / 100.0;
}
}
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata);
}
if (parser->petrel) {
sample.cns = data[offset + pnf + 22] / 100.0;
if (callback) callback (DC_SAMPLE_CNS, sample, userdata);
}
unsigned int o2 = data[offset + pnf + 7];
unsigned int he = data[offset + pnf + 8];
if ((o2 != o2_previous || he != he_previous || ccr != dil_previous) &&
(o2 != 0 || he != 0)) {
unsigned int idx = shearwater_predator_find_gasmix (parser, o2, he, ccr);
if (idx >= parser->ngasmixes) {
ERROR (abstract->context, "Invalid gas mix.");
return DC_STATUS_DATAFORMAT;
}
sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
o2_previous = o2;
he_previous = he;
dil_previous = ccr;
}
unsigned int decostop = array_uint16_be (data + offset + pnf + 2);
if (decostop) {
sample.deco.type = DC_DECO_DECOSTOP;
if (parser->units == IMPERIAL)
sample.deco.depth = decostop * FEET;
else
sample.deco.depth = decostop;
} else {
sample.deco.type = DC_DECO_NDL;
sample.deco.depth = 0.0;
}
sample.deco.time = data[offset + pnf + 9] * 60;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
if (parser->logversion >= 7) {
const unsigned int idx[2] = {27, 19};
for (unsigned int i = 0; i < 2; ++i) {
unsigned int pressure = array_uint16_be (data + offset + pnf + idx[i]);
unsigned int id = (parser->aimode == AI_HPCCR ? 4 : 0) + i;
if (pressure < 0xFFF0) {
pressure &= 0x0FFF;
sample.pressure.tank = parser->tankidx[id];
sample.pressure.value = pressure * 2 * PSI / BAR;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
}
}
if (data[offset + pnf + 21] < 0xF0) {
sample.rbt = data[offset + pnf + 21];
if (callback) callback (DC_SAMPLE_RBT, sample, userdata);
}
}
} else if (type == LOG_RECORD_DIVE_SAMPLE_EXT) {
if (parser->logversion >= 13) {
for (unsigned int i = 0; i < 2; ++i) {
unsigned int pressure = array_uint16_be (data + offset + pnf + i * 2);
unsigned int id = 2 + i;
if (pressure < 0xFFF0) {
pressure &= 0x0FFF;
sample.pressure.tank = parser->tankidx[id];
sample.pressure.value = pressure * 2 * PSI / BAR;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
}
}
}
if (parser->logversion >= 14) {
for (unsigned int i = 0; i < 2; ++i) {
unsigned int pressure = array_uint16_be (data + offset + pnf + 4 + i * 2);
unsigned int id = 4 + i;
if (pressure) {
sample.pressure.tank = parser->tankidx[id];
sample.pressure.value = pressure * 2 * PSI / BAR;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
}
}
}
} else if (type == LOG_RECORD_FREEDIVE_SAMPLE) {
for (unsigned int i = 0; i < 4; ++i) {
unsigned int idx = offset + i * SZ_SAMPLE_FREEDIVE;
if (array_isequal (data + idx, SZ_SAMPLE_FREEDIVE, 0x00)) {
break;
}
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
unsigned int depth = array_uint16_be (data + idx + 1);
sample.depth = (signed int)(depth - parser->atmospheric) * (BAR / 1000.0) / (parser->density * GRAVITY);
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
int temperature = (signed short) array_uint16_be (data + idx + 3);
sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
}
} else if (type == LOG_RECORD_INFO_EVENT) {
unsigned int event = data[offset + 1];
unsigned int timestamp = array_uint32_be (data + offset + 4);
unsigned int w1 = array_uint32_be (data + offset + 8);
unsigned int w2 = array_uint32_be (data + offset + 12);
if (event == INFO_EVENT_TAG_LOG) {
if (w1 != 0xFFFFFFFF) {
sample.bearing = w1;
if (callback) callback (DC_SAMPLE_BEARING, sample, userdata);
}
sample.event.type = SAMPLE_EVENT_BOOKMARK;
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = w2;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
}
}
offset += parser->samplesize;
}
return DC_STATUS_SUCCESS;
}