From 9bae07551fb7858f450d7501c19adff42f62b244 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 19 Apr 2016 22:35:31 +0200 Subject: [PATCH 1/5] Allow custom logbook and profile functions. By adding the logbook and profile functions to the vtable, a dive computer backend can now easily replace the default implementation with a custom one, without having to duplicate the common code. --- src/oceanic_atom2.c | 26 +++++++++++++++----------- src/oceanic_common.c | 10 ++++++---- src/oceanic_common.h | 12 ++++++++++++ src/oceanic_veo250.c | 26 +++++++++++++++----------- src/oceanic_vtpro.c | 26 +++++++++++++++----------- 5 files changed, 63 insertions(+), 37 deletions(-) diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index 3623b20..323b0ec 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -32,7 +32,7 @@ #include "ringbuffer.h" #include "checksum.h" -#define ISINSTANCE(device) dc_device_isinstance((device), &oceanic_atom2_device_vtable) +#define ISINSTANCE(device) dc_device_isinstance((device), &oceanic_atom2_device_vtable.base) #define VTX 0x4557 @@ -65,15 +65,19 @@ static dc_status_t oceanic_atom2_device_read (dc_device_t *abstract, unsigned in static dc_status_t oceanic_atom2_device_write (dc_device_t *abstract, unsigned int address, const unsigned char data[], unsigned int size); static dc_status_t oceanic_atom2_device_close (dc_device_t *abstract); -static const dc_device_vtable_t oceanic_atom2_device_vtable = { - sizeof(oceanic_atom2_device_t), - DC_FAMILY_OCEANIC_ATOM2, - oceanic_common_device_set_fingerprint, /* set_fingerprint */ - oceanic_atom2_device_read, /* read */ - oceanic_atom2_device_write, /* write */ - oceanic_common_device_dump, /* dump */ - oceanic_common_device_foreach, /* foreach */ - oceanic_atom2_device_close /* close */ +static const oceanic_common_device_vtable_t oceanic_atom2_device_vtable = { + { + sizeof(oceanic_atom2_device_t), + DC_FAMILY_OCEANIC_ATOM2, + oceanic_common_device_set_fingerprint, /* set_fingerprint */ + oceanic_atom2_device_read, /* read */ + oceanic_atom2_device_write, /* write */ + oceanic_common_device_dump, /* dump */ + oceanic_common_device_foreach, /* foreach */ + oceanic_atom2_device_close /* close */ + }, + oceanic_common_device_logbook, + oceanic_common_device_profile, }; static const oceanic_common_version_t aeris_f10_version[] = { @@ -544,7 +548,7 @@ oceanic_atom2_device_open2 (dc_device_t **out, dc_context_t *context, const char return DC_STATUS_INVALIDARGS; // Allocate memory. - device = (oceanic_atom2_device_t *) dc_device_allocate (context, &oceanic_atom2_device_vtable); + device = (oceanic_atom2_device_t *) dc_device_allocate (context, &oceanic_atom2_device_vtable.base); if (device == NULL) { ERROR (context, "Failed to allocate memory."); return DC_STATUS_NOMEMORY; diff --git a/src/oceanic_common.c b/src/oceanic_common.c index b67fca6..64e0867 100644 --- a/src/oceanic_common.c +++ b/src/oceanic_common.c @@ -29,6 +29,8 @@ #include "ringbuffer.h" #include "array.h" +#define VTABLE(abstract) ((oceanic_common_device_vtable_t *) abstract->vtable) + #define RB_LOGBOOK_DISTANCE(a,b,l) ringbuffer_distance (a, b, 0, l->rb_logbook_begin, l->rb_logbook_end) #define RB_LOGBOOK_INCR(a,b,l) ringbuffer_increment (a, b, l->rb_logbook_begin, l->rb_logbook_end) @@ -187,7 +189,7 @@ oceanic_common_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) } -static dc_status_t +dc_status_t oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook) { oceanic_common_device_t *device = (oceanic_common_device_t *) abstract; @@ -408,7 +410,7 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr } -static dc_status_t +dc_status_t oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook, dc_dive_callback_t callback, void *userdata) { oceanic_common_device_t *device = (oceanic_common_device_t *) abstract; @@ -655,7 +657,7 @@ oceanic_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac } // Download the logbook ringbuffer. - rc = oceanic_common_device_logbook (abstract, &progress, logbook); + rc = VTABLE(abstract)->logbook (abstract, &progress, logbook); if (rc != DC_STATUS_SUCCESS) { dc_buffer_free (logbook); return rc; @@ -668,7 +670,7 @@ oceanic_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac } // Download the profile ringbuffer. - rc = oceanic_common_device_profile (abstract, &progress, logbook, callback, userdata); + rc = VTABLE(abstract)->profile (abstract, &progress, logbook, callback, userdata); if (rc != DC_STATUS_SUCCESS) { dc_buffer_free (logbook); return rc; diff --git a/src/oceanic_common.h b/src/oceanic_common.h index e5e53f0..7c88e54 100644 --- a/src/oceanic_common.h +++ b/src/oceanic_common.h @@ -65,6 +65,12 @@ typedef struct oceanic_common_device_t { unsigned int multipage; } oceanic_common_device_t; +typedef struct oceanic_common_device_vtable_t { + dc_device_vtable_t base; + dc_status_t (*logbook) (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook); + dc_status_t (*profile) (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook, dc_dive_callback_t callback, void *userdata); +} oceanic_common_device_vtable_t; + typedef unsigned char oceanic_common_version_t[PAGESIZE + 1]; int @@ -73,6 +79,12 @@ oceanic_common_match (const unsigned char *version, const oceanic_common_version void oceanic_common_device_init (oceanic_common_device_t *device); +dc_status_t +oceanic_common_device_logbook (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook); + +dc_status_t +oceanic_common_device_profile (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook, dc_dive_callback_t callback, void *userdata); + dc_status_t oceanic_common_device_set_fingerprint (dc_device_t *device, const unsigned char data[], unsigned int size); diff --git a/src/oceanic_veo250.c b/src/oceanic_veo250.c index 93373a7..2ffa0fd 100644 --- a/src/oceanic_veo250.c +++ b/src/oceanic_veo250.c @@ -31,7 +31,7 @@ #include "ringbuffer.h" #include "checksum.h" -#define ISINSTANCE(device) dc_device_isinstance((device), &oceanic_veo250_device_vtable) +#define ISINSTANCE(device) dc_device_isinstance((device), &oceanic_veo250_device_vtable.base) #define MAXRETRIES 2 #define MULTIPAGE 4 @@ -48,15 +48,19 @@ typedef struct oceanic_veo250_device_t { static dc_status_t oceanic_veo250_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); static dc_status_t oceanic_veo250_device_close (dc_device_t *abstract); -static const dc_device_vtable_t oceanic_veo250_device_vtable = { - sizeof(oceanic_veo250_device_t), - DC_FAMILY_OCEANIC_VEO250, - oceanic_common_device_set_fingerprint, /* set_fingerprint */ - oceanic_veo250_device_read, /* read */ - NULL, /* write */ - oceanic_common_device_dump, /* dump */ - oceanic_common_device_foreach, /* foreach */ - oceanic_veo250_device_close /* close */ +static const oceanic_common_device_vtable_t oceanic_veo250_device_vtable = { + { + sizeof(oceanic_veo250_device_t), + DC_FAMILY_OCEANIC_VEO250, + oceanic_common_device_set_fingerprint, /* set_fingerprint */ + oceanic_veo250_device_read, /* read */ + NULL, /* write */ + oceanic_common_device_dump, /* dump */ + oceanic_common_device_foreach, /* foreach */ + oceanic_veo250_device_close /* close */ + }, + oceanic_common_device_logbook, + oceanic_common_device_profile, }; static const oceanic_common_version_t oceanic_vtpro_version[] = { @@ -229,7 +233,7 @@ oceanic_veo250_device_open (dc_device_t **out, dc_context_t *context, const char return DC_STATUS_INVALIDARGS; // Allocate memory. - device = (oceanic_veo250_device_t *) dc_device_allocate (context, &oceanic_veo250_device_vtable); + device = (oceanic_veo250_device_t *) dc_device_allocate (context, &oceanic_veo250_device_vtable.base); if (device == NULL) { ERROR (context, "Failed to allocate memory."); return DC_STATUS_NOMEMORY; diff --git a/src/oceanic_vtpro.c b/src/oceanic_vtpro.c index fdd993e..bf7b3fa 100644 --- a/src/oceanic_vtpro.c +++ b/src/oceanic_vtpro.c @@ -31,7 +31,7 @@ #include "ringbuffer.h" #include "checksum.h" -#define ISINSTANCE(device) dc_device_isinstance((device), &oceanic_vtpro_device_vtable) +#define ISINSTANCE(device) dc_device_isinstance((device), &oceanic_vtpro_device_vtable.base) #define MAXRETRIES 2 #define MULTIPAGE 4 @@ -48,15 +48,19 @@ typedef struct oceanic_vtpro_device_t { static dc_status_t oceanic_vtpro_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); static dc_status_t oceanic_vtpro_device_close (dc_device_t *abstract); -static const dc_device_vtable_t oceanic_vtpro_device_vtable = { - sizeof(oceanic_vtpro_device_t), - DC_FAMILY_OCEANIC_VTPRO, - oceanic_common_device_set_fingerprint, /* set_fingerprint */ - oceanic_vtpro_device_read, /* read */ - NULL, /* write */ - oceanic_common_device_dump, /* dump */ - oceanic_common_device_foreach, /* foreach */ - oceanic_vtpro_device_close /* close */ +static const oceanic_common_device_vtable_t oceanic_vtpro_device_vtable = { + { + sizeof(oceanic_vtpro_device_t), + DC_FAMILY_OCEANIC_VTPRO, + oceanic_common_device_set_fingerprint, /* set_fingerprint */ + oceanic_vtpro_device_read, /* read */ + NULL, /* write */ + oceanic_common_device_dump, /* dump */ + oceanic_common_device_foreach, /* foreach */ + oceanic_vtpro_device_close /* close */ + }, + oceanic_common_device_logbook, + oceanic_common_device_profile, }; static const oceanic_common_version_t oceanic_vtpro_version[] = { @@ -260,7 +264,7 @@ oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, const char return DC_STATUS_INVALIDARGS; // Allocate memory. - device = (oceanic_vtpro_device_t *) dc_device_allocate (context, &oceanic_vtpro_device_vtable); + device = (oceanic_vtpro_device_t *) dc_device_allocate (context, &oceanic_vtpro_device_vtable.base); if (device == NULL) { ERROR (context, "Failed to allocate memory."); return DC_STATUS_NOMEMORY; From b41ecd0c15872e3ed3ea62e8b15c9dd714bef48c Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 21 Apr 2016 19:31:48 +0200 Subject: [PATCH 2/5] Add a parameter with the model number. --- examples/dctool_parse.c | 2 +- include/libdivecomputer/oceanic_vtpro.h | 6 ++++++ src/device.c | 2 +- src/libdivecomputer.symbols | 2 ++ src/oceanic_vtpro.c | 9 +++++++++ src/oceanic_vtpro_parser.c | 9 +++++++++ src/parser.c | 2 +- 7 files changed, 29 insertions(+), 3 deletions(-) diff --git a/examples/dctool_parse.c b/examples/dctool_parse.c index 5057566..10f4ded 100644 --- a/examples/dctool_parse.c +++ b/examples/dctool_parse.c @@ -106,7 +106,7 @@ parse (dc_buffer_t *buffer, dc_context_t *context, dc_descriptor_t *descriptor, rc = reefnet_sensusultra_parser_create (&parser, context, devtime, systime); break; case DC_FAMILY_OCEANIC_VTPRO: - rc = oceanic_vtpro_parser_create (&parser, context); + rc = oceanic_vtpro_parser_create2 (&parser, context, model); break; case DC_FAMILY_OCEANIC_VEO250: rc = oceanic_veo250_parser_create (&parser, context, model); diff --git a/include/libdivecomputer/oceanic_vtpro.h b/include/libdivecomputer/oceanic_vtpro.h index ba693f6..f6d2a64 100644 --- a/include/libdivecomputer/oceanic_vtpro.h +++ b/include/libdivecomputer/oceanic_vtpro.h @@ -33,6 +33,9 @@ extern "C" { dc_status_t oceanic_vtpro_device_open (dc_device_t **device, dc_context_t *context, const char *name); +dc_status_t +oceanic_vtpro_device_open2 (dc_device_t **device, dc_context_t *context, const char *name, unsigned int model); + dc_status_t oceanic_vtpro_device_version (dc_device_t *device, unsigned char data[], unsigned int size); @@ -42,6 +45,9 @@ oceanic_vtpro_device_keepalive (dc_device_t *device); dc_status_t oceanic_vtpro_parser_create (dc_parser_t **parser, dc_context_t *context); +dc_status_t +oceanic_vtpro_parser_create2 (dc_parser_t **parser, dc_context_t *context, unsigned int model); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/device.c b/src/device.c index 3687463..f94adf2 100644 --- a/src/device.c +++ b/src/device.c @@ -129,7 +129,7 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr rc = reefnet_sensusultra_device_open (&device, context, name); break; case DC_FAMILY_OCEANIC_VTPRO: - rc = oceanic_vtpro_device_open (&device, context, name); + rc = oceanic_vtpro_device_open2 (&device, context, name, dc_descriptor_get_model (descriptor)); break; case DC_FAMILY_OCEANIC_VEO250: rc = oceanic_veo250_device_open (&device, context, name); diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index e59888d..41ecac8 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -58,6 +58,7 @@ mares_nemo_parser_create mares_darwin_parser_create mares_iconhd_parser_create oceanic_vtpro_parser_create +oceanic_vtpro_parser_create2 oceanic_veo250_parser_create oceanic_atom2_parser_create hw_ostc_parser_create @@ -102,6 +103,7 @@ oceanic_veo250_device_open oceanic_veo250_device_version oceanic_veo250_device_keepalive oceanic_vtpro_device_open +oceanic_vtpro_device_open2 oceanic_vtpro_device_version oceanic_vtpro_device_keepalive reefnet_sensus_device_open diff --git a/src/oceanic_vtpro.c b/src/oceanic_vtpro.c index bf7b3fa..2101167 100644 --- a/src/oceanic_vtpro.c +++ b/src/oceanic_vtpro.c @@ -43,6 +43,7 @@ typedef struct oceanic_vtpro_device_t { oceanic_common_device_t base; dc_serial_t *port; + unsigned int model; } oceanic_vtpro_device_t; static dc_status_t oceanic_vtpro_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); @@ -256,6 +257,13 @@ oceanic_vtpro_calibrate (oceanic_vtpro_device_t *device) dc_status_t oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, const char *name) +{ + return oceanic_vtpro_device_open2 (out, context, name, 0); +} + + +dc_status_t +oceanic_vtpro_device_open2 (dc_device_t **out, dc_context_t *context, const char *name, unsigned int model) { dc_status_t status = DC_STATUS_SUCCESS; oceanic_vtpro_device_t *device = NULL; @@ -278,6 +286,7 @@ oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, const char // Set the default values. device->port = NULL; + device->model = model; // Open the device. status = dc_serial_open (&device->port, context, name); diff --git a/src/oceanic_vtpro_parser.c b/src/oceanic_vtpro_parser.c index d2d7919..d87f05c 100644 --- a/src/oceanic_vtpro_parser.c +++ b/src/oceanic_vtpro_parser.c @@ -35,6 +35,7 @@ typedef struct oceanic_vtpro_parser_t oceanic_vtpro_parser_t; struct oceanic_vtpro_parser_t { dc_parser_t base; + unsigned int model; // Cached fields. unsigned int cached; unsigned int divetime; @@ -59,6 +60,13 @@ static const dc_parser_vtable_t oceanic_vtpro_parser_vtable = { dc_status_t oceanic_vtpro_parser_create (dc_parser_t **out, dc_context_t *context) +{ + return oceanic_vtpro_parser_create2 (out, context, 0); +} + + +dc_status_t +oceanic_vtpro_parser_create2 (dc_parser_t **out, dc_context_t *context, unsigned int model) { oceanic_vtpro_parser_t *parser = NULL; @@ -73,6 +81,7 @@ oceanic_vtpro_parser_create (dc_parser_t **out, dc_context_t *context) } // Set the default values. + parser->model = model; parser->cached = 0; parser->divetime = 0; parser->maxdepth = 0.0; diff --git a/src/parser.c b/src/parser.c index 2498a8e..3d61404 100644 --- a/src/parser.c +++ b/src/parser.c @@ -92,7 +92,7 @@ dc_parser_new (dc_parser_t **out, dc_device_t *device) rc = reefnet_sensusultra_parser_create (&parser, context, device->clock.devtime, device->clock.systime); break; case DC_FAMILY_OCEANIC_VTPRO: - rc = oceanic_vtpro_parser_create (&parser, context); + rc = oceanic_vtpro_parser_create2 (&parser, context, device->devinfo.model); break; case DC_FAMILY_OCEANIC_VEO250: rc = oceanic_veo250_parser_create (&parser, context, device->devinfo.model); From 5824254cf58f8d2a572aebb7e5b6d627a4d2f7a5 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 22 Apr 2016 21:34:13 +0200 Subject: [PATCH 3/5] Add support for the INTR protocol variant. There are two variants of the vtpro communication protocol: the existing MOD variant and the new INTR variant. The main difference is in the initialization sequence, with two completely different response packets: MOD--OK_V2.00 INTR-OK_V1.11 The INTR variant does not appear to return useful information in the version packet. --- src/oceanic_vtpro.c | 73 ++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/src/oceanic_vtpro.c b/src/oceanic_vtpro.c index 2101167..0b37779 100644 --- a/src/oceanic_vtpro.c +++ b/src/oceanic_vtpro.c @@ -40,10 +40,16 @@ #define NAK 0xA5 #define END 0x51 +typedef enum oceanic_vtpro_protocol_t { + MOD, + INTR, +} oceanic_vtpro_protocol_t; + typedef struct oceanic_vtpro_device_t { oceanic_common_device_t base; dc_serial_t *port; unsigned int model; + oceanic_vtpro_protocol_t protocol; } oceanic_vtpro_device_t; static dc_status_t oceanic_vtpro_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); @@ -179,8 +185,10 @@ oceanic_vtpro_init (oceanic_vtpro_device_t *device) dc_device_t *abstract = (dc_device_t *) device; // Send the command to the dive computer. - unsigned char command[2] = {0xAA, 0x00}; - status = dc_serial_write (device->port, command, sizeof (command), NULL); + unsigned char command[2][2] = { + {0xAA, 0x00}, + {0x20, 0x00}}; + status = dc_serial_write (device->port, command[device->protocol], sizeof (command[device->protocol]), NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to send the command."); return status; @@ -195,10 +203,10 @@ oceanic_vtpro_init (oceanic_vtpro_device_t *device) } // Verify the answer. - const unsigned char response[13] = { - 0x4D, 0x4F, 0x44, 0x2D, 0x2D, 0x4F, 0x4B, - 0x5F, 0x56, 0x32, 0x2E, 0x30, 0x30}; - if (memcmp (answer, response, sizeof (response)) != 0) { + const unsigned char response[2][13] = { + {0x4D, 0x4F, 0x44, 0x2D, 0x2D, 0x4F, 0x4B, 0x5F, 0x56, 0x32, 0x2E, 0x30, 0x30}, + {0x49, 0x4E, 0x54, 0x52, 0x2D, 0x4F, 0x4B, 0x5F, 0x56, 0x31, 0x2E, 0x31, 0x31}}; + if (memcmp (answer, response[device->protocol], sizeof (response[device->protocol])) != 0) { ERROR (abstract->context, "Unexpected answer byte(s)."); return DC_STATUS_PROTOCOL; } @@ -287,6 +295,7 @@ oceanic_vtpro_device_open2 (dc_device_t **out, dc_context_t *context, const char // Set the default values. device->port = NULL; device->model = model; + device->protocol = MOD; // Open the device. status = dc_serial_open (&device->port, context, name); @@ -324,7 +333,7 @@ oceanic_vtpro_device_open2 (dc_device_t **out, dc_context_t *context, const char } // Give the interface 100 ms to settle and draw power up. - dc_serial_sleep (device->port, 100); + dc_serial_sleep (device->port, device->protocol == MOD ? 100 : 1000); // Make sure everything is in a sane state. dc_serial_purge (device->port, DC_DIRECTION_ALL); @@ -444,32 +453,36 @@ oceanic_vtpro_device_version (dc_device_t *abstract, unsigned char data[], unsig return DC_STATUS_PROTOCOL; } - // Obtain the device identification string. This string is - // split over two packets, but we join both parts again. + if (device->protocol == MOD) { + // Obtain the device identification string. This string is + // split over two packets, but we join both parts again. + for (unsigned int i = 0; i < 2; ++i) { + unsigned char command[4] = {0x72, 0x03, i * 0x10, 0x00}; + unsigned char answer[PAGESIZE / 2 + 2] = {0}; + rc = oceanic_vtpro_transfer (device, command, sizeof (command), answer, sizeof (answer)); + if (rc != DC_STATUS_SUCCESS) + return rc; - for (unsigned int i = 0; i < 2; ++i) { - unsigned char command[4] = {0x72, 0x03, i * 0x10, 0x00}; - unsigned char answer[PAGESIZE / 2 + 2] = {0}; - rc = oceanic_vtpro_transfer (device, command, sizeof (command), answer, sizeof (answer)); - if (rc != DC_STATUS_SUCCESS) - return rc; + // Verify the checksum of the answer. + unsigned char crc = answer[PAGESIZE / 2]; + unsigned char ccrc = checksum_add_uint4 (answer, PAGESIZE / 2, 0x00); + if (crc != ccrc) { + ERROR (abstract->context, "Unexpected answer checksum."); + return DC_STATUS_PROTOCOL; + } - // Verify the checksum of the answer. - unsigned char crc = answer[PAGESIZE / 2]; - unsigned char ccrc = checksum_add_uint4 (answer, PAGESIZE / 2, 0x00); - if (crc != ccrc) { - ERROR (abstract->context, "Unexpected answer checksum."); - return DC_STATUS_PROTOCOL; + // Verify the last byte of the answer. + if (answer[PAGESIZE / 2 + 1] != END) { + ERROR (abstract->context, "Unexpected answer byte."); + return DC_STATUS_PROTOCOL; + } + + // Append the answer to the output buffer. + memcpy (data + i * PAGESIZE / 2, answer, PAGESIZE / 2); } - - // Verify the last byte of the answer. - if (answer[PAGESIZE / 2 + 1] != END) { - ERROR (abstract->context, "Unexpected answer byte."); - return DC_STATUS_PROTOCOL; - } - - // Append the answer to the output buffer. - memcpy (data + i * PAGESIZE / 2, answer, PAGESIZE / 2); + } else { + // Return an empty device identification string. + memset (data, 0x00, PAGESIZE); } return DC_STATUS_SUCCESS; From 9c251b68142e3b863bec6d7751ce358356ff28e7 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 29 Apr 2016 20:11:03 +0200 Subject: [PATCH 4/5] Ignore samples with all 0xFF bytes. --- src/oceanic_vtpro_parser.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/oceanic_vtpro_parser.c b/src/oceanic_vtpro_parser.c index d87f05c..89d984d 100644 --- a/src/oceanic_vtpro_parser.c +++ b/src/oceanic_vtpro_parser.c @@ -247,7 +247,8 @@ oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ dc_sample_value_t sample = {0}; // Ignore empty samples. - if (array_isequal (data + offset, PAGESIZE / 2, 0x00)) { + if (array_isequal (data + offset, PAGESIZE / 2, 0x00) || + array_isequal (data + offset, PAGESIZE / 2, 0xFF)) { offset += PAGESIZE / 2; continue; } @@ -273,7 +274,8 @@ oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ unsigned int idx = offset + PAGESIZE / 2 ; while (idx + PAGESIZE / 2 <= size - PAGESIZE) { // Ignore empty samples. - if (array_isequal (data + idx, PAGESIZE / 2, 0x00)) { + if (array_isequal (data + idx, PAGESIZE / 2, 0x00) || + array_isequal (data + idx, PAGESIZE / 2, 0xFF)) { idx += PAGESIZE / 2; continue; } From aaa7fbe08dadcd66a7bc3257a2d6e53025df4286 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 15 Jul 2015 21:38:01 +0200 Subject: [PATCH 5/5] Add support for the Aeris 500AI. The Aeris 500AI is quite different from the other vtpro compatible models. First, it uses the INTR protocol variant. Next, it doesn't appear to have a logbook ringbuffer. Instead it supports a new read logbook index command (0x52) that returns the logbook entries. This requires a custom implementation of the logbook function. --- src/descriptor.c | 1 + src/oceanic_vtpro.c | 140 ++++++++++++++++++++++++++++++++++--- src/oceanic_vtpro_parser.c | 95 +++++++++++++++++-------- 3 files changed, 197 insertions(+), 39 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index b774062..86f8daf 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -121,6 +121,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2}, {"Reefnet", "Sensus Ultra", DC_FAMILY_REEFNET_SENSUSULTRA, 3}, /* Oceanic VT Pro */ + {"Aeris", "500 AI", DC_FAMILY_OCEANIC_VTPRO, 0x4151}, {"Oceanic", "Versa Pro", DC_FAMILY_OCEANIC_VTPRO, 0x4155}, {"Aeris", "Atmos 2", DC_FAMILY_OCEANIC_VTPRO, 0x4158}, {"Oceanic", "Pro Plus 2", DC_FAMILY_OCEANIC_VTPRO, 0x4159}, diff --git a/src/oceanic_vtpro.c b/src/oceanic_vtpro.c index 0b37779..f0ea4ba 100644 --- a/src/oceanic_vtpro.c +++ b/src/oceanic_vtpro.c @@ -21,6 +21,7 @@ #include // memcpy #include // malloc, free +#include #include @@ -30,6 +31,7 @@ #include "serial.h" #include "ringbuffer.h" #include "checksum.h" +#include "array.h" #define ISINSTANCE(device) dc_device_isinstance((device), &oceanic_vtpro_device_vtable.base) @@ -40,6 +42,8 @@ #define NAK 0xA5 #define END 0x51 +#define AERIS500AI 0x4151 + typedef enum oceanic_vtpro_protocol_t { MOD, INTR, @@ -52,6 +56,7 @@ typedef struct oceanic_vtpro_device_t { oceanic_vtpro_protocol_t protocol; } oceanic_vtpro_device_t; +static dc_status_t oceanic_vtpro_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook); static dc_status_t oceanic_vtpro_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); static dc_status_t oceanic_vtpro_device_close (dc_device_t *abstract); @@ -66,7 +71,7 @@ static const oceanic_common_device_vtable_t oceanic_vtpro_device_vtable = { oceanic_common_device_foreach, /* foreach */ oceanic_vtpro_device_close /* close */ }, - oceanic_common_device_logbook, + oceanic_vtpro_device_logbook, oceanic_common_device_profile, }; @@ -109,6 +114,18 @@ static const oceanic_common_layout_t oceanic_wisdom_layout = { 0 /* pt_mode_logbook */ }; +static const oceanic_common_layout_t aeris_500ai_layout = { + 0x20000, /* memsize */ + 0x0000, /* cf_devinfo */ + 0x0110, /* cf_pointers */ + 0x0200, /* rb_logbook_begin */ + 0x0200, /* rb_logbook_end */ + 8, /* rb_logbook_entry_size */ + 0x00200, /* rb_profile_begin */ + 0x20000, /* rb_profile_end */ + 0, /* pt_mode_global */ + 1 /* pt_mode_logbook */ +}; static dc_status_t oceanic_vtpro_send (oceanic_vtpro_device_t *device, const unsigned char command[], unsigned int csize) @@ -167,11 +184,13 @@ oceanic_vtpro_transfer (oceanic_vtpro_device_t *device, const unsigned char comm return rc; } - // Receive the answer of the dive computer. - status = dc_serial_read (device->port, answer, asize, NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the answer."); - return status; + if (asize) { + // Receive the answer of the dive computer. + status = dc_serial_read (device->port, answer, asize, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the answer."); + return status; + } } return DC_STATUS_SUCCESS; @@ -262,6 +281,105 @@ oceanic_vtpro_calibrate (oceanic_vtpro_device_t *device) return DC_STATUS_SUCCESS; } +static dc_status_t +oceanic_aeris500ai_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + oceanic_vtpro_device_t *device = (oceanic_vtpro_device_t *) abstract; + + assert (device != NULL); + assert (device->base.layout != NULL); + assert (device->base.layout->rb_logbook_entry_size == PAGESIZE / 2); + assert (device->base.layout->rb_logbook_begin == device->base.layout->rb_logbook_end); + assert (progress != NULL); + + const oceanic_common_layout_t *layout = device->base.layout; + + // Erase the buffer. + if (!dc_buffer_clear (logbook)) + return DC_STATUS_NOMEMORY; + + // Read the pointer data. + unsigned char pointers[PAGESIZE] = {0}; + rc = oceanic_vtpro_device_read (abstract, layout->cf_pointers, pointers, sizeof (pointers)); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the memory page."); + return rc; + } + + // Get the logbook pointers. + unsigned int last = pointers[0x03]; + + // Update and emit a progress event. + progress->current += PAGESIZE; + progress->maximum += PAGESIZE + (last + 1) * PAGESIZE / 2; + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + + // Allocate memory for the logbook entries. + if (!dc_buffer_reserve (logbook, (last + 1) * PAGESIZE / 2)) + return DC_STATUS_NOMEMORY; + + // Send the logbook index command. + unsigned char command[] = {0x52, + (last >> 8) & 0xFF, // high + (last ) & 0xFF, // low + 0x00}; + rc = oceanic_vtpro_transfer (device, command, sizeof (command), NULL, 0); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the logbook index command."); + return rc; + } + + // Read the logbook index. + for (unsigned int i = 0; i < last + 1; ++i) { + // Receive the answer of the dive computer. + unsigned char answer[PAGESIZE / 2 + 1] = {0}; + rc = dc_serial_read (device->port, answer, sizeof(answer), NULL); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the answer."); + return rc; + } + + // Verify the checksum of the answer. + unsigned char crc = answer[PAGESIZE / 2]; + unsigned char ccrc = checksum_add_uint4 (answer, PAGESIZE / 2, 0x00); + if (crc != ccrc) { + ERROR (abstract->context, "Unexpected answer checksum."); + return DC_STATUS_PROTOCOL; + } + + // Update and emit a progress event. + progress->current += PAGESIZE / 2; + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + + // Ignore uninitialized entries. + if (array_isequal (answer, PAGESIZE / 2, 0xFF)) { + WARNING (abstract->context, "Uninitialized logbook entries detected!"); + continue; + } + + // Compare the fingerprint to identify previously downloaded entries. + if (memcmp (answer, device->base.fingerprint, PAGESIZE / 2) == 0) { + dc_buffer_clear (logbook); + } else { + dc_buffer_append (logbook, answer, PAGESIZE / 2); + } + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +oceanic_vtpro_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook) +{ + oceanic_vtpro_device_t *device = (oceanic_vtpro_device_t *) abstract; + + if (device->model == AERIS500AI) { + return oceanic_aeris500ai_device_logbook (abstract, progress, logbook); + } else { + return oceanic_common_device_logbook (abstract, progress, logbook); + } +} dc_status_t oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, const char *name) @@ -295,7 +413,11 @@ oceanic_vtpro_device_open2 (dc_device_t **out, dc_context_t *context, const char // Set the default values. device->port = NULL; device->model = model; - device->protocol = MOD; + if (model == AERIS500AI) { + device->protocol = INTR; + } else { + device->protocol = MOD; + } // Open the device. status = dc_serial_open (&device->port, context, name); @@ -361,7 +483,9 @@ oceanic_vtpro_device_open2 (dc_device_t **out, dc_context_t *context, const char } // Override the base class values. - if (OCEANIC_COMMON_MATCH (device->base.version, oceanic_wisdom_version)) { + if (model == AERIS500AI) { + device->base.layout = &aeris_500ai_layout; + } else if (OCEANIC_COMMON_MATCH (device->base.version, oceanic_wisdom_version)) { device->base.layout = &oceanic_wisdom_layout; } else { device->base.layout = &oceanic_vtpro_layout; diff --git a/src/oceanic_vtpro_parser.c b/src/oceanic_vtpro_parser.c index 89d984d..859025e 100644 --- a/src/oceanic_vtpro_parser.c +++ b/src/oceanic_vtpro_parser.c @@ -31,6 +31,8 @@ #define ISINSTANCE(parser) dc_parser_isinstance((parser), &oceanic_vtpro_parser_vtable) +#define AERIS500AI 0x4151 + typedef struct oceanic_vtpro_parser_t oceanic_vtpro_parser_t; struct oceanic_vtpro_parser_t { @@ -109,27 +111,41 @@ oceanic_vtpro_parser_set_data (dc_parser_t *abstract, const unsigned char *data, static dc_status_t oceanic_vtpro_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) { + oceanic_vtpro_parser_t *parser = (oceanic_vtpro_parser_t *) abstract; + if (abstract->size < 8) return DC_STATUS_DATAFORMAT; const unsigned char *p = abstract->data; if (datetime) { - // The logbook entry can only store the last digit of the year field, - // but the full year is also available in the dive header. - if (abstract->size < 40) - datetime->year = bcd2dec (p[4] & 0x0F) + 2000; - else - datetime->year = bcd2dec (((p[32 + 3] & 0xC0) >> 2) + ((p[32 + 2] & 0xF0) >> 4)) + 2000; - datetime->month = (p[4] & 0xF0) >> 4; - datetime->day = bcd2dec (p[3]); - datetime->hour = bcd2dec (p[1] & 0x7F); + // AM/PM bit of the 12-hour clock. + unsigned int pm = 0; + + if (parser->model == AERIS500AI) { + datetime->year = (p[2] & 0x0F) + 1999; + datetime->month = (p[3] & 0xF0) >> 4; + datetime->day = ((p[2] & 0xF0) >> 4) | ((p[3] & 0x02) << 3); + datetime->hour = bcd2dec (p[1] & 0x0F) + 10 * (p[3] & 0x01); + pm = p[3] & 0x08; + } else { + // The logbook entry can only store the last digit of the year field, + // but the full year is also available in the dive header. + if (abstract->size < 40) + datetime->year = bcd2dec (p[4] & 0x0F) + 2000; + else + datetime->year = bcd2dec (((p[32 + 3] & 0xC0) >> 2) + ((p[32 + 2] & 0xF0) >> 4)) + 2000; + datetime->month = (p[4] & 0xF0) >> 4; + datetime->day = bcd2dec (p[3]); + datetime->hour = bcd2dec (p[1] & 0x7F); + pm = p[1] & 0x80; + } datetime->minute = bcd2dec (p[0]); datetime->second = 0; // Convert to a 24-hour clock. datetime->hour %= 12; - if (p[1] & 0x80) + if (pm) datetime->hour += 12; } @@ -162,8 +178,17 @@ oceanic_vtpro_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns unsigned int footer = size - PAGESIZE; + unsigned int oxygen = 0; + unsigned int maxdepth = 0; unsigned int beginpressure = array_uint16_le(data + 0x26) & 0x0FFF; unsigned int endpressure = array_uint16_le(data + footer + 0x05) & 0x0FFF; + if (parser->model == AERIS500AI) { + oxygen = (array_uint16_le(data + footer + 2) & 0x0FF0) >> 4; + maxdepth = data[footer + 1]; + } else { + oxygen = data[footer + 3]; + maxdepth = array_uint16_le(data + footer + 0) & 0x0FFF; + } dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_tank_t *tank = (dc_tank_t *) value; @@ -174,15 +199,15 @@ oceanic_vtpro_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns *((unsigned int *) value) = parser->divetime; break; case DC_FIELD_MAXDEPTH: - *((double *) value) = (data[footer + 0] + ((data[footer + 1] & 0x0F) << 8)) * FEET; + *((double *) value) = maxdepth * FEET; break; case DC_FIELD_GASMIX_COUNT: *((unsigned int *) value) = 1; break; case DC_FIELD_GASMIX: gasmix->helium = 0.0; - if (data[footer + 3]) - gasmix->oxygen = data[footer + 3] / 100.0; + if (oxygen) + gasmix->oxygen = oxygen / 100.0; else gasmix->oxygen = 0.21; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; @@ -213,6 +238,8 @@ oceanic_vtpro_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns static dc_status_t oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) { + oceanic_vtpro_parser_t *parser = (oceanic_vtpro_parser_t *) abstract; + const unsigned char *data = abstract->data; unsigned int size = abstract->size; @@ -221,22 +248,18 @@ oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ unsigned int time = 0; unsigned int interval = 0; - switch ((data[0x27] >> 4) & 0x07) { - case 0: - interval = 2; - break; - case 1: - interval = 15; - break; - case 2: - interval = 30; - break; - case 3: - interval = 60; - break; - default: - interval = 0; - break; + if (parser->model == AERIS500AI) { + const unsigned int intervals[] = {2, 5, 10, 15, 20, 25, 30}; + unsigned int samplerate = (data[0x27] >> 4); + if (samplerate >= 3 && samplerate <= 9) { + interval = intervals[samplerate - 3]; + } + } else { + const unsigned int intervals[] = {2, 15, 30, 60}; + unsigned int samplerate = (data[0x27] >> 4) & 0x07; + if (samplerate <= 3) { + interval = intervals[samplerate]; + } } // Initialize the state for the timestamp processing. @@ -323,12 +346,22 @@ oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ if (callback) callback (DC_SAMPLE_VENDOR, sample, userdata); // Depth (ft) - unsigned int depth = data[offset + 3]; + unsigned int depth = 0; + if (parser->model == AERIS500AI) { + depth = (array_uint16_le(data + offset + 2) & 0x0FF0) >> 4; + } else { + depth = data[offset + 3]; + } sample.depth = depth * FEET; if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); // Temperature (°F) - unsigned int temperature = data[offset + 6]; + unsigned int temperature = 0; + if (parser->model == AERIS500AI) { + temperature = (array_uint16_le(data + offset + 6) & 0x0FF0) >> 4; + } else { + temperature = data[offset + 6]; + } sample.temperature = (temperature - 32.0) * (5.0 / 9.0); if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);