diff --git a/examples/universal.c b/examples/universal.c index 0d90801..0f85592 100644 --- a/examples/universal.c +++ b/examples/universal.c @@ -93,6 +93,7 @@ static const backend_table_t g_backends[] = { {"iconhd", DC_FAMILY_MARES_ICONHD}, {"ostc", DC_FAMILY_HW_OSTC}, {"frog", DC_FAMILY_HW_FROG}, + {"ostc3", DC_FAMILY_HW_OSTC3}, {"edy", DC_FAMILY_CRESSI_EDY}, {"leonardo", DC_FAMILY_CRESSI_LEONARDO}, {"n2ition3", DC_FAMILY_ZEAGLE_N2ITION3}, diff --git a/include/libdivecomputer/Makefile.am b/include/libdivecomputer/Makefile.am index a3862b7..9a68ffe 100644 --- a/include/libdivecomputer/Makefile.am +++ b/include/libdivecomputer/Makefile.am @@ -36,6 +36,7 @@ libdivecomputer_HEADERS = \ hw.h \ hw_ostc.h \ hw_frog.h \ + hw_ostc3.h \ cressi.h \ cressi_edy.h \ cressi_leonardo.h \ diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index 6434d67..e8a58e3 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -69,6 +69,7 @@ typedef enum dc_family_t { /* Heinrichs Weikamp */ DC_FAMILY_HW_OSTC = (6 << 16), DC_FAMILY_HW_FROG, + DC_FAMILY_HW_OSTC3, /* Cressi */ DC_FAMILY_CRESSI_EDY = (7 << 16), DC_FAMILY_CRESSI_LEONARDO, diff --git a/include/libdivecomputer/hw.h b/include/libdivecomputer/hw.h index c16c3e1..2ac6e29 100644 --- a/include/libdivecomputer/hw.h +++ b/include/libdivecomputer/hw.h @@ -24,5 +24,6 @@ #include "hw_ostc.h" #include "hw_frog.h" +#include "hw_ostc3.h" #endif /* HW_H */ diff --git a/include/libdivecomputer/hw_ostc3.h b/include/libdivecomputer/hw_ostc3.h new file mode 100644 index 0000000..021fd2f --- /dev/null +++ b/include/libdivecomputer/hw_ostc3.h @@ -0,0 +1,55 @@ +/* + * libdivecomputer + * + * Copyright (C) 2013 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef HW_OSTC3_H +#define HW_OSTC3_H + +#include "context.h" +#include "device.h" +#include "parser.h" +#include "buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define HW_OSTC3_DISPLAY_SIZE 16 +#define HW_OSTC3_CUSTOMTEXT_SIZE 60 + +dc_status_t +hw_ostc3_device_open (dc_device_t **device, dc_context_t *context, const char *name); + +dc_status_t +hw_ostc3_device_version (dc_device_t *device, unsigned char data[], unsigned int size); + +dc_status_t +hw_ostc3_device_clock (dc_device_t *device, const dc_datetime_t *datetime); + +dc_status_t +hw_ostc3_device_display (dc_device_t *device, const char *text); + +dc_status_t +hw_ostc3_device_customtext (dc_device_t *device, const char *text); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* HW_OSTC3_H */ diff --git a/src/Makefile.am b/src/Makefile.am index 22d7f42..3e1417c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -41,6 +41,7 @@ libdivecomputer_la_SOURCES = \ mares_iconhd.c mares_iconhd_parser.c \ hw_ostc.c hw_ostc_parser.c \ hw_frog.c \ + hw_ostc3.c \ cressi_edy.c cressi_edy_parser.c \ cressi_leonardo.c cressi_leonardo_parser.c \ zeagle_n2ition3.c \ diff --git a/src/descriptor.c b/src/descriptor.c index 132dce3..1f57af2 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -161,7 +161,8 @@ static const dc_descriptor_t g_descriptors[] = { {"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0}, {"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1}, {"Heinrichs Weikamp", "OSTC 2N", DC_FAMILY_HW_OSTC, 2}, - {"Heinrichs Weikamp", "Frog", DC_FAMILY_HW_FROG, 0}, + {"Heinrichs Weikamp", "Frog", DC_FAMILY_HW_FROG, 0}, + {"Heinrichs Weikamp", "OSTC 3", DC_FAMILY_HW_OSTC3, 0}, /* Cressi Edy */ {"Cressi", "Edy", DC_FAMILY_CRESSI_EDY, 0}, /* Cressi Leonardo */ diff --git a/src/device.c b/src/device.c index 286dbc6..0fa4e7e 100644 --- a/src/device.c +++ b/src/device.c @@ -126,6 +126,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_HW_FROG: rc = hw_frog_device_open (&device, context, name); break; + case DC_FAMILY_HW_OSTC3: + rc = hw_ostc3_device_open (&device, context, name); + break; case DC_FAMILY_CRESSI_EDY: rc = cressi_edy_device_open (&device, context, name); break; diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c new file mode 100644 index 0000000..3b19c03 --- /dev/null +++ b/src/hw_ostc3.c @@ -0,0 +1,535 @@ +/* + * libdivecomputer + * + * Copyright (C) 2013 Jef Driesen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include // memcmp, memcpy +#include // malloc, free + +#include + +#include "context-private.h" +#include "device-private.h" +#include "serial.h" +#include "checksum.h" +#include "ringbuffer.h" +#include "array.h" + +#define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc3_device_vtable) + +#define EXITCODE(rc) \ +( \ + rc == -1 ? DC_STATUS_IO : DC_STATUS_TIMEOUT \ +) + +#define SZ_DISPLAY 16 +#define SZ_CUSTOMTEXT 60 +#define SZ_VERSION (SZ_CUSTOMTEXT + 4) +#define SZ_MEMORY 0x200000 + +#define RB_LOGBOOK_SIZE 256 +#define RB_LOGBOOK_COUNT 256 + +#define READY 0x4D +#define HEADER 0x61 +#define CLOCK 0x62 +#define CUSTOMTEXT 0x63 +#define DIVE 0x66 +#define IDENTITY 0x69 +#define DISPLAY 0x6E +#define INIT 0xBB +#define EXIT 0xFF + +typedef struct hw_ostc3_device_t { + dc_device_t base; + serial_t *port; + unsigned char fingerprint[5]; +} hw_ostc3_device_t; + +static dc_status_t hw_ostc3_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); +static dc_status_t hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t hw_ostc3_device_close (dc_device_t *abstract); + +static const dc_device_vtable_t hw_ostc3_device_vtable = { + DC_FAMILY_HW_OSTC3, + hw_ostc3_device_set_fingerprint, /* set_fingerprint */ + NULL, /* read */ + NULL, /* write */ + NULL, /* dump */ + hw_ostc3_device_foreach, /* foreach */ + hw_ostc3_device_close /* close */ +}; + + +static int +hw_ostc3_strncpy (unsigned char *data, unsigned int size, const char *text) +{ + // Check the maximum length. + size_t length = (text ? strlen (text) : 0); + if (length > size) { + return -1; + } + + // Copy the text. + if (length) + memcpy (data, text, length); + + // Pad with spaces. + memset (data + length, 0x20, size - length); + + return 0; +} + + +static dc_status_t +hw_ostc3_transfer (hw_ostc3_device_t *device, + dc_event_progress_t *progress, + unsigned char cmd, + const unsigned char input[], + unsigned int isize, + unsigned char output[], + unsigned int osize) +{ + dc_device_t *abstract = (dc_device_t *) device; + + // Send the command. + unsigned char command[1] = {cmd}; + int n = serial_write (device->port, command, sizeof (command)); + if (n != sizeof (command)) { + ERROR (abstract->context, "Failed to send the command."); + return EXITCODE (n); + } + + // Read the echo. + unsigned char echo[1] = {0}; + n = serial_read (device->port, echo, sizeof (echo)); + if (n != sizeof (echo)) { + ERROR (abstract->context, "Failed to receive the echo."); + return EXITCODE (n); + } + + // Verify the echo. + if (memcmp (echo, command, sizeof (command)) != 0) { + ERROR (abstract->context, "Unexpected echo."); + return DC_STATUS_PROTOCOL; + } + + if (input) { + // Send the input data packet. + n = serial_write (device->port, input, isize); + if (n != isize) { + ERROR (abstract->context, "Failed to send the data packet."); + return EXITCODE (n); + } + } + + if (output) { + unsigned int nbytes = 0; + while (nbytes < osize) { + // Set the minimum packet size. + unsigned int len = 1024; + + // Increase the packet size if more data is immediately available. + int available = serial_get_received (device->port); + if (available > len) + len = available; + + // Limit the packet size to the total size. + if (nbytes + len > osize) + len = osize - nbytes; + + // Read the packet. + n = serial_read (device->port, output + nbytes, len); + if (n != len) { + ERROR (abstract->context, "Failed to receive the answer."); + return EXITCODE (n); + } + + // Update and emit a progress event. + if (progress) { + progress->current += len; + device_event_emit ((dc_device_t *) device, DC_EVENT_PROGRESS, progress); + } + + nbytes += len; + } + } + + if (cmd != EXIT) { + // Read the ready byte. + unsigned char ready[1] = {0}; + n = serial_read (device->port, ready, sizeof (ready)); + if (n != sizeof (ready)) { + ERROR (abstract->context, "Failed to receive the ready byte."); + return EXITCODE (n); + } + + // Verify the ready byte. + if (ready[0] != READY) { + ERROR (abstract->context, "Unexpected ready byte."); + return DC_STATUS_PROTOCOL; + } + } + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, const char *name) +{ + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + hw_ostc3_device_t *device = (hw_ostc3_device_t *) malloc (sizeof (hw_ostc3_device_t)); + if (device == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Initialize the base class. + device_init (&device->base, context, &hw_ostc3_device_vtable); + + // Set the default values. + device->port = NULL; + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + // Open the device. + int rc = serial_open (&device->port, context, name); + if (rc == -1) { + ERROR (context, "Failed to open the serial port."); + free (device); + return DC_STATUS_IO; + } + + // Set the serial communication protocol (115200 8N1). + rc = serial_configure (device->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE); + if (rc == -1) { + ERROR (context, "Failed to set the terminal attributes."); + serial_close (device->port); + free (device); + return DC_STATUS_IO; + } + + // Set the timeout for receiving data (3000ms). + if (serial_set_timeout (device->port, 3000) == -1) { + ERROR (context, "Failed to set the timeout."); + serial_close (device->port); + free (device); + return DC_STATUS_IO; + } + + // Make sure everything is in a sane state. + serial_sleep (device->port, 300); + serial_flush (device->port, SERIAL_QUEUE_BOTH); + + // Send the init command. + dc_status_t status = hw_ostc3_transfer (device, NULL, INIT, NULL, 0, NULL, 0); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to send the command."); + serial_close (device->port); + free (device); + return status; + } + + *out = (dc_device_t *) device; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +hw_ostc3_device_close (dc_device_t *abstract) +{ + hw_ostc3_device_t *device = (hw_ostc3_device_t*) abstract; + + // Send the exit command. + dc_status_t status = hw_ostc3_transfer (device, NULL, EXIT, NULL, 0, NULL, 0); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command."); + serial_close (device->port); + free (device); + return status; + } + + // Close the device. + if (serial_close (device->port) == -1) { + free (device); + return DC_STATUS_IO; + } + + // Free memory. + free (device); + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +hw_ostc3_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) +{ + hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; + + if (size && size != sizeof (device->fingerprint)) + return DC_STATUS_INVALIDARGS; + + if (size) + memcpy (device->fingerprint, data, sizeof (device->fingerprint)); + else + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +hw_ostc3_device_version (dc_device_t *abstract, unsigned char data[], unsigned int size) +{ + hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; + + if (!ISINSTANCE (abstract)) + return DC_STATUS_INVALIDARGS; + + if (size != SZ_VERSION) + return DC_STATUS_INVALIDARGS; + + // Send the command. + dc_status_t rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, data, size); + if (rc != DC_STATUS_SUCCESS) + return rc; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) +{ + hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; + + // Enable progress notifications. + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + progress.maximum = (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT) + SZ_MEMORY; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Download the version data. + unsigned char id[SZ_VERSION] = {0}; + dc_status_t rc = hw_ostc3_device_version (abstract, id, sizeof (id)); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the version."); + return rc; + } + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = 0; + devinfo.firmware = array_uint16_be (id + 2); + devinfo.serial = array_uint16_le (id + 0); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + // Allocate memory. + unsigned char *header = (unsigned char *) malloc (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT); + if (header == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Download the logbook headers. + rc = hw_ostc3_transfer (device, &progress, HEADER, + NULL, 0, header, RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the header."); + free (header); + return rc; + } + + // Locate the most recent dive. + // The device maintains an internal counter which is incremented for every + // dive, and the current value at the time of the dive is stored in the + // dive header. Thus the most recent dive will have the highest value. + unsigned int count = 0; + unsigned int latest = 0; + unsigned int maximum = 0; + for (unsigned int i = 0; i < RB_LOGBOOK_COUNT; ++i) { + unsigned int offset = i * RB_LOGBOOK_SIZE; + + // Ignore uninitialized header entries. + if (array_isequal (header + offset, RB_LOGBOOK_SIZE, 0xFF)) + break; + + // Get the internal dive number. + unsigned int current = array_uint16_le (header + offset + 80); + if (current > maximum) { + maximum = current; + latest = i; + } + + count++; + } + + // Calculate the total and maximum size. + unsigned int ndives = 0; + unsigned int size = 0; + unsigned int maxsize = 0; + for (unsigned int i = 0; i < count; ++i) { + unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; + unsigned int offset = idx * RB_LOGBOOK_SIZE; + + // Calculate the profile length. + unsigned int length = RB_LOGBOOK_SIZE + array_uint24_le (header + offset + 9) - 6; + + // Check the fingerprint data. + if (memcmp (header + offset + 12, device->fingerprint, sizeof (device->fingerprint)) == 0) + break; + + if (length > maxsize) + maxsize = length; + size += length; + ndives++; + } + + // Update and emit a progress event. + progress.maximum = (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT) + size; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Finish immediately if there are no dives available. + if (ndives == 0) { + free (header); + return DC_STATUS_SUCCESS; + } + + // Allocate enough memory for the largest dive. + unsigned char *profile = (unsigned char *) malloc (maxsize); + if (profile == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + free (header); + return DC_STATUS_NOMEMORY; + } + + // Download the dives. + for (unsigned int i = 0; i < ndives; ++i) { + unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; + unsigned int offset = idx * RB_LOGBOOK_SIZE; + + // Calculate the profile length. + unsigned int length = RB_LOGBOOK_SIZE + array_uint24_le (header + offset + 9) - 6; + + // Download the dive. + unsigned char number[1] = {idx}; + rc = hw_ostc3_transfer (device, &progress, DIVE, + number, sizeof (number), profile, length); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the dive."); + free (profile); + free (header); + return rc; + } + + // Verify the header in the logbook and profile are identical. + if (memcmp (profile, header + offset, RB_LOGBOOK_SIZE) != 0) { + ERROR (abstract->context, "Unexpected profile header."); + free (profile); + free (header); + return rc; + + } + + if (callback && !callback (profile, length, profile + 12, sizeof (device->fingerprint), userdata)) + break; + } + + free (profile); + free (header); + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +hw_ostc3_device_clock (dc_device_t *abstract, const dc_datetime_t *datetime) +{ + hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; + + if (!ISINSTANCE (abstract)) + return DC_STATUS_INVALIDARGS; + + if (datetime == NULL) { + ERROR (abstract->context, "Invalid parameter specified."); + return DC_STATUS_INVALIDARGS; + } + + // Send the command. + unsigned char packet[6] = { + datetime->hour, datetime->minute, datetime->second, + datetime->month, datetime->day, datetime->year - 2000}; + dc_status_t rc = hw_ostc3_transfer (device, NULL, CLOCK, packet, sizeof (packet), NULL, 0); + if (rc != DC_STATUS_SUCCESS) + return rc; + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +hw_ostc3_device_display (dc_device_t *abstract, const char *text) +{ + hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; + + if (!ISINSTANCE (abstract)) + return DC_STATUS_INVALIDARGS; + + // Pad the data packet with spaces. + unsigned char packet[SZ_DISPLAY] = {0}; + if (hw_ostc3_strncpy (packet, sizeof (packet), text) != 0) { + ERROR (abstract->context, "Invalid parameter specified."); + return DC_STATUS_INVALIDARGS; + } + + // Send the command. + dc_status_t rc = hw_ostc3_transfer (device, NULL, DISPLAY, packet, sizeof (packet), NULL, 0); + if (rc != DC_STATUS_SUCCESS) + return rc; + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +hw_ostc3_device_customtext (dc_device_t *abstract, const char *text) +{ + hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; + + if (!ISINSTANCE (abstract)) + return DC_STATUS_INVALIDARGS; + + // Pad the data packet with spaces. + unsigned char packet[SZ_CUSTOMTEXT] = {0}; + if (hw_ostc3_strncpy (packet, sizeof (packet), text) != 0) { + ERROR (abstract->context, "Invalid parameter specified."); + return DC_STATUS_INVALIDARGS; + } + + // Send the command. + dc_status_t rc = hw_ostc3_transfer (device, NULL, CUSTOMTEXT, packet, sizeof (packet), NULL, 0); + if (rc != DC_STATUS_SUCCESS) + return rc; + + return DC_STATUS_SUCCESS; +} diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index 1a0ba97..576afdf 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -30,7 +30,7 @@ #define ISINSTANCE(parser) dc_parser_isinstance((parser), &hw_ostc_parser_vtable) -#define NINFO 6 +#define MAXCONFIG 7 #define MAXGASMIX 5 typedef struct hw_ostc_parser_t hw_ostc_parser_t; @@ -41,6 +41,7 @@ struct hw_ostc_parser_t { }; typedef struct hw_ostc_sample_info_t { + unsigned int type; unsigned int divisor; unsigned int size; } hw_ostc_sample_info_t; @@ -92,6 +93,15 @@ static const hw_ostc_layout_t hw_ostc_layout_frog = { 47, /* duration */ }; +static const hw_ostc_layout_t hw_ostc_layout_ostc3 = { + 12, /* datetime */ + 17, /* maxdepth */ + 19, /* divetime */ + 24, /* atmospheric */ + 70, /* salinity */ + 75, /* duration */ +}; + dc_status_t hw_ostc_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int frog) { @@ -160,6 +170,10 @@ hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) layout = &hw_ostc_layout_frog; header = 256; break; + case 0x23: + layout = &hw_ostc_layout_ostc3; + header = 256; + break; default: return DC_STATUS_DATAFORMAT; } @@ -168,7 +182,7 @@ hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) return DC_STATUS_DATAFORMAT; unsigned int divetime = 0; - if (version == 0x21 || version == 0x22) { + if (version > 0x20) { // Use the dive time stored in the extended header, rounded down towards // the nearest minute, to match the value displayed by the ostc. divetime = (array_uint16_le (data + layout->duration) / 60) * 60; @@ -180,9 +194,15 @@ hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) const unsigned char *p = data + layout->datetime; dc_datetime_t dt; - dt.year = p[2] + 2000; - dt.month = p[0]; - dt.day = p[1]; + if (version == 0x23) { + dt.year = p[0] + 2000; + dt.month = p[1]; + dt.day = p[2]; + } else { + dt.year = p[2] + 2000; + dt.month = p[0]; + dt.day = p[1]; + } dt.hour = p[3]; dt.minute = p[4]; dt.second = 0; @@ -226,6 +246,10 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned layout = &hw_ostc_layout_frog; header = 256; break; + case 0x23: + layout = &hw_ostc_layout_ostc3; + header = 256; + break; default: return DC_STATUS_DATAFORMAT; } @@ -236,6 +260,8 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_salinity_t *water = (dc_salinity_t *) value; unsigned int salinity = data[layout->salinity]; + if (version == 0x23) + salinity += 100; if (value) { switch (type) { @@ -248,6 +274,8 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned case DC_FIELD_GASMIX_COUNT: if (version == 0x22) { *((unsigned int *) value) = 3; + } else if (version == 0x23) { + *((unsigned int *) value) = 5; } else { *((unsigned int *) value) = 6; } @@ -256,6 +284,9 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned if (version == 0x22) { gasmix->oxygen = data[25 + 2 * flags] / 100.0; gasmix->helium = 0.0; + } else if (version == 0x23) { + gasmix->oxygen = data[28 + 4 * flags + 0] / 100.0; + gasmix->helium = data[28 + 4 * flags + 1] / 100.0; } else { gasmix->oxygen = data[19 + 2 * flags + 0] / 100.0; gasmix->helium = data[19 + 2 * flags + 1] / 100.0; @@ -311,6 +342,10 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call layout = &hw_ostc_layout_frog; header = 256; break; + case 0x23: + layout = &hw_ostc_layout_ostc3; + header = 256; + break; default: return DC_STATUS_DATAFORMAT; } @@ -319,51 +354,83 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call return DC_STATUS_DATAFORMAT; // Get the sample rate. - unsigned int samplerate = data[36]; + unsigned int samplerate = 0; + if (version == 0x23) + samplerate = data[header + 3]; + else + samplerate = data[36]; // Get the salinity factor. unsigned int salinity = data[layout->salinity]; + if (version == 0x23) + salinity += 100; if (salinity < 100 || salinity > 104) salinity = 100; double hydrostatic = GRAVITY * salinity * 10.0; - // Get all the gas mixes. - unsigned int ngasmix = 0; + // Get all the gas mixes, and the index of the inital mix. + unsigned int ngasmix = 0, initial = 0; hw_ostc_gasmix_t gasmix[MAXGASMIX] = {{0}}; if (version == 0x22) { ngasmix = 3; + initial = data[31]; for (unsigned int i = 0; i < ngasmix; ++i) { gasmix[i].oxygen = data[25 + 2 * i]; gasmix[i].helium = 0; } + } else if (version == 0x23) { + ngasmix = 5; + for (unsigned int i = 0; i < ngasmix; ++i) { + gasmix[i].oxygen = data[28 + 4 * i + 0]; + gasmix[i].helium = data[28 + 4 * i + 1]; + // Find the first gas marked as the initial gas. + if (!initial && data[28 + 4 * i + 3] == 1) { + initial = i + 1; /* One based index! */ + } + } } else { ngasmix = 5; + initial = data[31]; for (unsigned int i = 0; i < ngasmix; ++i) { gasmix[i].oxygen = data[19 + 2 * i + 0]; gasmix[i].helium = data[19 + 2 * i + 1]; } } - - // Get the index of the inital mix. - unsigned int initial = data[31]; if (initial < 1 || initial > ngasmix) return DC_STATUS_DATAFORMAT; initial--; /* Convert to a zero based index. */ + // Get the number of sample descriptors. + unsigned int nconfig = 0; + if (version == 0x23) + nconfig = data[header + 4]; + else + nconfig = 6; + if (nconfig > MAXCONFIG) + return DC_STATUS_DATAFORMAT; + // Get the extended sample configuration. - hw_ostc_sample_info_t info[NINFO]; - for (unsigned int i = 0; i < NINFO; ++i) { - info[i].divisor = (data[37 + i] & 0x0F); - info[i].size = (data[37 + i] & 0xF0) >> 4; + hw_ostc_sample_info_t info[MAXCONFIG] = {{0}}; + for (unsigned int i = 0; i < nconfig; ++i) { + if (version == 0x23) { + info[i].type = data[header + 5 + 3 * i + 0]; + info[i].size = data[header + 5 + 3 * i + 1]; + info[i].divisor = data[header + 5 + 3 * i + 2]; + } else { + info[i].type = i; + info[i].divisor = (data[37 + i] & 0x0F); + info[i].size = (data[37 + i] & 0xF0) >> 4; + } + if (info[i].divisor) { - switch (i) { + switch (info[i].type) { case 0: // Temperature case 1: // Deco / NDL if (info[i].size != 2) return DC_STATUS_DATAFORMAT; break; case 5: // CNS - if (info[i].size != 1) + if (info[i].size != 1 && info[i].size != 2) return DC_STATUS_DATAFORMAT; break; default: // Not yet used. @@ -376,6 +443,8 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call unsigned int nsamples = 0; unsigned int offset = header; + if (version == 0x23) + offset += 5 + 3 * nconfig; while (offset + 3 <= size) { dc_sample_value_t sample = {0}; @@ -403,16 +472,21 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call // Extended sample info. unsigned int length = data[offset] & 0x7F; - unsigned int events = (data[offset] & 0x80) >> 7; offset += 1; // Check for buffer overflows. if (offset + length > size) return DC_STATUS_DATAFORMAT; - // Get the event byte. - if (events) { - events = data[offset++]; + // Get the event byte(s). + unsigned int nbits = 0; + unsigned int events = 0; + while (data[offset - 1] & 0x80) { + if (offset + 1 > size) + return DC_STATUS_DATAFORMAT; + events |= data[offset] << nbits; + nbits += 8; + offset++; } // Alarms @@ -475,11 +549,20 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call offset++; } + // SetPoint Change + if ((events & 0x40) && (version == 0x23)) { + if (offset + 1 > size) + return DC_STATUS_DATAFORMAT; + sample.setpoint = data[offset] / 100.0; + if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); + offset++; + } + // Extended sample info. - for (unsigned int i = 0; i < NINFO; ++i) { + for (unsigned int i = 0; i < nconfig; ++i) { if (info[i].divisor && (nsamples % info[i].divisor) == 0) { unsigned int value = 0; - switch (i) { + switch (info[i].type) { case 0: // Temperature (0.1 °C). value = array_uint16_le (data + offset); sample.temperature = value / 10.0; @@ -497,7 +580,10 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call if (callback) callback (DC_SAMPLE_DECO, sample, userdata); break; case 5: // CNS - sample.cns = data[offset] / 100.0; + if (info[i].size == 2) + sample.cns = array_uint16_le (data + offset) / 100.0; + else + sample.cns = data[offset] / 100.0; if (callback) callback (DC_SAMPLE_CNS, sample, userdata); break; default: // Not yet used. @@ -509,7 +595,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call } // SetPoint Change - if (events & 0x40) { + if ((events & 0x40) && (version != 0x23)) { if (offset + 1 > size) return DC_STATUS_DATAFORMAT; sample.setpoint = data[offset] / 100.0; diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 663ffd1..2188971 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -145,6 +145,11 @@ hw_frog_device_version hw_frog_device_clock hw_frog_device_display hw_frog_device_customtext +hw_ostc3_device_open +hw_ostc3_device_version +hw_ostc3_device_clock +hw_ostc3_device_display +hw_ostc3_device_customtext zeagle_n2ition3_device_open atomics_cobalt_device_open atomics_cobalt_device_version diff --git a/src/parser.c b/src/parser.c index c6a54f2..00a59d2 100644 --- a/src/parser.c +++ b/src/parser.c @@ -102,6 +102,7 @@ dc_parser_new (dc_parser_t **out, dc_device_t *device) rc = hw_ostc_parser_create (&parser, context, 0); break; case DC_FAMILY_HW_FROG: + case DC_FAMILY_HW_OSTC3: rc = hw_ostc_parser_create (&parser, context, 1); break; case DC_FAMILY_CRESSI_EDY: