Merge branch 'cochran'
This commit is contained in:
commit
b8b94c46fc
@ -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)
|
||||
{
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -28,31 +28,36 @@
|
||||
#include "device-private.h"
|
||||
#include "serial.h"
|
||||
#include "array.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#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 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
|
||||
|
||||
typedef enum cochran_endian_t {
|
||||
ENDIAN_LE,
|
||||
ENDIAN_BE,
|
||||
ENDIAN_WORD_BE,
|
||||
} cochran_endian_t;
|
||||
|
||||
typedef struct cochran_commander_model_t {
|
||||
unsigned char id[2 + 1];
|
||||
unsigned char id[3 + 1];
|
||||
unsigned int model;
|
||||
} 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;
|
||||
|
||||
@ -78,7 +83,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;
|
||||
@ -109,15 +116,15 @@ static const dc_device_vtable_t cochran_commander_device_vtable = {
|
||||
cochran_commander_device_close /* close */
|
||||
};
|
||||
|
||||
// Cochran Commander Nitrox
|
||||
static const cochran_device_layout_t cochran_cmdr_device_layout = {
|
||||
COCHRAN_MODEL_COMMANDER_AIR_NITROX, // model
|
||||
// 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_BE, // endian
|
||||
ENDIAN_WORD_BE, // endian
|
||||
115200, // baudrate
|
||||
0x046, // cf_dive_count
|
||||
0x06E, // cf_last_log
|
||||
0x200, // cf_last_interdive
|
||||
0x6c, // cf_last_log
|
||||
0x70, // cf_last_interdive
|
||||
0x0AA, // cf_serial_number
|
||||
0x00000000, // rb_logbook_begin
|
||||
0x00020000, // rb_logbook_end
|
||||
@ -125,6 +132,32 @@ static const cochran_device_layout_t cochran_cmdr_device_layout = {
|
||||
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
|
||||
24, // address_bits
|
||||
ENDIAN_WORD_BE, // endian
|
||||
115200, // baudrate
|
||||
0x046, // cf_dive_count
|
||||
0x06C, // cf_last_log
|
||||
0x070, // 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
|
||||
0, // pt_fingerprint
|
||||
6, // fingerprint_size
|
||||
30, // pt_profile_pre
|
||||
6, // pt_profile_begin
|
||||
128, // pt_profile_end
|
||||
@ -146,6 +179,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
|
||||
@ -167,6 +202,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
|
||||
@ -188,6 +225,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
|
||||
@ -199,15 +238,20 @@ 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},
|
||||
{"\x40""30", COCHRAN_MODEL_EMC_20},
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
@ -429,24 +473,165 @@ 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;
|
||||
|
||||
static void
|
||||
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
|
||||
* 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, unsigned int sample_start_address, unsigned int sample_end_address)
|
||||
{
|
||||
// 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;
|
||||
data->fp_dive_num = -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 = 0;
|
||||
if (device->layout->endian == ENDIAN_WORD_BE)
|
||||
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_log) & 0xfffff000) + 0x2000;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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 + device->layout->pt_fingerprint, device->layout->fingerprint_size)) {
|
||||
data->fp_dive_num = idx;
|
||||
break;
|
||||
}
|
||||
|
||||
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
|
||||
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 = idx;
|
||||
}
|
||||
// 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 +694,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)
|
||||
{
|
||||
@ -647,6 +735,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;
|
||||
@ -697,13 +788,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;
|
||||
}
|
||||
@ -767,111 +858,172 @@ 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;
|
||||
data.logbook = NULL;
|
||||
data.sample = NULL;
|
||||
status = cochran_commander_read_all (device, &data);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
goto error;
|
||||
|
||||
// Emit a device info event.
|
||||
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);
|
||||
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
|
||||
// Calculate max data sizes
|
||||
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;
|
||||
|
||||
// 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;
|
||||
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;
|
||||
// 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 (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 + layout->cf_dive_count);
|
||||
|
||||
if (data.dive_count == 0)
|
||||
// No dives to read
|
||||
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;
|
||||
} else {
|
||||
data.logbook_size = data.dive_count * 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// 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);
|
||||
|
||||
unsigned int dive_count = 0;
|
||||
if (data.dive_count < device->layout->rb_logbook_entry_count)
|
||||
dive_count = data.dive_count;
|
||||
cochran_commander_get_sample_parms(device, &data);
|
||||
|
||||
// Emit a device info event.
|
||||
dc_event_devinfo_t devinfo;
|
||||
devinfo.model = layout->model;
|
||||
devinfo.firmware = 0; // unknown
|
||||
if (layout->endian == ENDIAN_WORD_BE)
|
||||
devinfo.serial = array_uint32_word_be(data.config + layout->cf_serial_number);
|
||||
else
|
||||
dive_count = device->layout->rb_logbook_entry_count;
|
||||
devinfo.serial = array_uint32_le(data.config + layout->cf_serial_number);
|
||||
|
||||
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
|
||||
|
||||
unsigned int head_dive = 0, tail_dive = 0, dive_count = 0;
|
||||
|
||||
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 % 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) % layout->rb_logbook_entry_count;
|
||||
|
||||
// Number of dives to read
|
||||
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 (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 = (layout->rb_logbook_entry_count + head_dive - (i + 1)) % layout->rb_logbook_entry_count;
|
||||
|
||||
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 char *log_entry = data.logbook + idx * layout->rb_logbook_entry_size;
|
||||
|
||||
// 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;
|
||||
}
|
||||
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);
|
||||
|
||||
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 (idx == data.invalid_profile_dive_num)
|
||||
invalid_profile_flag = 1;
|
||||
|
||||
if (!invalid_profile_flag)
|
||||
sample_size = cochran_commander_profile_size(device, &data, idx, sample_start_address, sample_end_address);
|
||||
|
||||
// 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
|
||||
|
||||
// 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, idx);
|
||||
|
||||
if (sample_start_address <= sample_end_address) {
|
||||
memcpy(dive + device->layout->rb_logbook_entry_size, sample, 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;
|
||||
free(dive);
|
||||
goto error;
|
||||
}
|
||||
} 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;
|
||||
|
||||
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_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.");
|
||||
status = rc;
|
||||
free(dive);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (callback && !callback (dive, dive_size, dive, sizeof(device->fingerprint), userdata)) {
|
||||
if (callback && !callback (dive, dive_size, dive + layout->pt_fingerprint, layout->fingerprint_size, userdata)) {
|
||||
free(dive);
|
||||
break;
|
||||
}
|
||||
@ -881,6 +1033,5 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
|
||||
error:
|
||||
free(data.logbook);
|
||||
free(data.sample);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
@ -503,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;
|
||||
@ -537,45 +602,48 @@ 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.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
|
||||
if (last_gasmix != 1) {
|
||||
sample.gasmix = 1;
|
||||
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
|
||||
break;
|
||||
case 0xF3: // Switched to gas blend 1
|
||||
last_gasmix = sample.gasmix;
|
||||
}
|
||||
break;
|
||||
case 0xF3: // Switched to gas blend 1
|
||||
if (last_gasmix != 0) {
|
||||
sample.gasmix = 0;
|
||||
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
|
||||
break;
|
||||
last_gasmix = sample.gasmix;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user