From c26755624daf363a65ea6ce234a3160094da8df2 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 22 Jun 2017 21:17:46 +0200 Subject: [PATCH 01/13] Fix the id string offset Since commit 344bfab229a17c7227b9bec02f616505a8d9e998 only a subset of the id string is used to detect the model. But because the offset was never updated, the model detection always fails now. --- src/cochran_commander.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index d2c47b0..83dd14a 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -207,7 +207,7 @@ cochran_commander_get_model (cochran_commander_device_t *device) unsigned int model = 0xFFFFFFFF; for (unsigned int i = 0; i < C_ARRAY_SIZE(models); ++i) { - if (memcmp (device->id + 0x3B, models[i].id, sizeof(models[i].id) - 1) == 0) { + if (memcmp (device->id + 0x3D, models[i].id, sizeof(models[i].id) - 1) == 0) { model = models[i].model; break; } From 346391ae2c6e0c42d988e7950b8dd3c0b589b50c Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Wed, 31 May 2017 19:24:47 -0400 Subject: [PATCH 02/13] Change profile download to be incremental It will result in a 30 minute download for full computers but it significantly reduces the time to download partial dives. --- src/cochran_commander.c | 360 ++++++++++++++++++++++------------------ 1 file changed, 203 insertions(+), 157 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 83dd14a..e0c991f 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -28,6 +28,7 @@ #include "device-private.h" #include "serial.h" #include "array.h" +#include "ringbuffer.h" #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) @@ -41,6 +42,11 @@ typedef enum cochran_endian_t { ENDIAN_BE, } cochran_endian_t; +typedef enum cochran_profile_size_t { + PROFILE_SIZE_FULL, + PROFILE_SIZE_READ, +} cochran_profile_size_t; + typedef struct cochran_commander_model_t { unsigned char id[2 + 1]; unsigned int model; @@ -49,10 +55,10 @@ typedef struct cochran_commander_model_t { typedef struct cochran_data_t { unsigned char config[1024]; unsigned char *logbook; - unsigned char *sample; unsigned short int dive_count; int fp_dive_num; + int invalid_profile_dive_num; unsigned int logbook_size; @@ -430,23 +436,120 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t } -static void +/* + * For corrupt dives the end-of-samples pointer is 0xFFFFFFFF + * search for a reasonable size, e.g. using next dive start sample + * or end-of-samples to limit searching for recoverable samples + */ +static unsigned int +cochran_commander_guess_sample_end_address(cochran_commander_device_t *device, cochran_data_t *data, unsigned int log_num) +{ + const unsigned char *log_entry = data->logbook + device->layout->rb_logbook_entry_size * log_num; + + if (log_num == data->dive_count) + // Return next usable address from config page + return array_uint32_le(data->config + device->layout->rb_profile_end); + + // Next log's start address + return array_uint32_le(log_entry + device->layout->rb_logbook_entry_size + device->layout->pt_profile_begin); +} + + +static unsigned int +cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_t *data, int dive_num, cochran_profile_size_t type) +{ + + const unsigned char *log_entry = data->logbook + dive_num * device->layout->rb_logbook_entry_size; + unsigned int sample_start_address = 0xFFFFFFFF; + unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end); + + if (type == PROFILE_SIZE_FULL) { + // actual size, includes pre-dive events + sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_pre); + } else if (type == PROFILE_SIZE_READ) { + // read size, only include dive profile + sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); + } + + // Validate addresses + if (sample_start_address < device->layout->rb_profile_begin || + sample_start_address > device->layout->rb_profile_end || + sample_end_address < device->layout->rb_profile_begin || + (sample_end_address > device->layout->rb_profile_end && + sample_end_address != 0xFFFFFFFF)) { + return 0; + } + + if (sample_end_address == 0xFFFFFFFF) + // Corrupt dive, guess the end address + sample_end_address = cochran_commander_guess_sample_end_address(device, data, dive_num); + + return ringbuffer_distance(sample_start_address, sample_end_address, 0, device->layout->rb_profile_begin, device->layout->rb_profile_end); +} + + +/* + * Do several things. Find the log that matches the fingerprint, + * calculate the total read size for progress indicator, + * Determine the most recent dive without profile data. + */ + +static unsigned int cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_data_t *data) { - // Skip to fingerprint to reduce time - if (data->dive_count < device->layout->rb_logbook_entry_count) - data->fp_dive_num = data->dive_count; - else - data->fp_dive_num = device->layout->rb_logbook_entry_count; - data->fp_dive_num--; + // We track profile ringbuffer usage to determine which dives have profile data + int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin; - while (data->fp_dive_num >= 0 && memcmp(device->fingerprint, - data->logbook + data->fp_dive_num * device->layout->rb_logbook_entry_size, - sizeof(device->fingerprint))) - data->fp_dive_num--; + int dive_count = -1; + + // Start at end of log + if (data->dive_count < device->layout->rb_logbook_entry_count) + dive_count = data->dive_count; + else + dive_count = device->layout->rb_logbook_entry_count; + dive_count--; + + unsigned int sample_read_size = 0; + data->invalid_profile_dive_num = -1; + + // Remove the pre-dive events that occur after the last dive + unsigned int rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + unsigned int last_dive_end_address = array_uint32_le(data->logbook + dive_count * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end); + if (rb_head_ptr > last_dive_end_address) + profile_capacity_remaining -= rb_head_ptr - last_dive_end_address; + + // Loop through dives to find FP, Accumulate profile data size, + // and find the last dive with invalid profile + for (int i = dive_count; i > 0; i--) { + unsigned char *log_entry = data->logbook + i * device->layout->rb_logbook_entry_size; + + // We're done if we find the fingerprint + if (!memcmp(device->fingerprint, log_entry, sizeof(device->fingerprint))) { + data->fp_dive_num = i; + break; + } + + unsigned int sample_size = cochran_commander_profile_size(device, data, i, PROFILE_SIZE_FULL); + unsigned int read_size = cochran_commander_profile_size(device, data, i, PROFILE_SIZE_READ); + + // Determine if sample exists + if (profile_capacity_remaining > 0) { + // Subtract this dive's profile size including post-dive events + profile_capacity_remaining -= sample_size; + if (profile_capacity_remaining < 0) { + // Save the last dive that is missing profile data + data->invalid_profile_dive_num = i; + } + // Accumulate read size for progress bar + sample_read_size += read_size; + } + } + + return sample_read_size; } + static void cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_data_t *data) { @@ -509,103 +612,6 @@ cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_d } -/* - * For corrupt dives the end-of-samples pointer is 0xFFFFFFFF - * search for a reasonable size, e.g. using next dive start sample - * or end-of-samples to limit searching for recoverable samples - */ -static unsigned int -cochran_commander_guess_sample_end_address(cochran_commander_device_t *device, cochran_data_t *data, unsigned int log_num) -{ - const unsigned char *log_entry = data->logbook + device->layout->rb_logbook_entry_size * log_num; - - if (log_num == data->dive_count) - // Return next usable address from config page - return array_uint32_le(data->config + device->layout->rb_profile_end); - - // Next log's start address - return array_uint32_le(log_entry + device->layout->rb_logbook_entry_size + device->layout->pt_profile_begin); -} - - -static dc_status_t -cochran_commander_read_all (cochran_commander_device_t *device, cochran_data_t *data) -{ - dc_device_t *abstract = (dc_device_t *) device; - dc_status_t rc = DC_STATUS_SUCCESS; - - // Calculate max data sizes - unsigned int max_config = sizeof(data->config); - unsigned int max_logbook = device->layout->rb_logbook_end - device->layout->rb_logbook_begin; - unsigned int max_sample = device->layout->rb_profile_end - device->layout->rb_profile_begin; - - dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; - progress.maximum = max_config + max_logbook + max_sample; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - // Emit ID block - dc_event_vendor_t vendor; - vendor.data = device->id; - vendor.size = sizeof (device->id); - device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - - // Read config - rc = cochran_commander_read_config(device, &progress, data->config, sizeof(data->config)); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Determine size of dive list to read. - if (device->layout->endian == ENDIAN_LE) - data->dive_count = array_uint16_le (data->config + device->layout->cf_dive_count); - else - data->dive_count = array_uint16_be (data->config + device->layout->cf_dive_count); - - if (data->dive_count > device->layout->rb_logbook_entry_count) { - data->logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size; - } else { - data->logbook_size = data->dive_count * device->layout->rb_logbook_entry_size; - } - - progress.maximum -= max_logbook - data->logbook_size; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - // Allocate space for log book. - data->logbook = (unsigned char *) malloc(data->logbook_size); - if (data->logbook == NULL) { - ERROR (abstract->context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - // Request log book - rc = cochran_commander_read(device, &progress, 0, data->logbook, data->logbook_size); - if (rc != DC_STATUS_SUCCESS) - return rc; - - // Determine sample memory to read - cochran_commander_find_fingerprint(device, data); - cochran_commander_get_sample_parms(device, data); - - progress.maximum -= max_sample - data->sample_size; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - - if (data->sample_size > 0) { - data->sample = (unsigned char *) malloc(data->sample_size); - if (data->sample == NULL) { - ERROR (abstract->context, "Failed to allocate memory."); - return DC_STATUS_NOMEMORY; - } - - // Read the sample data - rc = cochran_commander_read (device, &progress, data->sample_data_offset, data->sample, data->sample_size); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the sample data."); - return rc; - } - } - - return DC_STATUS_SUCCESS; -} - dc_status_t cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const char *name) { @@ -771,10 +777,66 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call cochran_data_t data; data.logbook = NULL; - data.sample = NULL; - status = cochran_commander_read_all (device, &data); - if (status != DC_STATUS_SUCCESS) + + // Calculate max data sizes + unsigned int max_config = sizeof(data.config); + unsigned int max_logbook = device->layout->rb_logbook_end - device->layout->rb_logbook_begin; + unsigned int max_sample = device->layout->rb_profile_end - device->layout->rb_profile_begin; + + // setup progress indication + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + progress.maximum = max_config + max_logbook + max_sample; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Emit ID block + dc_event_vendor_t vendor; + vendor.data = device->id; + vendor.size = sizeof (device->id); + device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); + + // Read config + dc_status_t rc = DC_STATUS_SUCCESS; + rc = cochran_commander_read_config(device, &progress, data.config, sizeof(data.config)); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Determine size of dive list to read. + if (device->layout->endian == ENDIAN_LE) + data.dive_count = array_uint16_le (data.config + device->layout->cf_dive_count); + else + data.dive_count = array_uint16_be (data.config + device->layout->cf_dive_count); + + if (data.dive_count > device->layout->rb_logbook_entry_count) { + data.logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size; + } else { + data.logbook_size = data.dive_count * device->layout->rb_logbook_entry_size; + } + + // Update progress indicator with new maximum + progress.maximum -= max_logbook - data.logbook_size; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Allocate space for log book. + data.logbook = (unsigned char *) malloc(data.logbook_size); + if (data.logbook == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Request log book + rc = cochran_commander_read(device, &progress, 0, data.logbook, data.logbook_size); + if (rc != DC_STATUS_SUCCESS) { + status = rc; goto error; + } + + // Locate fingerprint, recent dive with invalid profile and calc read size + unsigned int profile_read_size = cochran_commander_find_fingerprint(device, &data); + // Update progress indicator with new maximum + progress.maximum -= (max_sample - profile_read_size); + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + cochran_commander_get_sample_parms(device, &data); // Emit a device info event. dc_event_devinfo_t devinfo; @@ -792,9 +854,6 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call goto error; } - // We track profile ringbuffer usage to determine which dives have profile data - int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin; - unsigned int dive_count = 0; if (data.dive_count < device->layout->rb_logbook_entry_count) dive_count = data.dive_count; @@ -808,44 +867,11 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call unsigned int sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end); - // Validate - if (sample_start_address < device->layout->rb_profile_begin || - sample_start_address > device->layout->rb_profile_end || - sample_end_address < device->layout->rb_profile_begin || - (sample_end_address > device->layout->rb_profile_end && - sample_end_address != 0xFFFFFFFF)) { - continue; - } - - if (sample_end_address == 0xFFFFFFFF) - // Corrupt dive, guess the end address - sample_end_address = cochran_commander_guess_sample_end_address(device, &data, i); - - // Determine if sample exists - if (profile_capacity_remaining > 0) { - // Subtract this dive's profile size including post-dive events - profile_capacity_remaining -= (last_start_address - sample_start_address); - // Adjust for a dive that wraps the buffer - if (sample_start_address > last_start_address) - profile_capacity_remaining -= device->layout->rb_profile_end - device->layout->rb_profile_begin; - } - last_start_address = sample_start_address; - - unsigned char *sample = NULL; int sample_size = 0; - if (profile_capacity_remaining < 0) { - // There is no profile for this dive - sample = NULL; - sample_size = 0; - } else { - // Calculate the size of the profile only - sample = data.sample + sample_start_address - data.sample_data_offset; - sample_size = sample_end_address - sample_start_address; - if (sample_size < 0) - // Adjust for ring buffer wrap-around - sample_size += device->layout->rb_profile_end - device->layout->rb_profile_begin; - } + // Determine if profile exists + if (i > data.invalid_profile_dive_num) + sample_size = cochran_commander_profile_size(device, &data, i, PROFILE_SIZE_READ); // Build dive blob unsigned int dive_size = device->layout->rb_logbook_entry_size + sample_size; @@ -857,17 +883,38 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call memcpy(dive, log_entry, device->layout->rb_logbook_entry_size); // log - // Copy profile data + // Read profile data if (sample_size) { + if (sample_end_address == 0xFFFFFFFF) + // Corrupt dive, guess the end address + sample_end_address = cochran_commander_guess_sample_end_address(device, &data, i); + if (sample_start_address <= sample_end_address) { - memcpy(dive + device->layout->rb_logbook_entry_size, sample, sample_size); + rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the sample data."); + status = rc; + free(dive); + goto error; + } } else { // It wrapped the buffer, copy two sections unsigned int size = device->layout->rb_profile_end - sample_start_address; - memcpy(dive + device->layout->rb_logbook_entry_size, sample, size); - memcpy(dive + device->layout->rb_logbook_entry_size + size, - data.sample, sample_end_address - device->layout->rb_profile_begin); + rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, size); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the sample data."); + status = rc; + free(dive); + goto error; + } + rc = cochran_commander_read (device, &progress, device->layout->rb_profile_begin, dive + device->layout->rb_logbook_entry_size + size, sample_end_address - device->layout->rb_profile_begin); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the sample data."); + status = rc; + free(dive); + goto error; + } } } @@ -881,6 +928,5 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call error: free(data.logbook); - free(data.sample); return status; } From b3d2c603ddec9758fb36706bbde46ce23ca9f0ed Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Wed, 31 May 2017 19:24:47 -0400 Subject: [PATCH 03/13] Retry read operations on failure [Jef Driesen: Modified to retry only for non-fatal errors.] --- src/cochran_commander.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index e0c991f..2854c78 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -32,6 +32,8 @@ #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) +#define MAXRETRIES 2 + #define COCHRAN_MODEL_COMMANDER_AIR_NITROX 0 #define COCHRAN_MODEL_EMC_14 1 #define COCHRAN_MODEL_EMC_16 2 @@ -435,6 +437,31 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t return DC_STATUS_SUCCESS; } +static unsigned int +cochran_commander_read_retry (cochran_commander_device_t *device, dc_event_progress_t *progress, unsigned int address, unsigned char data[], unsigned int size) +{ + // Save the state of the progress events. + unsigned int saved = progress->current; + + unsigned int nretries = 0; + dc_status_t rc = DC_STATUS_SUCCESS; + while ((rc = cochran_commander_read (device, progress, address, data, size)) != DC_STATUS_SUCCESS) { + // Automatically discard a corrupted packet, + // and request a new one. + if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT) + return rc; + + // Abort if the maximum number of retries is reached. + if (nretries++ >= MAXRETRIES) + return rc; + + // Restore the state of the progress events. + progress->current = saved; + } + + return rc; +} + /* * For corrupt dives the end-of-samples pointer is 0xFFFFFFFF @@ -890,7 +917,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call sample_end_address = cochran_commander_guess_sample_end_address(device, &data, i); if (sample_start_address <= sample_end_address) { - rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); + rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; @@ -901,14 +928,15 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // It wrapped the buffer, copy two sections unsigned int size = device->layout->rb_profile_end - sample_start_address; - rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, size); + rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, size); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; free(dive); goto error; } - rc = cochran_commander_read (device, &progress, device->layout->rb_profile_begin, dive + device->layout->rb_logbook_entry_size + size, sample_end_address - device->layout->rb_profile_begin); + + rc = cochran_commander_read_retry (device, &progress, device->layout->rb_profile_begin, dive + device->layout->rb_logbook_entry_size + size, sample_end_address - device->layout->rb_profile_begin); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; From 45f06056789a1986c26f8af38e1bc107eaa53c95 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Wed, 31 May 2017 19:24:47 -0400 Subject: [PATCH 04/13] Fix problems with wrapped logbook ringbuffer --- src/cochran_commander.c | 69 +++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 2854c78..b09e9fd 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -528,6 +528,7 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d int profile_capacity_remaining = device->layout->rb_profile_end - device->layout->rb_profile_begin; int dive_count = -1; + data->fp_dive_num = -1; // Start at end of log if (data->dive_count < device->layout->rb_logbook_entry_count) @@ -545,19 +546,32 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d if (rb_head_ptr > last_dive_end_address) profile_capacity_remaining -= rb_head_ptr - last_dive_end_address; + unsigned int head_dive = 0, tail_dive = 0; + + if (data->dive_count <= device->layout->rb_logbook_entry_count) { + head_dive = data->dive_count; + tail_dive = 0; + } else { + // Log wrapped + tail_dive = data->dive_count % device->layout->rb_logbook_entry_count; + head_dive = tail_dive; + } + // Loop through dives to find FP, Accumulate profile data size, // and find the last dive with invalid profile - for (int i = dive_count; i > 0; i--) { - unsigned char *log_entry = data->logbook + i * device->layout->rb_logbook_entry_size; + for (unsigned int i = 0; i <= dive_count; ++i) { + unsigned int idx = (device->layout->rb_logbook_entry_count + head_dive - (i + 1)) % device->layout->rb_logbook_entry_count; + + unsigned char *log_entry = data->logbook + idx * device->layout->rb_logbook_entry_size; // We're done if we find the fingerprint if (!memcmp(device->fingerprint, log_entry, sizeof(device->fingerprint))) { - data->fp_dive_num = i; + data->fp_dive_num = idx; break; } - unsigned int sample_size = cochran_commander_profile_size(device, data, i, PROFILE_SIZE_FULL); - unsigned int read_size = cochran_commander_profile_size(device, data, i, PROFILE_SIZE_READ); + unsigned int sample_size = cochran_commander_profile_size(device, data, idx, PROFILE_SIZE_FULL); + unsigned int read_size = cochran_commander_profile_size(device, data, idx, PROFILE_SIZE_READ); // Determine if sample exists if (profile_capacity_remaining > 0) { @@ -565,7 +579,7 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d profile_capacity_remaining -= sample_size; if (profile_capacity_remaining < 0) { // Save the last dive that is missing profile data - data->invalid_profile_dive_num = i; + data->invalid_profile_dive_num = idx; } // Accumulate read size for progress bar sample_read_size += read_size; @@ -833,6 +847,10 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call else data.dive_count = array_uint16_be (data.config + device->layout->cf_dive_count); + if (data.dive_count == 0) + // No dives to read + return DC_STATUS_SUCCESS; + if (data.dive_count > device->layout->rb_logbook_entry_count) { data.logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size; } else { @@ -881,15 +899,31 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call goto error; } - unsigned int dive_count = 0; - if (data.dive_count < device->layout->rb_logbook_entry_count) - dive_count = data.dive_count; - else - dive_count = device->layout->rb_logbook_entry_count; + unsigned int head_dive = 0, tail_dive = 0, dive_count = 0; + + if (data.dive_count <= device->layout->rb_logbook_entry_count) { + head_dive = data.dive_count; + tail_dive = 0; + } else { + // Log wrapped + tail_dive = data.dive_count % device->layout->rb_logbook_entry_count; + head_dive = tail_dive; + } + + // Change tail to dive following the fingerprint dive. + if (data.fp_dive_num > -1) + tail_dive = (data.fp_dive_num + 1) % device->layout->rb_logbook_entry_count; + + // Number of dives to read + dive_count = (device->layout->rb_logbook_entry_count + head_dive - tail_dive) % device->layout->rb_logbook_entry_count; + + int invalid_profile_flag = 0; // Loop through each dive - for (int i = dive_count - 1; i > data.fp_dive_num; i--) { - unsigned char *log_entry = data.logbook + i * device->layout->rb_logbook_entry_size; + for (unsigned int i = 0; i < dive_count; ++i) { + unsigned int idx = (device->layout->rb_logbook_entry_count + head_dive - (i + 1)) % device->layout->rb_logbook_entry_count; + + unsigned char *log_entry = data.logbook + idx * device->layout->rb_logbook_entry_size; unsigned int sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end); @@ -897,8 +931,11 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call int sample_size = 0; // Determine if profile exists - if (i > data.invalid_profile_dive_num) - sample_size = cochran_commander_profile_size(device, &data, i, PROFILE_SIZE_READ); + if (idx == data.invalid_profile_dive_num) + invalid_profile_flag = 1; + + if (!invalid_profile_flag) + sample_size = cochran_commander_profile_size(device, &data, idx, PROFILE_SIZE_READ); // Build dive blob unsigned int dive_size = device->layout->rb_logbook_entry_size + sample_size; @@ -914,7 +951,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call if (sample_size) { if (sample_end_address == 0xFFFFFFFF) // Corrupt dive, guess the end address - sample_end_address = cochran_commander_guess_sample_end_address(device, &data, i); + sample_end_address = cochran_commander_guess_sample_end_address(device, &data, idx); if (sample_start_address <= sample_end_address) { rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); From 216e393f434def2361a57178c8c4487999f32095 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Fri, 2 Jun 2017 19:51:28 -0400 Subject: [PATCH 05/13] Add support for Pre-21000 s/n Commander dive computers This adds support for older Cochran Commander dive computers, specifically Commanders with serial numbers prior to 21000. This also renames "Commander" model to "Commander II" and adds "Commander I" to refer to pre-21000 models. --- src/array.c | 7 +++ src/array.h | 3 ++ src/cochran_commander.c | 92 +++++++++++++++++++++++++------- src/cochran_commander_parser.c | 96 ++++++++++++++++++++++++++++------ src/descriptor.c | 10 ++-- 5 files changed, 170 insertions(+), 38 deletions(-) diff --git a/src/array.c b/src/array.c index 13a73e5..5574083 100644 --- a/src/array.c +++ b/src/array.c @@ -198,6 +198,13 @@ array_uint32_le (const unsigned char data[]) } +unsigned int +array_uint32_word_be (const unsigned char data[]) +{ + return data[1] + (data[0] << 8) + (data[3] << 16) + (data[2] << 24); +} + + void array_uint32_le_set (unsigned char data[], const unsigned int input) { diff --git a/src/array.h b/src/array.h index cd0a3a1..da70efa 100644 --- a/src/array.h +++ b/src/array.h @@ -64,6 +64,9 @@ array_uint32_be (const unsigned char data[]); unsigned int array_uint32_le (const unsigned char data[]); +unsigned int +array_uint32_word_be (const unsigned char data[]); + void array_uint32_le_set (unsigned char data[], const unsigned int input); diff --git a/src/cochran_commander.c b/src/cochran_commander.c index b09e9fd..95c00fe 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -34,14 +34,16 @@ #define MAXRETRIES 2 -#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 0 -#define COCHRAN_MODEL_EMC_14 1 -#define COCHRAN_MODEL_EMC_16 2 -#define COCHRAN_MODEL_EMC_20 3 +#define COCHRAN_MODEL_COMMANDER_PRE21000 0 +#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 1 +#define COCHRAN_MODEL_EMC_14 2 +#define COCHRAN_MODEL_EMC_16 3 +#define COCHRAN_MODEL_EMC_20 4 typedef enum cochran_endian_t { ENDIAN_LE, ENDIAN_BE, + ENDIAN_WORD_BE, } cochran_endian_t; typedef enum cochran_profile_size_t { @@ -50,7 +52,7 @@ typedef enum cochran_profile_size_t { } cochran_profile_size_t; typedef struct cochran_commander_model_t { - unsigned char id[2 + 1]; + unsigned char id[3 + 1]; unsigned int model; } cochran_commander_model_t; @@ -86,7 +88,9 @@ typedef struct cochran_device_layout_t { // Profile ringbuffer. unsigned int rb_profile_begin; unsigned int rb_profile_end; - // Profile pointers. + // pointers. + unsigned int pt_fingerprint; + unsigned int fingerprint_size; unsigned int pt_profile_pre; unsigned int pt_profile_begin; unsigned int pt_profile_end; @@ -117,6 +121,30 @@ static const dc_device_vtable_t cochran_commander_device_vtable = { cochran_commander_device_close /* close */ }; +// Cochran Commander pre-21000 s/n +static const cochran_device_layout_t cochran_cmdr_1_device_layout = { + COCHRAN_MODEL_COMMANDER_PRE21000, // model + 24, // address_bits + ENDIAN_WORD_BE, // endian + 115200, // baudrate + 0x046, // cf_dive_count + 0x6c, // cf_last_log + 0x70, // cf_last_interdive + 0x0AA, // cf_serial_number + 0x00000000, // rb_logbook_begin + 0x00020000, // rb_logbook_end + 256, // rb_logbook_entry_size + 512, // rb_logbook_entry_count + 0x00020000, // rb_profile_begin + 0x00100000, // rb_profile_end + 12, // pt_fingerprint + 4, // fingerprint_size + 28, // pt_profile_pre + 0, // pt_profile_begin + 128, // pt_profile_end +}; + + // Cochran Commander Nitrox static const cochran_device_layout_t cochran_cmdr_device_layout = { COCHRAN_MODEL_COMMANDER_AIR_NITROX, // model @@ -133,6 +161,8 @@ static const cochran_device_layout_t cochran_cmdr_device_layout = { 512, // rb_logbook_entry_count 0x00020000, // rb_profile_begin 0x00100000, // rb_profile_end + 0, // pt_fingerprint + 6, // fingerprint_size 30, // pt_profile_pre 6, // pt_profile_begin 128, // pt_profile_end @@ -154,6 +184,8 @@ static const cochran_device_layout_t cochran_emc14_device_layout = { 256, // rb_logbook_entry_count 0x00022000, // rb_profile_begin 0x00200000, // rb_profile_end + 0, // pt_fingerprint + 6, // fingerprint_size 30, // pt_profile_pre 6, // pt_profile_begin 256, // pt_profile_end @@ -175,6 +207,8 @@ static const cochran_device_layout_t cochran_emc16_device_layout = { 1024, // rb_logbook_entry_count 0x00094000, // rb_profile_begin 0x00800000, // rb_profile_end + 0, // pt_fingerprint + 6, // fingerprint_size 30, // pt_profile_pre 6, // pt_profile_begin 256, // pt_profile_end @@ -196,6 +230,8 @@ static const cochran_device_layout_t cochran_emc20_device_layout = { 1024, // rb_logbook_entry_count 0x00094000, // rb_profile_begin 0x01000000, // rb_profile_end + 0, // pt_fingerprint + 6, // fingerprint_size 30, // pt_profile_pre 6, // pt_profile_begin 256, // pt_profile_end @@ -207,10 +243,14 @@ static unsigned int cochran_commander_get_model (cochran_commander_device_t *device) { const cochran_commander_model_t models[] = { - {"\x11""2", COCHRAN_MODEL_COMMANDER_AIR_NITROX}, - {"73", COCHRAN_MODEL_EMC_14}, - {"A3", COCHRAN_MODEL_EMC_16}, - {"23", COCHRAN_MODEL_EMC_20}, + {"\x11""21", COCHRAN_MODEL_COMMANDER_PRE21000}, + {"\x11""22", COCHRAN_MODEL_COMMANDER_AIR_NITROX}, + {"730", COCHRAN_MODEL_EMC_14}, + {"731", COCHRAN_MODEL_EMC_14}, + {"A30", COCHRAN_MODEL_EMC_16}, + {"A31", COCHRAN_MODEL_EMC_16}, + {"230", COCHRAN_MODEL_EMC_20}, + {"231", COCHRAN_MODEL_EMC_20}, }; unsigned int model = 0xFFFFFFFF; @@ -541,7 +581,11 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d data->invalid_profile_dive_num = -1; // Remove the pre-dive events that occur after the last dive - unsigned int rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + unsigned int rb_head_ptr = 0; + if (device->layout->endian == ENDIAN_WORD_BE) + rb_head_ptr = (array_uint32_word_be(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + else + rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; unsigned int last_dive_end_address = array_uint32_le(data->logbook + dive_count * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end); if (rb_head_ptr > last_dive_end_address) profile_capacity_remaining -= rb_head_ptr - last_dive_end_address; @@ -565,7 +609,7 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d unsigned char *log_entry = data->logbook + idx * device->layout->rb_logbook_entry_size; // We're done if we find the fingerprint - if (!memcmp(device->fingerprint, log_entry, sizeof(device->fingerprint))) { + if (!memcmp(device->fingerprint, log_entry + device->layout->pt_fingerprint, device->layout->fingerprint_size)) { data->fp_dive_num = idx; break; } @@ -694,6 +738,9 @@ cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const c unsigned int model = cochran_commander_get_model(device); switch (model) { + case COCHRAN_MODEL_COMMANDER_PRE21000: + device->layout = &cochran_cmdr_1_device_layout; + break; case COCHRAN_MODEL_COMMANDER_AIR_NITROX: device->layout = &cochran_cmdr_device_layout; break; @@ -744,13 +791,13 @@ cochran_commander_device_set_fingerprint (dc_device_t *abstract, const unsigned { cochran_commander_device_t *device = (cochran_commander_device_t *) abstract; - if (size && size != sizeof (device->fingerprint)) + if (size && size != device->layout->fingerprint_size) return DC_STATUS_INVALIDARGS; if (size) - memcpy (device->fingerprint, data, sizeof (device->fingerprint)); + memcpy (device->fingerprint, data, device->layout->fingerprint_size); else - memset (device->fingerprint, 0xFF, sizeof (device->fingerprint)); + memset (device->fingerprint, 0xFF, sizeof(device->fingerprint)); return DC_STATUS_SUCCESS; } @@ -887,12 +934,21 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call dc_event_devinfo_t devinfo; devinfo.model = device->layout->model; devinfo.firmware = 0; // unknown - devinfo.serial = array_uint32_le(data.config + device->layout->cf_serial_number); + if (device->layout->endian == ENDIAN_WORD_BE) + devinfo.serial = array_uint32_word_be(data.config + device->layout->cf_serial_number); + else + devinfo.serial = array_uint32_le(data.config + device->layout->cf_serial_number); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Calculate profile RB effective head pointer // Cochran seems to erase 8K chunks so round up. - unsigned int last_start_address = (array_uint32_le(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + unsigned int last_start_address; + if (device->layout->endian == ENDIAN_WORD_BE) + last_start_address = (array_uint32_word_be(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + else + last_start_address = (array_uint32_le(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + if (last_start_address < device->layout->rb_profile_begin || last_start_address > device->layout->rb_profile_end) { ERROR(abstract->context, "Invalid profile ringbuffer head pointer in Cochran config block."); status = DC_STATUS_DATAFORMAT; @@ -983,7 +1039,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call } } - if (callback && !callback (dive, dive_size, dive, sizeof(device->fingerprint), userdata)) { + if (callback && !callback (dive, dive_size, dive + device->layout->pt_fingerprint, device->layout->fingerprint_size, userdata)) { free(dive); break; } diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index ab854ad..1c14536 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -31,10 +31,14 @@ #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) -#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 0 -#define COCHRAN_MODEL_EMC_14 1 -#define COCHRAN_MODEL_EMC_16 2 -#define COCHRAN_MODEL_EMC_20 3 +#define COCHRAN_MODEL_COMMANDER_PRE21000 0 +#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 1 +#define COCHRAN_MODEL_EMC_14 2 +#define COCHRAN_MODEL_EMC_16 3 +#define COCHRAN_MODEL_EMC_20 4 + +// Cochran time stamps start at Jan 1, 1992 +#define COCHRAN_EPOCH 694242000 #define UNSUPPORTED 0xFFFFFFFF @@ -43,11 +47,19 @@ typedef enum cochran_sample_format_t { SAMPLE_EMC, } cochran_sample_format_t; + +typedef enum cochran_date_encoding_t { + DATE_ENCODING_MSDHYM, + DATE_ENCODING_SMHDMY, + DATE_ENCODING_TICKS, +} cochran_date_encoding_t; + typedef struct cochran_parser_layout_t { cochran_sample_format_t format; unsigned int headersize; unsigned int samplesize; - unsigned int second, minute, hour, day, month, year; + cochran_date_encoding_t date_encoding; + unsigned int datetime; unsigned int pt_profile_begin; unsigned int water_conductivity; unsigned int pt_profile_pre; @@ -101,11 +113,36 @@ static const dc_parser_vtable_t cochran_commander_parser_vtable = { NULL /* destroy */ }; +static const cochran_parser_layout_t cochran_cmdr_1_parser_layout = { + SAMPLE_CMDR, // type + 256, // headersize + 2, // samplesize + DATE_ENCODING_TICKS, // date_encoding + 8, // datetime, 4 bytes + 0, // pt_profile_begin, 4 bytes + 24, // water_conductivity, 1 byte, 0=low(fresh), 2=high(sea) + 28, // pt_profile_pre, 4 bytes + 43, // start_temp, 1 byte, F + 54, // start_depth, 2 bytes, /4=ft + 68, // dive_number, 2 bytes + 73, // altitude, 1 byte, /4=kilofeet + 128, // pt_profile_end, 4 bytes + 153, // end_temp, 1 byte F + 166, // divetime, 2 bytes, minutes + 168, // max_depth, 2 bytes, /4=ft + 170, // avg_depth, 2 bytes, /4=ft + 210, // oxygen, 4 bytes (2 of) 2 bytes, /256=% + UNSUPPORTED, // helium, 4 bytes (2 of) 2 bytes, /256=% + 232, // min_temp, 1 byte, /2+20=F + 233, // max_temp, 1 byte, /2+20=F +}; + static const cochran_parser_layout_t cochran_cmdr_parser_layout = { SAMPLE_CMDR, // type 256, // headersize 2, // samplesize - 1, 0, 3, 2, 5, 4, // second, minute, hour, day, month, year, 1 byte each + DATE_ENCODING_MSDHYM, // date_encoding + 0, // datetime, 6 bytes 6, // pt_profile_begin, 4 bytes 24, // water_conductivity, 1 byte, 0=low(fresh), 2=high(sea) 30, // pt_profile_pre, 4 bytes @@ -128,7 +165,8 @@ static const cochran_parser_layout_t cochran_emc_parser_layout = { SAMPLE_EMC, // type 512, // headersize 3, // samplesize - 0, 1, 2, 3, 4, 5, // second, minute, hour, day, month, year, 1 byte each + DATE_ENCODING_SMHDMY, // date_encoding + 0, // datetime, 6 bytes 6, // pt_profile_begin, 4 bytes 24, // water_conductivity, 1 byte 0=low(fresh), 2=high(sea) 30, // pt_profile_pre, 4 bytes @@ -296,6 +334,11 @@ cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context, unsig parser->model = model; switch (model) { + case COCHRAN_MODEL_COMMANDER_PRE21000: + parser->layout = &cochran_cmdr_1_parser_layout; + parser->events = cochran_cmdr_event_bytes; + parser->nevents = C_ARRAY_SIZE(cochran_cmdr_event_bytes); + break; case COCHRAN_MODEL_COMMANDER_AIR_NITROX: parser->layout = &cochran_cmdr_parser_layout; parser->events = cochran_cmdr_event_bytes; @@ -340,13 +383,32 @@ cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dat if (abstract->size < layout->headersize) return DC_STATUS_DATAFORMAT; + dc_ticks_t ts = 0; + if (datetime) { - datetime->second = data[layout->second]; - datetime->minute = data[layout->minute]; - datetime->hour = data[layout->hour]; - datetime->day = data[layout->day]; - datetime->month = data[layout->month]; - datetime->year = data[layout->year] + (data[layout->year] > 91 ? 1900 : 2000); + switch (layout->date_encoding) + { + case DATE_ENCODING_MSDHYM: + datetime->second = data[layout->datetime + 1]; + datetime->minute = data[layout->datetime + 0]; + datetime->hour = data[layout->datetime + 3]; + datetime->day = data[layout->datetime + 2]; + datetime->month = data[layout->datetime + 5]; + datetime->year = data[layout->datetime + 4] + (data[layout->datetime + 4] > 91 ? 1900 : 2000); + break; + case DATE_ENCODING_SMHDMY: + datetime->second = data[layout->datetime + 0]; + datetime->minute = data[layout->datetime + 1]; + datetime->hour = data[layout->datetime + 2]; + datetime->day = data[layout->datetime + 3]; + datetime->month = data[layout->datetime + 4]; + datetime->year = data[layout->datetime + 5] + (data[layout->datetime + 5] > 91 ? 1900 : 2000); + break; + case DATE_ENCODING_TICKS: + ts = array_uint32_le(data + layout->datetime) + COCHRAN_EPOCH; + dc_datetime_localtime(datetime, ts); + break; + } } return DC_STATUS_SUCCESS; @@ -471,10 +533,11 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb // know what the dive summary values are (i.e. max depth, min temp) if (array_uint32_le(data + layout->pt_profile_end) == 0xFFFFFFFF) { corrupt_dive = 1; + dc_datetime_t d; + cochran_commander_parser_get_datetime(abstract, &d); WARNING(abstract->context, "Incomplete dive on %02d/%02d/%02d at %02d:%02d:%02d, trying to parse samples", - data[layout->year], data[layout->month], data[layout->day], - data[layout->hour], data[layout->minute], data[layout->second]); + d.year, d.month, d.day, d.hour, d.minute, d.second); // Eliminate inter-dive events size = cochran_commander_backparse(parser, samples, size); @@ -484,7 +547,8 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb // and temp every other second. // Prime values from the dive log section - if (parser->model == COCHRAN_MODEL_COMMANDER_AIR_NITROX) { + if (parser->model == COCHRAN_MODEL_COMMANDER_AIR_NITROX || + parser->model == COCHRAN_MODEL_COMMANDER_PRE21000) { // Commander stores start depth in quarter-feet start_depth = array_uint16_le (data + layout->start_depth) / 4.0; } else { diff --git a/src/descriptor.c b/src/descriptor.c index 9b537e1..633debd 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -313,10 +313,12 @@ static const dc_descriptor_t g_descriptors[] = { {"DiveSystem", "iDive2 Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x42}, {"DiveSystem", "iDive2 Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x44}, {"DiveSystem", "iDive2 Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x45}, - {"Cochran", "Commander", DC_FAMILY_COCHRAN_COMMANDER, 0}, - {"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 1}, - {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 2}, - {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 3}, + /* Cochran Commander */ + {"Cochran", "Commander I", DC_FAMILY_COCHRAN_COMMANDER, 0}, + {"Cochran", "Commander II", DC_FAMILY_COCHRAN_COMMANDER, 1}, + {"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 2}, + {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 3}, + {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 4}, }; typedef struct dc_descriptor_iterator_t { From 3a2f2ff0c3767ecfb3d6eda2137eecb1ced4cd6f Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Mon, 12 Jun 2017 15:58:25 -0400 Subject: [PATCH 06/13] Add new EMC device model string --- src/cochran_commander.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 95c00fe..28f2ee4 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -251,6 +251,7 @@ cochran_commander_get_model (cochran_commander_device_t *device) {"A31", COCHRAN_MODEL_EMC_16}, {"230", COCHRAN_MODEL_EMC_20}, {"231", COCHRAN_MODEL_EMC_20}, + {"\x40""30", COCHRAN_MODEL_EMC_20}, }; unsigned int model = 0xFFFFFFFF; From 3545bf158a57a317bbdea51fc9271c27db5561b1 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Mon, 12 Jun 2017 15:58:25 -0400 Subject: [PATCH 07/13] Use a local variable for the layout pointer --- src/cochran_commander.c | 63 +++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 28f2ee4..496d08b 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -862,6 +862,7 @@ static dc_status_t cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { cochran_commander_device_t *device = (cochran_commander_device_t *) abstract; + const cochran_device_layout_t *layout = device->layout; dc_status_t status = DC_STATUS_SUCCESS; cochran_data_t data; @@ -869,8 +870,8 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Calculate max data sizes unsigned int max_config = sizeof(data.config); - unsigned int max_logbook = device->layout->rb_logbook_end - device->layout->rb_logbook_begin; - unsigned int max_sample = device->layout->rb_profile_end - device->layout->rb_profile_begin; + unsigned int max_logbook = layout->rb_logbook_end - layout->rb_logbook_begin; + unsigned int max_sample = layout->rb_profile_end - layout->rb_profile_begin; // setup progress indication dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; @@ -890,19 +891,19 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call return rc; // Determine size of dive list to read. - if (device->layout->endian == ENDIAN_LE) - data.dive_count = array_uint16_le (data.config + device->layout->cf_dive_count); + if (layout->endian == ENDIAN_LE) + data.dive_count = array_uint16_le (data.config + layout->cf_dive_count); else - data.dive_count = array_uint16_be (data.config + device->layout->cf_dive_count); + data.dive_count = array_uint16_be (data.config + layout->cf_dive_count); if (data.dive_count == 0) // No dives to read return DC_STATUS_SUCCESS; - if (data.dive_count > device->layout->rb_logbook_entry_count) { - data.logbook_size = device->layout->rb_logbook_entry_count * device->layout->rb_logbook_entry_size; + if (data.dive_count > layout->rb_logbook_entry_count) { + data.logbook_size = layout->rb_logbook_entry_count * layout->rb_logbook_entry_size; } else { - data.logbook_size = data.dive_count * device->layout->rb_logbook_entry_size; + data.logbook_size = data.dive_count * layout->rb_logbook_entry_size; } // Update progress indicator with new maximum @@ -933,24 +934,24 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Emit a device info event. dc_event_devinfo_t devinfo; - devinfo.model = device->layout->model; + devinfo.model = layout->model; devinfo.firmware = 0; // unknown - if (device->layout->endian == ENDIAN_WORD_BE) - devinfo.serial = array_uint32_word_be(data.config + device->layout->cf_serial_number); + if (layout->endian == ENDIAN_WORD_BE) + devinfo.serial = array_uint32_word_be(data.config + layout->cf_serial_number); else - devinfo.serial = array_uint32_le(data.config + device->layout->cf_serial_number); + devinfo.serial = array_uint32_le(data.config + layout->cf_serial_number); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); // Calculate profile RB effective head pointer // Cochran seems to erase 8K chunks so round up. unsigned int last_start_address; - if (device->layout->endian == ENDIAN_WORD_BE) - last_start_address = (array_uint32_word_be(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + if (layout->endian == ENDIAN_WORD_BE) + last_start_address = (array_uint32_word_be(data.config + layout->cf_last_interdive) & 0xfffff000) + 0x2000; else - last_start_address = (array_uint32_le(data.config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + last_start_address = (array_uint32_le(data.config + layout->cf_last_interdive) & 0xfffff000) + 0x2000; - if (last_start_address < device->layout->rb_profile_begin || last_start_address > device->layout->rb_profile_end) { + if (last_start_address < layout->rb_profile_begin || last_start_address > layout->rb_profile_end) { ERROR(abstract->context, "Invalid profile ringbuffer head pointer in Cochran config block."); status = DC_STATUS_DATAFORMAT; goto error; @@ -958,32 +959,32 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call unsigned int head_dive = 0, tail_dive = 0, dive_count = 0; - if (data.dive_count <= device->layout->rb_logbook_entry_count) { + if (data.dive_count <= layout->rb_logbook_entry_count) { head_dive = data.dive_count; tail_dive = 0; } else { // Log wrapped - tail_dive = data.dive_count % device->layout->rb_logbook_entry_count; + tail_dive = data.dive_count % layout->rb_logbook_entry_count; head_dive = tail_dive; } // Change tail to dive following the fingerprint dive. if (data.fp_dive_num > -1) - tail_dive = (data.fp_dive_num + 1) % device->layout->rb_logbook_entry_count; + tail_dive = (data.fp_dive_num + 1) % layout->rb_logbook_entry_count; // Number of dives to read - dive_count = (device->layout->rb_logbook_entry_count + head_dive - tail_dive) % device->layout->rb_logbook_entry_count; + dive_count = (layout->rb_logbook_entry_count + head_dive - tail_dive) % layout->rb_logbook_entry_count; int invalid_profile_flag = 0; // Loop through each dive for (unsigned int i = 0; i < dive_count; ++i) { - unsigned int idx = (device->layout->rb_logbook_entry_count + head_dive - (i + 1)) % device->layout->rb_logbook_entry_count; + unsigned int idx = (layout->rb_logbook_entry_count + head_dive - (i + 1)) % layout->rb_logbook_entry_count; - unsigned char *log_entry = data.logbook + idx * device->layout->rb_logbook_entry_size; + unsigned char *log_entry = data.logbook + idx * layout->rb_logbook_entry_size; - unsigned int sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); - unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end); + unsigned int sample_start_address = array_uint32_le (log_entry + layout->pt_profile_begin); + unsigned int sample_end_address = array_uint32_le (log_entry + layout->pt_profile_end); int sample_size = 0; @@ -995,14 +996,14 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call sample_size = cochran_commander_profile_size(device, &data, idx, PROFILE_SIZE_READ); // Build dive blob - unsigned int dive_size = device->layout->rb_logbook_entry_size + sample_size; + unsigned int dive_size = layout->rb_logbook_entry_size + sample_size; unsigned char *dive = (unsigned char *) malloc(dive_size); if (dive == NULL) { status = DC_STATUS_NOMEMORY; goto error; } - memcpy(dive, log_entry, device->layout->rb_logbook_entry_size); // log + memcpy(dive, log_entry, layout->rb_logbook_entry_size); // log // Read profile data if (sample_size) { @@ -1011,7 +1012,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call sample_end_address = cochran_commander_guess_sample_end_address(device, &data, idx); if (sample_start_address <= sample_end_address) { - rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); + rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + layout->rb_logbook_entry_size, sample_size); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; @@ -1020,9 +1021,9 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call } } else { // It wrapped the buffer, copy two sections - unsigned int size = device->layout->rb_profile_end - sample_start_address; + unsigned int size = layout->rb_profile_end - sample_start_address; - rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, size); + rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + layout->rb_logbook_entry_size, size); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; @@ -1030,7 +1031,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call goto error; } - rc = cochran_commander_read_retry (device, &progress, device->layout->rb_profile_begin, dive + device->layout->rb_logbook_entry_size + size, sample_end_address - device->layout->rb_profile_begin); + rc = cochran_commander_read_retry (device, &progress, layout->rb_profile_begin, dive + layout->rb_logbook_entry_size + size, sample_end_address - layout->rb_profile_begin); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; @@ -1040,7 +1041,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call } } - if (callback && !callback (dive, dive_size, dive + device->layout->pt_fingerprint, device->layout->fingerprint_size, userdata)) { + if (callback && !callback (dive, dive_size, dive + layout->pt_fingerprint, layout->fingerprint_size, userdata)) { free(dive); break; } From 48d93e94047376a968175409e36e19c17abf97a3 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Fri, 30 Jun 2017 14:12:56 -0400 Subject: [PATCH 08/13] Fixed location and encoding of Commander II pointers Commander II pointers to profile ringbuffer data was wrong. After seeing the Commander I encoding I realized the Commander II encoding of RB pointers was in a flipped word big endian format. It only appeared to be in normal big endian format because of an adjacent pointer that usually shared the same first two bytes. --- src/cochran_commander.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 496d08b..8ccf4ed 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -149,11 +149,11 @@ static const cochran_device_layout_t cochran_cmdr_1_device_layout = { static const cochran_device_layout_t cochran_cmdr_device_layout = { COCHRAN_MODEL_COMMANDER_AIR_NITROX, // model 24, // address_bits - ENDIAN_BE, // endian + ENDIAN_WORD_BE, // endian 115200, // baudrate 0x046, // cf_dive_count - 0x06E, // cf_last_log - 0x200, // cf_last_interdive + 0x070, // cf_last_log + 0x06C, // cf_last_interdive 0x0AA, // cf_serial_number 0x00000000, // rb_logbook_begin 0x00020000, // rb_logbook_end From 9c795e6e4e9ecdfe3444c6de09451ca092ac2882 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Fri, 30 Jun 2017 14:12:57 -0400 Subject: [PATCH 09/13] Changed cochran_comander_profile_size function parameters This function is much more useful if it works like a ringbuffer_distance() function. It assumed the wrong values when calculating profile size and it didn't have easy access to values it needed to properly calculate profile sizes. It makes sense to keep since it validates pointers. --- src/cochran_commander.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 8ccf4ed..651a0f9 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -46,11 +46,6 @@ typedef enum cochran_endian_t { ENDIAN_WORD_BE, } cochran_endian_t; -typedef enum cochran_profile_size_t { - PROFILE_SIZE_FULL, - PROFILE_SIZE_READ, -} cochran_profile_size_t; - typedef struct cochran_commander_model_t { unsigned char id[3 + 1]; unsigned int model; @@ -524,21 +519,8 @@ cochran_commander_guess_sample_end_address(cochran_commander_device_t *device, c static unsigned int -cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_t *data, int dive_num, cochran_profile_size_t type) +cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_t *data, int dive_num, unsigned int sample_start_address, unsigned int sample_end_address) { - - const unsigned char *log_entry = data->logbook + dive_num * device->layout->rb_logbook_entry_size; - unsigned int sample_start_address = 0xFFFFFFFF; - unsigned int sample_end_address = array_uint32_le (log_entry + device->layout->pt_profile_end); - - if (type == PROFILE_SIZE_FULL) { - // actual size, includes pre-dive events - sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_pre); - } else if (type == PROFILE_SIZE_READ) { - // read size, only include dive profile - sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); - } - // Validate addresses if (sample_start_address < device->layout->rb_profile_begin || sample_start_address > device->layout->rb_profile_end || @@ -615,8 +597,12 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d break; } - unsigned int sample_size = cochran_commander_profile_size(device, data, idx, PROFILE_SIZE_FULL); - unsigned int read_size = cochran_commander_profile_size(device, data, idx, PROFILE_SIZE_READ); + unsigned int profile_pre = array_uint32_le(log_entry + device->layout->pt_profile_pre); + unsigned int profile_begin = array_uint32_le(log_entry + device->layout->pt_profile_begin); + unsigned int profile_end = array_uint32_le(log_entry + device->layout->pt_profile_end); + + unsigned int sample_size = cochran_commander_profile_size(device, data, idx, profile_pre, profile_end); + unsigned int read_size = cochran_commander_profile_size(device, data, idx, profile_begin, profile_end); // Determine if sample exists if (profile_capacity_remaining > 0) { @@ -993,7 +979,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call invalid_profile_flag = 1; if (!invalid_profile_flag) - sample_size = cochran_commander_profile_size(device, &data, idx, PROFILE_SIZE_READ); + sample_size = cochran_commander_profile_size(device, &data, idx, sample_start_address, sample_end_address); // Build dive blob unsigned int dive_size = layout->rb_logbook_entry_size + sample_size; From e1b679912abd107ba39050beeba4cc7858ed5ec8 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Fri, 30 Jun 2017 14:12:58 -0400 Subject: [PATCH 10/13] Fix bad profiles when profile ringbuffer wraps around The method used to calculate the data used by dives (to determine when we run out of ringbuffer) incorrectly didn't include surface sample data. Ten to twenty minute of sample data is recorded at the surface in case the diver re-descends, continuing the dive. The code then thought that older dive profiles were not yet overwritten. The improper data was returned to the user. --- src/cochran_commander.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 651a0f9..0203ae6 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -147,8 +147,8 @@ static const cochran_device_layout_t cochran_cmdr_device_layout = { ENDIAN_WORD_BE, // endian 115200, // baudrate 0x046, // cf_dive_count - 0x070, // cf_last_log - 0x06C, // cf_last_interdive + 0x06C, // cf_last_log + 0x070, // cf_last_interdive 0x0AA, // cf_serial_number 0x00000000, // rb_logbook_begin 0x00020000, // rb_logbook_end @@ -566,12 +566,9 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d // Remove the pre-dive events that occur after the last dive unsigned int rb_head_ptr = 0; if (device->layout->endian == ENDIAN_WORD_BE) - rb_head_ptr = (array_uint32_word_be(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + rb_head_ptr = (array_uint32_word_be(data->config + device->layout->cf_last_log) & 0xfffff000) + 0x2000; else - rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; - unsigned int last_dive_end_address = array_uint32_le(data->logbook + dive_count * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end); - if (rb_head_ptr > last_dive_end_address) - profile_capacity_remaining -= rb_head_ptr - last_dive_end_address; + rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_log) & 0xfffff000) + 0x2000; unsigned int head_dive = 0, tail_dive = 0; @@ -584,6 +581,18 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d head_dive = tail_dive; } + unsigned int last_profile_idx = (device->layout->rb_logbook_entry_count + head_dive - 1) % device->layout->rb_logbook_entry_count; + unsigned int last_profile_end = array_uint32_le(data->logbook + last_profile_idx * device->layout->rb_logbook_entry_size + device->layout->pt_profile_end); + unsigned int last_profile_pre = 0xFFFFFFFF; + + if (device->layout->endian == ENDIAN_WORD_BE) + last_profile_pre = array_uint32_word_be(data->config + device->layout->cf_last_log); + else + last_profile_pre = array_uint32_le(data->config + device->layout->cf_last_log); + + if (rb_head_ptr > last_profile_end) + profile_capacity_remaining -= rb_head_ptr - last_profile_end; + // Loop through dives to find FP, Accumulate profile data size, // and find the last dive with invalid profile for (unsigned int i = 0; i <= dive_count; ++i) { @@ -601,8 +610,9 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d unsigned int profile_begin = array_uint32_le(log_entry + device->layout->pt_profile_begin); unsigned int profile_end = array_uint32_le(log_entry + device->layout->pt_profile_end); - unsigned int sample_size = cochran_commander_profile_size(device, data, idx, profile_pre, profile_end); + unsigned int sample_size = cochran_commander_profile_size(device, data, idx, profile_pre, last_profile_pre); unsigned int read_size = cochran_commander_profile_size(device, data, idx, profile_begin, profile_end); + last_profile_pre = profile_pre; // Determine if sample exists if (profile_capacity_remaining > 0) { From 094b635f87183f399e3ad25b499f6a1a4fe99bb2 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Fri, 30 Jun 2017 14:12:59 -0400 Subject: [PATCH 11/13] Added decompression event handling for the Commander I finally found an example of decompression on a Commander II computer and it seems to be identical to the EMC. --- src/cochran_commander_parser.c | 71 ++++++++++++++++------------------ 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index 1c14536..8208e07 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -601,45 +601,42 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb if (s[0] & 0x80) { offset += cochran_commander_handle_event(parser, s[0], callback, userdata); - if (layout->format == SAMPLE_EMC) { - // EMC models have events indicating change in deco status - // Commander may have them but I don't have example data - switch (s[0]) { - case 0xC5: // Deco obligation begins - deco_obligation = 1; - break; - case 0xD8: // Deco obligation ends - deco_obligation = 0; - break; - case 0xAB: // Decrement ceiling (deeper) - deco_ceiling += 10; // feet + // Events indicating change in deco status + switch (s[0]) { + case 0xC5: // Deco obligation begins + deco_obligation = 1; + break; + case 0xD8: // Deco obligation ends + deco_obligation = 0; + break; + case 0xAB: // Decrement ceiling (deeper) + deco_ceiling += 10; // feet - sample.deco.type = DC_DECO_DECOSTOP; - sample.deco.time = (array_uint16_le(s + layout->samplesize) + 1) * 60; - sample.deco.depth = deco_ceiling * FEET; - if (callback) callback(DC_SAMPLE_DECO, sample, userdata); - break; - case 0xAD: // Increment ceiling (shallower) - deco_ceiling -= 10; // feet + sample.deco.type = DC_DECO_DECOSTOP; + sample.deco.time = (array_uint16_le(s + 3) + 1) * 60; + sample.deco.depth = deco_ceiling * FEET; + if (callback) callback(DC_SAMPLE_DECO, sample, userdata); + break; + case 0xAD: // Increment ceiling (shallower) + deco_ceiling -= 10; // feet - sample.deco.type = DC_DECO_DECOSTOP; - sample.deco.depth = deco_ceiling * FEET; - sample.deco.time = (array_uint16_le(s + layout->samplesize) + 1) * 60; - if (callback) callback(DC_SAMPLE_DECO, sample, userdata); - break; - case 0xC0: // Switched to FO2 21% mode (surface) - // Event seen upon surfacing - break; - case 0xCD: // Switched to deco blend - case 0xEF: // Switched to gas blend 2 - sample.gasmix = 1; - if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); - break; - case 0xF3: // Switched to gas blend 1 - sample.gasmix = 0; - if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); - break; - } + sample.deco.type = DC_DECO_DECOSTOP; + sample.deco.depth = deco_ceiling * FEET; + sample.deco.time = (array_uint16_le(s + 3) + 1) * 60; + if (callback) callback(DC_SAMPLE_DECO, sample, userdata); + break; + case 0xC0: // Switched to FO2 21% mode (surface) + // Event seen upon surfacing + break; + case 0xCD: // Switched to deco blend + case 0xEF: // Switched to gas blend 2 + sample.gasmix = 1; + if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + break; + case 0xF3: // Switched to gas blend 1 + sample.gasmix = 0; + if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + break; } continue; From 2c5e787b77ddb93ea97b7366ef291e0f1a8f6960 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Sun, 2 Jul 2017 10:07:45 -0400 Subject: [PATCH 12/13] Fixed duplicate gasmix event reports Newer cochran DCs record a gas change event at the begining of a dive. The code creates a gas change before processing samples so with newer DCs this resulted in duplicate events. --- src/cochran_commander_parser.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index 8208e07..5a0ad55 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -567,6 +567,7 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb sample.gasmix = 0; if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + unsigned int last_gasmix = sample.gasmix; while (offset < size) { const unsigned char *s = samples + offset; @@ -630,12 +631,18 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb break; case 0xCD: // Switched to deco blend case 0xEF: // Switched to gas blend 2 - sample.gasmix = 1; - if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + if (last_gasmix != 1) { + sample.gasmix = 1; + if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + last_gasmix = sample.gasmix; + } break; case 0xF3: // Switched to gas blend 1 - sample.gasmix = 0; - if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + if (last_gasmix != 0) { + sample.gasmix = 0; + if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + last_gasmix = sample.gasmix; + } break; } From fd1557f634bfebf669ba852b5c3272ab4dcfa1b0 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Sun, 2 Jul 2017 22:05:11 -0400 Subject: [PATCH 13/13] Removed unused code --- src/cochran_commander.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 0203ae6..1ad38f0 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -939,20 +939,6 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - // Calculate profile RB effective head pointer - // Cochran seems to erase 8K chunks so round up. - unsigned int last_start_address; - if (layout->endian == ENDIAN_WORD_BE) - last_start_address = (array_uint32_word_be(data.config + layout->cf_last_interdive) & 0xfffff000) + 0x2000; - else - last_start_address = (array_uint32_le(data.config + layout->cf_last_interdive) & 0xfffff000) + 0x2000; - - if (last_start_address < layout->rb_profile_begin || last_start_address > layout->rb_profile_end) { - ERROR(abstract->context, "Invalid profile ringbuffer head pointer in Cochran config block."); - status = DC_STATUS_DATAFORMAT; - goto error; - } - unsigned int head_dive = 0, tail_dive = 0, dive_count = 0; if (data.dive_count <= layout->rb_logbook_entry_count) {