diff --git a/src/atomics_cobalt.c b/src/atomics_cobalt.c index 1ee9018..88febec 100644 --- a/src/atomics_cobalt.c +++ b/src/atomics_cobalt.c @@ -215,6 +215,8 @@ atomics_cobalt_device_version (dc_device_t *abstract, unsigned char data[], unsi return EXITCODE(rc); } + HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Write", &bRequest, 1); + // Receive the answer from the dive computer. int length = 0; unsigned char packet[SZ_VERSION + 2] = {0}; @@ -225,6 +227,8 @@ atomics_cobalt_device_version (dc_device_t *abstract, unsigned char data[], unsi return EXITCODE(rc); } + HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Read", packet, length); + // Verify the checksum of the packet. unsigned short crc = array_uint16_le (packet + SZ_VERSION); unsigned short ccrc = checksum_add_uint16 (packet, SZ_VERSION, 0x0); @@ -271,6 +275,8 @@ atomics_cobalt_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init, return EXITCODE(rc); } + HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Write", &bRequest, 1); + unsigned int nbytes = 0; while (1) { // Receive the answer from the dive computer. @@ -283,6 +289,8 @@ atomics_cobalt_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init, return EXITCODE(rc); } + HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Read", packet, length); + // Update and emit a progress event. if (progress) { progress->current += length; diff --git a/src/descriptor.c b/src/descriptor.c index a76b64a..91a75fb 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -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}, diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index fe90f5d..5fa0024 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -859,6 +859,8 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call return DC_STATUS_DATAFORMAT; } + unsigned int ppo2[3] = {0}; + unsigned int count = 0; unsigned int value = 0; switch (info[i].type) { case 0: // Temperature (0.1 °C). @@ -880,12 +882,18 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call case 3: // ppO2 (0.01 bar). for (unsigned int j = 0; j < 3; ++j) { if (info[i].size == 3) { - value = data[offset + j]; + ppo2[j] = data[offset + j]; } else { - value = data[offset + j * 3]; + ppo2[j] = data[offset + j * 3]; + } + if (ppo2[j] != 0) + count++; + } + if (count) { + for (unsigned int j = 0; j < 3; ++j) { + sample.ppo2 = ppo2[j] / 100.0; + if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); } - sample.ppo2 = value / 100.0; - if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); } break; case 5: // CNS diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 7f774cc..7628426 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -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; diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c index 336822c..8992f64 100644 --- a/src/mares_iconhd_parser.c +++ b/src/mares_iconhd_parser.c @@ -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); diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index a17725e..11cf940 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -111,7 +111,6 @@ static const oceanic_common_version_t oceanic_atom2b_version[] = { {"ELEMENT2 \0\0 512K"}, {"OCEVEO20 \0\0 512K"}, {"TUSAZEN \0\0 512K"}, - {"PROPLUS3 \0\0 512K"}, }; static const oceanic_common_version_t oceanic_atom2c_version[] = { @@ -129,6 +128,10 @@ static const oceanic_common_version_t oceanic_default_version[] = { {"HOLLDG03 \0\0 512K"}, }; +static const oceanic_common_version_t oceanic_proplus3_version[] = { + {"PROPLUS3 \0\0 512K"}, +}; + static const oceanic_common_version_t tusa_zenair_version[] = { {"TUZENAIR \0\0 512K"}, {"AMPHOSSW \0\0 512K"}, @@ -266,6 +269,19 @@ static const oceanic_common_layout_t oceanic_atom2c_layout = { 0 /* pt_mode_logbook */ }; +static const oceanic_common_layout_t oceanic_proplus3_layout = { + 0x10000, /* memsize */ + 0x0000, /* cf_devinfo */ + 0x0040, /* cf_pointers */ + 0x03E0, /* rb_logbook_begin */ + 0x0A40, /* rb_logbook_end */ + 8, /* rb_logbook_entry_size */ + 0x0A40, /* rb_profile_begin */ + 0xFE00, /* rb_profile_end */ + 0, /* pt_mode_global */ + 0 /* pt_mode_logbook */ +}; + static const oceanic_common_layout_t tusa_zenair_layout = { 0xFFF0, /* memsize */ 0x0000, /* cf_devinfo */ @@ -597,6 +613,8 @@ oceanic_atom2_device_open2 (dc_device_t **out, dc_context_t *context, const char device->base.layout = &oceanic_atom2b_layout; } else if (OCEANIC_COMMON_MATCH (device->base.version, oceanic_atom2c_version)) { device->base.layout = &oceanic_atom2c_layout; + } else if (OCEANIC_COMMON_MATCH (device->base.version, oceanic_proplus3_version)) { + device->base.layout = &oceanic_proplus3_layout; } else if (OCEANIC_COMMON_MATCH (device->base.version, tusa_zenair_version)) { device->base.layout = &tusa_zenair_layout; } else if (OCEANIC_COMMON_MATCH (device->base.version, oceanic_oc1_version)) { diff --git a/src/uwatec_memomouse_parser.c b/src/uwatec_memomouse_parser.c index aa0a432..a47cdc4 100644 --- a/src/uwatec_memomouse_parser.c +++ b/src/uwatec_memomouse_parser.c @@ -22,6 +22,7 @@ #include #include +#include #include "context-private.h" #include "parser-private.h" @@ -141,6 +142,7 @@ uwatec_memomouse_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, header += 3; dc_gasmix_t *gasmix = (dc_gasmix_t *) value; + dc_tank_t *tank = (dc_tank_t *) value; if (value) { switch (type) { @@ -167,6 +169,25 @@ uwatec_memomouse_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, } gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; break; + case DC_FIELD_TANK_COUNT: + if (data[10]) { + *((unsigned int *) value) = 1; + } else { + *((unsigned int *) value) = 0; + } + break; + case DC_FIELD_TANK: + tank->type = DC_TANKVOLUME_NONE; + tank->volume = 0.0; + tank->workpressure = 0.0; + if (model == 0x1C) { + tank->beginpressure = data[10] * 20.0 * PSI / BAR; + } else { + tank->beginpressure = data[10]; + } + tank->endpressure = 0.0; + tank->gasmix = 0; + break; case DC_FIELD_TEMPERATURE_MINIMUM: *((double *) value) = (signed char) data[15] / 4.0; break; diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index 3bf7d2b..138b196 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -374,6 +374,12 @@ uwatec_smart_event_info_t uwatec_smart_galileo_events_2[] = { {EV_UNKNOWN, 0xFF, 0}, }; +static const +uwatec_smart_event_info_t uwatec_smart_trimix_events_2[] = { + {EV_UNKNOWN, 0x0F, 0}, + {EV_GASMIX, 0xF0, 4}, +}; + static unsigned int uwatec_smart_find_gasmix (uwatec_smart_parser_t *parser, unsigned int id) { @@ -419,6 +425,14 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser) if (data[43] & 0x80) { trimix = 1; } + + if (trimix) { + parser->events[2] = uwatec_smart_trimix_events_2; + parser->nevents[2] = C_ARRAY_SIZE (uwatec_smart_trimix_events_2); + } else { + parser->events[2] = uwatec_smart_galileo_events_2; + parser->nevents[2] = C_ARRAY_SIZE (uwatec_smart_galileo_events_2); + } } // Get the gas mixes and tanks. @@ -459,7 +473,8 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser) endpressure = array_uint16_le(data + offset + 2); } } - if (beginpressure != 0 || endpressure != 0) { + if ((beginpressure != 0 || endpressure != 0) && + (beginpressure != 0xFFFF) && (endpressure != 0xFFFF)) { tank[ntanks].id = id; tank[ntanks].beginpressure = beginpressure; tank[ntanks].endpressure = endpressure; @@ -1068,7 +1083,8 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t mixidx = idx; } - if (beginpressure != 0 || endpressure != 0) { + if ((beginpressure != 0 || endpressure != 0) && + (beginpressure != 0xFFFF) && (endpressure != 0xFFFF)) { idx = uwatec_smart_find_tank (parser, mixid); if (idx >= parser->ntanks) { if (idx >= NGASMIXES) {