Add support for Cressi Goa gauge and freedives

The gauge and freedives have a slightly different data format compared
to scuba dives. The main difference is the size of the header and two
new sample types.
This commit is contained in:
Jef Driesen 2019-11-24 08:24:36 +01:00
parent 57cd11fffe
commit c77a311a89

View File

@ -28,9 +28,16 @@
#define ISINSTANCE(parser) dc_device_isinstance((parser), &cressi_goa_parser_vtable)
#define SZ_HEADER 0x61
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
#define SZ_HEADER 23
#define SZ_HEADER_SCUBA 0x61
#define SZ_HEADER_FREEDIVE 0x2B
#define SZ_HEADER_GAUGE 0x2D
#define DEPTH 0
#define DEPTH2 1
#define TIME 2
#define TEMPERATURE 3
#define SCUBA 0
@ -63,6 +70,13 @@ static const dc_parser_vtable_t cressi_goa_parser_vtable = {
NULL /* destroy */
};
static const unsigned int headersizes[] = {
SZ_HEADER_SCUBA,
SZ_HEADER_SCUBA,
SZ_HEADER_FREEDIVE,
SZ_HEADER_GAUGE,
};
dc_status_t
cressi_goa_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
{
@ -102,7 +116,19 @@ cressi_goa_parser_set_data (dc_parser_t *abstract, const unsigned char *data, un
static dc_status_t
cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
if (abstract->size < SZ_HEADER)
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
if (size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
unsigned int divemode = data[2];
if (divemode >= C_ARRAY_SIZE(headersizes)) {
return DC_STATUS_DATAFORMAT;
}
unsigned int headersize = headersizes[divemode];
if (size < headersize)
return DC_STATUS_DATAFORMAT;
const unsigned char *p = abstract->data + 0x11;
@ -125,11 +151,19 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign
{
cressi_goa_parser_t *parser = (cressi_goa_parser_t *) abstract;
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
if (abstract->size < SZ_HEADER)
if (size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
unsigned int divemode = data[2];
if (divemode >= C_ARRAY_SIZE(headersizes)) {
return DC_STATUS_DATAFORMAT;
}
unsigned int headersize = headersizes[divemode];
if (size < headersize)
return DC_STATUS_DATAFORMAT;
if (!parser->cached) {
sample_statistics_t statistics = SAMPLE_STATISTICS_INITIALIZER;
@ -153,7 +187,7 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign
*((double *) value) = parser->maxdepth;
break;
case DC_FIELD_GASMIX_COUNT:
*((unsigned int *) value) = 2;
*((unsigned int *) value) = divemode == SCUBA || divemode == NITROX ? 2 : 0;
break;
case DC_FIELD_GASMIX:
gasmix->helium = 0.0;
@ -190,12 +224,28 @@ cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
unsigned int time = 0;
unsigned int interval = 5;
unsigned int complete = 1;
unsigned int gasmix_previous = 0xFFFFFFFF;
if (size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
unsigned int offset = SZ_HEADER;
unsigned int divemode = data[2];
if (divemode >= C_ARRAY_SIZE(headersizes)) {
return DC_STATUS_DATAFORMAT;
}
unsigned int headersize = headersizes[divemode];
if (size < headersize)
return DC_STATUS_DATAFORMAT;
unsigned int interval = divemode == FREEDIVE ? 2 : 5;
unsigned int time = 0;
unsigned int depth = 0;
unsigned int gasmix = 0, gasmix_previous = 0xFFFFFFFF;
unsigned int temperature = 0;
unsigned int have_temperature = 0;
unsigned int complete = 0;
unsigned int offset = headersize;
while (offset + 2 <= size) {
dc_sample_value_t sample = {0};
@ -204,35 +254,44 @@ cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
unsigned int type = (raw & 0x0003);
unsigned int value = (raw & 0xFFFC) >> 2;
if (complete) {
// Time (seconds).
if (type == DEPTH || type == DEPTH2) {
depth = (value & 0x07FF);
gasmix = (value & 0x0800) >> 11;
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
complete = 0;
complete = 1;
} else if (type == TEMPERATURE) {
temperature = value;
have_temperature = 1;
} else if (type == TIME) {
time += value;
}
if (type == DEPTH) {
if (complete) {
// Time (seconds).
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
// Temperature (1/10 °C).
if (have_temperature) {
sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
have_temperature = 0;
}
// Depth (1/10 m).
unsigned int depth = value & 0x07FF;
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
// Gas change.
unsigned int gasmix = (value & 0x0800) >> 11;
if (gasmix != gasmix_previous) {
sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
gasmix_previous = gasmix;
// Gas change
if (divemode == SCUBA || divemode == NITROX) {
if (gasmix != gasmix_previous) {
sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
gasmix_previous = gasmix;
}
}
complete = 1;
} else if (type == TEMPERATURE) {
// Temperature (1/10 °C).
sample.temperature = value / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
} else {
WARNING(abstract->context, "Unknown sample type %u.", type);
complete = 0;
}
offset += 2;