From 55e8f83eb5d934e65fbf587d427de267f174c651 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Sun, 2 Jul 2017 22:05:12 -0400 Subject: [PATCH 1/9] Use rbstream for reading profile data Per-dive reading of the DC takes too long because of latency on read commands. The rbstream solves this by reading ahead in large blocks. --- src/cochran_commander.c | 92 ++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 57 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 1ad38f0..1e4bfb3 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -29,6 +29,7 @@ #include "serial.h" #include "array.h" #include "ringbuffer.h" +#include "rbstream.h" #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) @@ -473,31 +474,6 @@ 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 @@ -607,11 +583,8 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d } 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, 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 @@ -623,7 +596,7 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d data->invalid_profile_dive_num = idx; } // Accumulate read size for progress bar - sample_read_size += read_size; + sample_read_size += sample_size; } } @@ -860,6 +833,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call 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; + dc_rbstream_t *rbstream = NULL; cochran_data_t data; data.logbook = NULL; @@ -957,6 +931,14 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Number of dives to read dive_count = (layout->rb_logbook_entry_count + head_dive - tail_dive) % layout->rb_logbook_entry_count; + // Create the ringbuffer stream. + unsigned int last_start_address = array_uint32_le (data.logbook + ((head_dive - 1) * layout->rb_logbook_entry_size) + layout->pt_profile_end); + status = dc_rbstream_new (&rbstream, abstract, 1, 131072, layout->rb_profile_begin, layout->rb_profile_end, last_start_address); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to create the ringbuffer stream."); + goto error; + } + int invalid_profile_flag = 0; // Loop through each dive @@ -968,14 +950,17 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call 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; + int sample_size = 0, pre_size = 0; // Determine if profile exists if (idx == data.invalid_profile_dive_num) invalid_profile_flag = 1; - if (!invalid_profile_flag) + if (!invalid_profile_flag) { sample_size = cochran_commander_profile_size(device, &data, idx, sample_start_address, sample_end_address); + pre_size = cochran_commander_profile_size(device, &data, idx, sample_end_address, last_start_address); + last_start_address = sample_start_address; + } // Build dive blob unsigned int dive_size = layout->rb_logbook_entry_size + sample_size; @@ -989,38 +974,30 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // 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, idx); - - if (sample_start_address <= sample_end_address) { - rc = cochran_commander_read_retry (device, &progress, sample_start_address, dive + layout->rb_logbook_entry_size, sample_size); + if (pre_size) { + // Read throwaway sample data, pre-dive events and post-dive surface sample + unsigned char *pre = (unsigned char *) malloc (pre_size); + if (pre == NULL) { + status = DC_STATUS_NOMEMORY; + goto error; + } + rc = dc_rbstream_read(rbstream, &progress, pre, pre_size); + free(pre); 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 = layout->rb_profile_end - sample_start_address; - - 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; - free(dive); - goto error; - } - - 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."); + ERROR (abstract->context, "Failed to read the pre-dive event data."); status = rc; free(dive); goto error; } } + // read sample data + rc = dc_rbstream_read(rbstream, &progress, dive + 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; + } } if (callback && !callback (dive, dive_size, dive + layout->pt_fingerprint, layout->fingerprint_size, userdata)) { @@ -1032,6 +1009,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call } error: + dc_rbstream_free(rbstream); free(data.logbook); return status; } From cd5eb61e904eefb97d180959b201a7b9589b1fae Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Fri, 14 Jul 2017 19:50:57 -0400 Subject: [PATCH 2/9] Make rbstream pagesize smaller and device specific The progress bar was taking 18 seconds between updates on a Commander II when using a 128K pagesize. Since devices differ in their baud rates, it makes sense to use smaller pages on slower devices. This change reduces it to 32K on a Commander II and to 64K on EMC devices. --- src/cochran_commander.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 1e4bfb3..5ceb046 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -71,6 +71,7 @@ typedef struct cochran_device_layout_t { unsigned int address_bits; cochran_endian_t endian; unsigned int baudrate; + unsigned int rbstream_size; // Config data. unsigned int cf_dive_count; unsigned int cf_last_log; @@ -123,6 +124,7 @@ static const cochran_device_layout_t cochran_cmdr_1_device_layout = { 24, // address_bits ENDIAN_WORD_BE, // endian 115200, // baudrate + 32768, // rbstream_size 0x046, // cf_dive_count 0x6c, // cf_last_log 0x70, // cf_last_interdive @@ -147,6 +149,7 @@ static const cochran_device_layout_t cochran_cmdr_device_layout = { 24, // address_bits ENDIAN_WORD_BE, // endian 115200, // baudrate + 32768, // rbstream_size 0x046, // cf_dive_count 0x06C, // cf_last_log 0x070, // cf_last_interdive @@ -170,6 +173,7 @@ static const cochran_device_layout_t cochran_emc14_device_layout = { 32, // address_bits ENDIAN_LE, // endian 806400, // baudrate + 65536, // rbstream_size 0x0D2, // cf_dive_count 0x13E, // cf_last_log 0x142, // cf_last_interdive @@ -193,6 +197,7 @@ static const cochran_device_layout_t cochran_emc16_device_layout = { 32, // address_bits ENDIAN_LE, // endian 806400, // baudrate + 65536, // rbstream_size 0x0D2, // cf_dive_count 0x13E, // cf_last_log 0x142, // cf_last_interdive @@ -216,6 +221,7 @@ static const cochran_device_layout_t cochran_emc20_device_layout = { 32, // address_bits ENDIAN_LE, // endian 806400, // baudrate + 65536, // rbstream_size 0x0D2, // cf_dive_count 0x13E, // cf_last_log 0x142, // cf_last_interdive @@ -933,7 +939,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Create the ringbuffer stream. unsigned int last_start_address = array_uint32_le (data.logbook + ((head_dive - 1) * layout->rb_logbook_entry_size) + layout->pt_profile_end); - status = dc_rbstream_new (&rbstream, abstract, 1, 131072, layout->rb_profile_begin, layout->rb_profile_end, last_start_address); + status = dc_rbstream_new (&rbstream, abstract, 1, layout->rbstream_size, layout->rb_profile_begin, layout->rb_profile_end, last_start_address); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to create the ringbuffer stream."); goto error; From 88b6e36e04c5fb0fcc527615bd6a5336ad343b0f Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 31 Jul 2017 16:50:21 +0200 Subject: [PATCH 3/9] Use only a single read operation The profile and throw-away data can be read all at once. This avoids the need for an extra memory allocation and fixes a memory leak. --- src/cochran_commander.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 5ceb046..27065f6 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -970,7 +970,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Build dive blob unsigned int dive_size = layout->rb_logbook_entry_size + sample_size; - unsigned char *dive = (unsigned char *) malloc(dive_size); + unsigned char *dive = (unsigned char *) malloc(dive_size + pre_size); if (dive == NULL) { status = DC_STATUS_NOMEMORY; goto error; @@ -980,24 +980,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Read profile data if (sample_size) { - if (pre_size) { - // Read throwaway sample data, pre-dive events and post-dive surface sample - unsigned char *pre = (unsigned char *) malloc (pre_size); - if (pre == NULL) { - status = DC_STATUS_NOMEMORY; - goto error; - } - rc = dc_rbstream_read(rbstream, &progress, pre, pre_size); - free(pre); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the pre-dive event data."); - status = rc; - free(dive); - goto error; - } - } - // read sample data - rc = dc_rbstream_read(rbstream, &progress, dive + layout->rb_logbook_entry_size, sample_size); + rc = dc_rbstream_read(rbstream, &progress, dive + layout->rb_logbook_entry_size, sample_size + pre_size); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); status = rc; From 8b41e72cf721bfeb592288357c27460196ddf358 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Sat, 15 Jul 2017 16:39:36 -0400 Subject: [PATCH 4/9] Remove unneeded function Since moving to per-dive download of profile data (and now rbstream download) the data->sample_data_offset and data->sample_size variables aren't used so calculating them doesn't make sense. --- src/cochran_commander.c | 68 ----------------------------------------- 1 file changed, 68 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 27065f6..c782fb9 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -61,9 +61,6 @@ typedef struct cochran_data_t { int invalid_profile_dive_num; unsigned int logbook_size; - - unsigned int sample_data_offset; - unsigned int sample_size; } cochran_data_t; typedef struct cochran_device_layout_t { @@ -610,69 +607,6 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d } - -static void -cochran_commander_get_sample_parms(cochran_commander_device_t *device, cochran_data_t *data) -{ - dc_device_t *abstract = (dc_device_t *) device; - unsigned int pre_dive_offset = 0, end_dive_offset = 0; - - 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; - - // Find lowest and highest offsets into sample data - unsigned int low_offset = 0xFFFFFFFF; - unsigned int high_offset = 0; - - for (int i = data->fp_dive_num + 1; i < dive_count; i++) { - pre_dive_offset = array_uint32_le (data->logbook + i * device->layout->rb_logbook_entry_size - + device->layout->pt_profile_pre); - end_dive_offset = array_uint32_le (data->logbook + i * device->layout->rb_logbook_entry_size - + device->layout->pt_profile_end); - - // Validate offsets, allow 0xFFFFFFF for end_dive_offset - // because we handle that as a special case. - if (pre_dive_offset < device->layout->rb_profile_begin || - pre_dive_offset > device->layout->rb_profile_end) { - ERROR(abstract->context, "Invalid pre-dive offset (%08x) on dive %d.", pre_dive_offset, i); - continue; - } - - if (end_dive_offset < device->layout->rb_profile_begin || - (end_dive_offset > device->layout->rb_profile_end && - end_dive_offset != 0xFFFFFFFF)) { - ERROR(abstract->context, "Invalid end-dive offset (%08x) on dive %d.", end_dive_offset, i); - continue; - } - - // Check for ring buffer wrap-around. - if (pre_dive_offset > end_dive_offset) - break; - - if (pre_dive_offset < low_offset) - low_offset = pre_dive_offset; - if (end_dive_offset > high_offset && end_dive_offset != 0xFFFFFFFF ) - high_offset = end_dive_offset; - } - - if (pre_dive_offset > end_dive_offset) { - high_offset = device->layout->rb_profile_end; - low_offset = device->layout->rb_profile_begin; - data->sample_data_offset = low_offset; - data->sample_size = high_offset - low_offset; - } else if (low_offset < 0xFFFFFFFF && high_offset > 0) { - data->sample_data_offset = low_offset; - data->sample_size = high_offset - data->sample_data_offset; - } else { - data->sample_data_offset = 0; - data->sample_size = 0; - } -} - - dc_status_t cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const char *name) { @@ -906,8 +840,6 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call 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; devinfo.model = layout->model; From 1d8f25ba9b73e094286df4b098ab5859cf61e881 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Sat, 15 Jul 2017 16:39:38 -0400 Subject: [PATCH 5/9] Dump function no longer assumes reads begin at byte 0 For previously supported Cochran computers high-speed read of log and profile data started at byte 0. Older models that lack the high-speed transfer function use the standard speed read commands and so the log and profile data are read at higher addresses. --- src/cochran_commander.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index c782fb9..7728b4f 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -728,6 +728,7 @@ cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) cochran_commander_device_t *device = (cochran_commander_device_t *) abstract; dc_status_t rc = DC_STATUS_SUCCESS; unsigned char config[1024]; + unsigned int size = device->layout->rb_profile_end - device->layout->rb_logbook_begin; // Make sure buffer is good. if (!dc_buffer_clear(buffer)) { @@ -736,14 +737,14 @@ cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) } // Reserve space - if (!dc_buffer_resize(buffer, device->layout->rb_profile_end)) { + if (!dc_buffer_resize(buffer, size)) { ERROR(abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } // Determine size for progress dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; - progress.maximum = sizeof(config) + device->layout->rb_profile_end; + progress.maximum = sizeof(config) + size; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Emit ID block @@ -757,7 +758,7 @@ cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return rc; // Read the sample data, from 0 to sample end will include logbook - rc = cochran_commander_read (device, &progress, 0, dc_buffer_get_data(buffer), device->layout->rb_profile_end); + rc = cochran_commander_read (device, &progress, device->layout->rb_logbook_begin, dc_buffer_get_data(buffer), device->layout->rb_profile_end); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); return rc; From fb65726a55df3509b746ddd0cc8e97ce3ace04b4 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Sat, 15 Jul 2017 16:39:37 -0400 Subject: [PATCH 6/9] Add support for the Commander TM The Cochran Commander TM appears to be a first generation Commander with limited storage and function compared to later models. The main differences are: - The TM doesn't support high-speed transfer so use the 0x05 read command and don't change to a higher baud rate. Still reset to 9600 to wait for the heartbeat. - The TM has a different config command (one byte). - The TM has only one config page. --- src/cochran_commander.c | 175 +++++++++++++++++++++++++------- src/cochran_commander_parser.c | 179 +++++++++++++++++++++++++++++++-- src/descriptor.c | 11 +- 3 files changed, 313 insertions(+), 52 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 7728b4f..50422fb 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -35,11 +35,12 @@ #define MAXRETRIES 2 -#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 +#define COCHRAN_MODEL_COMMANDER_TM 0 +#define COCHRAN_MODEL_COMMANDER_PRE21000 1 +#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 2 +#define COCHRAN_MODEL_EMC_14 3 +#define COCHRAN_MODEL_EMC_16 4 +#define COCHRAN_MODEL_EMC_20 5 typedef enum cochran_endian_t { ENDIAN_LE, @@ -88,6 +89,7 @@ typedef struct cochran_device_layout_t { unsigned int pt_profile_pre; unsigned int pt_profile_begin; unsigned int pt_profile_end; + unsigned int pt_dive_number; } cochran_device_layout_t; typedef struct cochran_commander_device_t { @@ -115,6 +117,31 @@ static const dc_device_vtable_t cochran_commander_device_vtable = { cochran_commander_device_close /* close */ }; +// Cochran Commander TM, pre-dates pre-21000 s/n +static const cochran_device_layout_t cochran_cmdr_tm_device_layout = { + COCHRAN_MODEL_COMMANDER_TM, // model + 24, // address_bits + ENDIAN_WORD_BE, // endian + 9600, // baudrate + 4096, // rbstream_size + 0x146, // cf_dive_count + 0x158, // cf_last_log + 0xffffff, // cf_last_interdive + 0x15c, // cf_serial_number + 0x010000, // rb_logbook_begin + 0x012328, // rb_logbook_end + 90, // rb_logbook_entry_size + 100, // rb_logbook_entry_count + 0x012328, // rb_profile_begin + 0x020000, // rb_profile_end + 15, // pt_fingerprint + 4, // fingerprint_size + 0, // pt_profile_pre + 0, // pt_profile_begin + 90, // pt_profile_end (Next begin pointer is the end) + 20, // pt_dive_number +}; + // Cochran Commander pre-21000 s/n static const cochran_device_layout_t cochran_cmdr_1_device_layout = { COCHRAN_MODEL_COMMANDER_PRE21000, // model @@ -137,6 +164,7 @@ static const cochran_device_layout_t cochran_cmdr_1_device_layout = { 28, // pt_profile_pre 0, // pt_profile_begin 128, // pt_profile_end + 68, // pt_dive_number }; @@ -162,6 +190,7 @@ static const cochran_device_layout_t cochran_cmdr_device_layout = { 30, // pt_profile_pre 6, // pt_profile_begin 128, // pt_profile_end + 70, // pt_dive_number }; // Cochran EMC-14 @@ -186,6 +215,7 @@ static const cochran_device_layout_t cochran_emc14_device_layout = { 30, // pt_profile_pre 6, // pt_profile_begin 256, // pt_profile_end + 86, // pt_dive_number }; // Cochran EMC-16 @@ -210,6 +240,7 @@ static const cochran_device_layout_t cochran_emc16_device_layout = { 30, // pt_profile_pre 6, // pt_profile_begin 256, // pt_profile_end + 86, // pt_dive_number }; // Cochran EMC-20 @@ -234,6 +265,7 @@ static const cochran_device_layout_t cochran_emc20_device_layout = { 30, // pt_profile_pre 6, // pt_profile_begin 256, // pt_profile_end + 86, // pt_dive_number }; @@ -242,6 +274,7 @@ static unsigned int cochran_commander_get_model (cochran_commander_device_t *device) { const cochran_commander_model_t models[] = { + {"\x0a""12", COCHRAN_MODEL_COMMANDER_TM}, {"\x11""21", COCHRAN_MODEL_COMMANDER_PRE21000}, {"\x11""22", COCHRAN_MODEL_COMMANDER_AIR_NITROX}, {"730", COCHRAN_MODEL_EMC_14}, @@ -334,7 +367,7 @@ cochran_commander_packet (cochran_commander_device_t *device, dc_event_progress_ } } - if (high_speed) { + if (high_speed && device->layout->baudrate != 9600) { // Give the DC time to process the command. dc_serial_sleep(device->port, 45); @@ -403,18 +436,24 @@ cochran_commander_read_config (cochran_commander_device_t *device, dc_event_prog dc_device_t *abstract = (dc_device_t *) device; dc_status_t rc = DC_STATUS_SUCCESS; - // Read two 512 byte blocks into one 1024 byte buffer - for (unsigned int i = 0; i < 2; i++) { - const unsigned int len = size / 2; + if ((size % 512) != 0) + return DC_STATUS_INVALIDARGS; + // Read two 512 byte blocks into one 1024 byte buffer + unsigned int pages = size / 512; + for (unsigned int i = 0; i < pages; i++) { unsigned char command[2] = {0x96, i}; - rc = cochran_commander_packet(device, progress, command, sizeof(command), data + i * len, len, 0); + unsigned int command_size = sizeof(command); + if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM) + command_size = 1; + + rc = cochran_commander_packet(device, progress, command, command_size, data + i * 512, 512, 0); if (rc != DC_STATUS_SUCCESS) return rc; dc_event_vendor_t vendor; - vendor.data = data + i * len; - vendor.size = len; + vendor.data = data + i * 512; + vendor.size = 512; device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); } @@ -448,15 +487,36 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t break; case 24: // Commander uses 24 byte addressing - command[0] = 0x15; - command[1] = (address ) & 0xff; - command[2] = (address >> 8) & 0xff; - command[3] = (address >> 16) & 0xff; - command[4] = (size ) & 0xff; - command[5] = (size >> 8 ) & 0xff; - command[6] = (size >> 16 ) & 0xff; - command[7] = 0x04; - command_size = 8; + if (device->layout->baudrate == 9600) { + // This read command will return 32K bytes if asked to read + // 0 bytes. So we can allow a size of up to 0x10000 but if + // the user asks for 0 bytes we should just return success + // otherwise we'll end end up running past the buffer. + if (size > 0x10000) + return DC_STATUS_INVALIDARGS; + if (size == 0) + return DC_STATUS_SUCCESS; + + // Older commander, use low-speed read command + command[0] = 0x05; + command[1] = (address ) & 0xff; + command[2] = (address >> 8) & 0xff; + command[3] = (address >> 16) & 0xff; + command[4] = (size ) & 0xff; + command[5] = (size >> 8 ) & 0xff; + command_size = 6; + } else { + // Newer commander with high-speed read command + command[0] = 0x15; + command[1] = (address ) & 0xff; + command[2] = (address >> 8) & 0xff; + command[3] = (address >> 16) & 0xff; + command[4] = (size ) & 0xff; + command[5] = (size >> 8 ) & 0xff; + command[6] = (size >> 16 ) & 0xff; + command[7] = 0x04; + command_size = 8; + } break; default: return DC_STATUS_UNSUPPORTED; @@ -526,6 +586,8 @@ cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_ static unsigned int cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_data_t *data) { + unsigned int base = device->layout->rb_logbook_begin; + // 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; @@ -544,10 +606,13 @@ 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_log) & 0xfffff000) + 0x2000; + if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM) + // TM uses SRAM and does not need to erase pages + rb_head_ptr = base + array_uint32_word_be(data->config + device->layout->cf_last_log); + else if (device->layout->endian == ENDIAN_WORD_BE) + rb_head_ptr = base + (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_log) & 0xfffff000) + 0x2000; + rb_head_ptr = base + (array_uint32_le(data->config + device->layout->cf_last_log) & 0xfffff000) + 0x2000; unsigned int head_dive = 0, tail_dive = 0; @@ -561,13 +626,13 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d } 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_end = base + 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); + last_profile_pre = base + 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); + last_profile_pre = base + 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; @@ -585,7 +650,7 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d break; } - unsigned int profile_pre = array_uint32_le(log_entry + device->layout->pt_profile_pre); + unsigned int profile_pre = base + array_uint32_le(log_entry + device->layout->pt_profile_pre); unsigned int sample_size = cochran_commander_profile_size(device, data, idx, profile_pre, last_profile_pre); last_profile_pre = profile_pre; @@ -648,6 +713,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_TM: + device->layout = &cochran_cmdr_tm_device_layout; + break; case COCHRAN_MODEL_COMMANDER_PRE21000: device->layout = &cochran_cmdr_1_device_layout; break; @@ -728,6 +796,7 @@ cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) cochran_commander_device_t *device = (cochran_commander_device_t *) abstract; dc_status_t rc = DC_STATUS_SUCCESS; unsigned char config[1024]; + unsigned int config_size = sizeof(config); unsigned int size = device->layout->rb_profile_end - device->layout->rb_logbook_begin; // Make sure buffer is good. @@ -742,9 +811,12 @@ cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return DC_STATUS_NOMEMORY; } + if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM) + config_size = 512; + // Determine size for progress dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; - progress.maximum = sizeof(config) + size; + progress.maximum = config_size + size; device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Emit ID block @@ -753,12 +825,12 @@ cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) vendor.size = sizeof (device->id); device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - rc = cochran_commander_read_config (device, &progress, config, sizeof(config)); + rc = cochran_commander_read_config (device, &progress, config, config_size); if (rc != DC_STATUS_SUCCESS) return rc; - // Read the sample data, from 0 to sample end will include logbook - rc = cochran_commander_read (device, &progress, device->layout->rb_logbook_begin, dc_buffer_get_data(buffer), device->layout->rb_profile_end); + // Read the sample data, logbook and sample data are contiguous + rc = cochran_commander_read (device, &progress, device->layout->rb_logbook_begin, dc_buffer_get_data(buffer), size); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); return rc; @@ -783,6 +855,10 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call unsigned int max_config = sizeof(data.config); unsigned int max_logbook = layout->rb_logbook_end - layout->rb_logbook_begin; unsigned int max_sample = layout->rb_profile_end - layout->rb_profile_begin; + unsigned int base = device->layout->rb_logbook_begin; + + if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM) + max_config = 512; // setup progress indication dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; @@ -797,7 +873,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Read config dc_status_t rc = DC_STATUS_SUCCESS; - rc = cochran_commander_read_config(device, &progress, data.config, sizeof(data.config)); + rc = cochran_commander_read_config(device, &progress, data.config, max_config); if (rc != DC_STATUS_SUCCESS) return rc; @@ -807,9 +883,11 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call else data.dive_count = array_uint16_be (data.config + layout->cf_dive_count); - if (data.dive_count == 0) + if (data.dive_count == 0) { // No dives to read + WARNING(abstract->context, "This dive computer has no recorded dives."); return DC_STATUS_SUCCESS; + } if (data.dive_count > layout->rb_logbook_entry_count) { data.logbook_size = layout->rb_logbook_entry_count * layout->rb_logbook_entry_size; @@ -829,7 +907,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call } // Request log book - rc = cochran_commander_read(device, &progress, 0, data.logbook, data.logbook_size); + rc = cochran_commander_read(device, &progress, layout->rb_logbook_begin, data.logbook, data.logbook_size); if (rc != DC_STATUS_SUCCESS) { status = rc; goto error; @@ -837,6 +915,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // 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); @@ -870,8 +949,13 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call // Number of dives to read dive_count = (layout->rb_logbook_entry_count + head_dive - tail_dive) % layout->rb_logbook_entry_count; + unsigned int last_start_address = 0; + if (layout->endian == ENDIAN_WORD_BE) + last_start_address = base + array_uint32_word_be(data.config + layout->cf_last_log ); + else + last_start_address = base + array_uint32_le(data.config + layout->cf_last_log ); + // Create the ringbuffer stream. - unsigned int last_start_address = array_uint32_le (data.logbook + ((head_dive - 1) * layout->rb_logbook_entry_size) + layout->pt_profile_end); status = dc_rbstream_new (&rbstream, abstract, 1, layout->rbstream_size, layout->rb_profile_begin, layout->rb_profile_end, last_start_address); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to create the ringbuffer stream."); @@ -886,8 +970,23 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call unsigned char *log_entry = data.logbook + idx * layout->rb_logbook_entry_size; - 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); + unsigned int sample_start_address = 0; + unsigned int sample_end_address = 0; + if (layout->model == COCHRAN_MODEL_COMMANDER_TM) { + sample_start_address = base + array_uint24_le (log_entry + layout->pt_profile_begin); + sample_end_address = last_start_address; + // Commander TM has SRAM which seems to randomize when they lose power for too long + // Check for bad entries. + if (sample_start_address < layout->rb_profile_begin || sample_start_address > layout->rb_profile_end || + sample_end_address < layout->rb_profile_begin || sample_end_address > layout->rb_profile_end || + array_uint16_le(log_entry + layout->pt_dive_number) % layout->rb_logbook_entry_count != idx) { + ERROR(abstract->context, "Corrupt dive (%d).", idx); + continue; + } + } else { + sample_start_address = base + array_uint32_le (log_entry + layout->pt_profile_begin); + sample_end_address = base + array_uint32_le (log_entry + layout->pt_profile_end); + } int sample_size = 0, pre_size = 0; diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index 5a0ad55..7c76a43 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -31,11 +31,12 @@ #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) -#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 +#define COCHRAN_MODEL_COMMANDER_TM 0 +#define COCHRAN_MODEL_COMMANDER_PRE21000 1 +#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 2 +#define COCHRAN_MODEL_EMC_14 3 +#define COCHRAN_MODEL_EMC_16 4 +#define COCHRAN_MODEL_EMC_20 5 // Cochran time stamps start at Jan 1, 1992 #define COCHRAN_EPOCH 694242000 @@ -43,6 +44,7 @@ #define UNSUPPORTED 0xFFFFFFFF typedef enum cochran_sample_format_t { + SAMPLE_TM, SAMPLE_CMDR, SAMPLE_EMC, } cochran_sample_format_t; @@ -58,6 +60,7 @@ typedef struct cochran_parser_layout_t { cochran_sample_format_t format; unsigned int headersize; unsigned int samplesize; + unsigned int pt_sample_interval; cochran_date_encoding_t date_encoding; unsigned int datetime; unsigned int pt_profile_begin; @@ -113,10 +116,36 @@ static const dc_parser_vtable_t cochran_commander_parser_vtable = { NULL /* destroy */ }; +static const cochran_parser_layout_t cochran_cmdr_tm_parser_layout = { + SAMPLE_TM, // format + 90, // headersize + 1, // samplesize + 72, // pt_sample_interval + DATE_ENCODING_TICKS, // date_encoding + 15, // datetime, 4 bytes + 0, // pt_profile_begin, 4 bytes + UNSUPPORTED, // water_conductivity, 1 byte, 0=low(fresh), 2=high(sea) + 0, // pt_profile_pre, 4 bytes + 83, // start_temp, 1 byte, F + UNSUPPORTED, // start_depth, 2 bytes, /4=ft + 20, // dive_number, 2 bytes + UNSUPPORTED, // altitude, 1 byte, /4=kilofeet + UNSUPPORTED, // pt_profile_end, 4 bytes + UNSUPPORTED, // end_temp, 1 byte F + 57, // divetime, 2 bytes, minutes + 49, // max_depth, 2 bytes, /4=ft + 51, // avg_depth, 2 bytes, /4=ft + 74, // oxygen, 4 bytes (2 of) 2 bytes, /256=% + UNSUPPORTED, // helium, 4 bytes (2 of) 2 bytes, /256=% + 82, // min_temp, 1 byte, /2+20=F + UNSUPPORTED, // max_temp, 1 byte, /2+20=F +}; + static const cochran_parser_layout_t cochran_cmdr_1_parser_layout = { - SAMPLE_CMDR, // type + SAMPLE_CMDR, // format 256, // headersize 2, // samplesize + UNSUPPORTED, // pt_sample_interval DATE_ENCODING_TICKS, // date_encoding 8, // datetime, 4 bytes 0, // pt_profile_begin, 4 bytes @@ -138,9 +167,10 @@ static const cochran_parser_layout_t cochran_cmdr_1_parser_layout = { }; static const cochran_parser_layout_t cochran_cmdr_parser_layout = { - SAMPLE_CMDR, // type + SAMPLE_CMDR, // format 256, // headersize 2, // samplesize + UNSUPPORTED, // pt_sample_interval DATE_ENCODING_MSDHYM, // date_encoding 0, // datetime, 6 bytes 6, // pt_profile_begin, 4 bytes @@ -162,9 +192,10 @@ static const cochran_parser_layout_t cochran_cmdr_parser_layout = { }; static const cochran_parser_layout_t cochran_emc_parser_layout = { - SAMPLE_EMC, // type + SAMPLE_EMC, // format 512, // headersize 3, // samplesize + UNSUPPORTED, // pt_sample_interval DATE_ENCODING_SMHDMY, // date_encoding 0, // datetime, 6 bytes 6, // pt_profile_begin, 4 bytes @@ -197,6 +228,7 @@ static const cochran_events_t cochran_events[] = { {0xC3, 1, SAMPLE_EVENT_OLF, SAMPLE_FLAGS_NONE}, // CNS Oxygen toxicity warning {0xC4, 1, SAMPLE_EVENT_MAXDEPTH, SAMPLE_FLAGS_NONE}, // Depth exceeds user set point {0xC5, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN}, // Entered decompression mode + {0xC7, 1, SAMPLE_EVENT_VIOLATION,SAMPLE_FLAGS_BEGIN}, // Entered Gauge mode (e.g. locked out) {0xC8, 1, SAMPLE_EVENT_PO2, SAMPLE_FLAGS_BEGIN}, // PO2 too high {0xCC, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN}, // Low Cylinder 1 pressure {0xCE, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN}, // Non-decompression warning @@ -334,6 +366,11 @@ cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context, unsig parser->model = model; switch (model) { + case COCHRAN_MODEL_COMMANDER_TM: + parser->layout = &cochran_cmdr_tm_parser_layout; + parser->events = NULL; // No inter-dive events on this model + parser->nevents = 0; + break; case COCHRAN_MODEL_COMMANDER_PRE21000: parser->layout = &cochran_cmdr_1_parser_layout; parser->events = cochran_cmdr_event_bytes; @@ -440,6 +477,8 @@ cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, *((unsigned int*) value) = (data[layout->min_temp] / 2.0 + 20 - 32) / 1.8; break; case DC_FIELD_TEMPERATURE_MAXIMUM: + if (layout->max_temp == UNSUPPORTED) + return DC_STATUS_UNSUPPORTED; if (data[layout->max_temp] == 0xFF) return DC_STATUS_UNSUPPORTED; *((unsigned int*) value) = (data[layout->max_temp] / 2.0 + 20 - 32) / 1.8; @@ -484,6 +523,8 @@ cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, // for density assume // 0 = 1000kg/m³, 2 = 1025kg/m³ // and other values are linear + if (layout->water_conductivity == UNSUPPORTED) + return DC_STATUS_UNSUPPORTED; if ((data[layout->water_conductivity] & 0x3) == 0) water->type = DC_WATER_FRESH; else @@ -493,6 +534,8 @@ cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, case DC_FIELD_ATMOSPHERIC: // Cochran measures air pressure and stores it as altitude. // Convert altitude (measured in 1/4 kilofeet) back to pressure. + if (layout->altitude == UNSUPPORTED) + return DC_STATUS_UNSUPPORTED; *(double *) value = ATM / BAR * pow(1 - 0.0000225577 * data[layout->altitude] * 250.0 * FEET, 5.25588); break; default: @@ -504,8 +547,114 @@ cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, } +/* + * Parse early Commander computers + */ static dc_status_t -cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + cochran_commander_parser_t *parser = (cochran_commander_parser_t *) abstract; + const cochran_parser_layout_t *layout = parser->layout; + const unsigned char *data = abstract->data; + const unsigned char *samples = data + layout->headersize; + + if (abstract->size < layout->headersize) + return DC_STATUS_DATAFORMAT; + + unsigned int size = abstract->size - layout->headersize; + unsigned int sample_interval = data[layout->pt_sample_interval]; + + dc_sample_value_t sample = {0}; + unsigned int time = 0, last_sample_time = 0; + unsigned int offset = 2; + unsigned int deco_ceiling = 0; + + unsigned int temp = samples[0]; // Half degrees F + unsigned int depth = samples[1]; // Half feet + + last_sample_time = sample.time = time; + if (callback) callback (DC_SAMPLE_TIME, sample, userdata); + + sample.depth = (depth / 2.0) * FEET; + if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); + + sample.temperature = (temp / 2.0 - 32.0) / 1.8; + if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); + + sample.gasmix = 0; + if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + + while (offset < size) { + const unsigned char *s = samples + offset; + + sample.time = time; + if (last_sample_time != sample.time) { + // We haven't issued this time yet. + last_sample_time = sample.time; + if (callback) callback (DC_SAMPLE_TIME, sample, userdata); + } + + if (*s & 0x80) { + // Event or temperate change byte + if (*s & 0x60) { + // Event byte + switch (*s) { + case 0xC5: // Deco obligation begins + break; + case 0xD8: // Deco obligation ends + break; + case 0xAB: // Decrement ceiling (deeper) + deco_ceiling += 10; // feet + + sample.deco.type = DC_DECO_DECOSTOP; + sample.deco.time = 60; // We don't know the duration + 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 = 60; // We don't know the duration + if (callback) callback(DC_SAMPLE_DECO, sample, userdata); + break; + } + } else { + // Temp change + if (*s & 0x10) + temp -= (*s & 0x0f); + else + temp += (*s & 0x0f); + sample.temperature = (temp / 2.0 - 32.0) / 1.8; + if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); + } + + offset++; + continue; + } + + // Depth sample + if (s[0] & 0x40) + depth -= s[0] & 0x3f; + else + depth += s[0] & 0x3f; + + sample.depth = (depth / 2.0) * FEET; + if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); + + offset++; + time += sample_interval; + } + return DC_STATUS_SUCCESS; +} + + +/* + * Parse Commander I (Pre-21000 s/n), II and EMC computers + */ +static dc_status_t +cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) { cochran_commander_parser_t *parser = (cochran_commander_parser_t *) abstract; const cochran_parser_layout_t *layout = parser->layout; @@ -720,3 +869,15 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb return DC_STATUS_SUCCESS; } + + +static dc_status_t +cochran_commander_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + cochran_commander_parser_t *parser = (cochran_commander_parser_t *) abstract; + + if (parser->model == COCHRAN_MODEL_COMMANDER_TM) + return cochran_commander_parser_samples_foreach_tm (abstract, callback, userdata); + else + return cochran_commander_parser_samples_foreach_emc (abstract, callback, userdata); +} diff --git a/src/descriptor.c b/src/descriptor.c index 633debd..8ae0842 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -314,11 +314,12 @@ static const dc_descriptor_t g_descriptors[] = { {"DiveSystem", "iDive2 Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x44}, {"DiveSystem", "iDive2 Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x45}, /* 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}, + {"Cochran", "Commander TM", DC_FAMILY_COCHRAN_COMMANDER, 0}, + {"Cochran", "Commander I", DC_FAMILY_COCHRAN_COMMANDER, 1}, + {"Cochran", "Commander II", DC_FAMILY_COCHRAN_COMMANDER, 2}, + {"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 3}, + {"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 4}, + {"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5}, }; typedef struct dc_descriptor_iterator_t { From adef3f67fce8a14febc9f608f22a929cf65311ac Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Sun, 13 Aug 2017 11:24:45 -0400 Subject: [PATCH 7/9] Add three event codes I found three previously undiscovered event codes. --- src/cochran_commander_parser.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index 7c76a43..9d42a44 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -221,7 +221,9 @@ static const cochran_events_t cochran_events[] = { {0xA9, 1, SAMPLE_EVENT_SURFACE, SAMPLE_FLAGS_END}, // Exited PDI mode {0xAB, 5, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Ceiling decrease {0xAD, 5, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Ceiling increase + {0xB5, 1, SAMPLE_EVENT_AIRTIME, SAMPLE_FLAGS_BEGIN}, // Air < 5 mins deco {0xBD, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Switched to nomal PO2 setting + {0xBE, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Ceiling > 60 ft {0xC0, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Switched to FO2 21% mode {0xC1, 1, SAMPLE_EVENT_ASCENT, SAMPLE_FLAGS_BEGIN}, // Ascent rate greater than limit {0xC2, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Low battery warning @@ -232,6 +234,7 @@ static const cochran_events_t cochran_events[] = { {0xC8, 1, SAMPLE_EVENT_PO2, SAMPLE_FLAGS_BEGIN}, // PO2 too high {0xCC, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN}, // Low Cylinder 1 pressure {0xCE, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN}, // Non-decompression warning + {0xCF, 1, SAMPLE_EVENT_OLF, SAMPLE_FLAGS_BEGIN}, // O2 Toxicity {0xCD, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Switched to deco blend {0xD0, 1, SAMPLE_EVENT_WORKLOAD, SAMPLE_FLAGS_BEGIN}, // Breathing rate alarm {0xD3, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE}, // Low gas 1 flow rate From 3f1131f80fe67f849dd22f35104c536675a5e7c4 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Mon, 14 Aug 2017 16:37:39 -0400 Subject: [PATCH 8/9] Fix profile buffer size and address size I dived the model enough to wrap the profile buffer and I was wrong about where the end was. Also, the buffer starts 3 bytes after where it could. We were treating profile pointers as 4 bytes when they are two bytes. This worked most of the time when short tissues were clear (tissue load follows the pointer). --- src/cochran_commander.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 50422fb..16707bf 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -129,11 +129,11 @@ static const cochran_device_layout_t cochran_cmdr_tm_device_layout = { 0xffffff, // cf_last_interdive 0x15c, // cf_serial_number 0x010000, // rb_logbook_begin - 0x012328, // rb_logbook_end + 0x01232b, // rb_logbook_end 90, // rb_logbook_entry_size 100, // rb_logbook_entry_count - 0x012328, // rb_profile_begin - 0x020000, // rb_profile_end + 0x01232b, // rb_profile_begin + 0x018000, // rb_profile_end 15, // pt_fingerprint 4, // fingerprint_size 0, // pt_profile_pre @@ -608,7 +608,7 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d unsigned int rb_head_ptr = 0; if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM) // TM uses SRAM and does not need to erase pages - rb_head_ptr = base + array_uint32_word_be(data->config + device->layout->cf_last_log); + rb_head_ptr = base + array_uint16_be(data->config + device->layout->cf_last_log); else if (device->layout->endian == ENDIAN_WORD_BE) rb_head_ptr = base + (array_uint32_word_be(data->config + device->layout->cf_last_log) & 0xfffff000) + 0x2000; else @@ -626,9 +626,16 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d } 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 = base + 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; + unsigned int last_profile_end = 0; + if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM) + // There is no end pointer in this model and no inter-dive + // events. We could use profile_begin from the next dive but + // since this is the last dive, we'll use rb_head_ptr + last_profile_end = rb_head_ptr; + else + last_profile_end = base + 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 = base + array_uint32_word_be(data->config + device->layout->cf_last_log); else @@ -650,7 +657,11 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d break; } - unsigned int profile_pre = base + array_uint32_le(log_entry + device->layout->pt_profile_pre); + unsigned int profile_pre = 0; + if (device->layout->model == COCHRAN_MODEL_COMMANDER_TM) + profile_pre = base + array_uint16_le(log_entry + device->layout->pt_profile_pre); + else + profile_pre = base + array_uint32_le(log_entry + device->layout->pt_profile_pre); unsigned int sample_size = cochran_commander_profile_size(device, data, idx, profile_pre, last_profile_pre); last_profile_pre = profile_pre; @@ -973,7 +984,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call unsigned int sample_start_address = 0; unsigned int sample_end_address = 0; if (layout->model == COCHRAN_MODEL_COMMANDER_TM) { - sample_start_address = base + array_uint24_le (log_entry + layout->pt_profile_begin); + sample_start_address = base + array_uint16_le (log_entry + layout->pt_profile_begin); sample_end_address = last_start_address; // Commander TM has SRAM which seems to randomize when they lose power for too long // Check for bad entries. From cdf7e8e635bc31db4684776cf49c6d07cfb1bc70 Mon Sep 17 00:00:00 2001 From: John Van Ostrand Date: Mon, 14 Aug 2017 16:37:40 -0400 Subject: [PATCH 9/9] Add event handling to TM model For some reason I had never added event processing to this model. --- src/cochran_commander_parser.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index 9d42a44..33f3b28 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -622,6 +622,9 @@ cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_ca sample.deco.time = 60; // We don't know the duration if (callback) callback(DC_SAMPLE_DECO, sample, userdata); break; + default: + cochran_commander_handle_event(parser, s[0], callback, userdata); + break; } } else { // Temp change