Aeris A300CS patches [V3]

Dirk Hohndel dirk at hohndel.org
Thu Oct 2 10:18:17 PDT 2014


OK, here we go. MUCH simpler now. This needs to be run through your
regression tests with other atom2 style dive computers. But it seems to
work well both with the computer I have access to and with the memory dump
you sent. Sadly, that one has even more boring dives than mine does - but
the poor person does appear to have one of the totally broken first
generation tank transmitters. It cuts out all the time and occationally
sends completely bogus values. I'm proud to say that the dive plot with
Subsurface looks orders of magnitude better than what the Aeris software
shows. :-)

/D
-------------- next part --------------
>From 856b0c27983e201601020f6db881a17102ee9d26 Mon Sep 17 00:00:00 2001
From: Dirk Hohndel <dirk at hohndel.org>
Date: Fri, 26 Sep 2014 10:58:15 -0700
Subject: [PATCH 1/5] Fix multipage algorithm to create aligned reads

At least the A300CS requires that reads are aligned to the pagesize that
is read.

Signed-off-by: Dirk Hohndel <dirk at hohndel.org>
---
 src/oceanic_common.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/oceanic_common.c b/src/oceanic_common.c
index f5f37a149596..d5313d563eb7 100644
--- a/src/oceanic_common.c
+++ b/src/oceanic_common.c
@@ -536,6 +536,15 @@ oceanic_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
 			if (nbytes + len > remaining)
 				len = remaining - nbytes; // End of profile.
 
+			// if we are able to read multiple pages, make sure we have an aligned address
+			if (device->multipage > 1 && len == PAGESIZE * device->multipage) {
+				if (address % (PAGESIZE * device->multipage)) {
+					// this wouldn't be an aligned read
+					// let's read a smaller subset first which should have the remaining
+					// reads be aligned
+					len = address % (PAGESIZE * device->multipage);
+				}
+			}
 			// Move to the start of the current page.
 			address -= len;
 			offset -= len;
-- 
1.8.0.rc0.18.gf84667d

-------------- next part --------------
>From 1b3a2fe3b3257ddb198507b2c87dbcb80735dc3d Mon Sep 17 00:00:00 2001
From: Dirk Hohndel <dirk at hohndel.org>
Date: Wed, 24 Sep 2014 21:26:32 -0700
Subject: [PATCH 2/5] Prepare Oceanic infrastructure for multi page reads

Some new Oceanic dive computers like the Aeris A300CS support a new read
command that always reads 256 byte pages instead of 16 byte pages, other
versions support reading 128 byte pages.

This patch adds a field to the oceanic_atom2_device_t structure to
indicate which type of device this is. The multipage algorithm will
request larger, aligned reads wherever possible and fall back to standard
16 byte reads otherwise.

Signed-off-by: Dirk Hohndel <dirk at hohndel.org>
---
 src/oceanic_atom2.c | 58 +++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 45 insertions(+), 13 deletions(-)

diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c
index 17e19580d42f..9fd10f56a90d 100644
--- a/src/oceanic_atom2.c
+++ b/src/oceanic_atom2.c
@@ -49,6 +49,8 @@ typedef struct oceanic_atom2_device_t {
 	oceanic_common_device_t base;
 	serial_t *port;
 	unsigned int delay;
+	unsigned char bigpage;
+
 } oceanic_atom2_device_t;
 
 static dc_status_t oceanic_atom2_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
@@ -406,9 +408,15 @@ oceanic_atom2_transfer (oceanic_atom2_device_t *device, const unsigned char comm
 			return EXITCODE (n);
 		}
 
-		// Verify the checksum of the answer.
-		unsigned char crc = answer[asize - 1];
-		unsigned char ccrc = checksum_add_uint8 (answer, asize - 1, 0x00);
+		// Verify the checksum of the answer; we can use unsigned short in both cases
+		unsigned short crc, ccrc;
+		if (asize == 256 + 2) { // 256 byte pages get two crc bytes
+			crc = (answer[asize - 1] << 8) | answer[asize - 2] ;
+			ccrc = checksum_add_uint16 (answer, asize - 2, 0x0000);
+		} else {
+			crc = answer[asize - 1];
+			ccrc = checksum_add_uint8 (answer, asize - 1, 0x00);
+		}
 		if (crc != ccrc) {
 			ERROR (abstract->context, "Unexpected answer checksum.");
 			return DC_STATUS_PROTOCOL;
@@ -451,6 +459,7 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, const char
 	// Set the default values.
 	device->port = NULL;
 	device->delay = 0;
+	device->bigpage = 1; // no big pages
 
 	// Open the device.
 	int rc = serial_open (&device->port, context, name);
@@ -598,33 +607,56 @@ oceanic_atom2_device_version (dc_device_t *abstract, unsigned char data[], unsig
 }
 
 
+/* if we have aligned reads of a larger pagesize (and the device supports that), use the larger
+ * block reads, otherwise just loop and read in PAGESIZE chunks */
 static dc_status_t
 oceanic_atom2_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size)
 {
 	oceanic_atom2_device_t *device = (oceanic_atom2_device_t*) abstract;
 
-	if ((address % PAGESIZE != 0) ||
-		(size    % PAGESIZE != 0))
+	// pick the best pagesize to use
+	unsigned int pagesize = PAGESIZE;
+
+	if (size == device->bigpage * PAGESIZE && address % (device->bigpage * PAGESIZE) == 0)
+		pagesize = device->bigpage * PAGESIZE;
+
+	// pick the correct read command and number of crc bytes based on the page size
+	unsigned char read_cmd, crc_size;
+	if (pagesize == 256) {
+		read_cmd = 0xB8;
+		crc_size = 2;
+	} else if (pagesize == 128) {
+		read_cmd = 0xB4;
+		crc_size = 1;
+	} else {
+		read_cmd = 0xB1;
+		crc_size = 1;
+	}
+
+	if ((address % pagesize != 0) ||
+	    (size    % pagesize != 0))
 		return DC_STATUS_INVALIDARGS;
 
 	unsigned int nbytes = 0;
+
 	while (nbytes < size) {
 		// Read the package.
-		unsigned int number = address / PAGESIZE;
-		unsigned char answer[PAGESIZE + 1] = {0};
-		unsigned char command[4] = {0xB1,
+		unsigned int number = address / PAGESIZE;      // this is always PAGESIZE, even in BIGPAGE mode
+		unsigned char answer[256 + 2] = {0};           // maximum we support for the known commands
+		unsigned int answersize = pagesize + crc_size;
+		unsigned char command[4] = {read_cmd,
 				(number >> 8) & 0xFF, // high
 				(number     ) & 0xFF, // low
 				0};
-		dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), answer, sizeof (answer));
+		dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), answer, answersize);
 		if (rc != DC_STATUS_SUCCESS)
 			return rc;
 
-		memcpy (data, answer, PAGESIZE);
+		memcpy (data, answer, pagesize);
 
-		nbytes += PAGESIZE;
-		address += PAGESIZE;
-		data += PAGESIZE;
+		nbytes += pagesize;
+		address += pagesize;
+		data += pagesize;
 	}
 
 	return DC_STATUS_SUCCESS;
-- 
1.8.0.rc0.18.gf84667d

-------------- next part --------------
>From 253ee26f6ae1dd0f7630ab0571171511996746f9 Mon Sep 17 00:00:00 2001
From: Dirk Hohndel <dirk at hohndel.org>
Date: Wed, 24 Sep 2014 21:39:28 -0700
Subject: [PATCH 3/5] Add initial support for the Aeris A300CS

This is ignoring a ton of data that the dive computer provides. But it
gives profile, tank pressure and temperatures - so it's a start.

This patch adds a set_dtr and set_rts call to the serial interface prior
to interacting with the device. This change is required for the A300CS to
talk to the computer.

TODO:
- we need to verify that this doesn't cause any issues with earlier models.

Signed-off-by: Dirk Hohndel <dirk at hohndel.org>
---
 src/descriptor.c           |  1 +
 src/oceanic_atom2.c        | 29 +++++++++++++++++++++++++++--
 src/oceanic_atom2_parser.c | 33 ++++++++++++++++++++++++++++-----
 3 files changed, 56 insertions(+), 7 deletions(-)

diff --git a/src/descriptor.c b/src/descriptor.c
index 403b67a8746f..617c279cc185 100644
--- a/src/descriptor.c
+++ b/src/descriptor.c
@@ -171,6 +171,7 @@ static const dc_descriptor_t g_descriptors[] = {
 	{"Sherwood", "Amphos",              DC_FAMILY_OCEANIC_ATOM2, 0x4545},
 	{"Oceanic",  "Pro Plus 3",          DC_FAMILY_OCEANIC_ATOM2, 0x4548},
 	{"Oceanic",  "OCi",                 DC_FAMILY_OCEANIC_ATOM2, 0x454B},
+	{"Aeris",    "A300CS",              DC_FAMILY_OCEANIC_ATOM2, 0x454C},
 	/* Mares Nemo */
 	{"Mares", "Nemo",         DC_FAMILY_MARES_NEMO, 0},
 	{"Mares", "Nemo Steel",   DC_FAMILY_MARES_NEMO, 0},
diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c
index 9fd10f56a90d..5119ec57b2ea 100644
--- a/src/oceanic_atom2.c
+++ b/src/oceanic_atom2.c
@@ -151,6 +151,10 @@ static const oceanic_common_version_t oceanic_reactpro_version[] = {
 	{"REACPRO2 \0\0 512K"},
 };
 
+static const oceanic_common_version_t aeris_a300cs_version[] = {
+	{"AER300CS \0\0 2048"},
+};
+
 static const oceanic_common_layout_t aeris_f10_layout = {
 	0x10000, /* memsize */
 	0x0000, /* cf_devinfo */
@@ -333,6 +337,19 @@ static const oceanic_common_layout_t oceanic_reactpro_layout = {
 	1 /* pt_mode_logbook */
 };
 
+static const oceanic_common_layout_t aeris_a300cs_layout = {
+	0x40000, /* memsize */
+	0x0000, /* cf_devinfo */
+	0x0040, /* cf_pointers */
+	0x0900, /* rb_logbook_begin */
+	0x1000, /* rb_logbook_end */
+	16, /* rb_logbook_entry_size */
+	0x1000, /* rb_profile_begin */
+	0x40000, /* rb_profile_end */
+	0, /* pt_mode_global */
+	1 /* pt_mode_logbook */
+};
+
 static dc_status_t
 oceanic_atom2_send (oceanic_atom2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char ack)
 {
@@ -470,8 +487,7 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, const char
 	}
 
 	// Set the serial communication protocol (38400 8N1).
-	rc = serial_configure (device->port, 38400, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE);
-	if (rc == -1) {
+	if (serial_configure (device->port, 38400, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE) == -1) {
 		ERROR (context, "Failed to set the terminal attributes.");
 		serial_close (device->port);
 		free (device);
@@ -489,6 +505,11 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, const char
 	// Give the interface 100 ms to settle and draw power up.
 	serial_sleep (device->port, 100);
 
+	// at least the Aeris A300CS needs these two commands to respond to the serial interface
+	// need to verify that these don't cause problem with earlier models
+	serial_set_dtr(device->port, 1);
+	serial_set_rts(device->port, 1);
+
 	// Make sure everything is in a sane state.
 	serial_flush (device->port, SERIAL_QUEUE_BOTH);
 
@@ -535,6 +556,10 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, const char
 		device->base.layout = &oceanic_veo1_layout;
 	} else if (OCEANIC_COMMON_MATCH (device->base.version, oceanic_reactpro_version)) {
 		device->base.layout = &oceanic_reactpro_layout;
+	} else if (OCEANIC_COMMON_MATCH (device->base.version, aeris_a300cs_version)) {
+		device->base.layout = &aeris_a300cs_layout;
+		device->bigpage = 16;
+		device->base.multipage = 16;
 	} else {
 		device->base.layout = &oceanic_default_layout;
 	}
diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c
index beed0a85278b..b081b3be812a 100644
--- a/src/oceanic_atom2_parser.c
+++ b/src/oceanic_atom2_parser.c
@@ -69,6 +69,7 @@
 #define AMPHOS      0x4545
 #define PROPLUS3    0x4548
 #define OCI         0x454B
+#define A300CS      0x454C
 
 typedef struct oceanic_atom2_parser_t oceanic_atom2_parser_t;
 
@@ -218,6 +219,14 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim
 			datetime->hour   = p[11];
 			datetime->minute = p[10];
 			break;
+		case A300CS:
+			datetime->year   = (p[10]) + 2000;
+			datetime->month  = (p[8]);
+			datetime->day    = (p[9]);
+			datetime->hour   = bcd2dec(p[1] & 0x1F);
+			datetime->minute = bcd2dec(p[0]);
+			pm = p[1] & 0x80;
+			break;
 		default:
 			datetime->year   = bcd2dec (((p[3] & 0xC0) >> 2) + (p[4] & 0x0F)) + 2000;
 			datetime->month  = (p[4] & 0xF0) >> 4;
@@ -345,7 +354,7 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns
 		case DC_FIELD_GASMIX_COUNT:
 			if (parser->model == DATAMASK || parser->model == COMPUMASK)
 				*((unsigned int *) value) = 1;
-			else if (parser->model == VT4 || parser->model == VT41 || parser->model == OCI)
+			else if (parser->model == VT4 || parser->model == VT41 || parser->model == OCI || parser->model == A300CS)
 				*((unsigned int *) value) = 4;
 			else if (parser->model == TX1)
 				*((unsigned int *) value) = 6;
@@ -357,6 +366,8 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns
 				oxygen = data[header + 3];
 			} else if (parser->model == OCI) {
 				oxygen = data[0x28 + flags];
+			} else if (parser->model == A300CS) {
+				oxygen = data[0x2A + flags];
 			} else if (parser->model == TX1) {
 				oxygen = data[0x3E + flags];
 				helium = data[0x48 + flags];
@@ -403,6 +414,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
 	} else if (parser->model == F10) {
 		headersize = 3 * PAGESIZE;
 		footersize = PAGESIZE / 2;
+	} else if (parser->model == A300CS) {
+		headersize = 5 * PAGESIZE;
 	}
 
 	if (size < headersize + footersize)
@@ -414,7 +427,10 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
 	unsigned int time = 0;
 	unsigned int interval = 1;
 	if (parser->model != F10) {
-		switch (data[0x17] & 0x03) {
+		int offset = 0x17;
+		if (parser->model == A300CS)
+			offset = 0x1f;
+		switch (data[offset] & 0x03) {
 		case 0:
 			interval = 2;
 			break;
@@ -433,7 +449,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
 	unsigned int samplesize = PAGESIZE / 2;
 	if (parser->model == OC1A || parser->model == OC1B ||
 		parser->model == OC1C || parser->model == OCI ||
-		parser->model == TX1)
+		parser->model == TX1 || parser->model == A300CS)
 		samplesize = PAGESIZE;
 	else if (parser->model == F10)
 		samplesize = 2;
@@ -458,7 +474,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
 	unsigned int tank = 0;
 	unsigned int pressure = 0;
 	if (have_pressure) {
-		pressure = data[header + 2] + (data[header + 3] << 8);
+		int idx = parser->model == A300CS ? 16 : 2;
+		pressure = data[header + idx] + (data[header + idx + 1] << 8);
 		if (pressure == 10000)
 			have_pressure = 0;
 	}
@@ -510,6 +527,10 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
 				// Tank pressure (1 psi) and number
 				tank = 0;
 				pressure = (((data[offset + 7] << 8) + data[offset + 6]) & 0x0FFF);
+			} else if (parser->model == A300CS) {
+				// Tank pressure (1 psi) and number (one based index)
+				tank = (data[offset + 1] & 0x03) - 1;
+				pressure = ((data[offset + 7] << 8) + data[offset + 6]) & 0x0FFF;
 			} else {
 				// Tank pressure (2 psi) and number (one based index)
 				tank = (data[offset + 1] & 0x03) - 1;
@@ -551,6 +572,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
 					temperature = data[offset + 1];
 				} else if (parser->model == VT4 || parser->model == VT41 || parser->model == ATOM3 || parser->model == ATOM31 || parser->model == A300AI) {
 					temperature = ((data[offset + 7] & 0xF0) >> 4) | ((data[offset + 7] & 0x0C) << 2) | ((data[offset + 5] & 0x0C) << 4);
+				} else if (parser->model == A300CS) {
+					temperature = data[offset + 11];
 				} else {
 					unsigned int sign;
 					if (parser->model == DG03 || parser->model == PROPLUS3)
@@ -583,7 +606,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
 					parser->model == ZENAIR ||parser->model == A300AI ||
 					parser->model == DG03 || parser->model == PROPLUS3)
 					pressure = (((data[offset + 0] & 0x03) << 8) + data[offset + 1]) * 5;
-				else if (parser->model == TX1)
+				else if (parser->model == TX1 || parser->model == A300CS)
 					pressure = array_uint16_le (data + offset + 4);
 				else
 					pressure -= data[offset + 1];
-- 
1.8.0.rc0.18.gf84667d

-------------- next part --------------
>From b9dbaed6b1130398124d64f400656677a5914deb Mon Sep 17 00:00:00 2001
From: Dirk Hohndel <dirk at hohndel.org>
Date: Tue, 30 Sep 2014 12:35:04 -0700
Subject: [PATCH 4/5] Aeris A300CS: add support for NDL / deco data

Encoded in every sample. The depth is in multiples of 10 feet which gives
somewhat odd metric stop depth - but rounding to full meters would take
care of that.

Signed-off-by: Dirk Hohndel <dirk at hohndel.org>
---
 src/oceanic_atom2_parser.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c
index b081b3be812a..3a577d2e05d8 100644
--- a/src/oceanic_atom2_parser.c
+++ b/src/oceanic_atom2_parser.c
@@ -631,6 +631,21 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
 			sample.depth = depth / 16.0 * FEET;
 			if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
 
+			// NDL / Deco
+			// bits 6..4 of byte 15 encode deco state & depth
+			// bytes 6 & 7 encode minutes of NDL / deco
+			if (parser->model == A300CS) {
+				if ((data[offset + 15] & 0x70) != 0) {
+					sample.deco.type = DC_DECO_DECOSTOP;
+					sample.deco.depth = ((data[offset + 15] & 0x70) >> 4) * 10 * FEET;
+				} else {
+					sample.deco.type = DC_DECO_NDL;
+					sample.deco.depth = 0.0;
+				}
+				sample.deco.time = (data[offset + 6] + (data[offset + 7] << 8)) & 0x03FF;
+				if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
+			}
+
 			complete = 1;
 		}
 
-- 
1.8.0.rc0.18.gf84667d

-------------- next part --------------
>From b036266462ce84a678d8b829b49bf872e44ba25c Mon Sep 17 00:00:00 2001
From: Dirk Hohndel <dirk at hohndel.org>
Date: Wed, 1 Oct 2014 15:28:37 -0700
Subject: [PATCH 5/5] Aeris A300CS: add reporting of water type (fresh water /
 salt water)

Signed-off-by: Dirk Hohndel <dirk at hohndel.org>
---
 src/oceanic_atom2_parser.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c
index 3a577d2e05d8..9e3f351aa2fd 100644
--- a/src/oceanic_atom2_parser.c
+++ b/src/oceanic_atom2_parser.c
@@ -378,6 +378,18 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns
 			gasmix->oxygen = (oxygen ? oxygen / 100.0 : 0.21);
 			gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
 			break;
+		case DC_FIELD_SALINITY:
+			if (parser->model == A300CS) {
+				dc_salinity_t water = { DC_WATER_SALT, 1003 };
+				if (data[0x18] & 0x80) {
+					water.type = DC_WATER_FRESH;
+					water.density = 1000;
+				}
+				*((dc_salinity_t *) value) = water;
+			} else {
+				return DC_STATUS_UNSUPPORTED;
+			}
+			break;
 		default:
 			return DC_STATUS_UNSUPPORTED;
 		}
-- 
1.8.0.rc0.18.gf84667d



More information about the devel mailing list