From 3ff890b3e23b78038dc05d6933488097268dc7eb Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 26 Aug 2015 07:51:53 +0200 Subject: [PATCH 1/7] Improve the detection of unused tanks. The begin/end pressure for unused tanks is normally zero. But I noticed that in some cases both pressure values are stored as 0xFFFF. Since that corresponds to a pressure of 511.99 bar, this is most likely some special magic value, and not a valid pressure. Tanks where either the begin or end pressure is 0xFFFF are now ignored too. --- src/uwatec_smart_parser.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index 3bf7d2b..ab4e7e1 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -459,7 +459,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 +1069,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) { From 55afe711f75e6d7c51e7eb9d656759f811ddff3f Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 27 Aug 2015 09:14:17 +0200 Subject: [PATCH 2/7] Fix the layout for the Oceanic Pro Plus 3. The logbook ringbuffer starts at address 0x03E0 instead of 0x240. Since none of the other models uses the same address, a completely new layout structure is necessary. --- src/oceanic_atom2.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) 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)) { From c72bdfe4e4175cb7ad2eb3dc4caa0e9fd9c12962 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 7 Sep 2015 22:51:44 +0200 Subject: [PATCH 3/7] Add support for logging I/O operations. The Atomics Cobalt backend uses libusb directly, without going through an internal I/O layer that support logging. Therefore the logging needs to be done in the backend itself. --- src/atomics_cobalt.c | 8 ++++++++ 1 file changed, 8 insertions(+) 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; From ddbcdeecbb803ba7ed33cf58dc03bae25207f706 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 4 Sep 2015 08:32:52 +0200 Subject: [PATCH 4/7] Ignore disconnected O2 sensors. Even if there are no O2 sensors connected (for example in auto or fixed setpoint mode), the device records a ppO2 sample with all three values set to zero. Such samples are now ignored, as if there was no ppO2 sample present. Reported-by: Anton Lundin --- src/hw_ostc_parser.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index b3ef2fb..394291f 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -767,6 +767,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). @@ -788,12 +790,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 From 52453f080db584445847c9e5a449346b730caf8a Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 10 Sep 2015 11:48:20 +0200 Subject: [PATCH 5/7] Add support for the tank field. The air integrated uwatec aladin models do record the total consumption, and not the begin/end pressure. Returning the total consumption as the begin pressure, combined with a zero end pressure is strictly speaking not correct, but it still provides useful information. --- src/uwatec_memomouse_parser.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) 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; From 3fa606a8a292c26dcb76250a87c05e7186ad6528 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 15 Sep 2015 21:43:50 +0200 Subject: [PATCH 6/7] Fix the gas switches for Galileo Trimix. The Galileo Trimix supports up to 10 tanks and gas mixes. However, the existing alarm based gas switch events have only 2 bits available, and can support at most 4 gas mixes. Therefore, the trimix variant stores another 4 bit value in the second alarm byte. For the first three gas mixes (and possibly also the fourth), both alarm bytes appear to be always set to the same value. For the higher mixes, the value in the first alarm byte is always zero. This doesn't cause any problems, because in the data stream the second alarm byte is stored after the first one, and our final value is always the last one. The non-trimix variant also has the second alarm byte, but the gas mix bits appear to be always zero. In order to avoid taking this zero as the final value, a separate table is used for the trimix variant. --- src/uwatec_smart_parser.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index ab4e7e1..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. From cb06adef35a26fcaab464a82b395fc81fe08f98d Mon Sep 17 00:00:00 2001 From: Giorgio Marzano Date: Wed, 9 Sep 2015 09:56:56 +0200 Subject: [PATCH 7/7] 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. --- src/descriptor.c | 3 +- src/mares_iconhd.c | 24 ++++++++-- src/mares_iconhd_parser.c | 95 ++++++++++++++++++++++++++++++++++----- 3 files changed, 107 insertions(+), 15 deletions(-) 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/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);