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/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/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_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..f0ea4ba 100644 --- a/src/oceanic_vtpro.c +++ b/src/oceanic_vtpro.c @@ -21,6 +21,7 @@ #include // memcpy #include // malloc, free +#include #include @@ -30,8 +31,9 @@ #include "serial.h" #include "ringbuffer.h" #include "checksum.h" +#include "array.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 @@ -40,23 +42,37 @@ #define NAK 0xA5 #define END 0x51 +#define AERIS500AI 0x4151 + +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_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); -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_vtpro_device_logbook, + oceanic_common_device_profile, }; static const oceanic_common_version_t oceanic_vtpro_version[] = { @@ -98,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) @@ -156,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; @@ -174,8 +204,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; @@ -190,10 +222,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; } @@ -249,9 +281,115 @@ 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) +{ + 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; @@ -260,7 +398,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; @@ -274,6 +412,12 @@ oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, const char // Set the default values. device->port = NULL; + device->model = model; + if (model == AERIS500AI) { + device->protocol = INTR; + } else { + device->protocol = MOD; + } // Open the device. status = dc_serial_open (&device->port, context, name); @@ -311,7 +455,7 @@ oceanic_vtpro_device_open (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); @@ -339,7 +483,9 @@ oceanic_vtpro_device_open (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; @@ -431,32 +577,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; diff --git a/src/oceanic_vtpro_parser.c b/src/oceanic_vtpro_parser.c index d2d7919..859025e 100644 --- a/src/oceanic_vtpro_parser.c +++ b/src/oceanic_vtpro_parser.c @@ -31,10 +31,13 @@ #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 { dc_parser_t base; + unsigned int model; // Cached fields. unsigned int cached; unsigned int divetime; @@ -59,6 +62,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 +83,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; @@ -100,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; } @@ -153,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; @@ -165,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; @@ -204,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; @@ -212,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. @@ -238,7 +270,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; } @@ -264,7 +297,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; } @@ -312,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); 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);