[PATCH 1/2] Cochran: Addressed simpler code review issues
John Van Ostrand
john at vanostrand.com
Sat Feb 27 09:56:24 PST 2016
Mostly style, best practice issues and a few bugs.
---
src/cochran_cmdr_parser.c | 135 +++++++++++++++----------
src/cochran_commander.c | 18 ++--
src/cochran_commander_parser.c | 79 +++++++++------
src/cochran_commander_parser.h | 104 ++++++++-----------
src/cochran_emc_parser.c | 222 +++++++++++++++--------------------------
5 files changed, 259 insertions(+), 299 deletions(-)
diff --git a/src/cochran_cmdr_parser.c b/src/cochran_cmdr_parser.c
index 4e2915b..20d4fd2 100644
--- a/src/cochran_cmdr_parser.c
+++ b/src/cochran_cmdr_parser.c
@@ -40,7 +40,8 @@ cochran_cmdr_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
unsigned int flags, void *value)
{
const unsigned char *data = abstract->data;
- const unsigned char *log = data + COCHRAN_MODEL_SIZE;
+ const unsigned char *log_entry = data + COCHRAN_MODEL_SIZE;
+ unsigned int minutes = 0, qfeet = 0;
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
dc_salinity_t *water = (dc_salinity_t *) value;
@@ -48,60 +49,72 @@ cochran_cmdr_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
if (value) {
switch (type) {
case DC_FIELD_TEMPERATURE_SURFACE:
- *((unsigned int*) value) = ((float) log[CMD_START_TEMP] - 32) / 1.8;
+ *((unsigned int*) value) = (log_entry[CMD_START_TEMP] - 32.0) / 1.8;
break;
case DC_FIELD_TEMPERATURE_MINIMUM:
- if (array_uint16_le(log + CMD_MIN_TEMP) == 0xFFFF)
- *((double *) value) = 0;
+ if (log_entry[CMD_MIN_TEMP] == 0xFF)
+ return DC_STATUS_UNSUPPORTED;
else
- *((unsigned int*) value) = ((float) log[CMD_MIN_TEMP] / 2
+ *((unsigned int*) value) = (log_entry[CMD_MIN_TEMP] / 2.0
+ 20 - 32) / 1.8;
break;
case DC_FIELD_TEMPERATURE_MAXIMUM:
- if (array_uint16_le(log + CMD_MAX_TEMP) == 0xFFFF)
- *((double *) value) = 0;
+ if (log_entry[CMD_MAX_TEMP] == 0xFF)
+ return DC_STATUS_UNSUPPORTED;
else
- *((unsigned int*) value) = ((float) log[CMD_MAX_TEMP] / 2
+ *((unsigned int*) value) = (log_entry[CMD_MAX_TEMP] / 2.0
+ 20 - 32) / 1.8;
break;
case DC_FIELD_DIVETIME:
- if (array_uint16_le(log + CMD_BT) == 0xFFFF)
- *((double *) value) = 0;
+ minutes = array_uint16_le(log_entry + CMD_BT);
+
+ if (minutes == 0xFFFF)
+ return DC_STATUS_UNSUPPORTED;
else
- *((unsigned int *) value) = array_uint16_le (log + CMD_BT) * 60;
+ *((unsigned int *) value) = minutes * 60;
break;
case DC_FIELD_MAXDEPTH:
- if (array_uint16_le(log + CMD_MAX_DEPTH) == 0xFFFF)
- *((double *) value) = 0;
+ qfeet = array_uint16_le(log_entry + CMD_MAX_DEPTH);
+ if (qfeet == 0xFFFF)
+ return DC_STATUS_UNSUPPORTED;
else
- *((double *) value) = (float) array_uint16_le (log
- + CMD_MAX_DEPTH) / 4 * FEET;
+ *((double *) value) = qfeet / 4.0 * FEET;
break;
case DC_FIELD_AVGDEPTH:
- if (array_uint16_le(log + CMD_AVG_DEPTH) == 0xFFFF)
- *((double *) value) = 0;
+ qfeet = array_uint16_le(log_entry + CMD_AVG_DEPTH);
+ if (qfeet == 0xFFFF)
+ return DC_STATUS_UNSUPPORTED;
else
- *((double *) value) = (float) array_uint16_le (log
- + CMD_AVG_DEPTH) / 4 * FEET;
+ *((double *) value) = qfeet / 4.0 * FEET;
break;
case DC_FIELD_GASMIX_COUNT:
*((unsigned int *) value) = 2;
break;
case DC_FIELD_GASMIX:
- gasmix->oxygen = (double) array_uint16_le (log
- + CMD_O2_PERCENT + 2 * flags) / 256 / 100;
+ // Gas percentages are decimal and encoded as
+ // highbyte = integer portion
+ // lowbyte = decimal portion, divide by 256 to get decimal value
+ gasmix->oxygen = array_uint16_le (log_entry
+ + CMD_O2_PERCENT + 2 * flags) / 256.0 / 100;
gasmix->helium = 0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
case DC_FIELD_SALINITY:
- // 0 = low conductivity, 1 = high, maybe there's a 2?
- water->type = ( (log[CMD_WATER_CONDUCTIVITY] & 0x3) == 0
+ // 0x00 = low conductivity, 0x10 = high, maybe there's a 0x01 and 0x11?
+ // Assume Cochran's conductivity ranges from 0 to 3
+ // 0 is fresh water, anything else is sea water
+ // for density assume
+ // 0 = 1000kg/m³, 2 = 1025kg/m³
+ // and other values are linear
+ water->type = ( (log_entry[CMD_WATER_CONDUCTIVITY] & 0x3) == 0
? DC_WATER_FRESH : DC_WATER_SALT );
- water->density = 1000 + 12.5 * (log[CMD_WATER_CONDUCTIVITY] & 0x3);
+ water->density = 1000 + 12.5 * (log_entry[CMD_WATER_CONDUCTIVITY] & 0x3);
break;
case DC_FIELD_ATMOSPHERIC:
+ // Cochran measures air pressure and stores it as altitude.
+ // Convert altitude (measured in 1/4 kilofeet) back to pressure.
*(double *) value = ATM / BAR * pow(1 - 0.0000225577
- * (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588);
+ * log_entry[CMD_ALTITUDE] * 250.0 * FEET, 5.25588);
break;
default:
return DC_STATUS_UNSUPPORTED;
@@ -116,31 +129,34 @@ dc_status_t
cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
dc_sample_callback_t callback, void *userdata)
{
- const unsigned char *log = abstract->data + COCHRAN_MODEL_SIZE;
- const unsigned char *samples = log + COCHRAN_CMDR_LOG_SIZE;
+ const unsigned char *log_entry = abstract->data + COCHRAN_MODEL_SIZE;
+ const unsigned char *samples = log_entry + COCHRAN_CMDR_LOG_SIZE;
unsigned int size = abstract->size - COCHRAN_MODEL_SIZE
- COCHRAN_CMDR_LOG_SIZE;
- const unsigned char *s;
- dc_sample_value_t sample = {0}, empty_sample = {0};
- unsigned int time = 0, last_sample_time;
+ dc_sample_value_t sample = {0};
+ unsigned int time = 0, last_sample_time = 0;
unsigned int offset = 0;
double temperature;
- double depth;
+ int depth_qfeet;
double ascent_rate;
unsigned char corrupt_dive = 0;
- int cmdr_event_bytes[15][2] = { {0x00, 17}, {0x01, 21}, {0x02, 18},
- {0x03, 17}, {0x06, 19}, {0x07, 19},
- {0x08, 19}, {0x09, 19}, {0x0a, 19},
- {0x0b, 21}, {0x0c, 19}, {0x0d, 19},
- {0x0e, 19}, {0x10, 21},
- { -1, 1} };
-
- if (array_uint32_le(log + COCHRAN_CMDR_LOG_SIZE / 2) == 0xFFFFFFFF) {
+ struct event_size cmdr_event_bytes[15] = { {0x00, 17}, {0x01, 21}, {0x02, 18},
+ {0x03, 17}, {0x06, 19}, {0x07, 19},
+ {0x08, 19}, {0x09, 19}, {0x0a, 19},
+ {0x0b, 21}, {0x0c, 19}, {0x0d, 19},
+ {0x0e, 19}, {0x10, 21},
+ { -1, 1} };
+
+ // In rare circumstances Cochran computers won't record the end-of-dive
+ // log entry block. When the end-sample pointer is 0xFFFFFFFF it's corrupt.
+ // That means we don't really know where the dive samples end and we don't
+ // know what the dive summary values are (i.e. max depth, min temp)
+ if (array_uint32_le(log_entry + COCHRAN_CMDR_LOG_SIZE / 2) == 0xFFFFFFFF) {
corrupt_dive = 1;
- WARNING(abstract->context, "Incomplete dive on %02d/%02d/%02d at %02d:%02d:%02d, trying to parse samples", log[CMD_YEAR], log[CMD_MON], log[CMD_DAY], log[CMD_HOUR], log[CMD_MIN], log[CMD_SEC]);
+ WARNING(abstract->context, "Incomplete dive on %02d/%02d/%02d at %02d:%02d:%02d, trying to parse samples", log_entry[CMD_YEAR], log_entry[CMD_MON], log_entry[CMD_DAY], log_entry[CMD_HOUR], log_entry[CMD_MIN], log_entry[CMD_SEC]);
// Eliminate inter-dive events
size = cochran_backparse(abstract, samples, size, &cmdr_event_bytes);
@@ -150,19 +166,19 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
// and temp ever other second.
// Prime values from the dive log section
- depth = array_uint16_le (log + CMD_START_DEPTH) / 4;
+ depth_qfeet = array_uint16_le (log_entry + CMD_START_DEPTH);
last_sample_time = sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
- sample.depth = depth * FEET;
+ sample.depth = (double) depth_qfeet * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
- sample.temperature = (float) *(log + CMD_START_TEMP) - 32 / 1.8;
+ sample.temperature = (log_entry[CMD_START_TEMP] - 32.0) / 1.8;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
while (offset < size) {
- sample = empty_sample;
+ const unsigned char *s = NULL;
sample.time = time;
if (callback && last_sample_time != sample.time) {
@@ -175,10 +191,15 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
// If corrupt_dive end before offset
if (corrupt_dive) {
- if ( s[0] == 0x10
- || s[0] == 0xFF
- || s[0] == 0xA8) {
- break;
+ // When we aren't sure where the sample data ends we can
+ // look for events that shouldn't be in the sample data.
+ // 0xFF is unwritten memory
+ // 0xA8 indicates start of post-dive interval
+ // 0xE3 (switch to FO2 mode) and 0xF3 (switch to blend 1) occur
+ // at dive start so when we see them after the first second we
+ // found the beginning of the next dive.
+ if (s[0] == 0xFF || s[0] == 0xA8) {
+ break;
}
if (time > 1 && (s[0] == 0xE3 || s[0] == 0xF3))
break;
@@ -187,25 +208,33 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
// Check for event
if (s[0] & 0x80) {
offset += cochran_commander_handle_event(abstract, callback,
- userdata, s[0], offset, time);
+ userdata, s[0], time);
continue;
}
// Depth is logged as change in feet, bit 0x40 means negative depth
- depth += (float) (s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
- sample.depth = depth * FEET;
+ if (s[0] & 0x40)
+ depth_qfeet -= (s[0] & 0x3f);
+ else
+ depth_qfeet += (s[0] & 0x3f);
+
+ sample.depth = depth_qfeet / 4.0 * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
// Ascent rate is logged in the 0th sample, temp in the 1st, repeat.
if (time % 2 == 0) {
// Ascent rate
- ascent_rate = (float) (s[1] & 0x7f) / 4 * (s[1] & 0x80 ? 1 : -1);
+ if (s[1] & 0x80)
+ ascent_rate = (s[1] & 0x7f) / 4.0;
+ else
+ ascent_rate = - (s[1] & 0x7f) / 4.0;
+
sample.ascent_rate = ascent_rate * FEET;
if (callback) callback (DC_SAMPLE_ASCENT_RATE, sample, userdata);
} else {
// Temperature logged in half degrees F above 20
- temperature = (float) s[1] / 2 + 20;
+ temperature = s[1] / 2.0 + 20;
sample.temperature = (temperature - 32.0) / 1.8;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
diff --git a/src/cochran_commander.c b/src/cochran_commander.c
index ae2dc45..d11df92 100644
--- a/src/cochran_commander.c
+++ b/src/cochran_commander.c
@@ -855,7 +855,7 @@ cochran_commander_device_dump (dc_device_t *abstract, dc_buffer_t *data)
static unsigned int
cochran_guess_sample_end_address(cochran_layout_t *layout, cochran_data_t *data, unsigned int log_num)
{
- const unsigned char *log = data->logbook + layout->rb_log_size * log_num;
+ const unsigned char *log_entry = data->logbook + layout->rb_log_size * log_num;
if (log_num == data->dive_count)
// Return next usable address from config0 page
@@ -863,7 +863,7 @@ cochran_guess_sample_end_address(cochran_layout_t *layout, cochran_data_t *data,
+ layout->rb_profile_end);
// Next log's start address
- return array_uint32_le(log + layout->rb_log_size + 6);
+ return array_uint32_le(log_entry + layout->rb_log_size + 6);
}
@@ -878,7 +878,7 @@ cochran_commander_device_foreach (dc_device_t *abstract,
unsigned int sample_start_address, sample_end_address;
dc_status_t rc;
- unsigned char *log, *fingerprint, *sample, *dive;
+ unsigned char *log_entry, *fingerprint, *sample, *dive;
int sample_size, dive_size;
rc = cochran_commander_device_read_all (abstract);
@@ -889,10 +889,10 @@ cochran_commander_device_foreach (dc_device_t *abstract,
// Loop through each dive
int i;
for (i = data->dive_count - 1; i > data->fp_dive_num; i--) {
- log = data->logbook + i * layout->rb_log_size;
+ log_entry = data->logbook + i * layout->rb_log_size;
- sample_start_address = array_uint32_le (log + 6);
- sample_end_address = array_uint32_le (log + layout->rb_log_size / 2);
+ sample_start_address = array_uint32_le (log_entry + 6);
+ sample_end_address = array_uint32_le (log_entry + layout->rb_log_size / 2);
if (sample_end_address == 0xFFFFFFFF)
// Corrupt dive, guess the end address
@@ -901,7 +901,7 @@ cochran_commander_device_foreach (dc_device_t *abstract,
sample = data->sample + sample_start_address - data->sample_data_offset;
// Determine size of sample
- unsigned int dive_num =array_uint16_le(log + layout->pt_log_dive_number);
+ unsigned int dive_num =array_uint16_le(log_entry + layout->pt_log_dive_number);
if (dive_num >= data->profile_tail)
sample_size = sample_end_address - sample_start_address;
else
@@ -912,7 +912,7 @@ cochran_commander_device_foreach (dc_device_t *abstract,
sample_size += layout->rb_profile_end
- layout->rb_profile_begin;
- fingerprint = log + layout->pt_log_dive_number;
+ fingerprint = log_entry + layout->pt_log_dive_number;
// Build dive blob
dive_size = COCHRAN_MODEL_SIZE + layout->rb_log_size + sample_size;
@@ -921,7 +921,7 @@ cochran_commander_device_foreach (dc_device_t *abstract,
return DC_STATUS_NOMEMORY;
memcpy(dive, data->id + 0x3B, 8); // model string
- memcpy(dive + COCHRAN_MODEL_SIZE, log, layout->rb_log_size); // log
+ memcpy(dive + COCHRAN_MODEL_SIZE, log_entry, layout->rb_log_size); // log
// Copy profile data
if (sample_size) {
diff --git a/src/cochran_commander_parser.c b/src/cochran_commander_parser.c
index e2b854c..a6380e4 100644
--- a/src/cochran_commander_parser.c
+++ b/src/cochran_commander_parser.c
@@ -43,9 +43,6 @@ static dc_status_t cochran_commander_parser_get_field (dc_parser_t *abstract,
static dc_status_t cochran_commander_parser_samples_foreach
(dc_parser_t *abstract, dc_sample_callback_t callback,
void *userdata);
-int cochran_commander_handle_event (dc_parser_t *abstract,
- dc_sample_callback_t callback, void *userdata, unsigned char code,
- unsigned int offset, unsigned int time);
typedef struct cochran_commander_parser_t cochran_commander_parser_t;
@@ -81,6 +78,8 @@ cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_NOMEMORY;
}
+ parser->layout = NULL;
+
*out = (dc_parser_t *) parser;
return DC_STATUS_SUCCESS;
@@ -92,12 +91,14 @@ cochran_commander_parser_set_data (dc_parser_t *abstract,
const unsigned char *data, unsigned int size)
{
cochran_commander_parser_t *parser = (cochran_commander_parser_t *) abstract;
- abstract->data = data;
- abstract->size = size;
// Determine cochran data format
// abstract->data is prefixed by the model string
- parser->layout = cochran_commander_get_layout(data);
+ // FIXME: Instead of prefixing the model string, pass the model
+ // number to the cochran_commander_parser_create() function. That's
+ // how other backends do this.
+ if (size >= COCHRAN_MODEL_SIZE)
+ parser->layout = cochran_commander_get_layout(data);
return DC_STATUS_SUCCESS;
}
@@ -110,22 +111,24 @@ cochran_commander_parser_get_datetime (dc_parser_t *abstract,
{
cochran_commander_parser_t *parser = (cochran_commander_parser_t *) abstract;
const unsigned char *data = abstract->data;
- const unsigned char *log = data + COCHRAN_MODEL_SIZE;
+ const unsigned char *log_entry = data + COCHRAN_MODEL_SIZE;
+
+ // FIXME: See my comment regarding a layout structure below.
if (parser->layout->date_format == DATE_SMHDMY) {
- datetime->second = log[0];
- datetime->minute = log[1];
- datetime->hour = log[2];
- datetime->day = log[3];
- datetime->month = log[4];
- datetime->year = log[5] + (log[5] > 91 ? 1900 : 2000);
+ datetime->second = log_entry[0];
+ datetime->minute = log_entry[1];
+ datetime->hour = log_entry[2];
+ datetime->day = log_entry[3];
+ datetime->month = log_entry[4];
+ datetime->year = log_entry[5] + (log_entry[5] > 91 ? 1900 : 2000);
} else {
- datetime->second = log[1];
- datetime->minute = log[0];
- datetime->hour = log[3];
- datetime->day = log[2];
- datetime->month = log[5];
- datetime->year = log[4] + (log[5] > 91 ? 1900 : 2000);
+ datetime->second = log_entry[1];
+ datetime->minute = log_entry[0];
+ datetime->hour = log_entry[3];
+ datetime->day = log_entry[2];
+ datetime->month = log_entry[5];
+ datetime->year = log_entry[4] + (log_entry[4] > 91 ? 1900 : 2000);
}
return DC_STATUS_SUCCESS;
@@ -137,14 +140,20 @@ cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
{
cochran_commander_parser_t *parser = (cochran_commander_parser_t *) abstract;
+ // FIXME: These two get_field functions appear very similar. I think
+ // you can easily merge them if you introduce a layout structure
+ // where you have the offset of each field. Check the uwatec parser
+ // with the uwatec_smart_header_info_t struct for an example. You
+ // already have the EMC_xxx and CMD_xxx macros.
+
+ // FIXME: I suggest using the same model numbers as in descriptor.c
+ // and just list all models explictly with extra case statements.
switch (parser->layout->model & 0xFF0000)
{
case COCHRAN_MODEL_COMMANDER_FAMILY:
return cochran_cmdr_parser_get_field(abstract, type, flags, value);
- break;
case COCHRAN_MODEL_EMC_FAMILY:
return cochran_emc_parser_get_field(abstract, type, flags, value);
- break;
}
return DC_STATUS_UNSUPPORTED;
@@ -156,6 +165,10 @@ cochran_commander_parser_samples_foreach (dc_parser_t *abstract,
{
cochran_commander_parser_t *parser = (cochran_commander_parser_t *) abstract;
+ // FIXME: Same remarks here. At first sight these two functions look
+ // again very similar. I wonder if we can merge them and deal with
+ // the difference by means of the info in a layout structure?
+
switch (parser->layout->model & 0xFF0000)
{
case COCHRAN_MODEL_COMMANDER_FAMILY:
@@ -187,7 +200,7 @@ cochran_commander_get_event_info(const unsigned char code,
int
cochran_commander_handle_event (dc_parser_t *abstract,
dc_sample_callback_t callback, void *userdata, unsigned char code,
- unsigned int offset, unsigned int time)
+ unsigned int time)
{
dc_sample_value_t sample = {0};
cochran_events_t event;
@@ -220,18 +233,15 @@ cochran_commander_handle_event (dc_parser_t *abstract,
break;
default:
// Don't send known events of type NONE
- if (! event.type == SAMPLE_EVENT_NONE) {
+ if (event.type != SAMPLE_EVENT_NONE) {
sample.event.type = event.type;
sample.event.flags = event.flag;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
}
}
} else {
- // Unknown event, send it so we know we missed something
- sample.event.type = SAMPLE_EVENT_NONE;
- sample.event.flags = SAMPLE_FLAGS_NONE;
- sample.event.value = code;
- if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
+ // Unknown event, send warning so we know we missed something
+ WARNING(abstract->context, "Unkown event 0x%02x", code);
}
return event.data_bytes;
@@ -243,13 +253,16 @@ cochran_commander_handle_event (dc_parser_t *abstract,
* Used to find the end of a dive that has an incomplete dive-end
* block. It parses backwards past inter-dive events.
*/
-
-int cochran_backparse(dc_parser_t *abstract, const char *samples, int size, int (*event_bytes)[15][2]) {
+int cochran_backparse(dc_parser_t *abstract, unsigned const char *samples, int size, struct event_size (*event_bytes)[15]) {
int result = size, best_result = size;
- for (int x = 0; (*event_bytes)[x][0] != -1; x++) {
- int ptr = size - (*event_bytes)[x][1];
- if (ptr > 0 && samples[ptr] == (*event_bytes)[x][0]) {
+ for (int x = 0; (*event_bytes)[x].code != -1; x++) {
+ int ptr = size - (*event_bytes)[x].size;
+ if (ptr > 0 && samples[ptr] == (*event_bytes)[x].code) {
+ // Recurse to find the largest match. Because we are parsing backwards
+ // and the events vary in size we can't be sure the byte that matches
+ // the event code is an event code or data from inside a longer or shorter
+ // event.
result = cochran_backparse(abstract, samples, ptr, event_bytes);
}
diff --git a/src/cochran_commander_parser.h b/src/cochran_commander_parser.h
index 730edf7..0a125e5 100644
--- a/src/cochran_commander_parser.h
+++ b/src/cochran_commander_parser.h
@@ -22,70 +22,52 @@
typedef struct cochran_events_t {
unsigned char code;
unsigned char data_bytes;
- const char *name;
parser_sample_event_t type;
parser_sample_flags_t flag;
} cochran_events_t;
+struct event_size {
+ int code;
+ int size;
+};
+
+// FIXME: Mark this variable as const!
+// FIXME: This does not belong in a header file! Now you end up with
+// multiple instances, one in each source file.
+// FIXME: I actually prefer to see the emc and cmdr files merged into
+// the commander file anyway. If you read my comments there, I think we
+// can share most of the code there, so using separate files becomes
+// pointless. And then the previous remark is no longer an issue.
static cochran_events_t cochran_events[] = {
- { 0xA8, 1, "Entered PDI mode",
- SAMPLE_EVENT_SURFACE, SAMPLE_FLAGS_BEGIN },
- { 0xA9, 1, "Exited PDI mode",
- SAMPLE_EVENT_SURFACE, SAMPLE_FLAGS_END },
- { 0xAB, 5, "Ceiling decrease",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE },
- { 0xAD, 5, "Ceiling increase",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE },
- { 0xBD, 1, "Switched to nomal PO2 setting",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE },
- { 0xC0, 1, "Switched to FO2 21% mode",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE },
- { 0xC1, 1, "Ascent rate greater than limit",
- SAMPLE_EVENT_ASCENT, SAMPLE_FLAGS_BEGIN },
- { 0xC2, 1, "Low battery warning",
- SAMPLE_EVENT_BATTERY, SAMPLE_FLAGS_NONE },
- { 0xC3, 1, "CNS Oxygen toxicity warning",
- SAMPLE_EVENT_OLF, SAMPLE_FLAGS_NONE },
- { 0xC4, 1, "Depth exceeds user set point",
- SAMPLE_EVENT_MAXDEPTH, SAMPLE_FLAGS_NONE },
- { 0xC5, 1, "Entered decompression mode",
- SAMPLE_EVENT_DEEPSTOP, SAMPLE_FLAGS_BEGIN },
- { 0xC8, 1, "PO2 too high",
- SAMPLE_EVENT_FLOOR, SAMPLE_FLAGS_BEGIN },
- { 0xCC, 1, "Low Cylinder 1 pressure",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN },
- { 0xCE, 1, "Non-decompression warning",
- SAMPLE_EVENT_RBT, SAMPLE_FLAGS_BEGIN },
- { 0xCD, 1, "Switched to deco blend",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE },
- { 0xD0, 1, "Breathing rate alarm",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN },
- { 0xD3, 1, "Low gas 1 flow rate",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE },
- { 0xD6, 1, "Depth is less than ceiling",
- SAMPLE_EVENT_CEILING, SAMPLE_FLAGS_BEGIN },
- { 0xD8, 1, "End decompression mode",
- SAMPLE_EVENT_DEEPSTOP, SAMPLE_FLAGS_END },
- { 0xE1, 1, "End ascent rate warning",
- SAMPLE_EVENT_ASCENT, SAMPLE_FLAGS_END },
- { 0xE2, 1, "Low SBAT battery warning",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE },
- { 0xE3, 1, "Switched to FO2 mode",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE },
- { 0xE5, 1, "Switched to PO2 mode",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE },
- { 0xEE, 1, "End non-decompresison warning",
- SAMPLE_EVENT_RBT, SAMPLE_FLAGS_END },
- { 0xEF, 1, "Switch to blend 2",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE },
- { 0xF0, 1, "Breathing rate alarm",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_END },
- { 0xF3, 1, "Switch to blend 1",
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE },
- { 0xF6, 1, "End Depth is less than ceiling",
- SAMPLE_EVENT_CEILING, SAMPLE_FLAGS_END },
- { 0x00, 1, NULL,
- SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }
+ { 0xA8, 1, SAMPLE_EVENT_SURFACE, SAMPLE_FLAGS_BEGIN }, // Entered PDI mode
+ { 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
+ { 0xBD, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, // Switched to nomal PO2 setting
+ { 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_BATTERY, SAMPLE_FLAGS_NONE }, // Low battery warning
+ { 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_DEEPSTOP, SAMPLE_FLAGS_BEGIN }, // Entered decompression mode
+ { 0xC8, 1, SAMPLE_EVENT_FLOOR, SAMPLE_FLAGS_BEGIN }, // PO2 too high
+ { 0xCC, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN }, // Low Cylinder 1 pressure
+ { 0xCE, 1, SAMPLE_EVENT_RBT, SAMPLE_FLAGS_BEGIN }, // Non-decompression warning
+ { 0xCD, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, // Switched to deco blend
+ { 0xD0, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_BEGIN }, // Breathing rate alarm
+ { 0xD3, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, // Low gas 1 flow rate
+ { 0xD6, 1, SAMPLE_EVENT_CEILING, SAMPLE_FLAGS_BEGIN }, // Depth is less than ceiling
+ { 0xD8, 1, SAMPLE_EVENT_DEEPSTOP, SAMPLE_FLAGS_END }, // End decompression mode
+ { 0xE1, 1, SAMPLE_EVENT_ASCENT, SAMPLE_FLAGS_END }, // End ascent rate warning
+ { 0xE2, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, // Low SBAT battery warning
+ { 0xE3, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, // Switched to FO2 mode
+ { 0xE5, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, // Switched to PO2 mode
+ { 0xEE, 1, SAMPLE_EVENT_RBT, SAMPLE_FLAGS_END }, // End non-decompresison warning
+ { 0xEF, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, // Switch to blend 2
+ { 0xF0, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_END }, // Breathing rate alarm
+ { 0xF3, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }, // Switch to blend 1
+ { 0xF6, 1, SAMPLE_EVENT_CEILING, SAMPLE_FLAGS_END }, // End Depth is less than ceiling
+ { 0x00, 1, SAMPLE_EVENT_NONE, SAMPLE_FLAGS_NONE }
};
@@ -101,8 +83,8 @@ void cochran_commander_get_event_info(const unsigned char code,
cochran_events_t *event);
int cochran_commander_handle_event (dc_parser_t *abstract,
dc_sample_callback_t callback, void *userdata, unsigned char code,
- unsigned int offset, unsigned int time);
-int cochran_backparse(dc_parser_t *abstract, const char *samples, int size, int (*event_bytes)[15][2]);
+ unsigned int time);
+int cochran_backparse(dc_parser_t *abstract, unsigned const char *samples, int size, struct event_size (*event_bytes)[15]);
// EMC FAMILY
diff --git a/src/cochran_emc_parser.c b/src/cochran_emc_parser.c
index c4a4d56..aae484f 100644
--- a/src/cochran_emc_parser.c
+++ b/src/cochran_emc_parser.c
@@ -35,103 +35,101 @@
#include "cochran_commander_parser.h"
-struct dive_stats {
- unsigned int dive_time;
- float max_depth;
- float avg_depth;
- float min_temp;
- float max_temp;
-};
-
// Size of inter-dive events
-int emc_event_bytes[15][2] = { {0x00, 19}, {0x01, 23}, {0x02, 20},
+struct event_size emc_event_bytes[15] = { {0x00, 19}, {0x01, 23}, {0x02, 20},
{0x03, 19}, {0x06, 21}, {0x07, 21},
{0x0a, 21}, {0x0b, 21}, {0x0f, 19},
{0x10, 21},
{ -1, 1} };
-static void cochran_emc_parse_dive_stats (dc_parser_t *abstract,
- struct dive_stats *stats);
-
dc_status_t
cochran_emc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
unsigned int flags, void *value)
{
- const unsigned char *log = abstract->data + COCHRAN_MODEL_SIZE;
+ const unsigned char *log_entry = abstract->data + COCHRAN_MODEL_SIZE;
unsigned char corrupt_dive = 0;
- if (array_uint32_le(log + COCHRAN_EMC_LOG_SIZE / 2) == 0xFFFFFFFF)
+ // FIXME: Is there a reason why you have this global check instead
+ // of checking the actual value for 0xFFFF, like you do in the
+ // commander function?
+ if (array_uint32_le(log_entry + COCHRAN_EMC_LOG_SIZE / 2) == 0xFFFFFFFF)
corrupt_dive = 1;
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
dc_salinity_t *water = (dc_salinity_t *) value;
- struct dive_stats stats;
-
if (value) {
+
+ // Parse data to recreate dive summary information
+ sample_statistics_t parsed_stats = {0};
+ if (corrupt_dive)
+ cochran_emc_parser_samples_foreach(abstract, sample_statistics_cb, (void *) &parsed_stats);
+
switch (type) {
case DC_FIELD_TEMPERATURE_SURFACE:
- *((unsigned int*) value) = ((float) log[EMC_START_TEMP] - 32) / 1.8;
+ *((unsigned int*) value) = (log_entry[EMC_START_TEMP] - 32.0) / 1.8;
break;
case DC_FIELD_TEMPERATURE_MINIMUM:
if (corrupt_dive) {
- cochran_emc_parse_dive_stats(abstract, &stats);
- *((unsigned int*) value) = stats.min_temp;
+ return DC_STATUS_UNSUPPORTED;
} else
- *((unsigned int*) value) = ((float) log[EMC_MIN_TEMP] / 2
+ *((unsigned int*) value) = (log_entry[EMC_MIN_TEMP] / 2.0
+ 20 - 32) / 1.8;
break;
case DC_FIELD_TEMPERATURE_MAXIMUM:
if (corrupt_dive) {
- cochran_emc_parse_dive_stats(abstract, &stats);
- *((unsigned int*) value) = stats.max_temp;
+ return DC_STATUS_UNSUPPORTED;
} else
- *((unsigned int*) value) = ((float) log[EMC_MAX_TEMP] / 2
+ *((unsigned int*) value) = (log_entry[EMC_MAX_TEMP] / 2.0
+ 20 - 32) / 1.8;
break;
case DC_FIELD_DIVETIME:
if (corrupt_dive) {
- cochran_emc_parse_dive_stats(abstract, &stats);
- *((unsigned int*) value) = stats.dive_time;
+ *((unsigned int*) value) = parsed_stats.divetime;
} else
- *((unsigned int *) value) = array_uint16_le (log + EMC_BT) * 60;
+ *((unsigned int *) value) = array_uint16_le (log_entry + EMC_BT) * 60;
break;
case DC_FIELD_MAXDEPTH:
if (corrupt_dive) {
- cochran_emc_parse_dive_stats(abstract, &stats);
- *((unsigned int*) value) = stats.max_depth;
+ *((unsigned int*) value) = parsed_stats.maxdepth;
} else
- *((double *) value) = array_uint16_le (log + EMC_MAX_DEPTH) / 4
+ *((double *) value) = array_uint16_le (log_entry + EMC_MAX_DEPTH) / 4.0
* FEET;
break;
case DC_FIELD_AVGDEPTH:
if (corrupt_dive) {
- cochran_emc_parse_dive_stats(abstract, &stats);
- *((unsigned int*) value) = stats.avg_depth;
+ return DC_STATUS_UNSUPPORTED;
} else
- *((double *) value) = array_uint16_le (log + EMC_AVG_DEPTH) / 4
+ *((double *) value) = array_uint16_le (log_entry + EMC_AVG_DEPTH) / 4.0
* FEET;
break;
case DC_FIELD_GASMIX_COUNT:
*((unsigned int *) value) = 10;
break;
case DC_FIELD_GASMIX:
- gasmix->oxygen = (double) array_uint16_le (log
- + EMC_O2_PERCENT + 2 * flags) / 256 / 100;
- gasmix->helium = (double) array_uint16_le (log
- + EMC_HE_PERCENT + 2 * flags) / 256 / 100;
+ // Gas percentages are decimal and encoded as
+ // highbyte = integer value
+ // lowbyte = decimal portion, divide by 256 to get decimal value
+ gasmix->oxygen = array_uint16_le (log_entry
+ + EMC_O2_PERCENT + 2 * flags) / 256.0 / 100;
+ gasmix->helium = array_uint16_le (log_entry
+ + EMC_HE_PERCENT + 2 * flags) / 256.0 / 100;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
case DC_FIELD_SALINITY:
- // 0 = low conductivity, 2 = high, maybe there's a 1?
- water->type = ( (log[EMC_WATER_CONDUCTIVITY] & 0x3) == 0
+ // 0x00 = low conductivity, 0x10 = high, maybe there's a 0x01 and 0x11?
+ water->type = ( (log_entry[EMC_WATER_CONDUCTIVITY] & 0x3) == 0
? DC_WATER_FRESH : DC_WATER_SALT );
- water->density = 1000 + 12.5 * (log[EMC_WATER_CONDUCTIVITY] & 0x3);
+ // Assume Cochran's conductivity ranges from 0 to 3
+ // so 0 = 1000kg/m³, 2 = 1025kg/m³
+ water->density = 1000 + 12.5 * (log_entry[EMC_WATER_CONDUCTIVITY] & 0x3);
break;
case DC_FIELD_ATMOSPHERIC:
+ // Cochran measures atm pressure and stores it as altitude.
+ // Convert altitude (measured in 1/4 kilofeet) back to pressure.
*(double *) value = ATM / BAR * pow(1 - 0.0000225577
- * (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588);
+ * log_entry[EMC_ALTITUDE] * 250.0 * FEET, 5.25588);
break;
default:
return DC_STATUS_UNSUPPORTED;
@@ -141,33 +139,37 @@ cochran_emc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
return DC_STATUS_SUCCESS;
}
-
+// TODO: Not reviewed in detail yet
dc_status_t
cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
dc_sample_callback_t callback, void *userdata)
{
const unsigned char *data = abstract->data;
- const unsigned char *log = data + COCHRAN_MODEL_SIZE;
- const unsigned char *samples = log + COCHRAN_EMC_LOG_SIZE;
+ const unsigned char *log_entry = data + COCHRAN_MODEL_SIZE;
+ const unsigned char *samples = log_entry + COCHRAN_EMC_LOG_SIZE;
unsigned int size = abstract->size - COCHRAN_MODEL_SIZE
- COCHRAN_EMC_LOG_SIZE;
- const unsigned char *s;
dc_sample_value_t sample = {0}, empty_sample = {0};
- unsigned int time = 0, last_sample_time;
+ unsigned int time = 0, last_sample_time = 0;
unsigned int offset = 0;
+ int depth_qfeet = 0;
double temperature;
- double depth;
+ double start_depth = 0;
double ascent_rate;
unsigned char deco_obligation = 0;
unsigned int deco_ceiling = 0;
- unsigned int deco_time;
+ unsigned int deco_time = 0;
unsigned char corrupt_dive = 0;
- if (array_uint32_le(log + COCHRAN_EMC_LOG_SIZE / 2) == 0xFFFFFFFF) {
+ // In rare circumstances Cochran computers won't record the end-of-dive
+ // log entry block. When the end-sample pointer is 0xFFFFFFFF it's corrupt.
+ // That means we don't really know where the dive samples end and we don't
+ // know what the dive summary values are (i.e. max depth, min temp)
+ if (array_uint32_le(log_entry + COCHRAN_EMC_LOG_SIZE / 2) == 0xFFFFFFFF) {
corrupt_dive = 1;
- WARNING(abstract->context, "Incomplete dive on %02d/%02d/%02d at %02d:%02d:%02d, trying to parse samples.", log[EMC_YEAR], log[EMC_MON], log[EMC_DAY], log[EMC_HOUR], log[EMC_MIN], log[EMC_SEC]);
+ WARNING(abstract->context, "Incomplete dive on %02d/%02d/%02d at %02d:%02d:%02d, trying to parse samples.", log_entry[EMC_YEAR], log_entry[EMC_MON], log_entry[EMC_DAY], log_entry[EMC_HOUR], log_entry[EMC_MIN], log_entry[EMC_SEC]);
// Eliminate inter-dive events
size = cochran_backparse(abstract, samples, size, &emc_event_bytes);
@@ -181,18 +183,20 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
*/
// Prime with values from the dive log section
- depth = array_uint16_le (log + EMC_START_DEPTH) / 256;
+ start_depth = array_uint16_le (log_entry + EMC_START_DEPTH) / 256;
last_sample_time = sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
- sample.depth = depth * FEET;
+ sample.depth = start_depth * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
- sample.temperature = (log[EMC_START_TEMP] - 32) / 1.8;
+ sample.temperature = (log_entry[EMC_START_TEMP] - 32) / 1.8;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
while (offset < size) {
+ const unsigned char *s = NULL;
+
sample = empty_sample;
sample.time = time;
@@ -206,9 +210,14 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
// If corrupt_dive end before offset
if (corrupt_dive) {
- if ( s[0] == 0x10
- || s[0] == 0xFF
- || s[0] == 0xA8) {
+ // When we aren't sure where the sample data ends we can
+ // look for events that shouldn't be in the sample data.
+ // 0xFF is unwritten memory
+ // 0xA8 indicates start of post-dive interval
+ // 0xE3 (switch to FO2 mode) and 0xF3 (switch to blend 1) occur
+ // at dive start so when we see them after the first second we
+ // found the beginning of the next dive.
+ if (s[0] == 0xFF || s[0] == 0xA8) {
break;
}
if (time > 1 && (s[0] == 0xE3 || s[0] == 0xF3))
@@ -218,7 +227,7 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
// Check for event
if (s[0] & 0x80) {
offset += cochran_commander_handle_event(abstract, callback,
- userdata, s[0], offset, time);
+ userdata, s[0], time);
switch (s[0])
{
case 0xC5: // Deco obligation begins
@@ -231,7 +240,7 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
deco_ceiling += 10; // feet
sample.deco.type = DC_DECO_DECOSTOP;
- sample.deco.time = (array_uint16_le(s + 3) + 1) * 60;
+ 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;
@@ -262,25 +271,33 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
// Depth is logged as change in feet, bit 0x40 means negative depth
- depth += (float) (s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
- sample.depth = depth * FEET;
+ if (s[0] & 0x40)
+ depth_qfeet -= (s[0] & 0x3f);
+ else
+ depth_qfeet += (s[0] & 0x3f);
+
+ sample.depth = (start_depth + depth_qfeet / 4.0) * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
// Ascent rate is logged in the 0th sample, temp in the 1st, repeat.
if (time % 2 == 0) {
// Ascent rate
- ascent_rate = (float) (s[1] & 0x7f) / 4 * (s[1] & 0x80 ? 1 : -1);
+ if (s[1] & 0x80)
+ ascent_rate = (s[1] & 0x7f) / 4.0;
+ else
+ ascent_rate = - (s[1] & 0x7f) / 4.0;
+
sample.ascent_rate = ascent_rate * FEET;
if (callback) callback (DC_SAMPLE_ASCENT_RATE, sample, userdata);
} else {
// Temperature logged in half degrees F above 20
- temperature = (float) s[1] / 2 + 20;
+ temperature = s[1] / 2.0 + 20;
sample.temperature = (temperature - 32.0) / 1.8;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
}
-
+
// Some samples are split over two seconds
// Skip over event bytes to find the next sample
const unsigned char *n = s + COCHRAN_EMC_SAMPLE_SIZE;
@@ -326,84 +343,3 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
return DC_STATUS_SUCCESS;
}
-
-void cochran_emc_parse_dive_stats (dc_parser_t *abstract,
- struct dive_stats *stats)
-{
- const unsigned char *log = abstract->data + COCHRAN_MODEL_SIZE;
- const unsigned char *samples = log + COCHRAN_EMC_LOG_SIZE;
- unsigned int size = abstract->size - COCHRAN_MODEL_SIZE
- - COCHRAN_EMC_LOG_SIZE;
- const unsigned char *s;
-
- unsigned int offset = 0;
- float depth_m = 0, depth, temp;
- unsigned int time = 0;
- unsigned char corrupt_dive = 0;
-
- if (array_uint32_le(log + COCHRAN_EMC_LOG_SIZE / 2) == 0xFFFFFFFF) {
- corrupt_dive = 1;
-
- // Eliminate inter-dive events
- size = cochran_backparse(abstract, samples, size, &emc_event_bytes);
- }
-
- // Prime with values from the dive log section
- depth = array_uint16_le (log + EMC_START_DEPTH) / 256;
-
- stats->max_depth = depth;
- stats->min_temp = 999;
- stats->avg_depth = 0;
- stats->max_temp = 0;
-
- while (offset < size) {
- s = samples + offset;
-
- if (corrupt_dive) {
- if (s[0] == 0x10
- || s[0] == 0xFF
- || s[0] == 0xA8) {
- // End corrupted dive
- break;
- }
- if (time > 1 && (s[0] == 0xE3 || s[0] == 0xF3))
- break;
- }
-
- // Check for event
- if (s[0] & 0x80) {
- cochran_events_t event;
- cochran_commander_get_event_info(s[0], &event);
-
- // Advance some bytes
- if (event.code)
- offset += event.data_bytes;
- else
- offset ++;
-
- continue;
- }
-
- // Depth is logged as change in feet, bit 0x40 means negative depth
- depth += (float) (s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
- depth_m = depth * FEET;
-
- if (depth_m > stats->max_depth) stats->max_depth = depth_m;
-
- stats->avg_depth = (stats->avg_depth * (time - 1) + depth_m) / time;
-
- if (time % 2 != 0) {
- // Temperature logged in half degrees F above 20
- temp = (float) s[1] / 2 + 20;
- temp = (temp - 32.0) / 1.8;
-
- if (temp < stats->min_temp) stats->min_temp = temp;
- if (temp > stats->max_temp) stats->max_temp = temp;
- }
-
- time ++;
- offset += COCHRAN_EMC_SAMPLE_SIZE;
- }
-
- stats->dive_time = time - 1;
-}
--
2.4.3
More information about the devel
mailing list