Merge branch 'mares'
This commit is contained in:
commit
327e39ed52
@ -199,6 +199,7 @@ 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", "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,6 +40,7 @@
|
||||
)
|
||||
|
||||
#define MATRIX 0x0F
|
||||
#define SMART 0x10
|
||||
#define ICONHD 0x14
|
||||
#define ICONHDNET 0x15
|
||||
#define PUCKPRO 0x18
|
||||
@ -49,6 +50,11 @@
|
||||
#define ACK 0xAA
|
||||
#define EOF 0xEA
|
||||
|
||||
#define AIR 0
|
||||
#define GAUGE 1
|
||||
#define NITROX 2
|
||||
#define FREEDIVE 3
|
||||
|
||||
typedef struct mares_iconhd_layout_t {
|
||||
unsigned int memsize;
|
||||
unsigned int rb_profile_begin;
|
||||
@ -115,6 +121,7 @@ mares_iconhd_get_model (mares_iconhd_device_t *device)
|
||||
{
|
||||
const mares_iconhd_model_t models[] = {
|
||||
{"Matrix", MATRIX},
|
||||
{"Smart", SMART},
|
||||
{"Icon HD", ICONHD},
|
||||
{"Icon AIR", ICONHDNET},
|
||||
{"Puck Pro", PUCKPRO},
|
||||
@ -286,6 +293,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, const char *
|
||||
case PUCKPRO:
|
||||
case PUCK2:
|
||||
case NEMOWIDE2:
|
||||
case SMART:
|
||||
device->layout = &mares_nemowide2_layout;
|
||||
device->packetsize = 256;
|
||||
break;
|
||||
@ -453,6 +461,8 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u
|
||||
unsigned int header = 0x5C;
|
||||
if (model == ICONHDNET)
|
||||
header = 0x80;
|
||||
else if (model == SMART)
|
||||
header = 4; // Type and number of samples only!
|
||||
|
||||
// Get the end of the profile ring buffer.
|
||||
unsigned int eop = 0;
|
||||
@ -480,19 +490,46 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u
|
||||
unsigned int offset = layout->rb_profile_end - layout->rb_profile_begin;
|
||||
while (offset >= header + 4) {
|
||||
// Get the number of samples in the profile data.
|
||||
unsigned int nsamples = array_uint16_le (buffer + offset - header + 2);
|
||||
if (nsamples == 0xFFFF)
|
||||
unsigned int type = 0, nsamples = 0;
|
||||
if (model == SMART) {
|
||||
type = array_uint16_le (buffer + offset - header + 2);
|
||||
nsamples = array_uint16_le (buffer + offset - header + 0);
|
||||
} else {
|
||||
type = array_uint16_le (buffer + offset - header + 0);
|
||||
nsamples = array_uint16_le (buffer + offset - header + 2);
|
||||
}
|
||||
if (nsamples == 0xFFFF || type == 0xFFFF)
|
||||
break;
|
||||
|
||||
// Get the dive mode.
|
||||
unsigned int mode = type & 0x03;
|
||||
|
||||
// Get the header/sample size and fingerprint offset.
|
||||
unsigned int headersize = 0x5C;
|
||||
unsigned int samplesize = 8;
|
||||
unsigned int fingerprint = 6;
|
||||
if (model == ICONHDNET) {
|
||||
headersize = 0x80;
|
||||
samplesize = 12;
|
||||
} else if (model == SMART) {
|
||||
if (mode == FREEDIVE) {
|
||||
headersize = 0x2E;
|
||||
samplesize = 6;
|
||||
fingerprint = 0x20;
|
||||
} else {
|
||||
headersize = 0x5C;
|
||||
samplesize = 8;
|
||||
fingerprint = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the total number of bytes for this dive.
|
||||
// If the buffer does not contain that much bytes, we reached the
|
||||
// end of the ringbuffer. The current dive is incomplete (partially
|
||||
// overwritten with newer data), and processing should stop.
|
||||
unsigned int nbytes = 4 + header;
|
||||
unsigned int nbytes = 4 + headersize + nsamples * samplesize;
|
||||
if (model == ICONHDNET)
|
||||
nbytes += nsamples * 12 + (nsamples / 4) * 8;
|
||||
else
|
||||
nbytes += nsamples * 8;
|
||||
nbytes += (nsamples / 4) * 8;
|
||||
if (offset < nbytes)
|
||||
break;
|
||||
|
||||
@ -501,17 +538,12 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u
|
||||
|
||||
// Verify that the length that is stored in the profile data
|
||||
// equals the calculated length. If both values are different,
|
||||
// something is wrong and an error is returned.
|
||||
// we assume we reached the last dive.
|
||||
unsigned int length = array_uint32_le (buffer + offset);
|
||||
if (length == 0 || length == 0xFFFFFFFF)
|
||||
if (length != nbytes)
|
||||
break;
|
||||
if (length != nbytes) {
|
||||
ERROR (context, "Calculated and stored size are not equal.");
|
||||
free (buffer);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned char *fp = buffer + offset + length - header + 6;
|
||||
unsigned char *fp = buffer + offset + length - headersize + fingerprint;
|
||||
if (device && memcmp (fp, device->fingerprint, sizeof (device->fingerprint)) == 0) {
|
||||
free (buffer);
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -29,14 +29,16 @@
|
||||
|
||||
#define ISINSTANCE(parser) dc_parser_isinstance((parser), &mares_iconhd_parser_vtable)
|
||||
|
||||
#define SMART 0x10
|
||||
#define ICONHD 0x14
|
||||
#define ICONHDNET 0x15
|
||||
|
||||
#define NGASMIXES 3
|
||||
|
||||
#define AIR 0
|
||||
#define GAUGE 1
|
||||
#define NITROX 2
|
||||
#define AIR 0
|
||||
#define GAUGE 1
|
||||
#define NITROX 2
|
||||
#define FREEDIVE 3
|
||||
|
||||
typedef struct mares_iconhd_parser_t mares_iconhd_parser_t;
|
||||
|
||||
@ -45,6 +47,8 @@ struct mares_iconhd_parser_t {
|
||||
unsigned int model;
|
||||
// Cached fields.
|
||||
unsigned int cached;
|
||||
unsigned int mode;
|
||||
unsigned int nsamples;
|
||||
unsigned int footer;
|
||||
unsigned int samplesize;
|
||||
unsigned int ngasmixes;
|
||||
@ -77,32 +81,70 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
unsigned int footersize = 0x5C;
|
||||
unsigned int samplesize = 8;
|
||||
if (parser->model == ICONHDNET) {
|
||||
footersize = 0x80;
|
||||
samplesize = 12;
|
||||
}
|
||||
unsigned int header = 0x5C;
|
||||
if (parser->model == ICONHDNET)
|
||||
header = 0x80;
|
||||
else if (parser->model == SMART)
|
||||
header = 4; // Type and number of samples only!
|
||||
|
||||
if (size < 4) {
|
||||
if (size < header + 4) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned int length = array_uint32_le (data);
|
||||
|
||||
if (size < length || length < footersize + 4) {
|
||||
if (length > size) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
const unsigned char *p = data + length - footersize;
|
||||
// Get the number of samples in the profile data.
|
||||
unsigned int type = 0, nsamples = 0;
|
||||
if (parser->model == SMART) {
|
||||
type = array_uint16_le (data + length - header + 2);
|
||||
nsamples = array_uint16_le (data + length - header + 0);
|
||||
} else {
|
||||
type = array_uint16_le (data + length - header + 0);
|
||||
nsamples = array_uint16_le (data + length - header + 2);
|
||||
}
|
||||
|
||||
// Get the dive mode.
|
||||
unsigned int mode = type & 0x03;
|
||||
|
||||
// Get the header and sample size.
|
||||
unsigned int headersize = 0x5C;
|
||||
unsigned int samplesize = 8;
|
||||
if (parser->model == ICONHDNET) {
|
||||
headersize = 0x80;
|
||||
samplesize = 12;
|
||||
} else if (parser->model == SMART) {
|
||||
if (mode == FREEDIVE) {
|
||||
headersize = 0x2E;
|
||||
samplesize = 6;
|
||||
} else {
|
||||
headersize = 0x5C;
|
||||
samplesize = 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the total number of bytes for this dive.
|
||||
unsigned int nbytes = 4 + headersize + nsamples * samplesize;
|
||||
if (parser->model == ICONHDNET)
|
||||
nbytes += (nsamples / 4) * 8;
|
||||
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) {
|
||||
p += 4;
|
||||
}
|
||||
|
||||
// Gas mixes
|
||||
unsigned int ngasmixes = 0;
|
||||
unsigned int oxygen[NGASMIXES] = {0};
|
||||
unsigned int mode = p[0] & 0x03;
|
||||
if (mode == GAUGE) {
|
||||
if (mode == GAUGE || mode == FREEDIVE) {
|
||||
ngasmixes = 0;
|
||||
} else if (mode == AIR) {
|
||||
oxygen[0] = 21;
|
||||
@ -113,15 +155,17 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
|
||||
// as the first gas marked as disabled is found.
|
||||
ngasmixes = 0;
|
||||
while (ngasmixes < NGASMIXES) {
|
||||
if (p[0x14 + ngasmixes * 4 + 1] & 0x80)
|
||||
if (p[0x10 + ngasmixes * 4 + 1] & 0x80)
|
||||
break;
|
||||
oxygen[ngasmixes] = p[0x14 + ngasmixes * 4];
|
||||
oxygen[ngasmixes] = p[0x10 + ngasmixes * 4];
|
||||
ngasmixes++;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the data for later use.
|
||||
parser->footer = length - footersize;
|
||||
parser->mode = mode;
|
||||
parser->nsamples = nsamples;
|
||||
parser->footer = length - headersize;
|
||||
parser->samplesize = samplesize;
|
||||
parser->ngasmixes = ngasmixes;
|
||||
for (unsigned int i = 0; i < ngasmixes; ++i) {
|
||||
@ -152,6 +196,8 @@ mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i
|
||||
// Set the default values.
|
||||
parser->model = model;
|
||||
parser->cached = 0;
|
||||
parser->mode = AIR;
|
||||
parser->nsamples = 0;
|
||||
parser->footer = 0;
|
||||
parser->samplesize = 0;
|
||||
parser->ngasmixes = 0;
|
||||
@ -182,6 +228,8 @@ mares_iconhd_parser_set_data (dc_parser_t *abstract, const unsigned char *data,
|
||||
|
||||
// Reset the cache.
|
||||
parser->cached = 0;
|
||||
parser->mode = AIR;
|
||||
parser->nsamples = 0;
|
||||
parser->footer = 0;
|
||||
parser->samplesize = 0;
|
||||
parser->ngasmixes = 0;
|
||||
@ -203,7 +251,16 @@ mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
const unsigned char *p = abstract->data + parser->footer + 6;
|
||||
const unsigned char *p = abstract->data + parser->footer;
|
||||
if (parser->model == SMART) {
|
||||
if (parser->mode == FREEDIVE) {
|
||||
p += 0x20;
|
||||
} else {
|
||||
p += 2;
|
||||
}
|
||||
} else {
|
||||
p += 6;
|
||||
}
|
||||
|
||||
if (datetime) {
|
||||
datetime->hour = array_uint16_le (p + 0);
|
||||
@ -229,16 +286,32 @@ 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) {
|
||||
p += 4;
|
||||
}
|
||||
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
|
||||
if (value) {
|
||||
switch (type) {
|
||||
case DC_FIELD_DIVETIME:
|
||||
*((unsigned int *) value) = array_uint16_le (p + 0x02) * 5;
|
||||
if (parser->mode == FREEDIVE) {
|
||||
unsigned int divetime = 0;
|
||||
unsigned int offset = 4;
|
||||
for (unsigned int i = 0; i < parser->nsamples; ++i) {
|
||||
divetime += array_uint16_le (abstract->data + offset + 2);
|
||||
offset += parser->samplesize;
|
||||
}
|
||||
*((unsigned int *) value) = divetime;
|
||||
} else {
|
||||
*((unsigned int *) value) = parser->nsamples * 5;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_MAXDEPTH:
|
||||
*((double *) value) = array_uint16_le (p + 0x04) / 10.0;
|
||||
if (parser->mode == FREEDIVE)
|
||||
*((double *) value) = array_uint16_le (p + 0x1A) / 10.0;
|
||||
else
|
||||
*((double *) value) = array_uint16_le (p + 0x00) / 10.0;
|
||||
break;
|
||||
case DC_FIELD_GASMIX_COUNT:
|
||||
*((unsigned int *) value) = parser->ngasmixes;
|
||||
@ -250,16 +323,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)
|
||||
*((double *) value) = array_uint16_le (p + 0x26) / 8000.0;
|
||||
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:
|
||||
*((double *) value) = (signed short) array_uint16_le (p + 0x46) / 10.0;
|
||||
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:
|
||||
*((double *) value) = (signed short) array_uint16_le (p + 0x48) / 10.0;
|
||||
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;
|
||||
break;
|
||||
case DC_FIELD_DIVEMODE:
|
||||
switch (p[0] & 0x03) {
|
||||
switch (parser->mode) {
|
||||
case AIR:
|
||||
case NITROX:
|
||||
*((dc_divemode_t *) value) = DC_DIVEMODE_OC;
|
||||
@ -267,6 +349,9 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
|
||||
case GAUGE:
|
||||
*((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE;
|
||||
break;
|
||||
case FREEDIVE:
|
||||
*((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE;
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
@ -295,63 +380,84 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
unsigned int time = 0;
|
||||
unsigned int interval = 5;
|
||||
|
||||
unsigned int offset = 4;
|
||||
unsigned int nsamples = 0;
|
||||
|
||||
// Previous gas mix - initialize with impossible value
|
||||
unsigned int gasmix_previous = 0xFFFFFFFF;
|
||||
|
||||
while (offset + parser->samplesize <= parser->footer) {
|
||||
unsigned int offset = 4;
|
||||
unsigned int nsamples = 0;
|
||||
while (nsamples < parser->nsamples) {
|
||||
dc_sample_value_t sample = {0};
|
||||
|
||||
// Time (seconds).
|
||||
time += interval;
|
||||
sample.time = time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
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);
|
||||
|
||||
// Depth (1/10 m).
|
||||
unsigned int depth = array_uint16_le (data + offset + 0);
|
||||
sample.depth = depth / 10.0;
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
// Surface Time (seconds).
|
||||
time += surftime;
|
||||
sample.time = time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
// Temperature (1/10 °C).
|
||||
unsigned int temperature = array_uint16_le (data + offset + 2) & 0x0FFF;
|
||||
sample.temperature = temperature / 10.0;
|
||||
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
|
||||
// Surface Depth (0 m).
|
||||
sample.depth = 0.0;
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
|
||||
// Current gas mix
|
||||
if (parser->ngasmixes > 0) {
|
||||
unsigned int gasmix = (data[offset + 3] & 0xF0) >> 4;
|
||||
if (gasmix >= parser->ngasmixes) {
|
||||
ERROR (abstract->context, "Invalid gas mix index.");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
if (gasmix != gasmix_previous) {
|
||||
sample.event.type = SAMPLE_EVENT_GASCHANGE;
|
||||
sample.event.time = 0;
|
||||
sample.event.value = parser->oxygen[gasmix];
|
||||
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
|
||||
gasmix_previous = gasmix;
|
||||
}
|
||||
}
|
||||
// Dive Time (seconds).
|
||||
time += divetime;
|
||||
sample.time = time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
offset += parser->samplesize;
|
||||
nsamples++;
|
||||
// Maximum Depth (1/10 m).
|
||||
sample.depth = maxdepth / 10.0;
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
|
||||
// Some extra data.
|
||||
if (parser->model == ICONHDNET && (nsamples % 4) == 0) {
|
||||
if (offset + 8 > parser->footer) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
offset += parser->samplesize;
|
||||
nsamples++;
|
||||
} else {
|
||||
// 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 + 0);
|
||||
sample.depth = depth / 10.0;
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
|
||||
// Temperature (1/10 °C).
|
||||
unsigned int temperature = array_uint16_le (data + offset + 2) & 0x0FFF;
|
||||
sample.temperature = temperature / 10.0;
|
||||
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
|
||||
|
||||
// Current gas mix
|
||||
if (parser->ngasmixes > 0) {
|
||||
unsigned int gasmix = (data[offset + 3] & 0xF0) >> 4;
|
||||
if (gasmix >= parser->ngasmixes) {
|
||||
ERROR (abstract->context, "Invalid gas mix index.");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
if (gasmix != gasmix_previous) {
|
||||
sample.event.type = SAMPLE_EVENT_GASCHANGE;
|
||||
sample.event.time = 0;
|
||||
sample.event.value = parser->oxygen[gasmix];
|
||||
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
|
||||
gasmix_previous = gasmix;
|
||||
}
|
||||
}
|
||||
|
||||
// Pressure (1/100 bar).
|
||||
unsigned int pressure = array_uint16_le(data + offset);
|
||||
sample.pressure.tank = 0;
|
||||
sample.pressure.value = pressure / 100.0;
|
||||
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
|
||||
offset += parser->samplesize;
|
||||
nsamples++;
|
||||
|
||||
offset += 8;
|
||||
// Some extra data.
|
||||
if (parser->model == ICONHDNET && (nsamples % 4) == 0) {
|
||||
// Pressure (1/100 bar).
|
||||
unsigned int pressure = array_uint16_le(data + offset);
|
||||
sample.pressure.tank = 0;
|
||||
sample.pressure.value = pressure / 100.0;
|
||||
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
|
||||
|
||||
offset += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user