[PATCH] Revisions requested

John Van Ostrand john at vanostrand.com
Mon Oct 27 17:18:41 PDT 2014


Removed dependency on packed structure.
Removed extraneous close/opens.
Formated lines to 78 chars.
Merged EMC and Commander backends to common.
Change nanosleep() calls to serial_sleep().
Change cochran_common_dump to use dc_buffer properly.

---
 examples/cochran_download.c       | 165 +++++++++++++++++++++--------
 examples/universal.c              |   3 +-
 include/libdivecomputer/cochran.h |  11 +-
 include/libdivecomputer/common.h  |   3 +-
 src/Makefile.am                   |   6 +-
 src/cochran_cmdr.c                |  66 ------------
 src/cochran_cmdr.h                |  83 ---------------
 src/cochran_cmdr_parser.c         |  63 +++++------
 src/cochran_common.c              | 218 +++++++++++++++++++++++++++-----------
 src/cochran_common.h              |  62 +++++++++--
 src/cochran_common_parser.c       |  78 ++++++++++++--
 src/cochran_common_parser.h       |  43 +++++++-
 src/cochran_emc.c                 |  67 ------------
 src/cochran_emc.h                 | 180 -------------------------------
 src/cochran_emc_parser.c          |  84 +++++++--------
 src/descriptor.c                  |   6 +-
 src/device.c                      |   7 +-
 src/parser.c                      |   7 +-
 18 files changed, 515 insertions(+), 637 deletions(-)
 delete mode 100644 src/cochran_cmdr.c
 delete mode 100644 src/cochran_cmdr.h
 delete mode 100644 src/cochran_emc.c
 delete mode 100644 src/cochran_emc.h

diff --git a/examples/cochran_download.c b/examples/cochran_download.c
index b42eb91..a9253af 100644
--- a/examples/cochran_download.c
+++ b/examples/cochran_download.c
@@ -58,14 +58,9 @@ usage (const char *filename)
 	fprintf (stderr, "Usage:\n\n");
 	fprintf (stderr, "   %s [options] devname\n\n", filename);
 	fprintf (stderr, "Options:\n\n");
-	fprintf (stderr, "   -b name        Set backend name (required).\n");
 	fprintf (stderr, "   -d dirname     Dump data to dirname.\n");
 	fprintf (stderr, "   -f             Force dump despite download errors\n");
 	fprintf (stderr, "   -h             Show this help message.\n\n");
-
-	fprintf (stderr, "Supported backends:\n\n");
-	fprintf (stderr, " emc,       Cochran EMC family\n");
-	fprintf (stderr, " commander, Cochran Commander family\n");
 	fprintf (stderr, "\n\n");
 }
 
@@ -108,20 +103,17 @@ search (dc_descriptor_t **out, const char *name, dc_family_t backend, unsigned i
 }
 
 static dc_status_t
-write_dump(dc_buffer_t *dump, const char *fname)
+write_dump(const char *dump, unsigned int size, const char *fname)
 {
-	int fd, size = 0, written;
-
-	if (dump)
-		size = dc_buffer_get_size(dump);
+	int fd, written;
 
 	if (size > 0) {
 		fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH );
 		if (fd == -1) {
-			printf("unable to open %s for writing\n", fname);
+			printf("Unable to open %s for writing\n", fname);
 			exit;
 		}
-		written = write(fd, dc_buffer_get_data(dump), size);
+		written = write(fd, dump, size);
 		if (written != size) {
 			printf("Error writing %s. Wrote %d expected %d.\n", fname, written, size);
 			return (DC_STATUS_UNSUPPORTED);
@@ -130,6 +122,59 @@ write_dump(dc_buffer_t *dump, const char *fname)
 	return (DC_STATUS_SUCCESS);
 }
 
+#define hexchar(n) ("0123456789abcdef"[(n) & 15])
+
+static int show_line(unsigned offset, const unsigned char *data,
+                unsigned size, int show_empty)
+{
+    unsigned char bits;
+    int i, off;
+    char buffer[120];
+
+    if (size > 16)
+        size = 16;
+
+    bits = 0;
+    memset(buffer, ' ', sizeof(buffer));
+    off = sprintf(buffer, "%06x ", offset);
+    for (i = 0; i < size; i++) {
+        char *hex = buffer + off + 3 * i;
+        char *asc = buffer + off + 50 + i;
+        unsigned char byte = data[i];
+
+        hex[0] = hexchar(byte >> 4);
+        hex[1] = hexchar(byte);
+        bits |= byte;
+        if (byte < 32 || byte > 126)
+            byte = '.';
+        asc[0] = byte;
+        asc[1] = 0;
+    }
+
+    if (bits) {
+        puts(buffer);
+        return 1;
+    }
+    if (show_empty)
+        puts("...");
+    return 0;
+}
+
+static void cochran_debug_write(const unsigned char *data, unsigned size)
+{
+    return;
+
+    int show = 1,  i;
+
+
+    for (i = 0; i < size; i += 16)
+        show = show_line(i, data + i, size - i, show);
+}
+
+
+#define array_uint32_le(p) (   (unsigned char) (p)[0]        + ((unsigned char) (p)[1] << 8) \
+							+ ((unsigned char) (p)[2] << 16) + ((unsigned char) (p)[3] << 24) )
+
 static dc_status_t
 dowork (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, const char *dirname, unsigned int force_flag)
 {
@@ -154,62 +199,96 @@ dowork (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname,
 	}
 
 	// Create directory if needed
-	stat(dirname, &dstat);
+	int src;
+	src = stat(dirname, &dstat);
 
-	if ( ! (dstat.st_mode & S_IFDIR) ) {
+	if ( src == -1 ) {
 		if (mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
 			printf("Unable to create directory\n");
 			exit;
 		}
 	}
 
-
 	// TODO Run dump and write files
-	struct cochran_data_dump dump = {0};
-	rc = dc_device_dump (device, (dc_buffer_t *) &dump);
+	dc_buffer_t *dump = dc_buffer_new(0);
+
+	rc = dc_device_dump (device, dump);
+
+	cochran_debug_write(dc_buffer_get_data(dump), dc_buffer_get_size(dump));
+	exit;
 
 	if (rc == DC_STATUS_SUCCESS || force_flag) {
 		char fname[128];
+		char *d = dc_buffer_get_data(dump);
+		unsigned int ptr = 0, start, size;
 
 		sprintf(fname, "%s/info-x05x9dxffx00x43x00.bin", dirname);
-		write_dump(dump.id0, fname);
-
-		if (dc_buffer_get_size(dump.id0) > 0)
+		start = array_uint32_le(d + ptr);
+		size = array_uint32_le(d + ptr + 5) - start;
+printf("%s, start = %d, size = %d\n", fname, start, size);
+		write_dump(d + start, size, fname);
+
+		ptr += 5;
+		start = array_uint32_le(d + ptr);
+		size = array_uint32_le(d + ptr + 5) - start;
+		if (*(d + ptr - 1) == 1)
 			sprintf(fname, "%s/info-xdbx7fxffx00x43x00.bin", dirname);
 		else
 			sprintf(fname, "%s/info-x05x9dxffx00x43x00.bin", dirname);
-		write_dump(dump.id1, fname);
+		write_dump(d + start, size, fname);
 
+		ptr += 5;
+		start = array_uint32_le(d + ptr);
+		size = array_uint32_le(d + ptr + 5) - start;
 		sprintf(fname, "%s/config-x96x00.bin", dirname);
-		write_dump(dump.config1, fname);
+printf("%s, start = %d, size = %d\n", fname, start, size);
+		write_dump(d + start, size, fname);
 
+		ptr += 5;
+		start = array_uint32_le(d + ptr);
+		size = array_uint32_le(d + ptr + 5) - start;
 		sprintf(fname, "%s/config-x96x01.bin", dirname);
-		write_dump(dump.config2, fname);
+printf("%s, start = %d, size = %d\n", fname, start, size);
+		write_dump(d + start, size, fname);
 
+		ptr += 5;
+		start = array_uint32_le(d + ptr);
+		size = array_uint32_le(d + ptr + 5) - start;
 		sprintf(fname, "%s/config-x96x02.bin", dirname);
-		write_dump(dump.config3, fname);
+printf("%s, start = %d, size = %d\n", fname, start, size);
+		write_dump(d + start, size, fname);
 
+		ptr += 5;
+		start = array_uint32_le(d + ptr);
+		size = array_uint32_le(d + ptr + 5) - start;
 		sprintf(fname, "%s/config-x96x03.bin", dirname);
-		write_dump(dump.config4, fname);
+printf("%s, start = %d, size = %d\n", fname, start, size);
+		write_dump(d + start, size, fname);
 
+		ptr += 5;
+		start = array_uint32_le(d + ptr);
+		size = array_uint32_le(d + ptr + 5) - start;
 		sprintf(fname, "%s/misc-x89x05x00x00x00xdcx05.bin", dirname);
-		write_dump(dump.misc, fname);
+printf("%s, start = %d, size = %d\n", fname, start, size);
+		write_dump(d + start, size, fname);
 
+		ptr += 5;
+		start = array_uint32_le(d + ptr);
+		size = array_uint32_le(d + ptr + 5) - start;
 		sprintf(fname, "%s/logbook.bin", dirname);
-		write_dump(dump.logbook, fname);
+printf("%s, start = %d, size = %d\n", fname, start, size);
+		write_dump(d + start, size, fname);
 
+		ptr += 5;
+		start = array_uint32_le(d + ptr);
+		size = array_uint32_le(d + ptr + 5) - start;
 		sprintf(fname, "%s/sample.bin", dirname);
-		write_dump(dump.sample, fname);
-
-		dc_buffer_free (dump.id0);
-		dc_buffer_free (dump.id1);
-		dc_buffer_free (dump.config1);
-		dc_buffer_free (dump.config2);
-		dc_buffer_free (dump.config3);
-		dc_buffer_free (dump.config4);
-		dc_buffer_free (dump.misc);
-		dc_buffer_free (dump.logbook);
-		dc_buffer_free (dump.sample);
+printf("%s, start = %d, size = %d\n", fname, start, size);
+		write_dump(d + start, size, fname);
+
+		dc_buffer_free (dump);
+	} else {
+		printf("Dive computer read failure\n");
 	}
 
 	// Close the device.
@@ -230,7 +309,7 @@ main (int argc, char *argv[])
 	extern int optind, opterr, optopt;
 
 	// Default values.
-	dc_family_t backend = DC_FAMILY_NULL;
+	dc_family_t backend = DC_FAMILY_COCHRAN;
 
 	// Command line arguments
 	const char *devname = NULL, *dirname;
@@ -239,14 +318,8 @@ main (int argc, char *argv[])
 
 	// Parse command-line options.
 	int opt = 0;
-	while ((opt = getopt (argc, argv, "b:d:ls:h")) != -1) {
+	while ((opt = getopt (argc, argv, "d:ls:h")) != -1) {
 		switch (opt) {
-		case 'b':	// backend
-			if ( ! strcmp ("emc", optarg) )
-				backend = DC_FAMILY_COCHRAN_EMC;
-			else if ( ! strcmp( "commander", optarg) )
-				backend = DC_FAMILY_COCHRAN_COMMANDER;
-			break;
 		case 'd':	// dump data to directory
 			dirname = optarg;
 			break;
diff --git a/examples/universal.c b/examples/universal.c
index 56445b7..c7d36f7 100644
--- a/examples/universal.c
+++ b/examples/universal.c
@@ -102,8 +102,7 @@ static const backend_table_t g_backends[] = {
 	{"predator",	DC_FAMILY_SHEARWATER_PREDATOR},
 	{"petrel",      DC_FAMILY_SHEARWATER_PETREL},
 	{"nitekq",      DC_FAMILY_DIVERITE_NITEKQ},
-	{"commander",	DC_FAMILY_COCHRAN_COMMANDER},
-	{"emc",			DC_FAMILY_COCHRAN_EMC},
+	{"cochran",		DC_FAMILY_COCHRAN},
 };
 
 static dc_family_t
diff --git a/include/libdivecomputer/cochran.h b/include/libdivecomputer/cochran.h
index 27fdcc5..f7506d7 100644
--- a/include/libdivecomputer/cochran.h
+++ b/include/libdivecomputer/cochran.h
@@ -43,16 +43,11 @@ struct cochran_data_dump {
 };
 
 dc_status_t
-cochran_cmdr_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+cochran_common_device_open (dc_device_t **device, dc_context_t *context,
+		const char *name);
 
 dc_status_t
-cochran_cmdr_parser_create (dc_parser_t **parser, dc_context_t *context);
-
-dc_status_t
-cochran_emc_device_open (dc_device_t **device, dc_context_t *context, const char *name);
-
-dc_status_t
-cochran_emc_parser_create (dc_parser_t **parser, dc_context_t *context);
+cochran_common_parser_create (dc_parser_t **parser, dc_context_t *context);
 
 #ifdef __cplusplus
 }
diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h
index ef86272..218548f 100644
--- a/include/libdivecomputer/common.h
+++ b/include/libdivecomputer/common.h
@@ -84,8 +84,7 @@ typedef enum dc_family_t {
 	/* Dive Rite */
 	DC_FAMILY_DIVERITE_NITEKQ = (11 << 16),
 	/* Cochran */
-	DC_FAMILY_COCHRAN_COMMANDER = (12 << 16),
-	DC_FAMILY_COCHRAN_EMC,
+	DC_FAMILY_COCHRAN = (12 << 16),
 } dc_family_t;
 
 #ifdef __cplusplus
diff --git a/src/Makefile.am b/src/Makefile.am
index d8098f6..33d4713 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -56,9 +56,9 @@ libdivecomputer_la_SOURCES = \
 	checksum.h checksum.c \
 	array.h array.c \
 	buffer.c \
-	cochran_common.h cochran_common.c cochran_common_parser.h cochran_common_parser.c \
-	cochran_cmdr.h cochran_cmdr.c cochran_cmdr_parser.c \
-	cochran_emc.h cochran_emc.c cochran_emc_parser.c
+	cochran_common.h cochran_common.c \
+	cochran_common_parser.h cochran_common_parser.c \
+	cochran_cmdr_parser.c cochran_emc_parser.c
 
 if OS_WIN32
 libdivecomputer_la_SOURCES += serial.h serial_win32.c
diff --git a/src/cochran_cmdr.c b/src/cochran_cmdr.c
deleted file mode 100644
index fb6e1e0..0000000
--- a/src/cochran_cmdr.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * libdivecomputer
- *
- * Copyright (C) 2014 John Van Ostrand
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include <stdlib.h>
-#include <time.h>
-
-#include <libdivecomputer/cochran.h>
-
-#include "context-private.h"
-#include "device-private.h"
-#include "serial.h"
-
-#include "cochran_common.h"
-#include "cochran_cmdr.h"
-
-
-static const dc_device_vtable_t cochran_cmdr_device_vtable = {
-	DC_FAMILY_COCHRAN_COMMANDER,
-	cochran_common_device_set_fingerprint,	/* set_fingerprint */
-	cochran_common_device_read,				/* read */
-	NULL,									/* write */
-	cochran_common_device_dump,				/* dump */
-	cochran_common_device_foreach,			/* foreach */
-	cochran_common_device_close				/* close */
-};
-
-
-dc_status_t
-cochran_cmdr_device_open (dc_device_t **out, dc_context_t *context, const char *name)
-{
-	dc_status_t rc;
-	rc = cochran_common_device_open(out, context, name, &cochran_cmdr_device_vtable);
-	if (rc != DC_STATUS_SUCCESS)
-		return rc;
-
-	cochran_device_t *device = (cochran_device_t *) *out;
-
-	// Check family
-	if ((device->data.model & 0xFF0000) != COCHRAN_MODEL_COMMANDER_FAMILY) {
-		ERROR (context, "Device not recognized.");
-		serial_close (device->port);
-		free (device->data.id);
-		free (device);
-		return DC_STATUS_UNSUPPORTED;
-	}
-
-	return DC_STATUS_SUCCESS;
-}
diff --git a/src/cochran_cmdr.h b/src/cochran_cmdr.h
deleted file mode 100644
index bac5f57..0000000
--- a/src/cochran_cmdr.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * libdivecomputer
- *
- * Copyright (C) 2014 John Van Ostrand
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-
-struct cochran_cmdr_log_t {
-	// Pre-dive 128 bytes
-	unsigned char minutes, seconds;			// 3 bytes
-	unsigned char day, hour, year, month;	// 3 bytes
- 	unsigned char sample_start_offset[4];	// 4 bytes
-	unsigned char start_timestamp[4];		// 4 bytes
-	unsigned char pre_dive_timestamp[4];	// 4 bytes
-	unsigned char unknown1[6];				// 6 bytes
-	unsigned char water_conductivity;		// 1 byte [0=low, 2=high]
-	unsigned char unknown2[5];				// 5 bytes
-// 30
-	unsigned char sample_pre_event_offset[4];// 4 bytes
-	unsigned char unknown3[4];				// 4 bytes
-	unsigned char start_battery_voltage[2];	// 2 bytes [/256]
-//40
-	unsigned char unknown4[4]; 				// 4 bytes
-	unsigned char entered_or_computed_po[2];// 2 bytes ???
-	unsigned char unknown5[8];				// 8 bytes
-	unsigned char start_depth[2];			// 2 byte [/4]
-//56
-	unsigned char unknown6[12];				// 12 bytes
-	unsigned char sit[2];					// 2 bytes
-//70
-	unsigned char number[2];				// 2 bytes
-	unsigned char unknown7[1];				// 1 byte
-	unsigned char altitude;					// 1 byte [/4 = kft]
-	unsigned char unknown8[28];				// 27 bytes
-	unsigned char alarm_depth[2];			// 2 bytes
-	unsigned char unknown9[4];				// 5 bytes
-//108
-	unsigned char repetitive_dive;			// 1 byte
-	unsigned char unknown10[3];				// 3 bytes
-	unsigned char start_tissue_nsat[16];	// 16 bytes [/256]
-
-	// Post-dive 128 bytes
-	unsigned char sample_end_offset[4];		// 4 bytes
-	unsigned char unknown11[21];			// 21 bytes
-	unsigned char temp;						// 1 byte
-	unsigned char unknown12[12];			// 12 bytes
-	unsigned char bt[2];					// 2 bytes [minutes]
-	unsigned char max_depth[2];				// 2 bytes [/4]
-	unsigned char avg_depth[2];				// 2 bytes
-	unsigned char unknown13[38];			// 38 bytes
-	unsigned char o2_percent[4][2];			// 8 bytes
-	unsigned char unknown14[22];			// 22 bytes
-	unsigned char end_tissue_nsat[16];		// 16 bytes [/256]
-} __attribute__((packed));
-
- 
-typedef struct cochran_cmdr_log_t cochran_cmdr_log_t;
-
-
-struct cochran_cmdr_config1_t {
-	unsigned char unknown1[209];
-	unsigned short int dive_count;
-	unsigned char unknown2[274];
-	unsigned short int serial_num; // @170
-	unsigned char unknown3[24];
-} __attribute__((packed));
-
-typedef struct cochran_emc_config1_t cochran_emc_config1_t;
diff --git a/src/cochran_cmdr_parser.c b/src/cochran_cmdr_parser.c
index 8c74705..8b94fcf 100644
--- a/src/cochran_cmdr_parser.c
+++ b/src/cochran_cmdr_parser.c
@@ -32,36 +32,18 @@
 #include "array.h"
 
 #include "cochran_common.h"
-#include "cochran_cmdr.h"
 #include "cochran_common_parser.h"
 
-#define SZ_SAMPLE	2
-
-static dc_status_t cochran_cmdr_parser_get_field(dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
-static dc_status_t cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
 
-static const dc_parser_vtable_t cochran_cmdr_parser_vtable = {
-	DC_FAMILY_COCHRAN_COMMANDER,
-	cochran_common_parser_set_data,			/* set_data */
-	cochran_common_parser_get_datetime,		/* datetime */
-	cochran_cmdr_parser_get_field,			/* fields */
-	cochran_cmdr_parser_samples_foreach,	/* samples_foreach */
-	cochran_common_parser_destroy			/* destroy */
-};
+#define SZ_SAMPLE	2
 
 
 dc_status_t
-cochran_cmdr_parser_create (dc_parser_t **out, dc_context_t *context)
-{
-	return cochran_common_parser_create(out, context, &cochran_cmdr_parser_vtable);
-}
-
-
-static dc_status_t
-cochran_cmdr_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
+cochran_cmdr_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
+		unsigned int flags, void *value)
 {
 	cochran_data_t *data = (cochran_data_t *) abstract->data;
-	cochran_cmdr_log_t *log = (cochran_cmdr_log_t *) data->current_log;
+	unsigned char *log = data->current_log;
 
 	dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
 	dc_salinity_t *water = (dc_salinity_t *) value;
@@ -69,31 +51,34 @@ cochran_cmdr_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
 	if (value) {
 		switch (type) {
 		case DC_FIELD_TEMPERATURE:
-			*((unsigned int*) value) = (log->temp - 32) / 1.8;
+			*((unsigned int*) value) = log[CMD_START_TEMP];
 		case DC_FIELD_DIVETIME:
-			*((unsigned int *) value) = array_uint16_le (log->bt) * 60;
+			*((unsigned int *) value) = array_uint16_le (log + EMC_BT) * 60;
 			break;
 		case DC_FIELD_MAXDEPTH:
-			*((double *) value) = array_uint16_le (log->max_depth) / 4 * FEET;
+			*((double *) value) = array_uint16_le (log + CMD_MAX_DEPTH) / 4 * FEET;
 			break;
 		case DC_FIELD_AVGDEPTH:
-			*((double *) value) = array_uint16_le (log->avg_depth) / 4 * FEET;
+			*((double *) value) = array_uint16_le (log + CMD_AVG_DEPTH) / 4 * FEET;
 			break;
 		case DC_FIELD_GASMIX_COUNT:
 			*((unsigned int *) value) = 2;
 			break;
 		case DC_FIELD_GASMIX:
-			gasmix->oxygen = (double) array_uint16_le ((char *)log->o2_percent + 2 * flags) / 256 / 100;
+			gasmix->oxygen = (double) array_uint16_le ((char *)log
+					+ CMD_O2_PERCENT + 2 * flags) / 256 / 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->water_conductivity == 0 ? DC_WATER_FRESH : DC_WATER_SALT );
-			water->density = 1000 + 12.5 * log->water_conductivity;
+			water->type = ( log[CMD_WATER_CONDUCTIVITY] == 0 ? DC_WATER_FRESH
+					: DC_WATER_SALT );
+			water->density = 1000 + 12.5 * log[CMD_WATER_CONDUCTIVITY];
 			break;
 		case DC_FIELD_ATMOSPHERIC:
-			*(double *) value = ATM / BAR * pow(1 - 0.0000225577 * (double) log->altitude * 250 * FEET, 5.25588);
+			*(double *) value = ATM / BAR * pow(1 - 0.0000225577
+					* (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588);
 			break;
 		default:
 			return DC_STATUS_UNSUPPORTED;
@@ -104,12 +89,13 @@ cochran_cmdr_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
 }
 
 
-static dc_status_t
-cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
+dc_status_t
+cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
+		dc_sample_callback_t callback, void *userdata)
 {
 	cochran_data_t *data = (cochran_data_t *)abstract->data;
 	unsigned char *sdata = data->current_sample;
-	cochran_cmdr_log_t *log = (cochran_cmdr_log_t *) data->current_log;
+	unsigned char *log = data->current_log;
 	unsigned int size = data->current_sample_size;
 	unsigned char *s;
 
@@ -126,7 +112,7 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
 	// and temp ever other second.
 
 	// Prime values from the dive log section
-	depth = array_uint16_le (log->start_depth) / 256;
+	depth = array_uint16_le (log + CMD_START_DEPTH) / 4;
 
 	last_sample_time = sample.time = time;
 	if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
@@ -134,7 +120,7 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
 	sample.depth = depth * FEET;
 	if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
 
-	// TODO: sample.temperature = (log->start_temperature - 32) / 1.8;
+	// TODO: sample.temperature = (log + CMD_START_TEMP - 32) / 1.8;
 	// TODO: if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
 
 	while (offset < size) {
@@ -165,7 +151,8 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
 
 		// Check for event
 		if (s[0] & 0x80) {
-			offset += cochran_common_handle_event(abstract, callback, userdata, s[0], offset, time);
+			offset += cochran_common_handle_event(abstract, callback, userdata,
+					s[0], offset, time);
 			if (s[0] == 0xC5)
 				deco_obligation = 1;	// Deco obligation begins
 			else if (s[0] == 0xC8) 
@@ -183,12 +170,12 @@ cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
 		// Ascent rate is logged in the 0th sample, temp in the 1st, repeat.
 		if (time % 2 == 0) {
 			// Ascent rate
-			ascent_rate = (s[1] & 0x7f) / 4 * (s[0] & 0x80 ? 1 : -1);
+			ascent_rate = (s[1] & 0x7f) / 4 * (s[1] & 0x80 ? 1 : -1);
 			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 = s[1] / 2 + 20;
+			temperature = (float) s[1] / 2 + 20;
 			sample.temperature = (temperature - 32.0) / 1.8;
 
 			if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
diff --git a/src/cochran_common.c b/src/cochran_common.c
index 877491d..7bbe277 100644
--- a/src/cochran_common.c
+++ b/src/cochran_common.c
@@ -39,9 +39,21 @@
 	rc == -1 ? DC_STATUS_IO : DC_STATUS_TIMEOUT \
 )
 
+static const dc_device_vtable_t cochran_common_device_vtable = {
+	DC_FAMILY_COCHRAN,
+	cochran_common_device_set_fingerprint,	/* set_fingerprint */
+	cochran_common_device_read,				/* read */
+	NULL,									/* write */
+	cochran_common_device_dump,				/* dump */
+	cochran_common_device_foreach,			/* foreach */
+	cochran_common_device_close				/* close */
+};
+
 
 dc_status_t
-cochran_packet (cochran_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, int high_speed)
+cochran_packet (cochran_device_t *device, const unsigned char command[],
+		unsigned int csize, unsigned char answer[], unsigned int asize,
+		int high_speed)
 {
 	dc_device_t *abstract = (dc_device_t *) device;
 	unsigned int bytes_read = 0, n, read_size;
@@ -50,11 +62,9 @@ cochran_packet (cochran_device_t *device, const unsigned char command[], unsigne
 	if (device_is_cancelled (abstract))
 		return DC_STATUS_CANCELLED;
 
-	struct timespec tr, ts = {0};
-	ts.tv_nsec = 16 * 1000000;	// 16 ms
 	// Send the command to the device, one byte at a time
 	for (ptr = 0; ptr < csize; ptr++) {
-		if (ptr) nanosleep(&ts, &tr);
+		if (ptr) serial_sleep(device->port, 16);	// 16 ms
 		n = serial_write(device->port, command + ptr, 1);
 		if (n != 1) {
 			ERROR (abstract->context, "Failed to send the command.");
@@ -63,9 +73,7 @@ cochran_packet (cochran_device_t *device, const unsigned char command[], unsigne
 	}
 
 	if (high_speed) {
-		struct timespec tr, ts = {0};
-		ts.tv_nsec = 45 * 1000000;  // 45 ms
-		nanosleep(&ts, &tr);
+		serial_sleep(device->port, 45);
 
 		// Weird but I only get the right result when I do it twice
 		// Rates are odd, like 825600 for the EMC, 115200 for commander
@@ -85,7 +93,8 @@ cochran_packet (cochran_device_t *device, const unsigned char command[], unsigne
 
 		n = serial_read (device->port, answer + bytes_read, read_size);
 		if (n != read_size) {
-			ERROR (abstract->context, "Failed to receive data, expected %u, read %u.", read_size, n);
+			ERROR (abstract->context, "Failed to receive data, expected %u,"
+					"read %u.", read_size, n);
 			return EXITCODE (n);
 		}
 
@@ -101,16 +110,10 @@ cochran_packet (cochran_device_t *device, const unsigned char command[], unsigne
 }
 
 
-dc_status_t
-cochran_common_serial_open(cochran_device_t *device, dc_context_t *context)
+static dc_status_t
+cochran_common_serial_setup (cochran_device_t *device, dc_context_t *context)
 {
-	// Open the device.
-	int rc = serial_open (&device->port, context, device->name);
-	if (rc == -1) {
-		ERROR (context, "Failed to open the serial port.");
-		free (device);
-		return DC_STATUS_IO;
-	}
+	int rc;
 
 	// Set the serial communication protocol (9600 8N2, no FC).
 	rc = serial_configure (device->port, 9600, 8, SERIAL_PARITY_NONE,
@@ -135,9 +138,7 @@ cochran_common_serial_open(cochran_device_t *device, dc_context_t *context)
 	serial_flush (device->port, SERIAL_QUEUE_INPUT);
 
 	serial_set_break(device->port, 1);
-	struct timespec tr, ts = {0};
-	ts.tv_nsec = 16 * 1000000;	// 16ms
-	nanosleep(&ts, &tr);
+	serial_sleep(device->port, 16);
 	
 	serial_set_break(device->port, 0);
 
@@ -162,7 +163,23 @@ cochran_common_serial_open(cochran_device_t *device, dc_context_t *context)
 
 
 dc_status_t
-cochran_common_device_open (dc_device_t **out, dc_context_t *context, const char *name, const dc_device_vtable_t *vtable)
+cochran_common_serial_open(cochran_device_t *device, dc_context_t *context)
+{
+	// Open the device.
+	int rc = serial_open (&device->port, context, device->name);
+	if (rc == -1) {
+		ERROR (context, "Failed to open the serial port.");
+		free (device);
+		return DC_STATUS_IO;
+	}
+
+	return cochran_common_serial_setup(device, context);
+}
+
+
+dc_status_t
+cochran_common_device_open (dc_device_t **out, dc_context_t *context,
+		const char *name)
 {
 	dc_status_t rc;
 
@@ -170,14 +187,15 @@ cochran_common_device_open (dc_device_t **out, dc_context_t *context, const char
 		return DC_STATUS_INVALIDARGS;
 
 	// Allocate memory.
-	cochran_device_t *device = (cochran_device_t *) malloc (sizeof (cochran_device_t));
+	cochran_device_t *device = (cochran_device_t *) malloc (
+			sizeof (cochran_device_t));
 	if (device == NULL) {
 		ERROR (context, "Failed to allocate memory.");
 		return DC_STATUS_NOMEMORY;
 	}
 
 	// Initialize the base class.
-	device_init (&device->base, context, vtable);
+	device_init (&device->base, context, &cochran_common_device_vtable);
 
 	// Set the default values.
 	device->port = NULL;
@@ -187,7 +205,8 @@ cochran_common_device_open (dc_device_t **out, dc_context_t *context, const char
 	device->data.sample = NULL;
 	cochran_common_device_set_fingerprint((dc_device_t *) device, "", 0);
 
-	if ((rc = cochran_common_serial_open(device, context)) != DC_STATUS_SUCCESS)
+	if ((rc = cochran_common_serial_open(device, context))
+			!= DC_STATUS_SUCCESS)
 		return rc;
 
 	// Read ID from the device
@@ -202,6 +221,15 @@ cochran_common_device_open (dc_device_t **out, dc_context_t *context, const char
 		return rc;
 	}
 
+	// Check ID
+	if ((device->data.model & 0xFF0000) == COCHRAN_MODEL_UNKNOWN) {
+		ERROR (context, "Device not recognized.");
+		serial_close (device->port);
+		free (device->data.id);
+		free (device);
+		return DC_STATUS_UNSUPPORTED;
+	}
+
 	*out = (dc_device_t *) device;
 
 	return DC_STATUS_SUCCESS;
@@ -236,7 +264,8 @@ cochran_common_device_close (dc_device_t *abstract)
 
 
 dc_status_t
-cochran_common_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
+cochran_common_device_set_fingerprint (dc_device_t *abstract,
+		const unsigned char data[], unsigned int size)
 {
 	cochran_device_t *device = (cochran_device_t *) abstract;
 	cochran_data_t *d = &(device->data);
@@ -254,7 +283,8 @@ cochran_common_device_set_fingerprint (dc_device_t *abstract, const unsigned cha
 
 
 dc_status_t
-cochran_common_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size)
+cochran_common_device_read (dc_device_t *abstract, unsigned int address,
+		unsigned char data[], unsigned int size)
 {
 	cochran_device_t *device = (cochran_device_t*) abstract;
 
@@ -294,7 +324,8 @@ cochran_common_device_read (dc_device_t *abstract, unsigned int address, unsigne
 	}
 
 	// Read data at high speed
-	dc_status_t rc = cochran_packet (device, command, command_size, data, size, 1);
+	dc_status_t rc = cochran_packet (device, command, command_size, data,
+			size, 1);
 	if (rc != DC_STATUS_SUCCESS)
 		return rc;
 
@@ -341,7 +372,7 @@ cochran_set_device_config (cochran_device_t *device)
 		device->data.address_length = COCHRAN_ADDRESS_LENGTH_32;
 		device->data.high_baud = 825600;
 	}
-	else if (memcmp(device->data.id + 0x3B, "AM\x11\x32\x32\x31\x32\x02", 8) == 0)
+	else if (memcmp(device->data.id + 0x3B, "AM\x11""2212\x02", 8) == 0)
 	{
 		device->data.model = COCHRAN_MODEL_COMMANDER_AIR_NITROX;
 		device->data.log_size = 256;
@@ -558,13 +589,10 @@ cochran_read_logbook (dc_device_t *abstract)
 	device->progress->maximum = d->logbook_size;
 	device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress);
 
-	// We have to close and restart to get the DC's attention
-	serial_close(device->port);
+	serial_sleep(device->port, 800);
 
-	struct timespec tr, ts = {0, 800 * 1000000}; // 800ms
-	nanosleep(&ts, &tr);
-
-	cochran_common_serial_open(device, abstract->context);
+	// set back to 9600 baud
+	cochran_common_serial_setup(device, abstract->context);
 
 	// Request log book
 	rc = cochran_common_device_read(abstract, 0, d->logbook, d->logbook_size);
@@ -662,9 +690,6 @@ cochran_read_samples(dc_device_t *abstract)
 			return DC_STATUS_NOMEMORY;
 		}
 
-		// We have to close the serial port to get the DC's attention
-		serial_close(device->port);
-
 		// Enable progress notifications.
 		device->progress = malloc(sizeof(dc_event_progress_t));
 		if (device->progress == NULL) {
@@ -676,13 +701,14 @@ cochran_read_samples(dc_device_t *abstract)
 		device->progress->maximum = d->sample_size;
 		device_event_emit (abstract, DC_EVENT_PROGRESS, device->progress);
 
-		struct timespec tr, ts = {0, 800* 1000000}; // 800 ms
-		nanosleep(&ts, &tr);
+		serial_sleep(device->port, 800);
 
-		cochran_common_serial_open(device, abstract->context);
+		// set back to 9600 baud
+		cochran_common_serial_setup(device, abstract->context);
 
 		// Read the sample data
-		rc = cochran_common_device_read (abstract, d->sample_data_offset, d->sample, d->sample_size);
+		rc = cochran_common_device_read (abstract, d->sample_data_offset,
+				d->sample, d->sample_size);
 		if (rc != DC_STATUS_SUCCESS) {
 			free (device->progress);
 			ERROR (abstract->context, "Failed to read the sample data.");
@@ -724,13 +750,17 @@ cochran_common_device_read_all (dc_device_t *abstract)
 }
 
 
+#define pack_uint32_array_le(d, i)	((d)[0] = (i) & 0xff, \
+									(d)[1] = ((i) >> 8) & 0xff, \
+									(d)[2] = ((i) >> 16) & 0xff, \
+									(d)[3] = ((i) >> 24) & 0xff)
+
 dc_status_t
 cochran_common_device_dump (dc_device_t *abstract, dc_buffer_t *data)
 {
 	cochran_device_t *device = (cochran_device_t *) abstract;
 	cochran_data_t *d = (cochran_data_t *) &(device->data);
-
-	struct cochran_data_dump *dump = (struct cochran_data_dump *) data;
+	int ptr = 0;
 
 	dc_status_t rc;
 
@@ -739,60 +769,119 @@ cochran_common_device_dump (dc_device_t *abstract, dc_buffer_t *data)
 	if (rc != DC_STATUS_SUCCESS)
 		return rc;
 
-	// Copy data for permanency.
+	// Reserve space for pointers
+	dc_buffer_resize(data, 10 * 5);
+	
+	pack_uint32_array_le(dc_buffer_get_data(data) + ptr,
+			dc_buffer_get_size(data));
 
 	if (d->id0) {
-		dump->id0 = dc_buffer_new(67);
-		dc_buffer_append (dump->id0, d->id0, 67);
+		dc_buffer_append(data, d->id0, 67);
+		*(dc_buffer_get_data(data) + ptr + 4) = 1;
+	} else {
+		dc_buffer_resize(data, dc_buffer_get_size(data) + 67);
 	}
 	
+	ptr += 5;
+	pack_uint32_array_le(dc_buffer_get_data(data) + ptr,
+			dc_buffer_get_size(data));
+
 	if (d->id) {
-		dump->id1 = dc_buffer_new(67);
-		dc_buffer_append (dump->id1, d->id, 67);
+		dc_buffer_append (data, d->id, 67);
+		*(dc_buffer_get_data(data) + ptr + 4) = 1;
+	} else {
+		dc_buffer_resize(data, dc_buffer_get_size(data) + 67);
 	}
 	
+	ptr += 5;
+	pack_uint32_array_le(dc_buffer_get_data(data) + ptr,
+			dc_buffer_get_size(data));
+
 	if (d->config1) {
-		dump->config1 = dc_buffer_new(512);
-		dc_buffer_append (dump->config1, d->config1, 512);
+		dc_buffer_append (data, d->config1, 512);
+		*(dc_buffer_get_data(data) + ptr + 4) = 1;
+	} else {
+		dc_buffer_resize(data, dc_buffer_get_size(data) + 512);
 	}
 	
+	ptr += 5;
+	pack_uint32_array_le(dc_buffer_get_data(data) + ptr,
+			dc_buffer_get_size(data));
+
 	if (d->config2) {
-		dump->config2 = dc_buffer_new(512);
-		dc_buffer_append (dump->config2, d->config2, 512);
+		dc_buffer_append (data, d->config2, 512);
+		*(dc_buffer_get_data(data) + ptr + 4) = 1;
+	} else {
+		dc_buffer_resize(data, dc_buffer_get_size(data) + 512);
 	}
 	
+	ptr += 5;
+	pack_uint32_array_le(dc_buffer_get_data(data) + ptr,
+			dc_buffer_get_size(data));
+
 	if (d->config3) {
-		dump->config3 = dc_buffer_new(512);
-		dc_buffer_append (dump->config3, d->config3, 512);
+		dc_buffer_append (data, d->config3, 512);
+		*(dc_buffer_get_data(data) + ptr + 4) = 1;
+	} else {
+		dc_buffer_resize(data, dc_buffer_get_size(data) + 512);
 	}
 	
+	ptr += 5;
+	pack_uint32_array_le(dc_buffer_get_data(data) + ptr,
+			dc_buffer_get_size(data));
+
 	if (d->config4) {
-		dump->config4 = dc_buffer_new(512);
-		dc_buffer_append (dump->config4, d->config4, 512);
+		dc_buffer_append (data, d->config4, 512);
+		*(dc_buffer_get_data(data) + ptr + 4) = 1;
+	} else {
+		dc_buffer_resize(data, dc_buffer_get_size(data) + 512);
 	}
 	
+	ptr += 5;
+	pack_uint32_array_le(dc_buffer_get_data(data) + ptr,
+			dc_buffer_get_size(data));
+
 	if (d->misc) {
-		dump->misc = dc_buffer_new(1500);
-		dc_buffer_append (dump->misc, d->misc, 1500);
+		dc_buffer_append (data, d->misc, 1500);
+		*(dc_buffer_get_data(data) + ptr + 4) = 1;
+	} else {
+		dc_buffer_resize(data, dc_buffer_get_size(data) + 1500);
 	}
 	
+	ptr += 5;
+	pack_uint32_array_le(dc_buffer_get_data(data) + ptr,
+			dc_buffer_get_size(data));
+
 	if (d->logbook) {
-		dump->logbook = dc_buffer_new(d->logbook_size);
-		dc_buffer_append (dump->logbook, d->logbook, d->logbook_size);
+		dc_buffer_append (data, d->logbook, d->logbook_size);
+		*(dc_buffer_get_data(data) + ptr + 4) = 1;
+	} else {
+		dc_buffer_resize(data, dc_buffer_get_size(data) + d->logbook_size);
 	}
 	
+	ptr += 5;
+	pack_uint32_array_le(dc_buffer_get_data(data) + ptr,
+			dc_buffer_get_size(data));
+
 	if (d->sample) {
-		dump->sample = dc_buffer_new(d->sample_size);
-		dc_buffer_append (dump->sample, d->sample, d->sample_size);
+		dc_buffer_append (data, d->sample, d->sample_size);
+		*(dc_buffer_get_data(data) + ptr + 4) = 1;
+	} else {
+		dc_buffer_resize(data, dc_buffer_get_size(data) + d->sample_size);
 	}
 
+	ptr += 5;
+	pack_uint32_array_le(dc_buffer_get_data(data) + ptr,
+			dc_buffer_get_size(data));
+
 	return DC_STATUS_SUCCESS;
 }
 
 
 
 dc_status_t
-cochran_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
+cochran_common_device_foreach (dc_device_t *abstract,
+		dc_dive_callback_t callback, void *userdata)
 {
 	cochran_device_t *device = (cochran_device_t *) abstract;
 	cochran_data_t *d = &(device->data);
@@ -853,7 +942,8 @@ cochran_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
 		}
 		d->current_dive_start_time = mktime(&t);
 
-		if (callback && !callback ((unsigned char *) d, sizeof(device->data), d->current_fingerprint, sizeof (d->fingerprint), userdata))
+		if (callback && !callback ((unsigned char *) d, sizeof(device->data),
+				d->current_fingerprint, sizeof (d->fingerprint), userdata))
 			return DC_STATUS_SUCCESS;
 	}
 
diff --git a/src/cochran_common.h b/src/cochran_common.h
index 136f153..7bba3fb 100644
--- a/src/cochran_common.h
+++ b/src/cochran_common.h
@@ -96,14 +96,62 @@ typedef struct cochran_device_t {
 	const char *name;				// serial port name
 	serial_t *port;
 	cochran_data_t data;			// dive data used in parsing
-	dc_event_progress_t *progress;	// We have to do progress in the _read function
+	dc_event_progress_t *progress;	// for progress in the _read function
 } cochran_device_t;
 
-dc_status_t cochran_packet (cochran_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, int high_speed);
-dc_status_t cochran_common_device_open (dc_device_t **out, dc_context_t *context, const char *name, const dc_device_vtable_t *vtable);
+
+// Commander log fields
+#define CMD_SEC					  1
+#define CMD_MIN					  0
+#define CMD_HOUR				  3
+#define CMD_DAY					  2
+#define CMD_MON					  5
+#define CMD_YEAR				  4
+#define CME_START_OFFSET		  6		// 4 bytes
+#define CMD_WATER_CONDUCTIVITY	 24		// 1 byte, 0=low, 2=high
+#define CMD_START_TEMP			 45		// 1 byte, F
+#define CMD_START_DEPTH			 56		// 2 byte, /4=ft
+#define CMD_ALTITUDE			 73		// 1 byte, /4=Kilofeet
+#define CMD_END_OFFSET			128		// 4 bytes
+#define CMD_MIN_TEMP			153		// 1 byte, F
+#define CMD_BT					166		// 2 bytes, minutes
+#define CMD_MAX_DEPTH			168		// 2 bytes, /4=ft
+#define CMD_AVG_DEPTH			170		// 2 bytes, /4=ft
+#define CMD_O2_PERCENT			210		// 8 bytes, 4 x 2 byte, /256=%
+
+// EMC log fields
+#define EMC_SEC					  0
+#define EMC_MIN					  1
+#define EMC_HOUR				  2
+#define EMC_DAY					  3
+#define EMC_MON					  4
+#define EMC_YEAR				  5
+#define EMC_START_OFFSET		  6		// 4 bytes
+#define EMC_WATER_CONDUCTIVITY	 25		// 1 byte, 0=low, 2=high
+#define EMC_START_DEPTH			 42		// 2 byte, /256=ft
+#define EMC_START_TEMP			 55		// 1 byte, F
+#define EMC_ALTITUDE			 89		// 1 byte, /4=Kilofeet
+#define EMC_O2_PERCENT			144		// 20 bytes, 10 x 2 bytes, /256=%
+#define EMC_HE_PERCENT			164		// 20 bytes, 10 x 2 bytes, /256=%
+#define EMC_END_OFFSET			256		// 4 bytes
+#define EMC_MIN_TEMP			293		// 1 byte, F
+#define EMC_BT					304		// 2 bytes, minutes
+#define EMC_MAX_DEPTH			306		// 2 bytes, /4=ft
+#define EMC_AVG_DEPTH			310		// 2 bytes, /4=ft
+
+
+dc_status_t cochran_packet (cochran_device_t *device,
+		const unsigned char command[], unsigned int csize,
+		unsigned char answer[], unsigned int asize, int high_speed);
+dc_status_t cochran_common_device_open (dc_device_t **out,
+		dc_context_t *context, const char *name);
 dc_status_t cochran_common_device_close (dc_device_t *abstract);
-dc_status_t cochran_common_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
-dc_status_t cochran_common_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
+dc_status_t cochran_common_device_set_fingerprint (dc_device_t *abstract,
+		const unsigned char data[], unsigned int size);
+dc_status_t cochran_common_device_read (dc_device_t *abstract,
+		unsigned int address, unsigned char data[], unsigned int size);
 static dc_status_t cochran_read_id (dc_device_t *abstract);
-dc_status_t cochran_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-dc_status_t cochran_common_device_dump (dc_device_t *abstract, dc_buffer_t *data);
+dc_status_t cochran_common_device_foreach (dc_device_t *abstract,
+		dc_dive_callback_t callback, void *userdata);
+dc_status_t cochran_common_device_dump (dc_device_t *abstract,
+		dc_buffer_t *data);
diff --git a/src/cochran_common_parser.c b/src/cochran_common_parser.c
index 744e5e7..9cc4040 100644
--- a/src/cochran_common_parser.c
+++ b/src/cochran_common_parser.c
@@ -34,15 +34,34 @@
 #include "cochran_common_parser.h"
 
 
-dc_status_t cochran_common_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
-dc_status_t cochran_common_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
-dc_status_t cochran_common_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
+dc_status_t cochran_common_parser_set_data (dc_parser_t *abstract,
+		const unsigned char *data, unsigned int size);
+dc_status_t cochran_common_parser_get_datetime (dc_parser_t *abstract,
+		dc_datetime_t *datetime);
+static dc_status_t cochran_common_parser_get_field (dc_parser_t *abstract,
+		dc_field_type_t type, unsigned int flags, void *value);
 dc_status_t cochran_common_parser_destroy (dc_parser_t *abstract);
-int cochran_common_handle_event (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata, unsigned char code, unsigned int offset, unsigned int time);
+static dc_status_t cochran_common_parser_samples_foreach
+		(dc_parser_t *abstract, dc_sample_callback_t callback,
+		void *userdata);
+int cochran_common_handle_event (dc_parser_t *abstract,
+		dc_sample_callback_t callback, void *userdata, unsigned char code,
+		unsigned int offset, unsigned int time);
+
+
+static dc_parser_vtable_t cochran_common_parser_vtable = {
+	DC_FAMILY_COCHRAN,
+	cochran_common_parser_set_data,			/* set_data */
+	cochran_common_parser_get_datetime,		/* datetime */
+	cochran_common_parser_get_field,		/* fields */
+	cochran_common_parser_samples_foreach,	/* samples_foreach */
+	cochran_common_parser_destroy			/* destroy */
+};
+
 
 
 dc_status_t
-cochran_common_parser_create (dc_parser_t **out, dc_context_t *context, const dc_parser_vtable_t *vtable)
+cochran_common_parser_create (dc_parser_t **out, dc_context_t *context)
 {
 	if (out == NULL)
 		return DC_STATUS_INVALIDARGS;
@@ -54,7 +73,7 @@ cochran_common_parser_create (dc_parser_t **out, dc_context_t *context, const dc
 		return DC_STATUS_NOMEMORY;
 	}
 
-	parser_init (parser, context, vtable);
+	parser_init (parser, context, &cochran_common_parser_vtable);
 
 	*out = parser;
 
@@ -73,7 +92,8 @@ cochran_common_parser_destroy (dc_parser_t *abstract)
 
 
 dc_status_t
-cochran_common_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
+cochran_common_parser_set_data (dc_parser_t *abstract,
+		const unsigned char *data, unsigned int size)
 {
 	abstract->data = data;
 	abstract->size = size;
@@ -84,7 +104,8 @@ cochran_common_parser_set_data (dc_parser_t *abstract, const unsigned char *data
 
 // There are two date formats used by Cochran
 dc_status_t
-cochran_common_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
+cochran_common_parser_get_datetime (dc_parser_t *abstract,
+		dc_datetime_t *datetime)
 {
 	cochran_data_t *data = (cochran_data_t *) abstract->data;
 	const unsigned char *log = data->current_log;
@@ -108,9 +129,48 @@ cochran_common_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dateti
 	return DC_STATUS_SUCCESS;
 }
 
+static dc_status_t
+cochran_common_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
+			unsigned int flags, void *value)
+{
+	cochran_data_t *data = (cochran_data_t *) abstract->data;
+
+	switch (data->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;
+	}
+}
+
+static dc_status_t
+cochran_common_parser_samples_foreach (dc_parser_t *abstract,
+			dc_sample_callback_t callback, void *userdata)
+{
+	cochran_data_t *data = (cochran_data_t *) abstract->data;
+
+	switch (data->model & 0xFF0000)
+	{
+	case COCHRAN_MODEL_COMMANDER_FAMILY:
+		return cochran_cmdr_parser_samples_foreach(abstract, callback,
+				userdata);
+		break;
+	case COCHRAN_MODEL_EMC_FAMILY:
+		return cochran_emc_parser_samples_foreach(abstract, callback,
+				userdata);
+		break;
+	}
+}
+
+
 
 int
-cochran_common_handle_event (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata, unsigned char code, unsigned int offset, unsigned int time)
+cochran_common_handle_event (dc_parser_t *abstract,
+		dc_sample_callback_t callback, void *userdata, unsigned char code,
+		unsigned int offset, unsigned int time)
 {
 	cochran_data_t *data = (cochran_data_t *) abstract->data;
 	cochran_events_t *e = cochran_events;
diff --git a/src/cochran_common_parser.h b/src/cochran_common_parser.h
index d19a619..9c4a67d 100644
--- a/src/cochran_common_parser.h
+++ b/src/cochran_common_parser.h
@@ -36,6 +36,8 @@ static cochran_events_t cochran_events[] = {
 					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_GASCHANGE,			SAMPLE_FLAGS_NONE },
 	{ 0xC1, 1, "Ascent rate greater than limit",
@@ -50,20 +52,34 @@ static cochran_events_t cochran_events[] = {
 					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_BEGIN },
+	{ 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_GASCHANGE2,		SAMPLE_FLAGS_NONE },
+	{ 0xF0, 1, "Breathing rate alarm",
+					SAMPLE_EVENT_NONE,				SAMPLE_FLAGS_END },
 	{ 0xF3, 1, "Switch to blend 1",
 					SAMPLE_EVENT_GASCHANGE2,		SAMPLE_FLAGS_NONE },
 	{ 0xF6, 1, "End Depth is less than ceiling",
@@ -73,8 +89,27 @@ static cochran_events_t cochran_events[] = {
 };
 
 
-dc_status_t cochran_common_parser_create (dc_parser_t **out, dc_context_t *context, const dc_parser_vtable_t *vtable);
+// Common
+dc_status_t cochran_common_parser_create (dc_parser_t **out,
+		dc_context_t *context);
 dc_status_t cochran_common_parser_destroy (dc_parser_t *abstract);
-dc_status_t cochran_common_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
-dc_status_t cochran_common_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
-int cochran_common_handle_event (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata, unsigned char code, unsigned int offset, unsigned int time);
+dc_status_t cochran_common_parser_set_data (dc_parser_t *abstract,
+		const unsigned char *data, unsigned int size);
+dc_status_t cochran_common_parser_get_datetime (dc_parser_t *abstract,
+		dc_datetime_t *datetime);
+int cochran_common_handle_event (dc_parser_t *abstract,
+		dc_sample_callback_t callback, void *userdata, unsigned char code,
+		unsigned int offset, unsigned int time);
+
+// EMC FAMILY
+dc_status_t cochran_emc_parser_get_field (dc_parser_t *abstract,
+		dc_field_type_t type, unsigned int flags, void *value);
+dc_status_t cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
+		dc_sample_callback_t callback, void *userdata);
+
+// COMMANDER FAMILY
+dc_status_t cochran_cmdr_parser_get_field(dc_parser_t *abstract,
+		dc_field_type_t type, unsigned int flags, void *value);
+dc_status_t cochran_cmdr_parser_samples_foreach (dc_parser_t *abstract,
+		dc_sample_callback_t callback, void *userdata);
+
diff --git a/src/cochran_emc.c b/src/cochran_emc.c
deleted file mode 100644
index c4b0367..0000000
--- a/src/cochran_emc.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * libdivecomputer
- *
- * Copyright (C) 2014 John Van Ostrand
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include <stdlib.h>
-#include <time.h>
-
-#include <libdivecomputer/cochran.h>
-
-#include "context-private.h"
-#include "device-private.h"
-#include "serial.h"
-
-#include "cochran_common.h"
-#include "cochran_emc.h"
-
-
-static const dc_device_vtable_t cochran_emc_device_vtable = {
-	DC_FAMILY_COCHRAN_EMC,
-	cochran_common_device_set_fingerprint,	/* set_fingerprint */
-	cochran_common_device_read,				/* read */
-	NULL,									/* write */
-	cochran_common_device_dump, 			/* dump */
-	cochran_common_device_foreach,			/* foreach */
-	cochran_common_device_close				/* close */
-};
-
-
-dc_status_t
-cochran_emc_device_open (dc_device_t **out, dc_context_t *context, const char *name)
-{
-	dc_status_t rc;
-
-	rc = cochran_common_device_open(out, context, name, &cochran_emc_device_vtable);
-	if (rc != DC_STATUS_SUCCESS)
-		return rc;
-
-	cochran_device_t *device = (cochran_device_t *) *out;
-
-	// Check family
-	if ((device->data.model & 0xFF0000) != COCHRAN_MODEL_EMC_FAMILY) {
-		ERROR (context, "Device not recognized.");
-		serial_close (device->port);
-		free (device->data.id);
-		free (device);
-		return DC_STATUS_UNSUPPORTED;
-	}
-
-	return DC_STATUS_SUCCESS;
-}
diff --git a/src/cochran_emc.h b/src/cochran_emc.h
deleted file mode 100644
index e21036b..0000000
--- a/src/cochran_emc.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * libdivecomputer
- *
- * Copyright (C) 2014 John Van Ostrand
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-// 512 bytes for each dive in the log book
-struct cochran_emc_log_t {
-	// Pre-dive 256 bytes
-	unsigned char seconds, minutes, hour;		// 3 bytes
-	unsigned char day, month, year;				// 3 bytes
-	unsigned char sample_start_offset[4];		// 4 bytes
-	unsigned char start_timestamp[4];			// 4 bytes [secs from jan 1,92]
-	unsigned char pre_dive_timestamp[4];		// 4 bytes [secs from Jan 1,92]
-	unsigned char unknown1[6];					// 6 bytes
-	unsigned char water_conductivity;			// 1 byte  [0 =low, 2-high]
-	unsigned char unknown2[5];					// 5 bytes
-//30
-	unsigned char sample_pre_event_offset[4];	// 4 bytes
-	unsigned char config_bitfield[6];			// 6 bytes
-	unsigned char unknown3[2];					// 2 bytes
-	unsigned char start_depth[2];				// 2 bytes [/256]
-	unsigned char unknown4[2];					// 2 bytes
-	unsigned char start_battery_voltage[2];		// 2 bytes [/256]
-//48
-	unsigned char unknown5[7];					// 7 bytes
-	unsigned char start_temperature;			// 1 byte  [F]
-	unsigned char unknown6[28];					// 28 bytes
-	unsigned char sit[2];						// 2 bytes [minutes]
-	unsigned char number[2];					// 2 bytes
-	unsigned char unknown7[1]; 					// 1 bytes
-	unsigned char altitude;						// 1 byte [/4 = kft]
-	unsigned char start_nofly[2];				// 2 bytes [/256 = hours]
-//92
-	unsigned char unknown8[18]; 				// 18 bytes
-	unsigned char post_dive_sit[2];				// 2 bytes  [seconds]
-	unsigned char po2_set_point[9][2];			// 18 bytes	[/256 = %]
-	unsigned char unknown9[12]; 				// 12 bytes
-	unsigned char po2_alarm[2]; 				// 2 bytes  [/256 = %]
-//144
-	unsigned char o2_percent[10][2];			// 20 bytes	[/256 = %]
-	unsigned char he_percent[10][2];			// 20 bytes	[/256 = %]
-	unsigned char alarm_depth[2];				// 2 bytes
-	unsigned char unknown10[14]; 				// 14 bytes
-	unsigned char conservatism;					// 1 bytes [/256 = fraction]
-	unsigned char unknown11[2];					// 2 bytes
-	unsigned char repetitive_dive;				// 1 byte
-	unsigned char unknown12[12];				// 12 bytes
-	unsigned char start_tissue_nsat[20][2];		// 40 bytes [/256]
-
-	// Post-dive 256 bytes
-	unsigned char sample_end_offset[4];			// 4 bytes
-	unsigned char unknown13[33];				// 33 bytes
-	unsigned char temp;							// 1 byte  [F]
-	unsigned char unknown14[10];				// 10 bytes
-// 48
-	unsigned char bt[2];						// 2 bytes [minutes]
-	unsigned char max_depth[2];					// 2 bytes [/4 = ft]
-	unsigned char unknown15[2];					// 2 bytes
-	unsigned char avg_depth[2];					// 2 bytes [/4 = ft]
-	unsigned char min_ndc[2];					// 2 bytes [minutes]
-	unsigned char min_ndx_bt[2];				// 2 bytes [minutes]
-	unsigned char max_forecast_deco[2];			// 2 bytes [minutes]
-	unsigned char max_forecast_deco_bt[2];		// 2 bytes [minutes]
-//64
-	unsigned char max_ceiling[2];				// 2 bytes [*10 = ft]
-	unsigned char max_ceiling_bt[2];			// 2 bytes [minutes]
-	unsigned char unknown16[10];				// 18 bytes
-	unsigned char max_ascent_rate; 				// 1 byte  [ft/min]
-	unsigned char unknown17[3];					// 3 bytes
-	unsigned char max_ascent_rate_bt[2];		// 2 bytes [seconds]
-//84
-	unsigned char unknown18[54];				// 54 bytes
-//138
-	unsigned char end_battery_voltage[2];		// 2 bytes [/256 = v]
-	unsigned char unknown19[8];					// 8 bytes
-	unsigned char min_temp_bt[2];    			// 2 bytes [seconds]
-//150
-	unsigned char unknown20[22];				// 22 bytes
-//172
-	unsigned char end_nofly[2];					// 2 bytes	[/256 = hours]
-	unsigned char alarm_count[2];				// 2 byte
-	unsigned char actual_deco_time[2];			// 2 bytes [seconds]
-//178
-	unsigned char unknown21[38];				// 38 bytes
-//216
-	unsigned char end_tissue_nsat[20][2];		// 40 bytes [/256 = fraction]
-} __attribute__((packed));
-
-typedef struct cochran_emc_log_t cochran_emc_log_t;
-
-typedef enum cochran_emc_bitfield_config_t {
-	BF_TEMP_DEPENDENT_N2,
-	BF_ASCENT_RATE_BAR_GRAPH,
-	BF_BLEND_2_SWITCHING,
-	BF_ALTITUDE_AS_ONE_ZONE,
-	BF_DECOMPRESSION_TIME_DISPLAY,
-	BF_BLEND_3_SWITCHING,
-	BF_VARIABLE_ASCENT_RATE_ALARM,
-	BF_ASCENT_RATE_RESPONSE,
-	BF_REPETITIVE_DIVE_DEPENDENT_N2,
-	BF_TRAINING_MODE,
-	BF_CONSTANT_MODE_COMPUTATIONS,
-	BF_DISPLAYED_UNITS,
-	BF_AUDIBLE_ALARM,
-	BF_CLOCK,
-	BF_CEILING_DISPLAY_DIV_BY_10,
-	BF_GAS_2_AS_FIRST_GAS,
-	BF_ENABLE_HELIUM_COMPUTATIONS,
-	BF_AUTOMATIC_PO2_FO2_SWITCHING,
-	BF_TOUCH_PROGRAMMING_PO2_FO2_SWITCH,
-} cochran_emc_bitfield_config_t;
-
-
-typedef struct cochran_emc_bitfield_t {
-	cochran_emc_bitfield_config_t config;
-	unsigned char word;
-	unsigned char byte;
-	unsigned char mask;
-	unsigned char shift;
-} cochran_emc_bitfield_t;
-
-static cochran_emc_bitfield_t cochran_emc_bits[] = {
-// Word BD
-	{ BF_TEMP_DEPENDENT_N2, 0xBD, 0, 0x40, 6 }, 			// 0=normal,
-															// 1=reduced
-	{ BF_ASCENT_RATE_BAR_GRAPH, 0xBD, 0, 0x20, 5 },			// 0=fixed,
-															// 1=proportional
-	{ BF_BLEND_2_SWITCHING, 0xBD, 0, 0x04, 2 },				// 0=dis, 1=ena
-	{ BF_ALTITUDE_AS_ONE_ZONE, 0xBD, 0, 0x02, 1},			// 0=off, 1=on
-
-	{ BF_DECOMPRESSION_TIME_DISPLAY, 0xBD, 1, 0xC0, 5},		// 111=both,
-															// 011=stop,
-															// 001=total
-	{ BF_BLEND_3_SWITCHING, 0xBD, 1, 0x10, 4 },				// 0=dis, 1=ena
-	{ BF_VARIABLE_ASCENT_RATE_ALARM, 0xBD, 1, 0x04, 3},		// 0=off, 1=on
-	{ BF_ASCENT_RATE_RESPONSE, 0xBD, 1, 0x07, 0},	
-
-//WORD BE
-	{ BF_REPETITIVE_DIVE_DEPENDENT_N2, 0xBE, 0, 0x80, 7 },	// 0=off, 1=on
-	{ BF_TRAINING_MODE, 0xBE, 0, 0x04, 2 }, 				// 0=off, 1=on
-	{ BF_CONSTANT_MODE_COMPUTATIONS, 0xBE, 0, 0x04, 2 },	// 0=FO2, 1=PO2
-	{ BF_DISPLAYED_UNITS, 0xBE, 0, 0x01, 0 },				// 1=metric,
-															// 0=imperial
-
-// WORD BF
-	{ BF_AUDIBLE_ALARM, 0xBF, 0, 0x40, 6 }, 				// 0=on, 1=off ***
-	{ BF_CLOCK, 0xBF, 0, 0x20, 5 },							// 0=off, 1=on
-	{ BF_CEILING_DISPLAY_DIV_BY_10, 0xBF, 0, 0x10, 4 },		// 0=off, 1=on
-	{ BF_GAS_2_AS_FIRST_GAS, 0xBF, 0, 0x02, 1 },			// 0=dis, 1=ena
-	{ BF_ENABLE_HELIUM_COMPUTATIONS, 0xBF, 0, 0x01, 0 },	// 0=dis, 1=ena
-
-	{ BF_AUTOMATIC_PO2_FO2_SWITCHING, 0xBF, 1, 0x04, 2 },	// 0=dis, 1=ena
-	{ BF_TOUCH_PROGRAMMING_PO2_FO2_SWITCH, 0xBF, 1, 0x02, 1 },	// 0=dis, 1=ena
-};
-
-struct cochran_emc_config1_t {
-	unsigned char unknown1[209];
-	unsigned short int dive_count;
-	unsigned char unknown2[274];
-	unsigned short int serial_num;
-	unsigned char unknown3[24];
-} __attribute__((packed));
-
-typedef struct cochran_emc_config1_t cochran_emc_config1_t;
diff --git a/src/cochran_emc_parser.c b/src/cochran_emc_parser.c
index cf6e9af..f336e43 100644
--- a/src/cochran_emc_parser.c
+++ b/src/cochran_emc_parser.c
@@ -32,7 +32,6 @@
 #include "array.h"
 
 #include "cochran_common.h"
-#include "cochran_emc.h"
 #include "cochran_common_parser.h"
 
 
@@ -44,33 +43,16 @@ struct dive_stats {
 };
 
 
-static dc_status_t cochran_emc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
-static dc_status_t cochran_emc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
-static void cochran_emc_parse_dive_stats (dc_parser_t *abstract, struct dive_stats *stats);
-
-
-static dc_parser_vtable_t cochran_emc_parser_vtable = {
-	DC_FAMILY_COCHRAN_EMC,
-	cochran_common_parser_set_data,		/* set_data */
-	cochran_common_parser_get_datetime,	/* datetime */
-	cochran_emc_parser_get_field,		/* fields */
-	cochran_emc_parser_samples_foreach,	/* samples_foreach */
-	cochran_common_parser_destroy			/* destroy */
-};
+static void cochran_emc_parse_dive_stats (dc_parser_t *abstract,
+		struct dive_stats *stats);
 
 
 dc_status_t
-cochran_emc_parser_create (dc_parser_t **out, dc_context_t *context)
-{
-	return cochran_common_parser_create(out, context, &cochran_emc_parser_vtable);
-}
-
-
-static dc_status_t
-cochran_emc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
+cochran_emc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
+		unsigned int flags, void *value)
 {
 	cochran_data_t *data = (cochran_data_t *) abstract->data;
-	cochran_emc_log_t *log = (cochran_emc_log_t *) data->current_log;
+	unsigned char *log = data->current_log;
 
 	dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
 	dc_salinity_t *water = (dc_salinity_t *) value;
@@ -86,44 +68,50 @@ cochran_emc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsig
 				cochran_emc_parse_dive_stats(abstract, &stats);
 				*((unsigned int*) value) = min_temp;
 			} else
-				*((unsigned int*) value) = (log->start_temperature - 32) / 1.8;
+				*((unsigned int*) value) = (log[EMC_START_TEMP] - 32) / 1.8;
 			break;
 		case DC_FIELD_DIVETIME:
 			if (data->corrupt_dive) {
 				cochran_emc_parse_dive_stats(abstract, &stats);
 				*((unsigned int*) value) = dive_time;
 			} else
-				*((unsigned int *) value) = array_uint16_le (log->bt) * 60;
+				*((unsigned int *) value) = array_uint16_le (log + EMC_BT) * 60;
 			break;
 		case DC_FIELD_MAXDEPTH:
 			if (data->corrupt_dive) {
 				cochran_emc_parse_dive_stats(abstract, &stats);
 				*((unsigned int*) value) = max_depth;
 			} else
-				*((double *) value) = array_uint16_le (log->max_depth) / 4 * FEET;
+				*((double *) value) = array_uint16_le (log + EMC_MAX_DEPTH) / 4
+						* FEET;
 			break;
 		case DC_FIELD_AVGDEPTH:
 			if (data->corrupt_dive) {
 				cochran_emc_parse_dive_stats(abstract, &stats);
 				*((unsigned int*) value) = avg_depth;
 			} else
-				*((double *) value) = array_uint16_le (log->avg_depth) / 4 * FEET;
+				*((double *) value) = array_uint16_le (log + EMC_AVG_DEPTH) / 4
+						* FEET;
 			break;
 		case DC_FIELD_GASMIX_COUNT:
 			*((unsigned int *) value) = 10;
 			break;
 		case DC_FIELD_GASMIX:
-			gasmix->oxygen = (double) array_uint16_le ((char *)log->o2_percent + 2 * flags) / 256 / 100;
-			gasmix->helium = (double) array_uint16_le ((char *)log->he_percent + 2 * flags) / 256 / 100;
+			gasmix->oxygen = (double) array_uint16_le ((char *) log
+					+ EMC_O2_PERCENT + 2 * flags) / 256 / 100;
+			gasmix->helium = (double) array_uint16_le ((char *) log
+					+ EMC_HE_PERCENT + 2 * flags) / 256 / 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->water_conductivity == 0 ? DC_WATER_FRESH : DC_WATER_SALT );
-			water->density = 1000 + 12.5 * log->water_conductivity;
+			water->type = ( log[EMC_WATER_CONDUCTIVITY] == 0 ? DC_WATER_FRESH
+					: DC_WATER_SALT );
+			water->density = 1000 + 12.5 * log[EMC_WATER_CONDUCTIVITY];
 			break;
 		case DC_FIELD_ATMOSPHERIC:
-			*(double *) value = ATM / BAR * pow(1 - 0.0000225577 * (double) log->altitude * 250 * FEET, 5.25588);
+			*(double *) value = ATM / BAR * pow(1 - 0.0000225577
+					* (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588);
 			break;
 		default:
 			return DC_STATUS_UNSUPPORTED;
@@ -172,12 +160,13 @@ cochran_emc_guess_sample_size(cochran_data_t *data)
 	
 
 
-static dc_status_t
-cochran_emc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
+dc_status_t
+cochran_emc_parser_samples_foreach (dc_parser_t *abstract,
+		dc_sample_callback_t callback, void *userdata)
 {
 	cochran_data_t *data = (cochran_data_t *) abstract->data;
 	unsigned char *sdata = data->current_sample;
-	cochran_emc_log_t *log = (cochran_emc_log_t *) data->current_log;
+	unsigned char *log = data->current_log;
 	unsigned int size = data->current_sample_size;
 	unsigned char *s;
 
@@ -196,12 +185,15 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
 		size = cochran_emc_guess_sample_size(data);
 	}
 
-	// Cochran samples depth every second and varies between ascent rate
-	// and temp ever other second.
-	// In the 21st, 22nd, 23rd, 24th samples are NDL remaining, deco time and ceiling
+	/*
+	 * Cochran samples depth every second and varies between ascent rate
+	 * and temp ever other second.
+	 * In the 21st, 22nd, 23rd, 24th samples are NDL remaining,
+	 *deco time and ceiling
+	 */
 
 	// Prime with values from the dive log section
-	depth = array_uint16_le (log->start_depth) / 256;
+	depth = array_uint16_le (log + EMC_START_DEPTH) / 256;
 
 	last_sample_time = sample.time = time;
 	if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
@@ -209,7 +201,7 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
 	sample.depth = depth * FEET;
 	if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
 
-	sample.temperature = (log->start_temperature - 32) / 1.8;
+	sample.temperature = (log[EMC_START_TEMP] - 32) / 1.8;
 	if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
 
 	while (offset < size) {
@@ -237,7 +229,8 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
 
 		// Check for event
 		if (s[0] & 0x80) {
-			offset += cochran_common_handle_event(abstract, callback, userdata, s[0], offset, time);
+			offset += cochran_common_handle_event(abstract, callback, userdata,
+				s[0], offset, time);
 			switch (s[0])
 			{
 			case 0xC5:	// Deco obligation begins
@@ -276,12 +269,12 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
 		// Ascent rate is logged in the 0th sample, temp in the 1st, repeat.
 		if (time % 2 == 0) {
 			// Ascent rate
-			ascent_rate = (s[1] & 0x7f) / 4 * (s[0] & 0x80 ? 1 : -1);
+			ascent_rate = (s[1] & 0x7f) / 4 * (s[1] & 0x80 ? 1 : -1);
 			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 = s[1] / 2 + 20;
+			temperature = (float) s[1] / 2 + 20;
 			sample.temperature = (temperature - 32.0) / 1.8;
 
 			if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
@@ -323,11 +316,12 @@ cochran_emc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
 	return DC_STATUS_SUCCESS;
 }
 
-void cochran_emc_parse_dive_stats (dc_parser_t *abstract, struct dive_stats *stats)
+void cochran_emc_parse_dive_stats (dc_parser_t *abstract,
+		struct dive_stats *stats)
 {
 	cochran_data_t *data = (cochran_data_t *) abstract->data;
 	unsigned char *sdata = data->current_sample;
-	cochran_emc_log_t *log = (cochran_emc_log_t *) data->current_log;
+	unsigned char *log = data->current_log;
 	unsigned int size = data->current_sample_size;
 	unsigned char *s;
 	unsigned int offset = 0;
diff --git a/src/descriptor.c b/src/descriptor.c
index 20f74e6..8836709 100644
--- a/src/descriptor.c
+++ b/src/descriptor.c
@@ -223,9 +223,9 @@ static const dc_descriptor_t g_descriptors[] = {
 	{"Shearwater", "Petrel",   DC_FAMILY_SHEARWATER_PETREL, 3},
 	/* Dive Rite NiTek Q */
 	{"Dive Rite", "NiTek Q",   DC_FAMILY_DIVERITE_NITEKQ, 0},
-	{"Cochran", "Commander",	DC_FAMILY_COCHRAN_COMMANDER, 0},
-	{"Cochran", "EMC-20H",		DC_FAMILY_COCHRAN_EMC, 0},
-	{"Cochran", "EMC-16",		DC_FAMILY_COCHRAN_EMC, 0},
+	{"Cochran", "Commander",	DC_FAMILY_COCHRAN, 0},
+	{"Cochran", "EMC-16",		DC_FAMILY_COCHRAN, 1},
+	{"Cochran", "EMC-20H",		DC_FAMILY_COCHRAN, 2},
 };
 
 typedef struct dc_descriptor_iterator_t {
diff --git a/src/device.c b/src/device.c
index e8661b0..cdd41ab 100644
--- a/src/device.c
+++ b/src/device.c
@@ -155,11 +155,8 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
 	case DC_FAMILY_DIVERITE_NITEKQ:
 		rc = diverite_nitekq_device_open (&device, context, name);
 		break;
-	case DC_FAMILY_COCHRAN_COMMANDER:
-		rc = cochran_cmdr_device_open (&device, context, name);
-		break;
-	case DC_FAMILY_COCHRAN_EMC:
-		rc = cochran_emc_device_open (&device, context, name);
+	case DC_FAMILY_COCHRAN:
+		rc = cochran_common_device_open (&device, context, name);
 		break;
 	default:
 		return DC_STATUS_INVALIDARGS;
diff --git a/src/parser.c b/src/parser.c
index dc6655b..07e9d2d 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -132,11 +132,8 @@ dc_parser_new (dc_parser_t **out, dc_device_t *device)
 	case DC_FAMILY_DIVERITE_NITEKQ:
 		rc = diverite_nitekq_parser_create (&parser, context);
 		break;
-	case DC_FAMILY_COCHRAN_COMMANDER:
-		rc = cochran_cmdr_parser_create (&parser, context);
-		break;
-	case DC_FAMILY_COCHRAN_EMC:
-		rc = cochran_emc_parser_create (&parser, context);
+	case DC_FAMILY_COCHRAN:
+		rc = cochran_common_parser_create (&parser, context);
 		break;
 	default:
 		return DC_STATUS_INVALIDARGS;
-- 
1.8.3.1



More information about the devel mailing list