Add support for the Mares Smart Apnea.
The Mares Smart Apnea uses a different data format than the regular Smart, because it records not only a summary of each freedive in the session, but also a full depth profile. Because both the regular Smart and the Smart Apnea have the same model number (0x10), another way to distinguish the two variants is needed. Therefore, the Smart Apnea gets a modified model number, with one of the higher bit set. The actual type is detected from the contents of the version packet. The new Smart Apnea is also capable of recording multiple samples per second (e.g. 2, 4 or 8). But since our smallest unit of time is one second, we can't represent this, and the extra samples will get dropped for now.
This commit is contained in:
parent
3fa606a8a2
commit
cb06adef35
@ -199,7 +199,8 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Mares", "Airlab", DC_FAMILY_MARES_DARWIN , 1},
|
||||
/* Mares Icon HD */
|
||||
{"Mares", "Matrix", DC_FAMILY_MARES_ICONHD , 0x0F},
|
||||
{"Mares", "Smart", DC_FAMILY_MARES_ICONHD , 0x10},
|
||||
{"Mares", "Smart", DC_FAMILY_MARES_ICONHD , 0x000010},
|
||||
{"Mares", "Smart Apnea", DC_FAMILY_MARES_ICONHD , 0x010010},
|
||||
{"Mares", "Icon HD", DC_FAMILY_MARES_ICONHD , 0x14},
|
||||
{"Mares", "Icon HD Net Ready", DC_FAMILY_MARES_ICONHD , 0x15},
|
||||
{"Mares", "Puck Pro", DC_FAMILY_MARES_ICONHD , 0x18},
|
||||
|
||||
@ -40,7 +40,8 @@
|
||||
)
|
||||
|
||||
#define MATRIX 0x0F
|
||||
#define SMART 0x10
|
||||
#define SMART 0x000010
|
||||
#define SMARTAPNEA 0x010010
|
||||
#define ICONHD 0x14
|
||||
#define ICONHDNET 0x15
|
||||
#define PUCKPRO 0x18
|
||||
@ -122,6 +123,7 @@ mares_iconhd_get_model (mares_iconhd_device_t *device)
|
||||
const mares_iconhd_model_t models[] = {
|
||||
{"Matrix", MATRIX},
|
||||
{"Smart", SMART},
|
||||
{"Smart Apnea", SMARTAPNEA},
|
||||
{"Icon HD", ICONHD},
|
||||
{"Icon AIR", ICONHDNET},
|
||||
{"Puck Pro", PUCKPRO},
|
||||
@ -463,6 +465,8 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u
|
||||
header = 0x80;
|
||||
else if (model == SMART)
|
||||
header = 4; // Type and number of samples only!
|
||||
else if (model == SMARTAPNEA)
|
||||
header = 6; // Type and number of samples only!
|
||||
|
||||
// Get the end of the profile ring buffer.
|
||||
unsigned int eop = 0;
|
||||
@ -491,7 +495,7 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u
|
||||
while (offset >= header + 4) {
|
||||
// Get the number of samples in the profile data.
|
||||
unsigned int type = 0, nsamples = 0;
|
||||
if (model == SMART) {
|
||||
if (model == SMART || model == SMARTAPNEA) {
|
||||
type = array_uint16_le (buffer + offset - header + 2);
|
||||
nsamples = array_uint16_le (buffer + offset - header + 0);
|
||||
} else {
|
||||
@ -521,6 +525,10 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u
|
||||
samplesize = 8;
|
||||
fingerprint = 2;
|
||||
}
|
||||
} else if (model == SMARTAPNEA) {
|
||||
headersize = 0x50;
|
||||
samplesize = 14;
|
||||
fingerprint = 0x40;
|
||||
}
|
||||
|
||||
// Calculate the total number of bytes for this dive.
|
||||
@ -528,8 +536,18 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u
|
||||
// end of the ringbuffer. The current dive is incomplete (partially
|
||||
// overwritten with newer data), and processing should stop.
|
||||
unsigned int nbytes = 4 + headersize + nsamples * samplesize;
|
||||
if (model == ICONHDNET)
|
||||
if (model == ICONHDNET) {
|
||||
nbytes += (nsamples / 4) * 8;
|
||||
} else if (model == SMARTAPNEA) {
|
||||
if (offset < headersize)
|
||||
break;
|
||||
|
||||
unsigned int settings = array_uint16_le (buffer + offset - headersize + 0x1C);
|
||||
unsigned int divetime = array_uint32_le (buffer + offset - headersize + 0x24);
|
||||
unsigned int samplerate = 1 << ((settings >> 9) & 0x03);
|
||||
|
||||
nbytes += divetime * samplerate * 2;
|
||||
}
|
||||
if (offset < nbytes)
|
||||
break;
|
||||
|
||||
|
||||
@ -29,7 +29,8 @@
|
||||
|
||||
#define ISINSTANCE(parser) dc_parser_isinstance((parser), &mares_iconhd_parser_vtable)
|
||||
|
||||
#define SMART 0x10
|
||||
#define SMART 0x000010
|
||||
#define SMARTAPNEA 0x010010
|
||||
#define ICONHD 0x14
|
||||
#define ICONHDNET 0x15
|
||||
|
||||
@ -86,6 +87,8 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
|
||||
header = 0x80;
|
||||
else if (parser->model == SMART)
|
||||
header = 4; // Type and number of samples only!
|
||||
else if (parser->model == SMARTAPNEA)
|
||||
header = 6; // Type and number of samples only!
|
||||
|
||||
if (size < header + 4) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
@ -100,7 +103,7 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
|
||||
|
||||
// Get the number of samples in the profile data.
|
||||
unsigned int type = 0, nsamples = 0;
|
||||
if (parser->model == SMART) {
|
||||
if (parser->model == SMART || parser->model == SMARTAPNEA) {
|
||||
type = array_uint16_le (data + length - header + 2);
|
||||
nsamples = array_uint16_le (data + length - header + 0);
|
||||
} else {
|
||||
@ -125,19 +128,34 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
|
||||
headersize = 0x5C;
|
||||
samplesize = 8;
|
||||
}
|
||||
} else if (parser->model == SMARTAPNEA) {
|
||||
headersize = 0x50;
|
||||
samplesize = 14;
|
||||
}
|
||||
|
||||
// Calculate the total number of bytes for this dive.
|
||||
unsigned int nbytes = 4 + headersize + nsamples * samplesize;
|
||||
if (parser->model == ICONHDNET)
|
||||
if (parser->model == ICONHDNET) {
|
||||
nbytes += (nsamples / 4) * 8;
|
||||
} else if (parser->model == SMARTAPNEA) {
|
||||
if (length < headersize) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned int settings = array_uint16_le (data + length - headersize + 0x1C);
|
||||
unsigned int divetime = array_uint32_le (data + length - headersize + 0x24);
|
||||
unsigned int samplerate = 1 << ((settings >> 9) & 0x03);
|
||||
|
||||
nbytes += divetime * samplerate * 2;
|
||||
}
|
||||
if (length != nbytes) {
|
||||
ERROR (abstract->context, "Calculated and stored size are not equal.");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
const unsigned char *p = data + length - headersize;
|
||||
if (parser->model != SMART) {
|
||||
if (parser->model != SMART && parser->model != SMARTAPNEA) {
|
||||
p += 4;
|
||||
}
|
||||
|
||||
@ -258,6 +276,8 @@ mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime
|
||||
} else {
|
||||
p += 2;
|
||||
}
|
||||
} else if (parser->model == SMARTAPNEA) {
|
||||
p += 0x40;
|
||||
} else {
|
||||
p += 6;
|
||||
}
|
||||
@ -286,7 +306,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
|
||||
return rc;
|
||||
|
||||
const unsigned char *p = abstract->data + parser->footer;
|
||||
if (parser->model != SMART) {
|
||||
if (parser->model != SMART && parser->model != SMARTAPNEA) {
|
||||
p += 4;
|
||||
}
|
||||
|
||||
@ -295,7 +315,9 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
|
||||
if (value) {
|
||||
switch (type) {
|
||||
case DC_FIELD_DIVETIME:
|
||||
if (parser->mode == FREEDIVE) {
|
||||
if (parser->model == SMARTAPNEA) {
|
||||
*((unsigned int *) value) = array_uint16_le (p + 0x24);
|
||||
} else if (parser->mode == FREEDIVE) {
|
||||
unsigned int divetime = 0;
|
||||
unsigned int offset = 4;
|
||||
for (unsigned int i = 0; i < parser->nsamples; ++i) {
|
||||
@ -308,7 +330,9 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_MAXDEPTH:
|
||||
if (parser->mode == FREEDIVE)
|
||||
if (parser->model == SMARTAPNEA)
|
||||
*((double *) value) = array_uint16_le (p + 0x3A) / 10.0;
|
||||
else if (parser->mode == FREEDIVE)
|
||||
*((double *) value) = array_uint16_le (p + 0x1A) / 10.0;
|
||||
else
|
||||
*((double *) value) = array_uint16_le (p + 0x00) / 10.0;
|
||||
@ -323,19 +347,25 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
|
||||
break;
|
||||
case DC_FIELD_ATMOSPHERIC:
|
||||
// Pressure (1/8 millibar)
|
||||
if (parser->mode == FREEDIVE)
|
||||
if (parser->model == SMARTAPNEA)
|
||||
*((double *) value) = array_uint16_le (p + 0x38) / 1000.0;
|
||||
else if (parser->mode == FREEDIVE)
|
||||
*((double *) value) = array_uint16_le (p + 0x18) / 1000.0;
|
||||
else
|
||||
*((double *) value) = array_uint16_le (p + 0x22) / 8000.0;
|
||||
break;
|
||||
case DC_FIELD_TEMPERATURE_MINIMUM:
|
||||
if (parser->mode == FREEDIVE)
|
||||
if (parser->model == SMARTAPNEA)
|
||||
*((double *) value) = (signed short) array_uint16_le (p + 0x3C) / 10.0;
|
||||
else if (parser->mode == FREEDIVE)
|
||||
*((double *) value) = (signed short) array_uint16_le (p + 0x1C) / 10.0;
|
||||
else
|
||||
*((double *) value) = (signed short) array_uint16_le (p + 0x42) / 10.0;
|
||||
break;
|
||||
case DC_FIELD_TEMPERATURE_MAXIMUM:
|
||||
if (parser->mode == FREEDIVE)
|
||||
if (parser->model == SMARTAPNEA)
|
||||
*((double *) value) = (signed short) array_uint16_le (p + 0x3E) / 10.0;
|
||||
else if (parser->mode == FREEDIVE)
|
||||
*((double *) value) = (signed short) array_uint16_le (p + 0x1E) / 10.0;
|
||||
else
|
||||
*((double *) value) = (signed short) array_uint16_le (p + 0x44) / 10.0;
|
||||
@ -379,6 +409,19 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
|
||||
unsigned int time = 0;
|
||||
unsigned int interval = 5;
|
||||
unsigned int samplerate = 1;
|
||||
if (parser->model == SMARTAPNEA) {
|
||||
unsigned int settings = array_uint16_le (data + parser->footer + 0x1C);
|
||||
samplerate = 1 << ((settings >> 9) & 0x03);
|
||||
if (samplerate > 1) {
|
||||
// The Smart Apnea supports multiple samples per second
|
||||
// (e.g. 2, 4 or 8). Since our smallest unit of time is one
|
||||
// second, we can't represent this, and the extra samples
|
||||
// will get dropped.
|
||||
WARNING(abstract->context, "Multiple samples per second are not supported!");
|
||||
}
|
||||
interval = 1;
|
||||
}
|
||||
|
||||
// Previous gas mix - initialize with impossible value
|
||||
unsigned int gasmix_previous = 0xFFFFFFFF;
|
||||
@ -388,7 +431,37 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
while (nsamples < parser->nsamples) {
|
||||
dc_sample_value_t sample = {0};
|
||||
|
||||
if (parser->mode == FREEDIVE) {
|
||||
if (parser->model == SMARTAPNEA) {
|
||||
unsigned int maxdepth = array_uint16_le (data + offset + 0);
|
||||
unsigned int divetime = array_uint16_le (data + offset + 2);
|
||||
unsigned int surftime = array_uint16_le (data + offset + 4);
|
||||
|
||||
// Surface Time (seconds).
|
||||
time += surftime;
|
||||
sample.time = time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
// Surface Depth (0 m).
|
||||
sample.depth = 0.0;
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
|
||||
offset += parser->samplesize;
|
||||
nsamples++;
|
||||
|
||||
for (unsigned int i = 0; i < divetime; ++i) {
|
||||
// Time (seconds).
|
||||
time += interval;
|
||||
sample.time = time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
// Depth (1/10 m).
|
||||
unsigned int depth = array_uint16_le (data + offset);
|
||||
sample.depth = depth / 10.0;
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
|
||||
offset += 2 * samplerate;
|
||||
}
|
||||
} else if (parser->mode == FREEDIVE) {
|
||||
unsigned int maxdepth = array_uint16_le (data + offset + 0);
|
||||
unsigned int divetime = array_uint16_le (data + offset + 2);
|
||||
unsigned int surftime = array_uint16_le (data + offset + 4);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user