diff --git a/src/descriptor.c b/src/descriptor.c index 4dee61c..bfee247 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -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}, diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 3c0437c..7f774cc 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -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; diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c index ccf297b..336822c 100644 --- a/src/mares_iconhd_parser.c +++ b/src/mares_iconhd_parser.c @@ -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; + } } }