From 3c17132a55c8d7816796ce75107fa55e5b11f6db Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sun, 8 Jan 2017 23:19:13 +0100 Subject: [PATCH 01/10] Implement ndl/deco for the Hollis DG03 --- src/oceanic_atom2_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index 51ff7d1..fda96a5 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -862,7 +862,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ decostop = (data[offset + 15] & 0x70) >> 4; decotime = array_uint16_le(data + offset + 6) & 0x03FF; have_deco = 1; - } else if (parser->model == ZEN) { + } else if (parser->model == ZEN || parser->model == DG03) { decostop = (data[offset + 5] & 0xF0) >> 4; decotime = array_uint16_le(data + offset + 4) & 0x0FFF; have_deco = 1; From ef1e64ac035f95e408adbc2fb64ca4015774a73c Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 10 Jan 2017 19:05:40 +0100 Subject: [PATCH 02/10] Fix the memory layout for the Hollis DG03 The profile ringbuffer ends at 0xFE00 instead of 0x10000. --- src/oceanic_atom2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index 802707e..01e4fd4 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -116,6 +116,7 @@ static const oceanic_common_version_t oceanic_atom2b_version[] = { {"OCEVEO20 \0\0 512K"}, {"TUSAZEN \0\0 512K"}, {"AQUAI300 \0\0 512K"}, + {"HOLLDG03 \0\0 512K"}, }; static const oceanic_common_version_t oceanic_atom2c_version[] = { @@ -130,7 +131,6 @@ static const oceanic_common_version_t oceanic_default_version[] = { {"ELITET31 \0\0 512K"}, {"DATAMASK \0\0 512K"}, {"COMPMASK \0\0 512K"}, - {"HOLLDG03 \0\0 512K"}, }; static const oceanic_common_version_t oceanic_proplus3_version[] = { From ef47084e0553a38429744fefa086ab2247d7e0d1 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 17 Jan 2017 22:57:25 +0100 Subject: [PATCH 03/10] Skip the extra samples one by one Skipping the extra samples by increasing the length is not always reliable. If there are empty samples present, they will get skipped instead of the real samples. And if the number of samples isn't an exact multiple of the samplerate, we're accessing data beyond the end of the dive profile. --- src/oceanic_atom2_parser.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index fda96a5..12b832f 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -671,6 +671,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ // Initial gas mix. unsigned int gasmix_previous = 0xFFFFFFFF; + unsigned int count = 0; unsigned int complete = 1; unsigned int offset = parser->headersize; while (offset + samplesize <= size - parser->footersize) { @@ -700,7 +701,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ // The sample size is usually fixed, but some sample types have a // larger size. Check whether we have that many bytes available. - unsigned int length = samplesize * samplerate; + unsigned int length = samplesize; if (sampletype == 0xBB) { length = PAGESIZE; if (offset + length > size - PAGESIZE) @@ -751,6 +752,13 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ complete = 1; } } else { + // Skip the extra samples. + if ((count % samplerate) != 0) { + offset += samplesize; + count++; + continue; + } + // Temperature (°F) if (have_temperature) { if (parser->model == GEO || parser->model == ATOM1 || @@ -912,6 +920,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ if (callback) callback (DC_SAMPLE_RBT, sample, userdata); } + count++; complete = 1; } From 0429ea146eaa7949b58fbc2ab25fe700eb6c173d Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 17 Jan 2017 23:02:17 +0100 Subject: [PATCH 04/10] Fix the length check --- src/oceanic_atom2_parser.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index 12b832f..3e67bdb 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -704,8 +704,10 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ unsigned int length = samplesize; if (sampletype == 0xBB) { length = PAGESIZE; - if (offset + length > size - PAGESIZE) + if (offset + length > size - parser->footersize) { + ERROR (abstract->context, "Buffer overflow detected!"); return DC_STATUS_DATAFORMAT; + } } // Vendor specific data From 7c0f8f9b9d92c1e0e7b1c6a5fe77eeca2b1d97f1 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 10 Jan 2017 21:30:17 +0100 Subject: [PATCH 05/10] Output samples only once all raw data is available Originally, the time and vendor sample values are emitted immediately after the previous sample is complete. This is now postponed until all raw samples are available. This will be required for the Aqualung i450t. That model appears to ignore the fixed sample rate and instead store a timestamp in each sample. That means the timestamp is only available once the last raw sample data has been reached. --- src/oceanic_atom2_parser.c | 40 ++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index 3e67bdb..3c63a5d 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -673,6 +673,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ unsigned int count = 0; unsigned int complete = 1; + unsigned int previous = 0; unsigned int offset = parser->headersize; while (offset + samplesize <= size - parser->footersize) { dc_sample_value_t sample = {0}; @@ -685,12 +686,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ continue; } - // Time. if (complete) { - time += interval; - sample.time = time; - if (callback) callback (DC_SAMPLE_TIME, sample, userdata); - + previous = offset; complete = 0; } @@ -710,12 +707,6 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ } } - // Vendor specific data - sample.vendor.type = SAMPLE_VENDOR_OCEANIC_ATOM2; - sample.vendor.size = length; - sample.vendor.data = data + offset; - if (callback) callback (DC_SAMPLE_VENDOR, sample, userdata); - // Check for a tank switch sample. if (sampletype == 0xAA) { if (parser->model == DATAMASK || parser->model == COMPUMASK) { @@ -743,12 +734,20 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ unsigned int nsamples = surftime / interval; for (unsigned int i = 0; i < nsamples; ++i) { - if (complete) { - time += interval; - sample.time = time; - if (callback) callback (DC_SAMPLE_TIME, sample, userdata); + // Time + time += interval; + sample.time = time; + if (callback) callback (DC_SAMPLE_TIME, sample, userdata); + + // Vendor specific data + if (i == 0) { + sample.vendor.type = SAMPLE_VENDOR_OCEANIC_ATOM2; + sample.vendor.size = (offset - previous) + length; + sample.vendor.data = data + previous; + if (callback) callback (DC_SAMPLE_VENDOR, sample, userdata); } + // Depth sample.depth = 0.0; if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); complete = 1; @@ -761,6 +760,17 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ continue; } + // Time. + time += interval; + sample.time = time; + if (callback) callback (DC_SAMPLE_TIME, sample, userdata); + + // Vendor specific data + sample.vendor.type = SAMPLE_VENDOR_OCEANIC_ATOM2; + sample.vendor.size = (offset - previous) + length; + sample.vendor.data = data + previous; + if (callback) callback (DC_SAMPLE_VENDOR, sample, userdata); + // Temperature (°F) if (have_temperature) { if (parser->model == GEO || parser->model == ATOM1 || From 8a4c1f1ef7f1a5b5371aa4b3f3974e57bd2994e5 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 12 Jan 2017 13:36:00 +0100 Subject: [PATCH 06/10] Split the raw data into multiple vendor samples After the previous commit, the raw data is now reported with one large vendor sample. Because that makes the data more difficult to interpret (for example during debugging), a small helper function is added to split the data again in multiple vendor samples. --- src/oceanic_atom2_parser.c | 51 ++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index 3c63a5d..98923c7 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -557,6 +557,41 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns return DC_STATUS_SUCCESS; } +static void +oceanic_atom2_parser_vendor (oceanic_atom2_parser_t *parser, const unsigned char *data, unsigned int size, unsigned int samplesize, dc_sample_callback_t callback, void *userdata) +{ + unsigned int offset = 0; + while (offset + samplesize <= size) { + dc_sample_value_t sample = {0}; + + // Ignore empty samples. + if ((parser->mode != FREEDIVE && + array_isequal (data + offset, samplesize, 0x00)) || + array_isequal (data + offset, samplesize, 0xFF)) { + offset += samplesize; + continue; + } + + // Get the sample type. + unsigned int sampletype = data[offset + 0]; + if (parser->mode == FREEDIVE) + sampletype = 0; + + // Get the sample size. + unsigned int length = samplesize; + if (sampletype == 0xBB) { + length = PAGESIZE; + } + + // Vendor specific data + sample.vendor.type = SAMPLE_VENDOR_OCEANIC_ATOM2; + sample.vendor.size = length; + sample.vendor.data = data + offset; + if (callback) callback (DC_SAMPLE_VENDOR, sample, userdata); + + offset += length; + } +} static dc_status_t oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) @@ -741,10 +776,10 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ // Vendor specific data if (i == 0) { - sample.vendor.type = SAMPLE_VENDOR_OCEANIC_ATOM2; - sample.vendor.size = (offset - previous) + length; - sample.vendor.data = data + previous; - if (callback) callback (DC_SAMPLE_VENDOR, sample, userdata); + oceanic_atom2_parser_vendor (parser, + data + previous, + (offset - previous) + length, + samplesize, callback, userdata); } // Depth @@ -766,10 +801,10 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ if (callback) callback (DC_SAMPLE_TIME, sample, userdata); // Vendor specific data - sample.vendor.type = SAMPLE_VENDOR_OCEANIC_ATOM2; - sample.vendor.size = (offset - previous) + length; - sample.vendor.data = data + previous; - if (callback) callback (DC_SAMPLE_VENDOR, sample, userdata); + oceanic_atom2_parser_vendor (parser, + data + previous, + (offset - previous) + length, + samplesize, callback, userdata); // Temperature (°F) if (have_temperature) { From e5805f3f7d9130812fadff51aaf87f092f4d25ab Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 13 Jan 2017 10:42:55 +0100 Subject: [PATCH 07/10] Fix the Aqualung i450T time samples The Aqualung i450T appears to ignore the fixed sample rate and instead store a timestamp in each sample. The presence of the surface samples in combination with this timestamp based format is odd. Even the official Diverlog software is confused: the Windows versions seems to ignore them, but the Mac version takes them into account. --- src/oceanic_atom2_parser.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index 98923c7..08a2981 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -607,6 +607,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ if (status != DC_STATUS_SUCCESS) return status; + unsigned int extratime = 0; unsigned int time = 0; unsigned int interval = 1; unsigned int samplerate = 1; @@ -787,6 +788,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); complete = 1; } + + extratime += surftime; } else { // Skip the extra samples. if ((count % samplerate) != 0) { @@ -796,7 +799,23 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ } // Time. - time += interval; + if (parser->model == I450T) { + unsigned int minute = bcd2dec(data[offset + 0]); + unsigned int hour = bcd2dec(data[offset + 1] & 0x0F); + unsigned int second = bcd2dec(data[offset + 2]); + unsigned int timestamp = (hour * 3600) + (minute * 60 ) + second + extratime; + if (timestamp < time) { + ERROR (abstract->context, "Timestamp moved backwards."); + return DC_STATUS_DATAFORMAT; + } else if (timestamp == time) { + WARNING (abstract->context, "Unexpected sample with the same timestamp ignored."); + offset += length; + continue; + } + time = timestamp; + } else { + time += interval; + } sample.time = time; if (callback) callback (DC_SAMPLE_TIME, sample, userdata); From ae954af768c7b52e8348e3a5aa5456b6fe6455bc Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sat, 21 Jan 2017 09:39:28 +0100 Subject: [PATCH 08/10] Parse the sample interval correctly The Cressi Drake supports a sample interval of 1, 2, 3 and 4 seconds. --- src/cressi_leonardo_parser.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/cressi_leonardo_parser.c b/src/cressi_leonardo_parser.c index cd97d9a..54ffbb4 100644 --- a/src/cressi_leonardo_parser.c +++ b/src/cressi_leonardo_parser.c @@ -125,7 +125,11 @@ cressi_leonardo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, u unsigned int interval = 20; if (parser->model == DRAKE) { - interval = 1; + interval = data[0x17]; + } + if (interval == 0) { + ERROR(abstract->context, "Invalid sample interval"); + return DC_STATUS_DATAFORMAT; } dc_gasmix_t *gasmix = (dc_gasmix_t *) value; @@ -172,7 +176,11 @@ cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac unsigned int time = 0; unsigned int interval = 20; if (parser->model == DRAKE) { - interval = 1; + interval = data[0x17]; + } + if (interval == 0) { + ERROR(abstract->context, "Invalid sample interval"); + return DC_STATUS_DATAFORMAT; } unsigned int gasmix_previous = 0xFFFFFFFF; From 9cb6856bfb34593df4ed811e3d15d6a56dbbc34a Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 30 Jan 2017 20:50:26 +0100 Subject: [PATCH 09/10] Fix the ndl/deco and rbt samples The Air Time Remaining (ATR) and Dive Time Remaining (DTR) fields have been swapped. --- src/oceanic_atom2_parser.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index 08a2981..2fcf8b3 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -945,14 +945,10 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ decotime = array_uint16_le(data + offset + 6); have_deco = 1; } else if (parser->model == ATOM31 || parser->model == VISION || - parser->model == XPAIR) { + parser->model == XPAIR || parser->model == I550T) { decostop = (data[offset + 5] & 0xF0) >> 4; decotime = array_uint16_le(data + offset + 4) & 0x03FF; have_deco = 1; - } else if (parser->model == I550T) { - decostop = (data[offset + 7] & 0xF0) >> 4; - decotime = array_uint16_le(data + offset + 6) & 0x03FF; - have_deco = 1; } if (have_deco) { if (decostop) { @@ -974,10 +970,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ } else if (parser->model == I450T) { rbt = array_uint16_le(data + offset + 8) & 0x01FF; have_rbt = 1; - } else if (parser->model == I550T) { - rbt = array_uint16_le(data + offset + 4) & 0x03FF; - have_rbt = 1; - } else if (parser->model == VISION || parser->model == XPAIR) { + } else if (parser->model == VISION || parser->model == XPAIR || + parser->model == I550T) { rbt = array_uint16_le(data + offset + 6) & 0x03FF; have_rbt = 1; } From 0832f97492675b753a96047974e75c947c67ea79 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 31 Jan 2017 22:15:53 +0100 Subject: [PATCH 10/10] Fix the name of the Aqualung i550 The Aqualung i550 doesn't have a "T" (which probably stands for transmitter) in its name. --- src/descriptor.c | 2 +- src/oceanic_atom2_parser.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index 5010635..f058e94 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -202,7 +202,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559}, {"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A}, {"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641}, - {"Aqualung", "i550T", DC_FAMILY_OCEANIC_ATOM2, 0x4642}, + {"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642}, /* Mares Nemo */ {"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0}, {"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0}, diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index 2fcf8b3..ea041a2 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -83,7 +83,7 @@ #define I300 0x4559 #define I750TC 0x455A #define I450T 0x4641 -#define I550T 0x4642 +#define I550 0x4642 #define NORMAL 0 #define GAUGE 1 @@ -154,7 +154,7 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned model == OCS || model == PROPLUS3 || model == A300 || model == MANTA || model == INSIGHT2 || model == ZEN || - model == I300 || model == I550T) { + model == I300 || model == I550) { parser->headersize -= PAGESIZE; } else if (model == VT4 || model == VT41) { parser->headersize += PAGESIZE; @@ -245,7 +245,7 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim case ATOM31: case A300AI: case OCI: - case I550T: + case I550: case VISION: case XPAIR: datetime->year = ((p[5] & 0xE0) >> 5) + ((p[7] & 0xE0) >> 2) + 2000; @@ -850,7 +850,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ } else { unsigned int sign; if (parser->model == DG03 || parser->model == PROPLUS3 || - parser->model == I550T) + parser->model == I550) sign = (~data[offset + 5] & 0x04) >> 2; else if (parser->model == VOYAGER2G || parser->model == AMPHOS || parser->model == AMPHOSAIR || parser->model == ZENAIR) @@ -881,7 +881,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == ATOM3 || parser->model == ATOM31 || parser->model == ZENAIR ||parser->model == A300AI || parser->model == DG03 || parser->model == PROPLUS3 || - parser->model == AMPHOSAIR || parser->model == I550T || + parser->model == AMPHOSAIR || parser->model == I550 || parser->model == VISION || parser->model == XPAIR) pressure = (((data[offset + 0] & 0x03) << 8) + data[offset + 1]) * 5; else if (parser->model == TX1 || parser->model == A300CS || @@ -945,7 +945,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ decotime = array_uint16_le(data + offset + 6); have_deco = 1; } else if (parser->model == ATOM31 || parser->model == VISION || - parser->model == XPAIR || parser->model == I550T) { + parser->model == XPAIR || parser->model == I550) { decostop = (data[offset + 5] & 0xF0) >> 4; decotime = array_uint16_le(data + offset + 4) & 0x03FF; have_deco = 1; @@ -971,7 +971,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ rbt = array_uint16_le(data + offset + 8) & 0x01FF; have_rbt = 1; } else if (parser->model == VISION || parser->model == XPAIR || - parser->model == I550T) { + parser->model == I550) { rbt = array_uint16_le(data + offset + 6) & 0x03FF; have_rbt = 1; }