From db48150a999eb32d094681a4b0a05960f275cfd6 Mon Sep 17 00:00:00 2001 From: rmultan Date: Tue, 23 Jul 2024 14:56:01 +0200 Subject: [PATCH] Add support for Mares Sirius Cherry pick changes from libdivecomputer to allow support for Mares Sirius computer. Signed-off-by: rmultan --- src/descriptor.c | 7 + src/device.c | 2 +- src/mares_iconhd.c | 281 +++++++++++++++++----- src/mares_iconhd.h | 4 +- src/mares_iconhd_parser.c | 495 +++++++++++++++++++++----------------- src/parser.c | 2 +- 6 files changed, 508 insertions(+), 283 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index f23c93dd..f47cb479 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -307,6 +307,10 @@ static const dc_descriptor_t g_descriptors[] = { {"Mares", "Smart Air", DC_FAMILY_MARES_ICONHD , 0x24, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares}, {"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares}, {"Mares", "Horizon", DC_FAMILY_MARES_ICONHD , 0x2C, DC_TRANSPORT_SERIAL, NULL}, + {"Mares", "Puck Air 2", DC_FAMILY_MARES_ICONHD , 0x2D, DC_TRANSPORT_BLE, dc_filter_mares}, + {"Mares", "Sirius", DC_FAMILY_MARES_ICONHD , 0x2F, DC_TRANSPORT_BLE, dc_filter_mares}, + {"Mares", "Quad Ci", DC_FAMILY_MARES_ICONHD , 0x31, DC_TRANSPORT_BLE, dc_filter_mares}, + {"Mares", "Puck 4", DC_FAMILY_MARES_ICONHD , 0x35, DC_TRANSPORT_BLE, dc_filter_mares}, /* Heinrichs Weikamp */ {"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, DC_TRANSPORT_SERIAL, NULL}, {"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, DC_TRANSPORT_SERIAL, NULL}, @@ -715,6 +719,9 @@ dc_filter_mares (dc_descriptor_t *descriptor, dc_transport_t transport, const vo static const char * const bluetooth[] = { "Mares bluelink pro", "Mares Genius", + "Sirius", + "Quad Ci", + "Puck4", }; if (transport == DC_TRANSPORT_BLE) { diff --git a/src/device.c b/src/device.c index 64431595..f1a0ee7a 100644 --- a/src/device.c +++ b/src/device.c @@ -175,7 +175,7 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr rc = mares_darwin_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); break; case DC_FAMILY_MARES_ICONHD: - rc = mares_iconhd_device_open (&device, context, iostream); + rc = mares_iconhd_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); break; case DC_FAMILY_HW_OSTC: rc = hw_ostc_device_open (&device, context, iostream); diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index b19f5bff..5a48bdcc 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -48,11 +48,39 @@ #define SMARTAIR 0x24 #define QUAD 0x29 #define HORIZON 0x2C +#define PUCKAIR2 0x2D +#define SIRIUS 0x2F +#define QUADCI 0x31 +#define PUCK4 0x35 + +#define ISSMART(model) ( \ + (model) == SMART || \ + (model) == SMARTAPNEA || \ + (model) == SMARTAIR) + +#define ISGENIUS(model) ( \ + (model) == GENIUS || \ + (model) == HORIZON || \ + (model) == PUCKAIR2 || \ + (model) == SIRIUS || \ + (model) == QUADCI || \ + (model) == PUCK4) + +#define ISSIRIUS(model) ( \ + (model) == PUCKAIR2 || \ + (model) == SIRIUS || \ + (model) == QUADCI || \ + (model) == PUCK4) #define MAXRETRIES 4 +#define MAXPACKET 244 + +#define FIXED 0 +#define VARIABLE 1 + #define ACK 0xAA -#define EOF 0xEA +#define END 0xEA #define XOR 0xA5 #define CMD_VERSION 0xC2 @@ -62,6 +90,8 @@ #define CMD_OBJ_EVEN 0xAC #define CMD_OBJ_ODD 0xFE +#define OBJ_DEVICE 0x2000 +#define OBJ_DEVICE_SERIAL 0x04 #define OBJ_LOGBOOK 0x2008 #define OBJ_LOGBOOK_COUNT 0x01 #define OBJ_DIVE 0x3000 @@ -93,6 +123,7 @@ typedef struct mares_iconhd_device_t { unsigned char version[140]; unsigned int model; unsigned int packetsize; + unsigned int ble; } mares_iconhd_device_t; static dc_status_t mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); @@ -160,6 +191,10 @@ mares_iconhd_get_model (mares_iconhd_device_t *device) {"Smart Air", SMARTAIR}, {"Quad", QUAD}, {"Horizon", HORIZON}, + {"Puck Air 2", PUCKAIR2}, + {"Sirius", SIRIUS}, + {"Quad Ci", QUADCI}, + {"Puck4", PUCK4}, }; // Check the product name in the version packet against the list @@ -176,10 +211,11 @@ mares_iconhd_get_model (mares_iconhd_device_t *device) } static dc_status_t -mares_iconhd_packet (mares_iconhd_device_t *device, +mares_iconhd_packet_fixed (mares_iconhd_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, - unsigned char answer[], unsigned int asize) + unsigned char answer[], unsigned int asize, + unsigned int *actual) { dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; @@ -194,7 +230,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device, }; status = dc_iostream_write (device->iostream, command, sizeof(command), NULL); if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to send the command."); + ERROR (abstract->context, "Failed to send the command header."); return status; } @@ -202,7 +238,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device, if (size && transport == DC_TRANSPORT_BLE) { status = dc_iostream_write (device->iostream, data, size, NULL); if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to send the command."); + ERROR (abstract->context, "Failed to send the command data."); return status; } } @@ -211,13 +247,13 @@ mares_iconhd_packet (mares_iconhd_device_t *device, unsigned char header[1] = {0}; status = dc_iostream_read (device->iostream, header, sizeof (header), NULL); if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the answer."); + ERROR (abstract->context, "Failed to receive the packet header."); return status; } // Verify the header byte. if (header[0] != ACK) { - ERROR (abstract->context, "Unexpected answer byte."); + ERROR (abstract->context, "Unexpected packet header byte (%02x).", header[0]); return DC_STATUS_PROTOCOL; } @@ -233,7 +269,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device, // Read the packet. status = dc_iostream_read (device->iostream, answer, asize, NULL); if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the answer."); + ERROR (abstract->context, "Failed to receive the packet data."); return status; } @@ -241,25 +277,130 @@ mares_iconhd_packet (mares_iconhd_device_t *device, unsigned char trailer[1] = {0}; status = dc_iostream_read (device->iostream, trailer, sizeof (trailer), NULL); if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the answer."); + ERROR (abstract->context, "Failed to receive the packet trailer."); + return status; + } + + // Verify the trailer byte. + if (trailer[0] != END) { + ERROR (abstract->context, "Unexpected packet trailer byte (%02x).", trailer[0]); + return DC_STATUS_PROTOCOL; + } + + if (actual) { + *actual = asize; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +mares_iconhd_packet_variable (mares_iconhd_device_t *device, + unsigned char cmd, + const unsigned char data[], unsigned int size, + unsigned char answer[], unsigned int asize, + unsigned int *actual) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned char packet[MAXPACKET] = {0}; + size_t length = 0; + + if (device_is_cancelled (abstract)) + return DC_STATUS_CANCELLED; + + // Send the command header to the dive computer. + const unsigned char command[2] = { + cmd, cmd ^ XOR, + }; + status = dc_iostream_write (device->iostream, command, sizeof(command), NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command header."); + return status; + } + + // Read either the entire data packet (if there is no command data to send), + // or only the header byte (if there is also command data to send). + status = dc_iostream_read (device->iostream, packet, sizeof (packet), &length); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet header."); return status; } + if (size) { + // Send the command payload to the dive computer. + status = dc_iostream_write (device->iostream, data, size, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command data."); + return status; + } + + // Read the data packet. + size_t len = 0; + status = dc_iostream_read (device->iostream, packet + length, sizeof (packet) - length, &len); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet data."); + return status; + } + + length += len; + } + + if (length < 2 || length - 2 > asize) { + ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", length); + return DC_STATUS_PROTOCOL; + } + + // Verify the header byte. + if (packet[0] != ACK) { + ERROR (abstract->context, "Unexpected packet header byte (%02x).", packet[0]); + return DC_STATUS_PROTOCOL; + } + // Verify the trailer byte. - if (trailer[0] != EOF) { - ERROR (abstract->context, "Unexpected answer byte."); + if (packet[length - 1] != END) { + ERROR (abstract->context, "Unexpected packet trailer byte (%02x).", packet[length - 1]); return DC_STATUS_PROTOCOL; } + if (actual == NULL) { + // Verify the actual length. + if (length - 2 != asize) { + ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", length - 2); + return DC_STATUS_PROTOCOL; + } + } else { + // Return the actual length. + *actual = length - 2; + } + + memcpy (answer, packet + 1, length - 2); + return DC_STATUS_SUCCESS; } static dc_status_t -mares_iconhd_transfer (mares_iconhd_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize) +mares_iconhd_packet (mares_iconhd_device_t *device, + unsigned char cmd, + const unsigned char data[], unsigned int size, + unsigned char answer[], unsigned int asize, + unsigned int *actual) +{ + dc_transport_t transport = dc_iostream_get_transport (device->iostream); + + if (transport == DC_TRANSPORT_BLE && device->ble == VARIABLE) { + return mares_iconhd_packet_variable (device, cmd, data, size, answer, asize, actual); + } else { + return mares_iconhd_packet_fixed (device, cmd, data, size, answer, asize, actual); + } +} + +static dc_status_t +mares_iconhd_transfer (mares_iconhd_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize, unsigned int *actual) { unsigned int nretries = 0; dc_status_t rc = DC_STATUS_SUCCESS; - while ((rc = mares_iconhd_packet (device, cmd, data, size, answer, asize)) != DC_STATUS_SUCCESS) { + while ((rc = mares_iconhd_packet (device, cmd, data, size, answer, asize, actual)) != DC_STATUS_SUCCESS) { // Automatically discard a corrupted packet, // and request a new one. if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT) @@ -283,7 +424,9 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; dc_transport_t transport = dc_iostream_get_transport (device->iostream); - const unsigned int maxpacket = (transport == DC_TRANSPORT_BLE) ? 124 : 504; + const unsigned int maxpacket = (transport == DC_TRANSPORT_BLE) ? + (device->ble == VARIABLE ? MAXPACKET - 3 : 124) : + 504; // Update and emit a progress event. unsigned int initial = 0; @@ -301,7 +444,7 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro subindex & 0xFF }; memset (cmd_init + 6, 0x00, sizeof(cmd_init) - 6); - status = mares_iconhd_transfer (device, CMD_OBJ_INIT, cmd_init, sizeof (cmd_init), rsp_init, sizeof (rsp_init)); + status = mares_iconhd_transfer (device, CMD_OBJ_INIT, cmd_init, sizeof (cmd_init), rsp_init, sizeof (rsp_init), NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to transfer the init packet."); return status; @@ -355,13 +498,21 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro } // Transfer the segment packet. + unsigned int length = 0; unsigned char rsp_segment[1 + 504]; - status = mares_iconhd_transfer (device, cmd, NULL, 0, rsp_segment, len + 1); + status = mares_iconhd_transfer (device, cmd, NULL, 0, rsp_segment, len + 1, &length); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to transfer the segment packet."); return status; } + if (length < 1) { + ERROR (abstract->context, "Unexpected packet length (%u).", length); + return DC_STATUS_PROTOCOL; + } + + length--; + // Verify the packet header. if ((rsp_segment[0] & 0xF0) >> 4 != toggle) { ERROR (abstract->context, "Unexpected packet header (%02x).", rsp_segment[0]); @@ -369,12 +520,12 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro } // Append the payload to the output buffer. - if (!dc_buffer_append (buffer, rsp_segment + 1, len)) { + if (!dc_buffer_append (buffer, rsp_segment + 1, length)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } - nbytes += len; + nbytes += length; npackets++; // Update and emit the progress events. @@ -388,7 +539,7 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro } dc_status_t -mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream) +mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model) { dc_status_t status = DC_STATUS_SUCCESS; mares_iconhd_device_t *device = NULL; @@ -411,10 +562,11 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ memset (device->version, 0, sizeof (device->version)); device->model = 0; device->packetsize = 0; + device->ble = ISSIRIUS(model) ? VARIABLE : FIXED; // Create the packet stream. - if (transport == DC_TRANSPORT_BLE) { - status = dc_packet_open (&device->iostream, context, iostream, 20, 20); + if (transport == DC_TRANSPORT_BLE && device->ble == FIXED) { + status = dc_packet_open (&device->iostream, context, iostream, 244, 20); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to create the packet stream."); goto error_free; @@ -456,7 +608,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ // Send the version command. status = mares_iconhd_transfer (device, CMD_VERSION, NULL, 0, - device->version, sizeof (device->version)); + device->version, sizeof (device->version), NULL); if (status != DC_STATUS_SUCCESS) { goto error_free_iostream; } @@ -468,7 +620,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ unsigned int memsize = 0; if (device->model == QUAD) { unsigned char rsp_flash[4] = {0}; - status = mares_iconhd_transfer (device, CMD_FLASHSIZE, NULL, 0, rsp_flash, sizeof (rsp_flash)); + status = mares_iconhd_transfer (device, CMD_FLASHSIZE, NULL, 0, rsp_flash, sizeof (rsp_flash), NULL); if (status != DC_STATUS_SUCCESS) { WARNING (context, "Failed to read the flash memory size."); } else { @@ -521,25 +673,13 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ break; } - if (dc_iostream_get_transport(device->iostream) == DC_TRANSPORT_BLE) { - /* - * Don't ask for larger amounts of data with the BLE - * transport - it will fail. I suspect there is a buffer - * overflow in the BlueLink Pro dongle when bluetooth is - * slower than the serial protocol that the dongle talks to - * the dive computer. - */ - if (device->packetsize > 128) - device->packetsize = 128; - } - *out = (dc_device_t *) device; return DC_STATUS_SUCCESS; error_free_iostream: - if (transport == DC_TRANSPORT_BLE) { + if (transport == DC_TRANSPORT_BLE && device->ble == FIXED) { dc_iostream_close (device->iostream); } error_free: @@ -552,9 +692,10 @@ static dc_status_t mares_iconhd_device_close (dc_device_t *abstract) { mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract; + dc_transport_t transport = dc_iostream_get_transport (device->iostream); // Close the packet stream. - if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) { + if (transport == DC_TRANSPORT_BLE && device->ble == FIXED) { return dc_iostream_close (device->iostream); } @@ -602,7 +743,7 @@ mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned (len >> 8) & 0xFF, (len >> 16) & 0xFF, (len >> 24) & 0xFF}; - rc = mares_iconhd_transfer (device, CMD_READ, command, sizeof (command), data, len); + rc = mares_iconhd_transfer (device, CMD_READ, command, sizeof (command), data, len, NULL); if (rc != DC_STATUS_SUCCESS) return rc; @@ -659,6 +800,21 @@ mares_iconhd_device_foreach_raw (dc_device_t *abstract, dc_dive_callback_t callb const mares_iconhd_layout_t *layout = device->layout; + // Read the serial number. + unsigned char serial[4] = {0}; + rc = mares_iconhd_device_read (abstract, 0x0C, serial, sizeof (serial)); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the memory."); + return rc; + } + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = device->model; + devinfo.firmware = 0; + devinfo.serial = array_uint32_le (serial); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + // Enable progress notifications. dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; progress.maximum = layout->rb_profile_end - layout->rb_profile_begin; @@ -735,7 +891,7 @@ mares_iconhd_device_foreach_raw (dc_device_t *abstract, dc_dive_callback_t callb // Get the number of samples in the profile data. unsigned int type = 0, nsamples = 0; - if (model == SMART || model == SMARTAPNEA || model == SMARTAIR) { + if (ISSMART(model)) { type = array_uint16_le (buffer + offset - header + 2); nsamples = array_uint16_le (buffer + offset - header + 0); } else { @@ -864,6 +1020,33 @@ mares_iconhd_device_foreach_object (dc_device_t *abstract, dc_dive_callback_t ca return DC_STATUS_NOMEMORY; } + // Read the serial number. + rc = mares_iconhd_read_object (device, NULL, buffer, OBJ_DEVICE, OBJ_DEVICE_SERIAL); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the serial number."); + dc_buffer_free (buffer); + return rc; + } + + if (dc_buffer_get_size (buffer) < 16) { + ERROR (abstract->context, "Unexpected number of bytes received (" DC_PRINTF_SIZE ").", + dc_buffer_get_size (buffer)); + dc_buffer_free (buffer); + return DC_STATUS_PROTOCOL; + } + + unsigned int serial = array_convert_str2num (dc_buffer_get_data (buffer) + 10, 6); + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = device->model; + devinfo.firmware = 0; + devinfo.serial = serial; + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + // Erase the buffer. + dc_buffer_clear (buffer); + // Read the number of dives. rc = mares_iconhd_read_object (device, NULL, buffer, OBJ_LOGBOOK, OBJ_LOGBOOK_COUNT); if (rc != DC_STATUS_SUCCESS) { @@ -926,7 +1109,6 @@ mares_iconhd_device_foreach_object (dc_device_t *abstract, dc_dive_callback_t ca static dc_status_t mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { - dc_status_t rc = DC_STATUS_SUCCESS; mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract; // Emit a vendor event. @@ -935,24 +1117,9 @@ mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, vendor.size = sizeof (device->version); device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - // Read the serial number. - unsigned char serial[4] = {0}; - rc = mares_iconhd_device_read (abstract, 0x0C, serial, sizeof (serial)); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the memory."); - return rc; - } - - // Emit a device info event. - dc_event_devinfo_t devinfo; - devinfo.model = device->model; - devinfo.firmware = 0; - devinfo.serial = array_uint32_le (serial); - device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - - if (device->model == GENIUS || device->model == HORIZON) { + if (ISGENIUS(device->model)) { return mares_iconhd_device_foreach_object (abstract, callback, userdata); } else { return mares_iconhd_device_foreach_raw (abstract, callback, userdata); } -} +} \ No newline at end of file diff --git a/src/mares_iconhd.h b/src/mares_iconhd.h index c055012d..a6cb6a8f 100644 --- a/src/mares_iconhd.h +++ b/src/mares_iconhd.h @@ -32,10 +32,10 @@ extern "C" { #endif /* __cplusplus */ dc_status_t -mares_iconhd_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); +mares_iconhd_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model); dc_status_t -mares_iconhd_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model, unsigned int serial); +mares_iconhd_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model); #ifdef __cplusplus } diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c index c845e65f..dd6ab93b 100644 --- a/src/mares_iconhd_parser.c +++ b/src/mares_iconhd_parser.c @@ -20,8 +20,6 @@ */ #include -#include /* for snprintf */ -#include /* for strdup */ #include @@ -47,6 +45,23 @@ #define QUADAIR 0x23 #define SMARTAIR 0x24 #define HORIZON 0x2C +#define PUCKAIR2 0x2D +#define SIRIUS 0x2F +#define QUADCI 0x31 +#define PUCK4 0x35 + +#define ISSMART(model) ( \ + (model) == SMART || \ + (model) == SMARTAPNEA || \ + (model) == SMARTAIR) + +#define ISGENIUS(model) ( \ + (model) == GENIUS || \ + (model) == HORIZON || \ + (model) == PUCKAIR2 || \ + (model) == SIRIUS || \ + (model) == QUADCI || \ + (model) == PUCK4) #define NGASMIXES_ICONHD 3 #define NGASMIXES_GENIUS 5 @@ -165,7 +180,6 @@ struct mares_iconhd_parser_t { unsigned int samplerate; unsigned int ntanks; unsigned int ngasmixes; - unsigned int serial; mares_iconhd_gasmix_t gasmix[NGASMIXES]; mares_iconhd_tank_t tank[NTANKS]; const mares_iconhd_layout_t *layout; @@ -283,28 +297,6 @@ static const dc_parser_vtable_t mares_iconhd_parser_vtable = { NULL /* destroy */ }; -static unsigned int -mares_genius_isvalid (const unsigned char data[], size_t size, unsigned int type) -{ - if (size < 10) { - return 0; - } - - unsigned int head = array_uint32_be(data); - unsigned int tail = array_uint32_be(data + size - 4); - if (head != type || tail != type) { - return 0; - } - - unsigned short crc = array_uint16_le(data + size - 6); - unsigned short ccrc = checksum_crc16_ccitt(data + 4, size - 10, 0x0000, 0x0000); - if (crc != ccrc) { - return 0; - } - - return 1; -} - static dc_status_t mares_iconhd_cache (mares_iconhd_parser_t *parser) { @@ -335,7 +327,7 @@ mares_iconhd_cache (mares_iconhd_parser_t *parser) // Get the number of samples in the profile data. unsigned int type = 0, nsamples = 0; - if (parser->model == SMART || parser->model == SMARTAPNEA || parser->model == SMARTAIR) { + if (ISSMART (parser->model)) { type = array_uint16_le (data + length - header + 2); nsamples = array_uint16_le (data + length - header + 0); } else { @@ -390,7 +382,7 @@ mares_iconhd_cache (mares_iconhd_parser_t *parser) } const unsigned char *p = data + length - headersize; - if (parser->model != SMART && parser->model != SMARTAPNEA && parser->model != SMARTAIR) { + if (!ISSMART(parser->model)) { p += 4; } @@ -548,30 +540,22 @@ mares_genius_cache (mares_iconhd_parser_t *parser) // Get the dive mode. unsigned int mode = settings & 0xF; - // Get the sample size. - unsigned int samplesize = logformat == 1 ? SDPT_SIZE: DPRS_SIZE; - - // Calculate the total number of bytes for this dive. - unsigned int nbytes = headersize + 4 + DSTR_SIZE + TISS_SIZE + nsamples * samplesize + (nsamples / 4) * AIRS_SIZE + DEND_SIZE; - if (nbytes > size) { - ERROR (abstract->context, "Buffer overflow detected!"); - return DC_STATUS_DATAFORMAT; - } - - // Get the profile type and version. - unsigned int profile_type = array_uint16_le (data + headersize); - unsigned int profile_minor = data[headersize + 2]; - unsigned int profile_major = data[headersize + 3]; - // Get the surface timeout setting (in minutes). // For older firmware versions the value is hardcoded to 3 minutes, but // starting with the newer v01.02.00 firmware the value is configurable and // stored in the settings. To detect whether the setting is available, we // need to check the profile version instead of the header version. unsigned int surftime = 3; - if (profile_type == 0 && - OBJVERSION(profile_major,profile_minor) >= OBJVERSION(1,0)) { - surftime = (settings >> 13) & 0x3F; + if (headersize + 4 <= size) { + // Get the profile type and version. + unsigned int profile_type = array_uint16_le (data + headersize); + unsigned int profile_minor = data[headersize + 2]; + unsigned int profile_major = data[headersize + 3]; + + if (profile_type == 0 && + OBJVERSION(profile_major,profile_minor) >= OBJVERSION(1,0)) { + surftime = (settings >> 13) & 0x3F; + } } // Gas mixes and tanks. @@ -622,7 +606,7 @@ mares_genius_cache (mares_iconhd_parser_t *parser) parser->logformat = logformat; parser->mode = mode; parser->nsamples = nsamples; - parser->samplesize = samplesize; + parser->samplesize = 0; parser->headersize = headersize; parser->settings = settings; parser->surftime = surftime * 60; @@ -649,7 +633,7 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser) return DC_STATUS_SUCCESS; } - if (parser->model == GENIUS || parser->model == HORIZON) { + if (ISGENIUS(parser->model)) { return mares_genius_cache (parser); } else { return mares_iconhd_cache (parser); @@ -657,7 +641,7 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser) } dc_status_t -mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model, unsigned int serial) +mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model) { mares_iconhd_parser_t *parser = NULL; @@ -675,7 +659,7 @@ mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, const unsi parser->model = model; parser->cached = 0; parser->logformat = 0; - parser->mode = (model == GENIUS || model == HORIZON) ? GENIUS_AIR : ICONHD_AIR; + parser->mode = ISGENIUS(model) ? GENIUS_AIR : ICONHD_AIR; parser->nsamples = 0; parser->samplesize = 0; parser->headersize = 0; @@ -685,7 +669,6 @@ mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, const unsi parser->samplerate = 0; parser->ntanks = 0; parser->ngasmixes = 0; - parser->serial = serial; for (unsigned int i = 0; i < NGASMIXES; ++i) { parser->gasmix[i].oxygen = 0; parser->gasmix[i].helium = 0; @@ -715,9 +698,9 @@ mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime // Pointer to the header data. const unsigned char *p = abstract->data; - if (parser->model != GENIUS && parser->model != HORIZON) { + if (!ISGENIUS(parser->model)) { p += abstract->size - parser->headersize; - if (parser->model != SMART && parser->model != SMARTAPNEA && parser->model != SMARTAIR) { + if (!ISSMART(parser->model)) { p += 4; } } @@ -726,7 +709,7 @@ mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime p += parser->layout->datetime; if (datetime) { - if (parser->model == GENIUS || parser->model == HORIZON) { + if (ISGENIUS(parser->model)) { unsigned int timestamp = array_uint32_le (p); datetime->hour = (timestamp ) & 0x1F; datetime->minute = (timestamp >> 5) & 0x3F; @@ -748,7 +731,6 @@ mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime return DC_STATUS_SUCCESS; } -#define BUFLEN 16 static dc_status_t mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) @@ -762,9 +744,9 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi // Pointer to the header data. const unsigned char *p = abstract->data; - if (parser->model != GENIUS && parser->model != HORIZON) { + if (!ISGENIUS(parser->model)) { p += abstract->size - parser->headersize; - if (parser->model != SMART && parser->model != SMARTAPNEA && parser->model != SMARTAIR) { + if (!ISSMART(parser->model)) { p += 4; } } @@ -775,14 +757,12 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi extra = 8; } - unsigned int metric = (parser->model == GENIUS || parser->model == HORIZON) ? + unsigned int metric = ISGENIUS(parser->model) ? p[0x34 + extra] : parser->settings & 0x0100; dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_tank_t *tank = (dc_tank_t *) value; dc_salinity_t *water = (dc_salinity_t *) value; - dc_field_string_t *string = (dc_field_string_t *) value; - char buf[BUFLEN]; if (value) { switch (type) { @@ -834,7 +814,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi *((double *) value) = array_uint16_le (p + parser->layout->atmospheric) / (1000.0 * parser->layout->atmospheric_divisor); break; case DC_FIELD_SALINITY: - if (parser->model == GENIUS || parser->model == HORIZON) { + if (ISGENIUS(parser->model)) { unsigned int salinity = (parser->settings >> 5) & 0x03; switch (salinity) { case WATER_FRESH: @@ -876,7 +856,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi *((double *) value) = (signed short) array_uint16_le (p + parser->layout->temperature_max) / 10.0; break; case DC_FIELD_DIVEMODE: - if (parser->model == GENIUS || parser->model == HORIZON) { + if (ISGENIUS(parser->model)) { switch (parser->mode) { case GENIUS_AIR: case GENIUS_NITROX_SINGLE: @@ -914,17 +894,6 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi } } break; - case DC_FIELD_STRING: - switch(flags) { - case 0: /* serial */ - string->desc = "Serial"; - snprintf(buf, BUFLEN, "%u", parser->serial); - break; - default: - return DC_STATUS_UNSUPPORTED; - } - string->value = strdup(buf); - break; default: return DC_STATUS_UNSUPPORTED; } @@ -935,15 +904,9 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi static dc_status_t -mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +mares_iconhd_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) { mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract; - - // Cache the parser data. - dc_status_t rc = mares_iconhd_parser_cache (parser); - if (rc != DC_STATUS_SUCCESS) - return rc; - const unsigned char *data = abstract->data; // Previous gas mix - initialize with impossible value @@ -951,40 +914,6 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t unsigned int offset = 4; unsigned int marker = 0; - if (parser->model == GENIUS || parser->model == HORIZON) { - // Skip the dive header. - data += parser->headersize; - - // Check the profile type and version. - unsigned int type = array_uint16_le (data); - unsigned int minor = data[2]; - unsigned int major = data[3]; - if (type > 1 || - (type == 0 && OBJVERSION(major,minor) > OBJVERSION(1,0)) || - (type == 1 && OBJVERSION(major,minor) > OBJVERSION(0,2))) { - ERROR (abstract->context, "Unsupported object type (%u) or version (%u.%u).", - type, major, minor); - return DC_STATUS_DATAFORMAT; - } - - // Skip the DSTR record. - if (!mares_genius_isvalid (data + offset, DSTR_SIZE, DSTR_TYPE)) { - ERROR (abstract->context, "Invalid DSTR record."); - return DC_STATUS_DATAFORMAT; - } - offset += DSTR_SIZE; - - // Skip the TISS record. - if (!mares_genius_isvalid (data + offset, TISS_SIZE, TISS_TYPE)) { - ERROR (abstract->context, "Invalid TISS record."); - return DC_STATUS_DATAFORMAT; - } - offset += TISS_SIZE; - - // Size of the record type marker. - marker = 4; - } - unsigned int time = 0; unsigned int nsamples = 0; while (nsamples < parser->nsamples) { @@ -1021,7 +950,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t offset += 2; } - } else if (parser->model != GENIUS && parser->model != HORIZON && parser->mode == ICONHD_FREEDIVE) { + } else if (parser->mode == ICONHD_FREEDIVE) { unsigned int maxdepth = array_uint16_le (data + offset + 0); unsigned int divetime = array_uint16_le (data + offset + 2); unsigned int surftime = array_uint16_le (data + offset + 4); @@ -1047,54 +976,179 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t offset += parser->samplesize; nsamples++; } else { + unsigned int depth = array_uint16_le (data + offset + 0); + unsigned int temperature = array_uint16_le (data + offset + 2) & 0x0FFF; + unsigned int gasmix = (data[offset + 3] & 0xF0) >> 4; + + // Time (seconds). + time += parser->interval; + sample.time = time; + if (callback) callback (DC_SAMPLE_TIME, &sample, userdata); + + // Depth (1/10 m). + sample.depth = depth / 10.0; + if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata); + + // Temperature (1/10 °C). + sample.temperature = temperature / 10.0; + if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata); + + // Current gas mix + if (parser->ngasmixes > 0) { + if (gasmix >= parser->ngasmixes) { + ERROR (abstract->context, "Invalid gas mix index."); + return DC_STATUS_DATAFORMAT; + } + if (gasmix != gasmix_previous) { + sample.gasmix = gasmix; + if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata); + gasmix_previous = gasmix; + } + } + + offset += parser->samplesize; + nsamples++; + + // Some extra data. + if (parser->layout->tanks != UNSUPPORTED && (nsamples % 4) == 0) { + // Pressure (1/100 bar). + unsigned int pressure = array_uint16_le(data + offset + marker + 0); + if (gasmix < parser->ntanks) { + sample.pressure.tank = gasmix; + sample.pressure.value = pressure / 100.0; + if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata); + } else if (pressure != 0) { + WARNING (abstract->context, "Invalid tank with non-zero pressure."); + } + + offset += 8; + } + } + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +mares_genius_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract; + const unsigned char *data = abstract->data; + const unsigned int size = abstract->size; + + // Previous gas mix - initialize with impossible value + unsigned int gasmix_previous = 0xFFFFFFFF; + unsigned int tank = 0xFFFFFFFF; + + // Skip the dive header. + unsigned int offset = parser->headersize; + + if (offset + 4 > size) { + ERROR (abstract->context, "Buffer overflow detected!"); + return DC_STATUS_DATAFORMAT; + } + + // Check the profile type and version. + unsigned int profile_type = array_uint16_le (data + offset); + unsigned int profile_minor = data[offset + 2]; + unsigned int profile_major = data[offset + 3]; + if (profile_type > 1 || + (profile_type == 0 && OBJVERSION(profile_major,profile_minor) > OBJVERSION(1,0)) || + (profile_type == 1 && OBJVERSION(profile_major,profile_minor) > OBJVERSION(0,2))) { + ERROR (abstract->context, "Unsupported object type (%u) or version (%u.%u).", + profile_type, profile_major, profile_minor); + return DC_STATUS_DATAFORMAT; + } + offset += 4; + + unsigned int time = 0; + unsigned int marker = 4; + while (offset < size) { + dc_sample_value_t sample = {0}; + + if (offset + 10 > size) { + ERROR (abstract->context, "Buffer overflow detected!"); + return DC_STATUS_DATAFORMAT; + } + + // Get the record type and length. + unsigned int type = array_uint32_be(data + offset); + unsigned int length = 0; + switch (type) { + case DSTR_TYPE: + length = DSTR_SIZE; + break; + case TISS_TYPE: + length = TISS_SIZE; + break; + case DPRS_TYPE: + length = DPRS_SIZE; + break; + case SDPT_TYPE: + length = SDPT_SIZE; + break; + case AIRS_TYPE: + length = AIRS_SIZE; + break; + case DEND_TYPE: + length = DEND_SIZE; + break; + default: + ERROR (abstract->context, "Unknown record type (%08x).", type); + return DC_STATUS_DATAFORMAT; + } + + if (offset + length > size) { + ERROR (abstract->context, "Buffer overflow detected!"); + return DC_STATUS_DATAFORMAT; + } + + unsigned int etype = array_uint32_be(data + offset + length - 4); + if (etype != type) { + ERROR (abstract->context, "Invalid record end type (%08x).", etype); + return DC_STATUS_DATAFORMAT; + } + + unsigned short crc = array_uint16_le(data + offset + length - 6); + unsigned short ccrc = checksum_crc16_ccitt(data + offset + 4, length - 10, 0x0000, 0x0000); + if (crc != ccrc) { + ERROR (abstract->context, "Invalid record checksum (%04x %04x).", crc, ccrc); + return DC_STATUS_DATAFORMAT; + } + + if (type == DPRS_TYPE || type == SDPT_TYPE) { unsigned int depth = 0, temperature = 0; unsigned int gasmix = 0, alarms = 0; unsigned int decostop = 0, decodepth = 0, decotime = 0, tts = 0; unsigned int bookmark = 0; - if (parser->model == GENIUS || parser->model == HORIZON) { - if (parser->logformat == 1) { - if (!mares_genius_isvalid (data + offset, SDPT_SIZE, SDPT_TYPE)) { - ERROR (abstract->context, "Invalid SDPT record."); - return DC_STATUS_DATAFORMAT; - } - - unsigned int misc = 0, deco = 0; - depth = array_uint16_le (data + offset + marker + 2); - temperature = array_uint16_le (data + offset + marker + 6); - alarms = array_uint32_le (data + offset + marker + 0x14); - misc = array_uint32_le (data + offset + marker + 0x18); - deco = array_uint32_le (data + offset + marker + 0x1C); - bookmark = (misc >> 2) & 0x0F; - gasmix = (misc >> 6) & 0x0F; - decostop = (misc >> 10) & 0x01; - if (decostop) { - decodepth = (deco >> 3) & 0x7F; - decotime = (deco >> 10) & 0xFF; - tts = (deco >> 18) & 0x3FFF; - } else { - decotime = deco & 0xFF; - } + if (type == SDPT_TYPE) { + unsigned int misc = 0, deco = 0; + depth = array_uint16_le (data + offset + marker + 2); + temperature = array_uint16_le (data + offset + marker + 6); + alarms = array_uint32_le (data + offset + marker + 0x14); + misc = array_uint32_le (data + offset + marker + 0x18); + deco = array_uint32_le (data + offset + marker + 0x1C); + bookmark = (misc >> 2) & 0x0F; + gasmix = (misc >> 6) & 0x0F; + decostop = (misc >> 10) & 0x01; + if (decostop) { + decodepth = (deco >> 3) & 0x7F; + decotime = (deco >> 10) & 0xFF; + tts = (deco >> 18) & 0x3FFF; } else { - if (!mares_genius_isvalid (data + offset, DPRS_SIZE, DPRS_TYPE)) { - ERROR (abstract->context, "Invalid DPRS record."); - return DC_STATUS_DATAFORMAT; - } - - unsigned int misc = 0; - depth = array_uint16_le (data + offset + marker + 0); - temperature = array_uint16_le (data + offset + marker + 4); - decotime = array_uint16_le (data + offset + marker + 0x0A); - alarms = array_uint32_le (data + offset + marker + 0x0C); - misc = array_uint32_le (data + offset + marker + 0x14); - bookmark = (misc >> 2) & 0x0F; - gasmix = (misc >> 6) & 0x0F; - decostop = (misc >> 18) & 0x01; - decodepth = (misc >> 19) & 0x7F; + decotime = deco & 0xFF; } } else { - depth = array_uint16_le (data + offset + 0); - temperature = array_uint16_le (data + offset + 2) & 0x0FFF; - gasmix = (data[offset + 3] & 0xF0) >> 4; + unsigned int misc = 0; + depth = array_uint16_le (data + offset + marker + 0); + temperature = array_uint16_le (data + offset + marker + 4); + decotime = array_uint16_le (data + offset + marker + 0x0A); + alarms = array_uint32_le (data + offset + marker + 0x0C); + misc = array_uint32_le (data + offset + marker + 0x14); + bookmark = (misc >> 2) & 0x0F; + gasmix = (misc >> 6) & 0x0F; + decostop = (misc >> 18) & 0x01; + decodepth = (misc >> 19) & 0x7F; } // Time (seconds). @@ -1123,6 +1177,9 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t } } + // Current tank + tank = gasmix; + // Bookmark if (bookmark) { sample.event.type = SAMPLE_EVENT_BOOKMARK; @@ -1132,82 +1189,76 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata); } - if (parser->model == GENIUS || parser->model == HORIZON) { - // Deco stop / NDL. - if (decostop) { - sample.deco.type = DC_DECO_DECOSTOP; - sample.deco.depth = decodepth; - } else { - sample.deco.type = DC_DECO_NDL; - sample.deco.depth = 0.0; - } - sample.deco.time = decotime * 60; - sample.deco.tts = tts; - if (callback) callback (DC_SAMPLE_DECO, &sample, userdata); - - // Alarms - for (unsigned int v = alarms, i = 0; v; v >>= 1, ++i) { - if ((v & 1) == 0) { - continue; - } - - switch (i) { - case ALARM_FAST_ASCENT: - case ALARM_UNCONTROLLED_ASCENT: - sample.event.type = SAMPLE_EVENT_ASCENT; - break; - case ALARM_MISSED_DECO: - case ALARM_DIVE_VIOLATION_DECO: - sample.event.type = SAMPLE_EVENT_CEILING; - break; - default: - sample.event.type = SAMPLE_EVENT_NONE; - break; - } - - if (sample.event.type != SAMPLE_EVENT_NONE) { - sample.event.time = 0; - sample.event.flags = 0; - sample.event.value = 0; - if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata); - } - } + // Deco stop / NDL. + if (decostop) { + sample.deco.type = DC_DECO_DECOSTOP; + sample.deco.depth = decodepth; + } else { + sample.deco.type = DC_DECO_NDL; + sample.deco.depth = 0.0; } - - offset += parser->samplesize; - nsamples++; - - // Some extra data. - if (parser->layout->tanks != UNSUPPORTED && (nsamples % 4) == 0) { - if ((parser->model == GENIUS || parser->model == HORIZON) && - !mares_genius_isvalid (data + offset, AIRS_SIZE, AIRS_TYPE)) { - ERROR (abstract->context, "Invalid AIRS record."); - return DC_STATUS_DATAFORMAT; + sample.deco.time = decotime * 60; + sample.deco.tts = tts; + if (callback) callback (DC_SAMPLE_DECO, &sample, userdata); + + // Alarms + for (unsigned int v = alarms, i = 0; v; v >>= 1, ++i) { + if ((v & 1) == 0) { + continue; } - // Pressure (1/100 bar). - unsigned int pressure = array_uint16_le(data + offset + marker + 0); - if (gasmix < parser->ntanks) { - sample.pressure.tank = gasmix; - sample.pressure.value = pressure / 100.0; - if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata); - } else if (pressure != 0) { - WARNING (abstract->context, "Invalid tank with non-zero pressure."); + switch (i) { + case ALARM_FAST_ASCENT: + case ALARM_UNCONTROLLED_ASCENT: + sample.event.type = SAMPLE_EVENT_ASCENT; + break; + case ALARM_MISSED_DECO: + case ALARM_DIVE_VIOLATION_DECO: + sample.event.type = SAMPLE_EVENT_CEILING; + break; + default: + sample.event.type = SAMPLE_EVENT_NONE; + break; } - offset += (parser->model == GENIUS || parser->model == HORIZON) ? AIRS_SIZE : 8; + if (sample.event.type != SAMPLE_EVENT_NONE) { + sample.event.time = 0; + sample.event.flags = 0; + sample.event.value = 0; + if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata); + } + } + } else if (type == AIRS_TYPE) { + // Pressure (1/100 bar). + unsigned int pressure = array_uint16_le(data + offset + marker + 0); + if (tank < parser->ntanks) { + sample.pressure.tank = tank; + sample.pressure.value = pressure / 100.0; + if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata); + } else if (pressure != 0) { + WARNING (abstract->context, "Invalid tank with non-zero pressure."); } } - } - if (parser->model == GENIUS || parser->model == HORIZON) { - // Skip the DEND record. - if (!mares_genius_isvalid (data + offset, DEND_SIZE, DEND_TYPE)) { - ERROR (abstract->context, "Invalid DEND record."); - return DC_STATUS_DATAFORMAT; - } - offset += DEND_SIZE; + offset += length; } return DC_STATUS_SUCCESS; } + +static dc_status_t +mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract; + + // Cache the parser data. + dc_status_t rc = mares_iconhd_parser_cache (parser); + if (rc != DC_STATUS_SUCCESS) + return rc; + + if (ISGENIUS(parser->model)) { + return mares_genius_foreach (abstract, callback, userdata); + } else { + return mares_iconhd_foreach (abstract, callback, userdata); + } +} \ No newline at end of file diff --git a/src/parser.c b/src/parser.c index d63c5c67..635232e7 100644 --- a/src/parser.c +++ b/src/parser.c @@ -140,7 +140,7 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, const unsigned rc = mares_darwin_parser_create (&parser, context, data, size, model); break; case DC_FAMILY_MARES_ICONHD: - rc = mares_iconhd_parser_create (&parser, context, data, size, model, serial); + rc = mares_iconhd_parser_create (&parser, context, data, size, model); break; case DC_FAMILY_HW_OSTC: rc = hw_ostc_parser_create (&parser, context, data, size, serial);