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.
This commit also fixes problems with dive computers where the log ringbuffer has wrapped. --- src/array.c | 7 ++ src/array.h | 3 + src/cochran_commander.c | 155 +++++++++++++++++++++++++++++++++-------- src/cochran_commander_parser.c | 83 +++++++++++++++++++--- src/descriptor.c | 9 +-- 5 files changed, 213 insertions(+), 44 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 b3e5fed..1c1009f 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -32,14 +32,16 @@
#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
typedef enum cochran_endian_t { ENDIAN_LE, ENDIAN_BE, + ENDIAN_WORD_BE, } cochran_endian_t;
typedef enum cochran_profile_size_t { @@ -48,7 +50,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;
@@ -84,7 +86,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; @@ -115,6 +119,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 @@ -131,6 +159,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 @@ -152,6 +182,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 @@ -173,6 +205,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 @@ -194,6 +228,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 @@ -205,10 +241,12 @@ 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}, + {"A30", COCHRAN_MODEL_EMC_16}, + {"230", COCHRAN_MODEL_EMC_20}, + {"231", COCHRAN_MODEL_EMC_20}, };
unsigned int model = 0xFFFFFFFF; @@ -501,6 +539,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) @@ -513,18 +552,36 @@ 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 - int rb_head_ptr = (array_uint32_le(data->config + device->layout->cf_last_interdive) & 0xfffff000) + 0x2000; + int rb_head_ptr; + 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; 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;
+ 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 int i = head_dive; + do { + i = ringbuffer_decrement(i, 1, 0, device->layout->rb_logbook_entry_count); + 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))) { + if (!memcmp(device->fingerprint, log_entry + device->layout->pt_fingerprint, device->layout->fingerprint_size)) { data->fp_dive_num = i; break; } @@ -543,7 +600,7 @@ cochran_commander_find_fingerprint(cochran_commander_device_t *device, cochran_d // Accumulate read size for progress bar sample_read_size += read_size; } - } + } while (i != tail_dive);
return sample_read_size; } @@ -653,6 +710,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; @@ -703,13 +763,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; } @@ -806,9 +866,13 @@ 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 { + } else { data.logbook_size = data.dive_count * device->layout->rb_logbook_entry_size; }
@@ -842,26 +906,47 @@ 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; 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; + + 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; + } + if (data.fp_dive_num > -1) + tail_dive = data.fp_dive_num + 1; + + int invalid_profile_flag = 0;
// Loop through each dive - for (int i = dive_count - 1; i > data.fp_dive_num; i--) { + unsigned int i = head_dive; + do { + i = ringbuffer_decrement(i, 1, 0, device->layout->rb_logbook_entry_count); + unsigned char *log_entry = data.logbook + i * device->layout->rb_logbook_entry_size;
unsigned int sample_start_address = array_uint32_le (log_entry + device->layout->pt_profile_begin); @@ -870,7 +955,10 @@ 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) + if (i == data.invalid_profile_dive_num) + invalid_profile_flag = 1; + + if (!invalid_profile_flag) sample_size = cochran_commander_profile_size(device, &data, i, PROFILE_SIZE_READ);
// Build dive blob @@ -892,7 +980,10 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
if (sample_start_address <= sample_end_address) { do { + int saved_progress = progress.current; rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, sample_size); + if (rc != DC_STATUS_SUCCESS) + progress.current = saved_progress; } while (rc != DC_STATUS_SUCCESS && tries++ < 3); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); @@ -905,7 +996,10 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
tries = 0; do { + int saved_progress = progress.current; rc = cochran_commander_read (device, &progress, sample_start_address, dive + device->layout->rb_logbook_entry_size, size); + if (rc != DC_STATUS_SUCCESS) + progress.current = saved_progress; } while (rc != DC_STATUS_SUCCESS && tries++ < 3); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); @@ -914,7 +1008,10 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call } tries = 0; do { + int saved_progress = progress.current; 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) + progress.current = saved_progress; } while (rc != DC_STATUS_SUCCESS && tries++ < 3); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to read the sample data."); @@ -924,13 +1021,13 @@ 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; }
free(dive); - } + } while (i != tail_dive);
error: free(data.logbook); diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c index ab854ad..d9521c0 100644 --- a/src/cochran_commander_parser.c +++ b/src/cochran_commander_parser.c @@ -21,6 +21,7 @@
#include <stdlib.h> #include <math.h> +#include <time.h>
#include <libdivecomputer/units.h>
@@ -31,10 +32,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_TIME_SHIFT 694242000
#define UNSUPPORTED 0xFFFFFFFF
@@ -43,11 +48,19 @@ typedef enum cochran_sample_format_t { SAMPLE_EMC, } cochran_sample_format_t;
+ +typedef enum cochran_date_encoding_t { + DATE_ENCODING_ELEMENTAL, + DATE_ENCODING_SECONDS_SINCE, +} cochran_date_encoding_t; + typedef struct cochran_parser_layout_t { cochran_sample_format_t format; unsigned int headersize; unsigned int samplesize; + cochran_date_encoding_t date_encoding; unsigned int second, minute, hour, day, month, year; + unsigned int timestamp; unsigned int pt_profile_begin; unsigned int water_conductivity; unsigned int pt_profile_pre; @@ -101,11 +114,38 @@ 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_SECONDS_SINCE, // date_encoding + 1, 0, 3, 2, 5, 4, // second, minute, hour, day, month, year, 1 byte each + 8, // timestamp, 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 + DATE_ENCODING_ELEMENTAL, // date_encoding 1, 0, 3, 2, 5, 4, // second, minute, hour, day, month, year, 1 byte each + 0, // timestamp, 4 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 +168,9 @@ static const cochran_parser_layout_t cochran_emc_parser_layout = { SAMPLE_EMC, // type 512, // headersize 3, // samplesize + DATE_ENCODING_ELEMENTAL, // date_encoding 0, 1, 2, 3, 4, 5, // second, minute, hour, day, month, year, 1 byte each + 0, // timestamp, 4 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 +338,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; @@ -341,12 +388,25 @@ cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dat return DC_STATUS_DATAFORMAT;
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); + if (layout->date_encoding == DATE_ENCODING_SECONDS_SINCE) { + time_t ts; + struct tm *t; + ts = array_uint32_le(data + layout->timestamp) + COCHRAN_TIME_SHIFT; + t = localtime(&ts); + datetime->second = t->tm_sec; + datetime->minute = t->tm_min; + datetime->hour = t->tm_hour; + datetime->day = t->tm_mday; + datetime->month = t->tm_mon + 1; + datetime->year = 1900 + t->tm_year; + } else { + 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); + } }
return DC_STATUS_SUCCESS; @@ -484,7 +544,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 b59ea45..76157da 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -292,10 +292,11 @@ static const dc_descriptor_t g_descriptors[] = { {"DiveSystem", "iX3M Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x23}, {"DiveSystem", "iX3M Tec", DC_FAMILY_DIVESYSTEM_IDIVE, 0x24}, {"DiveSystem", "iX3M Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x25}, - {"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 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 {
On 2017-06-03 01:51, John Van Ostrand wrote:
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.
This commit also fixes problems with dive computers where the log ringbuffer has wrapped.
Don't mix two unrelated changes. It only makes it more difficult to see what change is what. Use two separate patches instead.
+unsigned int +array_uint32_word_be (const unsigned char data[]) +{
- return data[1] + (data[0] << 8) + (data[3] << 16) + (data[2] << 24);
+}
That's a weird encoding! I wonder who thought that would be a good idea :-)
@@ -205,10 +241,12 @@ 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},
{"A30", COCHRAN_MODEL_EMC_16},
{"230", COCHRAN_MODEL_EMC_20},
};{"231", COCHRAN_MODEL_EMC_20},
How can this work? Originally you had this:
{"AM\x11""2212\x02", COCHRAN_MODEL_COMMANDER_AIR_NITROX}, {"AM7303\x8b\x43", COCHRAN_MODEL_EMC_14}, {"AMA315\xC3\xC5", COCHRAN_MODEL_EMC_16}, {"AM2315\xA3\x71", COCHRAN_MODEL_EMC_20},
Thus for the EMC 16 that would be "A31" and not "A30". Does that mean it has always been wrong?
- 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 int i = head_dive;
- do {
i = ringbuffer_decrement(i, 1, 0,
device->layout->rb_logbook_entry_count);
- unsigned char *log_entry = data->logbook + i *
device->layout->rb_logbook_entry_size;
... }
- }
- } while (i != tail_dive);
I find this construct more complex than necessary. With a do-while loop the important part, the condition, is hidden at the very end. You also need to be careful to handle the dive_count=0 case (which you do earlier in the code). In other backends I use this loop to get the same result:
for (unsigned int i = 0; i < dive_count; ++i) { unsigned int idx = (layout->rb_logbook_entry_count + head_dive - (i + 1)) % layout->rb_logbook_entry_count; ... }
+// Cochran time stamps start at Jan 1, 1992 +#define COCHRAN_TIME_SHIFT 694242000
This time shift is typically named the epoch.
+typedef enum cochran_date_encoding_t {
- DATE_ENCODING_ELEMENTAL,
- DATE_ENCODING_SECONDS_SINCE,
+} cochran_date_encoding_t;
This two representations are typically named broken-down time (dc_datetime_t, struct tm) and seconds since the epoch (dc_ticks_t, time_t). So I suggest to rename to DATETIME and TICKS. ELEMENTAL sounds a bit weird to me.
typedef struct cochran_parser_layout_t { cochran_sample_format_t format; unsigned int headersize; unsigned int samplesize;
- cochran_date_encoding_t date_encoding; unsigned int second, minute, hour, day, month, year;
- unsigned int timestamp; unsigned int pt_profile_begin; unsigned int water_conductivity; unsigned int pt_profile_pre;
You now have several fields for the same thing. Depending on the encoding you either use timestamp or second/minute/etc. And that second one comes in two different variants (1, 0, 3, 2, 5, 4 and 0, 1, 2, 3, 4, 5). Maybe this can be dealt with three encodings:
DATETIME_ENCODING_MSDHYM DATETIME_ENCODING_SMHDMY DATETIME_ENCODING_TICKS
and just one "datetime" offset.
if (layout->date_encoding == DATE_ENCODING_SECONDS_SINCE) {
time_t ts;
struct tm *t;
ts = array_uint32_le(data + layout->timestamp) +
COCHRAN_TIME_SHIFT;
t = localtime(&ts);
datetime->second = t->tm_sec;
datetime->minute = t->tm_min;
datetime->hour = t->tm_hour;
datetime->day = t->tm_mday;
datetime->month = t->tm_mon + 1;
datetime->year = 1900 + t->tm_year;
Use the dc_datetime_localtime function instead of localtime directly! It will use the thread-safe localtime_r function (if available) and take care of the conversion to dc_datetime_t automatically.
diff --git a/src/descriptor.c b/src/descriptor.c index b59ea45..76157da 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -292,10 +292,11 @@ static const dc_descriptor_t g_descriptors[] = { {"DiveSystem", "iX3M Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x23}, {"DiveSystem", "iX3M Tec", DC_FAMILY_DIVESYSTEM_IDIVE, 0x24}, {"DiveSystem", "iX3M Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x25},
- {"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 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},
The model numbers are supposed to be stable and never change (except for fixing bugs). Otherwise you won't be able to parse dives that have been downloaded with a previous libdivecomputer version. So the general rule is that if you want to add a new model, it will need to use the next available number instead of shifting the existing ones. (I think there are very few Cochran users out there, so the chance that someone is affected is probably small, so I could make a small exception.)
I assume there is no way to detect the dataformat based on the dive data itself?
Jef