diff --git a/src/descriptor.c b/src/descriptor.c index 403b67a..617c279 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 17e1958..733c547 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -36,6 +36,7 @@ #define MAXRETRIES 2 #define MAXDELAY 16 +#define INVALID 0xFFFFFFFF #define EXITCODE(rc) \ ( \ @@ -49,6 +50,9 @@ typedef struct oceanic_atom2_device_t { oceanic_common_device_t base; serial_t *port; unsigned int delay; + unsigned int bigpage; + unsigned char cache[256]; + unsigned int cached; } 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); @@ -149,6 +153,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 */ @@ -331,6 +339,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 */ + 0x3FE00, /* 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) { @@ -369,7 +390,7 @@ oceanic_atom2_send (oceanic_atom2_device_t *device, const unsigned char command[ static dc_status_t -oceanic_atom2_transfer (oceanic_atom2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize) +oceanic_atom2_transfer (oceanic_atom2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int crc_size) { dc_device_t *abstract = (dc_device_t *) device; @@ -407,8 +428,14 @@ oceanic_atom2_transfer (oceanic_atom2_device_t *device, const unsigned char comm } // Verify the checksum of the answer. - unsigned char crc = answer[asize - 1]; - unsigned char ccrc = checksum_add_uint8 (answer, asize - 1, 0x00); + unsigned short crc, ccrc; + if (crc_size == 2) { + crc = array_uint16_le (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 +478,9 @@ 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 + device->cached = INVALID; + memset(device->cache, 0, sizeof(device->cache)); // Open the device. int rc = serial_open (&device->port, context, name); @@ -480,6 +510,10 @@ 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); + // Set the DTR/RTS lines. + 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); @@ -526,6 +560,9 @@ 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; } else { device->base.layout = &oceanic_default_layout; } @@ -567,7 +604,7 @@ oceanic_atom2_device_keepalive (dc_device_t *abstract) // Send the command to the dive computer. unsigned char command[4] = {0x91, 0x05, 0xA5, 0x00}; - dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), NULL, 0); + dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), NULL, 0, 0); if (rc != DC_STATUS_SUCCESS) return rc; @@ -588,7 +625,7 @@ oceanic_atom2_device_version (dc_device_t *abstract, unsigned char data[], unsig unsigned char answer[PAGESIZE + 1] = {0}; unsigned char command[2] = {0x84, 0x00}; - 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, sizeof (answer), 1); if (rc != DC_STATUS_SUCCESS) return rc; @@ -607,24 +644,59 @@ oceanic_atom2_device_read (dc_device_t *abstract, unsigned int address, unsigned (size % PAGESIZE != 0)) return DC_STATUS_INVALIDARGS; + // Pick the correct read command and number of checksum bytes. + unsigned char read_cmd = 0x00; + unsigned int crc_size = 0; + switch (device->bigpage) { + case 1: + read_cmd = 0xB1; + crc_size = 1; + break; + case 8: + read_cmd = 0xB4; + crc_size = 1; + break; + case 16: + read_cmd = 0xB8; + crc_size = 2; + break; + default: + return DC_STATUS_INVALIDARGS; + } + + // Pick the best pagesize to use. + unsigned int pagesize = device->bigpage * PAGESIZE; + 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, - (number >> 8) & 0xFF, // high - (number ) & 0xFF, // low - 0}; - dc_status_t rc = oceanic_atom2_transfer (device, command, sizeof (command), answer, sizeof (answer)); - if (rc != DC_STATUS_SUCCESS) - return rc; + unsigned int page = address / pagesize; + if (page != device->cached) { + // Read the package. + unsigned int number = page * device->bigpage; // This is always PAGESIZE, even in big page mode. + unsigned char answer[256 + 2] = {0}; // Maximum we support for the known commands. + 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, pagesize + crc_size, crc_size); + if (rc != DC_STATUS_SUCCESS) + return rc; - memcpy (data, answer, PAGESIZE); + // Cache the page. + memcpy (device->cache, answer, pagesize); + device->cached = page; + } - nbytes += PAGESIZE; - address += PAGESIZE; - data += PAGESIZE; + unsigned int offset = address % pagesize; + unsigned int length = pagesize - offset; + if (nbytes + length > size) + length = size - nbytes; + + memcpy (data, device->cache + offset, length); + + nbytes += length; + address += length; + data += length; } return DC_STATUS_SUCCESS; @@ -640,6 +712,9 @@ oceanic_atom2_device_write (dc_device_t *abstract, unsigned int address, const u (size % PAGESIZE != 0)) return DC_STATUS_INVALIDARGS; + // Invalidate the cache. + device->cached = INVALID; + unsigned int nbytes = 0; while (nbytes < size) { // Prepare to write the package. @@ -648,7 +723,7 @@ oceanic_atom2_device_write (dc_device_t *abstract, unsigned int address, const u (number >> 8) & 0xFF, // high (number ) & 0xFF, // low 0x00}; - dc_status_t rc = oceanic_atom2_transfer (device, prepare, sizeof (prepare), NULL, 0); + dc_status_t rc = oceanic_atom2_transfer (device, prepare, sizeof (prepare), NULL, 0, 0); if (rc != DC_STATUS_SUCCESS) return rc; @@ -656,7 +731,7 @@ oceanic_atom2_device_write (dc_device_t *abstract, unsigned int address, const u unsigned char command[PAGESIZE + 2] = {0}; memcpy (command, data, PAGESIZE); command[PAGESIZE] = checksum_add_uint8 (command, PAGESIZE, 0x00); - rc = oceanic_atom2_transfer (device, command, sizeof (command), NULL, 0); + rc = oceanic_atom2_transfer (device, command, sizeof (command), NULL, 0, 0); if (rc != DC_STATUS_SUCCESS) return rc; diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index 423c5a5..8dedf3f 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,13 @@ 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]); + break; default: datetime->year = bcd2dec (((p[3] & 0xC0) >> 2) + (p[4] & 0x0F)) + 2000; datetime->month = (p[4] & 0xF0) >> 4; @@ -325,6 +333,7 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns } dc_gasmix_t *gasmix = (dc_gasmix_t *) value; + dc_salinity_t *water = (dc_salinity_t *) value; unsigned int oxygen = 0; unsigned int helium = 0; @@ -344,20 +353,34 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns *((double *) value) = array_uint16_le (data + footer + 4) / 16.0 * FEET; break; case DC_FIELD_GASMIX_COUNT: - if (parser->model == DATAMASK || parser->model == COMPUMASK) + 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) { *((unsigned int *) value) = 4; - else if (parser->model == TX1) + } else if (parser->model == TX1) { *((unsigned int *) value) = 6; - else + } else if (parser->model == A300CS) { + if (data[0x39] & 0x04) { + *((unsigned int *) value) = 1; + } else if (data[0x39] & 0x08) { + *((unsigned int *) value) = 2; + } else if (data[0x39] & 0x10) { + *((unsigned int *) value) = 3; + } else { + *((unsigned int *) value) = 4; + } + } else { *((unsigned int *) value) = 3; + } break; case DC_FIELD_GASMIX: if (parser->model == DATAMASK || parser->model == COMPUMASK) { 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]; @@ -368,6 +391,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) { + if (data[0x18] & 0x80) { + water->type = DC_WATER_FRESH; + } else { + water->type = DC_WATER_SALT; + } + water->density = 0.0; + } else { + return DC_STATUS_UNSUPPORTED; + } + break; default: return DC_STATUS_UNSUPPORTED; } @@ -404,6 +439,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) @@ -415,7 +452,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) { + unsigned int idx = 0x17; + if (parser->model == A300CS) + idx = 0x1f; + switch (data[idx] & 0x03) { case 0: interval = 2; break; @@ -434,7 +474,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; @@ -459,7 +499,10 @@ 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); + unsigned int idx = 2; + if (parser->model == A300CS) + idx = 16; + pressure = data[header + idx] + (data[header + idx + 1] << 8); if (pressure == 10000) have_pressure = 0; } @@ -511,6 +554,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; @@ -552,6 +599,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) @@ -584,7 +633,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]; @@ -609,6 +658,22 @@ 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) { + unsigned int deco = (data[offset + 15] & 0x70) >> 4; + if (deco) { + sample.deco.type = DC_DECO_DECOSTOP; + sample.deco.depth = deco * 10 * FEET; + } else { + sample.deco.type = DC_DECO_NDL; + sample.deco.depth = 0.0; + } + sample.deco.time = array_uint16_le(data + offset + 6) & 0x03FF; + if (callback) callback (DC_SAMPLE_DECO, sample, userdata); + } + complete = 1; }