diff --git a/contrib/udev/libdivecomputer.rules b/contrib/udev/libdivecomputer.rules
index 08c234e..14d0ce6 100644
--- a/contrib/udev/libdivecomputer.rules
+++ b/contrib/udev/libdivecomputer.rules
@@ -7,11 +7,17 @@ SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0030", GROUP="plugde
# Suunto EON Core
SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0033", GROUP="plugdev"
+# Suunto D5
+SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0035", GROUP="plugdev"
+
# Scubapro G2
SUBSYSTEM=="usb", ATTR{idVendor}=="2e6c", ATTR{idProduct}=="3201", GROUP="plugdev"
# Scubapro G2 Console
SUBSYSTEM=="usb", ATTR{idVendor}=="2e6c", ATTR{idProduct}=="3211", GROUP="plugdev"
+# Scubapro G2 HUD
+SUBSYSTEM=="usb", ATTR{idVendor}=="2e6c", ATTR{idProduct}=="0x4201", GROUP="plugdev"
+
# Scubapro Aladin Square
SUBSYSTEM=="usb", ATTR{idVendor}=="c251", ATTR{idProduct}=="2006", GROUP="plugdev"
diff --git a/src/aes.c b/src/aes.c
index a2f00dd..55eac5c 100644
--- a/src/aes.c
+++ b/src/aes.c
@@ -1,4 +1,28 @@
/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
This is an implementation of the AES128 algorithm, specifically ECB and CBC mode.
diff --git a/src/aes.h b/src/aes.h
index 051d813..fedd0a5 100644
--- a/src/aes.h
+++ b/src/aes.h
@@ -1,3 +1,29 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+*/
#ifndef _AES_H_
#define _AES_H_
diff --git a/src/descriptor.c b/src/descriptor.c
index c9f51ee..0d9a561 100644
--- a/src/descriptor.c
+++ b/src/descriptor.c
@@ -254,6 +254,7 @@ static const dc_descriptor_t g_descriptors[] = {
{"Mares", "Icon HD Net Ready", DC_FAMILY_MARES_ICONHD , 0x15, DC_TRANSPORT_SERIAL, NULL},
{"Mares", "Puck Pro", DC_FAMILY_MARES_ICONHD , 0x18, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Nemo Wide 2", DC_FAMILY_MARES_ICONHD , 0x19, DC_TRANSPORT_SERIAL, NULL},
+ {"Mares", "Genius", DC_FAMILY_MARES_ICONHD , 0x1C, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Puck 2", DC_FAMILY_MARES_ICONHD , 0x1F, DC_TRANSPORT_SERIAL, NULL},
{"Mares", "Quad Air", DC_FAMILY_MARES_ICONHD , 0x23, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Smart Air", DC_FAMILY_MARES_ICONHD , 0x24, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c
index 1119b23..1c71de9 100644
--- a/src/divesystem_idive.c
+++ b/src/divesystem_idive.c
@@ -486,7 +486,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
if (!dc_buffer_append(buffer, packet, commands->header.size)) {
ERROR (abstract->context, "Insufficient buffer space available.");
dc_buffer_free(buffer);
- return rc;
+ return DC_STATUS_NOMEMORY;
}
for (unsigned int j = 0; j < nsamples; j += commands->nsamples) {
@@ -515,7 +515,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
if (!dc_buffer_append(buffer, packet, commands->sample.size * n)) {
ERROR (abstract->context, "Insufficient buffer space available.");
dc_buffer_free(buffer);
- return rc;
+ return DC_STATUS_NOMEMORY;
}
}
diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c
index ec9a4eb..bb318e1 100644
--- a/src/mares_iconhd.c
+++ b/src/mares_iconhd.c
@@ -28,11 +28,15 @@
#include "device-private.h"
#include "array.h"
#include "rbstream.h"
+#include "platform.h"
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
#define ISINSTANCE(device) dc_device_isinstance((device), &mares_iconhd_device_vtable)
+#define NSTEPS 1000
+#define STEP(i,n) (NSTEPS * (i) / (n))
+
#define MATRIX 0x0F
#define SMART 0x000010
#define SMARTAPNEA 0x010010
@@ -40,6 +44,7 @@
#define ICONHDNET 0x15
#define PUCKPRO 0x18
#define NEMOWIDE2 0x19
+#define GENIUS 0x1C
#define PUCK2 0x1F
#define QUADAIR 0x23
#define SMARTAIR 0x24
@@ -49,6 +54,20 @@
#define ACK 0xAA
#define EOF 0xEA
+#define XOR 0xA5
+
+#define CMD_VERSION 0xC2
+#define CMD_FLASHSIZE 0xB3
+#define CMD_READ 0xE7
+#define CMD_OBJ_INIT 0xBF
+#define CMD_OBJ_EVEN 0xAC
+#define CMD_OBJ_ODD 0xFE
+
+#define OBJ_LOGBOOK 0x2008
+#define OBJ_LOGBOOK_COUNT 0x01
+#define OBJ_DIVE 0x3000
+#define OBJ_DIVE_HEADER 0x02
+#define OBJ_DIVE_DATA 0x03
#define AIR 0
#define GAUGE 1
@@ -71,6 +90,7 @@ typedef struct mares_iconhd_device_t {
dc_iostream_t *iostream;
const mares_iconhd_layout_t *layout;
unsigned char fingerprint[10];
+ unsigned int fingerprint_size;
unsigned char version[140];
unsigned int model;
unsigned int packetsize;
@@ -131,6 +151,7 @@ mares_iconhd_get_model (mares_iconhd_device_t *device)
{"Icon AIR", ICONHDNET},
{"Puck Pro", PUCKPRO},
{"Nemo Wide 2", NEMOWIDE2},
+ {"Genius", GENIUS},
{"Puck 2", PUCK2},
{"Quad Air", QUADAIR},
{"Smart Air", SMARTAIR},
@@ -311,6 +332,119 @@ mares_iconhd_transfer (mares_iconhd_device_t *device, const unsigned char comman
return DC_STATUS_SUCCESS;
}
+static dc_status_t
+mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *progress, dc_buffer_t *buffer, unsigned int index, unsigned int subindex)
+{
+ 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;
+
+ // Update and emit a progress event.
+ unsigned int initial = 0;
+ if (progress) {
+ initial = progress->current;
+ device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
+ }
+
+ // Transfer the init packet.
+ unsigned char rsp_init[16];
+ unsigned char cmd_init[18] = {
+ CMD_OBJ_INIT,
+ CMD_OBJ_INIT ^ XOR,
+ 0x40,
+ (index >> 0) & 0xFF,
+ (index >> 8) & 0xFF,
+ subindex & 0xFF
+ };
+ memset (cmd_init + 6, 0x00, sizeof(cmd_init) - 6);
+ status = mares_iconhd_transfer (device, cmd_init, sizeof (cmd_init), rsp_init, sizeof (rsp_init));
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to transfer the init packet.");
+ return status;
+ }
+
+ // Verify the packet header.
+ if (memcmp (cmd_init + 3, rsp_init + 1, 3) != 0) {
+ ERROR (abstract->context, "Unexpected packet header.");
+ return DC_STATUS_PROTOCOL;
+ }
+
+ unsigned int nbytes = 0, size = 0;
+ if (rsp_init[0] == 0x41) {
+ // A large (and variable size) payload is split into multiple
+ // data packets. The first packet contains only the total size
+ // of the payload.
+ size = array_uint32_le (rsp_init + 4);
+ } else if (rsp_init[0] == 0x42) {
+ // A short (and fixed size) payload is embedded into the first
+ // data packet.
+ size = sizeof(rsp_init) - 4;
+
+ // Append the payload to the output buffer.
+ if (!dc_buffer_append (buffer, rsp_init + 4, sizeof(rsp_init) - 4)) {
+ ERROR (abstract->context, "Insufficient buffer space available.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ nbytes += sizeof(rsp_init) - 4;
+ } else {
+ ERROR (abstract->context, "Unexpected packet type (%02x).", rsp_init[0]);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ // Update and emit a progress event.
+ if (progress) {
+ progress->current = initial + STEP (nbytes, size);
+ device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
+ }
+
+ unsigned int npackets = 0;
+ while (nbytes < size) {
+ // Get the command byte.
+ unsigned char toggle = npackets % 2;
+ unsigned char cmd = toggle == 0 ? CMD_OBJ_EVEN : CMD_OBJ_ODD;
+
+ // Get the packet size.
+ unsigned int len = size - nbytes;
+ if (len > maxpacket) {
+ len = maxpacket;
+ }
+
+ // Transfer the segment packet.
+ unsigned char rsp_segment[1 + 504];
+ unsigned char cmd_segment[] = {cmd, cmd ^ XOR};
+ status = mares_iconhd_transfer (device, cmd_segment, sizeof (cmd_segment), rsp_segment, len + 1);
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to transfer the segment packet.");
+ return status;
+ }
+
+ // Verify the packet header.
+ if ((rsp_segment[0] & 0xF0) >> 4 != toggle) {
+ ERROR (abstract->context, "Unexpected packet header (%02x).", rsp_segment[0]);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ // Append the payload to the output buffer.
+ if (!dc_buffer_append (buffer, rsp_segment + 1, len)) {
+ ERROR (abstract->context, "Insufficient buffer space available.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ nbytes += len;
+ npackets++;
+
+ // Update and emit the progress events.
+ if (progress) {
+ progress->current = initial + STEP (nbytes, size);
+ device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
+ }
+ }
+
+ return status;
+}
+
dc_status_t
mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
{
@@ -331,6 +465,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
device->iostream = iostream;
device->layout = NULL;
memset (device->fingerprint, 0, sizeof (device->fingerprint));
+ device->fingerprint_size = sizeof (device->fingerprint);
memset (device->version, 0, sizeof (device->version));
device->model = 0;
device->packetsize = 0;
@@ -370,7 +505,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
// Send the version command.
- unsigned char command[] = {0xC2, 0x67};
+ unsigned char command[] = {CMD_VERSION, CMD_VERSION ^ XOR};
status = mares_iconhd_transfer (device, command, sizeof (command),
device->version, sizeof (device->version));
if (status != DC_STATUS_SUCCESS) {
@@ -383,7 +518,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
// Read the size of the flash memory.
unsigned int memsize = 0;
if (device->model == QUAD) {
- unsigned char cmd_flash[] = {0xB3, 0x16};
+ unsigned char cmd_flash[] = {CMD_FLASHSIZE, CMD_FLASHSIZE ^ XOR};
unsigned char rsp_flash[4] = {0};
status = mares_iconhd_transfer (device, cmd_flash, sizeof (cmd_flash), rsp_flash, sizeof (rsp_flash));
if (status != DC_STATUS_SUCCESS) {
@@ -421,6 +556,11 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
device->layout = &mares_iconhdnet_layout;
device->packetsize = 256;
break;
+ case GENIUS:
+ device->layout = &mares_iconhdnet_layout;
+ device->packetsize = 256;
+ device->fingerprint_size = 4;
+ break;
case ICONHDNET:
device->layout = &mares_iconhdnet_layout;
device->packetsize = 4096;
@@ -447,13 +587,13 @@ mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char
{
mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract;
- if (size && size != sizeof (device->fingerprint))
+ if (size && size != device->fingerprint_size)
return DC_STATUS_INVALIDARGS;
if (size)
- memcpy (device->fingerprint, data, sizeof (device->fingerprint));
+ memcpy (device->fingerprint, data, device->fingerprint_size);
else
- memset (device->fingerprint, 0, sizeof (device->fingerprint));
+ memset (device->fingerprint, 0, device->fingerprint_size);
return DC_STATUS_SUCCESS;
}
@@ -473,7 +613,7 @@ mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned
len = device->packetsize;
// Read the packet.
- unsigned char command[] = {0xE7, 0x42,
+ unsigned char command[] = {CMD_READ, CMD_READ ^ XOR,
(address ) & 0xFF,
(address >> 8) & 0xFF,
(address >> 16) & 0xFF,
@@ -516,48 +656,19 @@ mares_iconhd_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
dc_buffer_get_size (buffer), device->packetsize);
}
-
static dc_status_t
-mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
+mares_iconhd_device_foreach_raw (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;
- if (!ISINSTANCE (abstract))
- return DC_STATUS_INVALIDARGS;
-
const mares_iconhd_layout_t *layout = device->layout;
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
- progress.maximum = layout->rb_profile_end - layout->rb_profile_begin + 4;
+ progress.maximum = layout->rb_profile_end - layout->rb_profile_begin;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
- // Emit a vendor event.
- dc_event_vendor_t vendor;
- vendor.data = device->version;
- 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;
- }
-
- // Update and emit a progress event.
- progress.current += sizeof (serial);
- device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
-
- // 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);
-
// Get the model code.
unsigned int model = device->model;
@@ -734,3 +845,113 @@ mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
return rc;
}
+
+static dc_status_t
+mares_iconhd_device_foreach_object (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;
+
+ // Enable progress notifications.
+ dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
+ device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+
+ // Allocate memory for the dives.
+ dc_buffer_t *buffer = dc_buffer_new (4096);
+ if (buffer == NULL) {
+ ERROR (abstract->context, "Failed to allocate memory.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ // Read the number of dives.
+ rc = mares_iconhd_read_object (device, NULL, buffer, OBJ_LOGBOOK, OBJ_LOGBOOK_COUNT);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to read the number of dives.");
+ dc_buffer_free (buffer);
+ return rc;
+ }
+
+ if (dc_buffer_get_size (buffer) < 2) {
+ ERROR (abstract->context, "Unexpected number of bytes received (" DC_PRINTF_SIZE ").",
+ dc_buffer_get_size (buffer));
+ dc_buffer_free (buffer);
+ return DC_STATUS_PROTOCOL;
+ }
+
+ // Get the number of dives.
+ unsigned int ndives = array_uint16_le (dc_buffer_get_data(buffer));
+
+ // Update and emit a progress event.
+ progress.current = 1 * NSTEPS;
+ progress.maximum = (1 + ndives * 2) * NSTEPS;
+ device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+
+ // Download the dives.
+ for (unsigned int i = 0; i < ndives; ++i) {
+ // Erase the buffer.
+ dc_buffer_clear (buffer);
+
+ // Read the dive header.
+ rc = mares_iconhd_read_object (device, &progress, buffer, OBJ_DIVE + i, OBJ_DIVE_HEADER);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to read the dive header.");
+ break;
+ }
+
+ // Check the fingerprint data.
+ if (memcmp (dc_buffer_get_data (buffer) + 0x08, device->fingerprint, device->fingerprint_size) == 0) {
+ INFO (abstract->context, "Stopping due to detecting a matching fingerprint");
+ break;
+ }
+
+ // Read the dive data.
+ rc = mares_iconhd_read_object (device, &progress, buffer, OBJ_DIVE + i, OBJ_DIVE_DATA);
+ if (rc != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to read the dive data.");
+ break;
+ }
+
+ const unsigned char *data = dc_buffer_get_data (buffer);
+ if (callback && !callback (data, dc_buffer_get_size (buffer), data + 0x08, device->fingerprint_size, userdata)) {
+ break;
+ }
+ }
+
+ dc_buffer_free(buffer);
+
+ return rc;
+}
+
+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.
+ dc_event_vendor_t vendor;
+ vendor.data = device->version;
+ 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) {
+ return mares_iconhd_device_foreach_object (abstract, callback, userdata);
+ } else {
+ return mares_iconhd_device_foreach_raw (abstract, callback, userdata);
+ }
+}
diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c
index c12e8c4..a76ca20 100644
--- a/src/mares_iconhd_parser.c
+++ b/src/mares_iconhd_parser.c
@@ -27,6 +27,7 @@
#include "context-private.h"
#include "parser-private.h"
#include "array.h"
+#include "checksum.h"
#define ISINSTANCE(parser) dc_parser_isinstance((parser), &mares_iconhd_parser_vtable)
@@ -34,19 +35,94 @@
#define SMARTAPNEA 0x010010
#define ICONHD 0x14
#define ICONHDNET 0x15
+#define GENIUS 0x1C
#define QUADAIR 0x23
#define SMARTAIR 0x24
-#define NGASMIXES 3
-#define NTANKS NGASMIXES
+#define NGASMIXES_ICONHD 3
+#define NGASMIXES_GENIUS 5
+#define NGASMIXES NGASMIXES_GENIUS
-#define AIR 0
-#define GAUGE 1
-#define NITROX 2
-#define FREEDIVE 3
+#define NTANKS_ICONHD NGASMIXES_ICONHD
+#define NTANKS_GENIUS NGASMIXES_GENIUS
+#define NTANKS NGASMIXES
+
+#define ICONHD_AIR 0
+#define ICONHD_GAUGE 1
+#define ICONHD_NITROX 2
+#define ICONHD_FREEDIVE 3
+
+#define GENIUS_AIR 0
+#define GENIUS_NITROX_SINGLE 1
+#define GENIUS_NITROX_MULTI 2
+#define GENIUS_TRIMIX 3
+#define GENIUS_GAUGE 4
+#define GENIUS_FREEDIVE 5
+
+// Record types and sizes
+#define DSTR_TYPE 0x44535452 // Dive start record
+#define DSTR_SIZE 58
+#define TISS_TYPE 0x54495353 // Tissue record
+#define TISS_SIZE 138
+#define DPRS_TYPE 0x44505253 // Sample record
+#define DPRS_SIZE 34
+#define AIRS_TYPE 0x41495253 // Air integration record
+#define AIRS_SIZE 16
+#define DEND_TYPE 0x44454E44 // Dive end record
+#define DEND_SIZE 162
+
+#define GASMIX_OFF 0
+#define GASMIX_READY 1
+#define GASMIX_INUSE 2
+#define GASMIX_IGNRD 3
+
+#define WATER_SALT 0
+#define WATER_FRESH 1
+#define WATER_EN13319 2
+
+#define ALARM_NONE 0
+#define ALARM_SLOW_DOWN 1
+#define ALARM_FAST_ASCENT 2
+#define ALARM_UNCONTROLLED_ASCENT 3
+#define ALARM_MOD_REACHED 4
+#define ALARM_CNS_DANGER 5
+#define ALARM_CNS_EXTREME 6
+#define ALARM_MISSED_DECO 7
+#define ALARM_DIVE_VIOLATION_DECO 8
+#define ALARM_LOW_BATTERY 9
+#define ALARM_VERY_LOW_BATTERY 10
+#define ALARM_PROBE_LOW_BATTERY 11
+#define ALARM_LOW_TANK_PRESSURE 12
+#define ALARM_TANK_RESERVE_REACHED 13
+#define ALARM_TANK_LOST_LINK 14
+#define ALARM_MAX_DIVE_DEPTH 15
+#define ALARM_RUN_AWAY_DECO 16
+#define ALARM_TANK_HALF_REACHED 17
+#define ALARM_NODECO_2MIN 18
+#define ALARM_NODECO_DECO 19
+#define ALARM_MULTIGAS_ATANKISLOW 20
+#define ALARM_DIVETIME_HALFTIME 21
+#define ALARM_DIVETIME_FULLTIME 22
+#define ALARM_GAS_SWITCHPOINT 23
+#define ALARM_GAS_IGNORED 24
+#define ALARM_GAS_CHANGED 25
+#define ALARM_GAS_NOTCHANGED 26
+#define ALARM_GAS_ADDED 27
typedef struct mares_iconhd_parser_t mares_iconhd_parser_t;
+typedef struct mares_iconhd_gasmix_t {
+ unsigned int oxygen;
+ unsigned int helium;
+} mares_iconhd_gasmix_t;
+
+typedef struct mares_iconhd_tank_t {
+ unsigned int volume;
+ unsigned int workpressure;
+ unsigned int beginpressure;
+ unsigned int endpressure;
+} mares_iconhd_tank_t;
+
struct mares_iconhd_parser_t {
dc_parser_t base;
unsigned int model;
@@ -54,14 +130,15 @@ struct mares_iconhd_parser_t {
unsigned int cached;
unsigned int mode;
unsigned int nsamples;
- unsigned int footer;
unsigned int samplesize;
+ unsigned int headersize;
unsigned int settings;
unsigned int interval;
unsigned int samplerate;
unsigned int ntanks;
unsigned int ngasmixes;
- unsigned int oxygen[NGASMIXES];
+ mares_iconhd_gasmix_t gasmix[NGASMIXES];
+ mares_iconhd_tank_t tank[NTANKS];
};
static dc_status_t mares_iconhd_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
@@ -79,17 +156,35 @@ 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);
+ if (crc != ccrc) {
+ return 0;
+ }
+
+ return 1;
+}
+
static dc_status_t
-mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
+mares_iconhd_cache (mares_iconhd_parser_t *parser)
{
dc_parser_t *abstract = (dc_parser_t *) parser;
const unsigned char *data = parser->base.data;
unsigned int size = parser->base.size;
- if (parser->cached) {
- return DC_STATUS_SUCCESS;
- }
-
unsigned int header = 0x5C;
if (parser->model == ICONHDNET)
header = 0x80;
@@ -134,7 +229,7 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
headersize = 0x84;
samplesize = 12;
} else if (parser->model == SMART) {
- if (mode == FREEDIVE) {
+ if (mode == ICONHD_FREEDIVE) {
headersize = 0x2E;
samplesize = 6;
} else {
@@ -160,7 +255,7 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
unsigned int settings = 0;
if (parser->model == SMARTAPNEA) {
settings = array_uint16_le (p + 0x1C);
- } else if (parser->mode == FREEDIVE) {
+ } else if (parser->mode == ICONHD_FREEDIVE) {
settings = array_uint16_le (p + 0x08);
} else {
settings = array_uint16_le (p + 0x0C);
@@ -195,56 +290,190 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
// Gas mixes
unsigned int ngasmixes = 0;
- unsigned int oxygen[NGASMIXES] = {0};
- if (mode == GAUGE || mode == FREEDIVE) {
+ mares_iconhd_gasmix_t gasmix[NGASMIXES_ICONHD] = {0};
+ if (mode == ICONHD_GAUGE || mode == ICONHD_FREEDIVE) {
ngasmixes = 0;
- } else if (mode == AIR) {
- oxygen[0] = 21;
+ } else if (mode == ICONHD_AIR) {
+ gasmix[0].oxygen = 21;
+ gasmix[0].helium = 0;
ngasmixes = 1;
} else {
// Count the number of active gas mixes. The active gas
// mixes are always first, so we stop counting as soon
// as the first gas marked as disabled is found.
ngasmixes = 0;
- while (ngasmixes < NGASMIXES) {
+ while (ngasmixes < NGASMIXES_ICONHD) {
if (p[0x10 + ngasmixes * 4 + 1] & 0x80)
break;
- oxygen[ngasmixes] = p[0x10 + ngasmixes * 4];
+ gasmix[ngasmixes].oxygen = p[0x10 + ngasmixes * 4];
+ gasmix[ngasmixes].helium = 0;
ngasmixes++;
}
}
// Tanks
unsigned int ntanks = 0;
+ mares_iconhd_tank_t tank[NTANKS_ICONHD] = {0};
if (parser->model == ICONHDNET || parser->model == QUADAIR || parser->model == SMARTAIR) {
unsigned int tankoffset = (parser->model == ICONHDNET) ? 0x58 : 0x5C;
- while (ntanks < NTANKS) {
- unsigned int beginpressure = array_uint16_le (p + tankoffset + ntanks * 4 + 0);
- unsigned int endpressure = array_uint16_le (p + tankoffset + ntanks * 4 + 2);
- if (beginpressure == 0 && (endpressure == 0 || endpressure == 36000))
+ while (ntanks < NTANKS_ICONHD) {
+ tank[ntanks].volume = array_uint16_le (p + tankoffset + 0x0C + ntanks * 8 + 0);
+ tank[ntanks].workpressure = array_uint16_le (p + tankoffset + 0x0C + ntanks * 8 + 2);
+ tank[ntanks].beginpressure = array_uint16_le (p + tankoffset + ntanks * 4 + 0);
+ tank[ntanks].endpressure = array_uint16_le (p + tankoffset + ntanks * 4 + 2);
+ if (tank[ntanks].beginpressure == 0 && (tank[ntanks].endpressure == 0 || tank[ntanks].endpressure == 36000))
break;
ntanks++;
}
}
+ // Limit the size to the actual length.
+ parser->base.size = length;
+
+ // Cache the data for later use.
+ parser->mode = mode;
+ parser->nsamples = nsamples;
+ parser->samplesize = samplesize;
+ parser->headersize = headersize;
+ parser->settings = settings;
+ parser->interval = interval;
+ parser->samplerate = samplerate;
+ parser->ntanks = ntanks;
+ parser->ngasmixes = ngasmixes;
+ for (unsigned int i = 0; i < ngasmixes; ++i) {
+ parser->gasmix[i] = gasmix[i];
+ }
+ for (unsigned int i = 0; i < ntanks; ++i) {
+ parser->tank[i] = tank[i];
+ }
+ parser->cached = 1;
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+mares_genius_cache (mares_iconhd_parser_t *parser)
+{
+ dc_parser_t *abstract = (dc_parser_t *) parser;
+ const unsigned char *data = parser->base.data;
+ unsigned int size = parser->base.size;
+
+ if (size < 4) {
+ ERROR (abstract->context, "Buffer overflow detected!");
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ // Check the header type and version.
+ unsigned int type = array_uint16_le (data);
+ unsigned int major = data[2];
+ unsigned int minor = data[3];
+ if (type != 1 || major != 0 || minor != 0) {
+ ERROR (abstract->context, "Unsupported object type (%u) or version (%u.%u).",
+ type, major, minor);
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ // Get the header size.
+ unsigned int headersize = 0xB8;
+ if (headersize > size) {
+ ERROR (abstract->context, "Buffer overflow detected!");
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ // Get the number of samples in the profile data.
+ unsigned int nsamples = array_uint16_le (data + 0x20);
+
+ // Get the dive settings.
+ unsigned int settings = array_uint32_le (data + 0x0C);
+
+ // Get the dive mode.
+ unsigned int mode = settings & 0xF;
+
+ // Calculate the total number of bytes for this dive.
+ unsigned int nbytes = headersize + 4 + DSTR_SIZE + TISS_SIZE + nsamples * DPRS_SIZE + (nsamples / 4) * AIRS_SIZE + DEND_SIZE;
+ if (nbytes > size) {
+ ERROR (abstract->context, "Buffer overflow detected!");
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ // Gas mixes and tanks.
+ unsigned int ntanks = 0;
+ unsigned int ngasmixes = 0;
+ mares_iconhd_gasmix_t gasmix[NGASMIXES_GENIUS] = {0};
+ mares_iconhd_tank_t tank[NTANKS_GENIUS] = {0};
+ for (unsigned int i = 0; i < NGASMIXES_GENIUS; i++) {
+ unsigned int offset = 0x54 + i * 20;
+ unsigned int gasmixparams = array_uint32_le(data + offset + 0);
+ unsigned int beginpressure = array_uint16_le(data + offset + 4);
+ unsigned int endpressure = array_uint16_le(data + offset + 6);
+ unsigned int volume = array_uint16_le(data + offset + 8);
+ unsigned int workpressure = array_uint16_le(data + offset + 10);
+
+ unsigned int o2 = (gasmixparams ) & 0x7F;
+ unsigned int n2 = (gasmixparams >> 7) & 0x7F;
+ unsigned int he = (gasmixparams >> 14) & 0x7F;
+ unsigned int state = (gasmixparams >> 21) & 0x03;
+ unsigned int changed = (gasmixparams >> 23) & 0x01;
+
+ if (o2 + n2 + he != 100) {
+ WARNING (abstract->context, "Invalid gas mix (%u%% He, %u%% O2, %u%% N2).", he, o2, n2);
+ }
+
+ // The active gas mixes are always first, so we stop processing
+ // as soon as the first gas mix marked as disabled is found.
+ if (state != GASMIX_OFF && ngasmixes == i) {
+ gasmix[i].oxygen = o2;
+ gasmix[i].helium = he;
+ ngasmixes++;
+ }
+
+ // Assume the active transmitters are always first, so we can
+ // stop processing as soon as the first inactive transmitter is
+ // found.
+ if ((beginpressure != 0 || (endpressure != 0 && endpressure != 36000)) &&
+ (ntanks == i)) {
+ tank[i].volume = volume;
+ tank[i].workpressure = workpressure;
+ tank[i].beginpressure = beginpressure;
+ tank[i].endpressure = endpressure;
+ ntanks++;
+ }
+ }
+
// Cache the data for later use.
parser->mode = mode;
parser->nsamples = nsamples;
- parser->footer = length - headersize;
- parser->samplesize = samplesize;
+ parser->samplesize = DPRS_SIZE;
+ parser->headersize = headersize;
parser->settings = settings;
- parser->interval = interval;
- parser->samplerate = samplerate;
+ parser->interval = 5;
+ parser->samplerate = 1;
parser->ntanks = ntanks;
parser->ngasmixes = ngasmixes;
for (unsigned int i = 0; i < ngasmixes; ++i) {
- parser->oxygen[i] = oxygen[i];
+ parser->gasmix[i] = gasmix[i];
+ }
+ for (unsigned int i = 0; i < ntanks; ++i) {
+ parser->tank[i] = tank[i];
}
parser->cached = 1;
return DC_STATUS_SUCCESS;
}
+static dc_status_t
+mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
+{
+ if (parser->cached) {
+ return DC_STATUS_SUCCESS;
+ }
+
+ if (parser->model == GENIUS) {
+ return mares_genius_cache (parser);
+ } else {
+ return mares_iconhd_cache (parser);
+ }
+}
dc_status_t
mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
@@ -264,17 +493,24 @@ mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i
// Set the default values.
parser->model = model;
parser->cached = 0;
- parser->mode = AIR;
+ parser->mode = (model == GENIUS) ? GENIUS_AIR : ICONHD_AIR;
parser->nsamples = 0;
- parser->footer = 0;
parser->samplesize = 0;
+ parser->headersize = 0;
parser->settings = 0;
parser->interval = 0;
parser->samplerate = 0;
parser->ntanks = 0;
parser->ngasmixes = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
- parser->oxygen[i] = 0;
+ parser->gasmix[i].oxygen = 0;
+ parser->gasmix[i].helium = 0;
+ }
+ for (unsigned int i = 0; i < NTANKS; ++i) {
+ parser->tank[i].volume = 0;
+ parser->tank[i].workpressure = 0;
+ parser->tank[i].beginpressure = 0;
+ parser->tank[i].endpressure = 0;
}
*out = (dc_parser_t*) parser;
@@ -290,17 +526,24 @@ mares_iconhd_parser_set_data (dc_parser_t *abstract, const unsigned char *data,
// Reset the cache.
parser->cached = 0;
- parser->mode = AIR;
+ parser->mode = (parser->model == GENIUS) ? GENIUS_AIR : ICONHD_AIR;
parser->nsamples = 0;
- parser->footer = 0;
parser->samplesize = 0;
+ parser->headersize = 0;
parser->settings = 0;
parser->interval = 0;
parser->samplerate = 0;
parser->ntanks = 0;
parser->ngasmixes = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
- parser->oxygen[i] = 0;
+ parser->gasmix[i].oxygen = 0;
+ parser->gasmix[i].helium = 0;
+ }
+ for (unsigned int i = 0; i < NTANKS; ++i) {
+ parser->tank[i].volume = 0;
+ parser->tank[i].workpressure = 0;
+ parser->tank[i].beginpressure = 0;
+ parser->tank[i].endpressure = 0;
}
return DC_STATUS_SUCCESS;
@@ -317,28 +560,43 @@ mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime
if (rc != DC_STATUS_SUCCESS)
return rc;
- const unsigned char *p = abstract->data + parser->footer;
- if (parser->model == SMART) {
- if (parser->mode == FREEDIVE) {
- p += 0x20;
- } else {
- p += 2;
+ // Pointer to the header data.
+ const unsigned char *p = abstract->data;
+ if (parser->model != GENIUS) {
+ p += abstract->size - parser->headersize;
+ if (parser->model != SMART && parser->model != SMARTAPNEA && parser->model != SMARTAIR) {
+ p += 4;
}
+ }
+
+ // Offset to the date/time field.
+ if (parser->model == GENIUS) {
+ p += 0x08;
} else if (parser->model == SMARTAPNEA) {
p += 0x40;
- } else if (parser->model == SMARTAIR) {
- p += 2;
+ } else if (parser->mode == ICONHD_FREEDIVE) {
+ p += 0x20;
} else {
- p += 6;
+ p += 2;
}
if (datetime) {
- datetime->hour = array_uint16_le (p + 0);
- datetime->minute = array_uint16_le (p + 2);
- datetime->second = 0;
- datetime->day = array_uint16_le (p + 4);
- datetime->month = array_uint16_le (p + 6) + 1;
- datetime->year = array_uint16_le (p + 8) + 1900;
+ if (parser->model == GENIUS) {
+ unsigned int timestamp = array_uint32_le (p);
+ datetime->hour = (timestamp ) & 0x1F;
+ datetime->minute = (timestamp >> 5) & 0x3F;
+ datetime->second = 0;
+ datetime->day = (timestamp >> 11) & 0x1F;
+ datetime->month = (timestamp >> 16) & 0x0F;
+ datetime->year = (timestamp >> 20) & 0x0FFF;
+ } else {
+ datetime->hour = array_uint16_le (p + 0);
+ datetime->minute = array_uint16_le (p + 2);
+ datetime->second = 0;
+ datetime->day = array_uint16_le (p + 4);
+ datetime->month = array_uint16_le (p + 6) + 1;
+ datetime->year = array_uint16_le (p + 8) + 1900;
+ }
datetime->timezone = DC_TIMEZONE_NONE;
}
@@ -356,13 +614,16 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
if (rc != DC_STATUS_SUCCESS)
return rc;
- const unsigned char *p = abstract->data + parser->footer;
- if (parser->model != SMART && parser->model != SMARTAPNEA && parser->model != SMARTAIR) {
- p += 4;
+ // Pointer to the header data.
+ const unsigned char *p = abstract->data;
+ if (parser->model != GENIUS) {
+ p += abstract->size - parser->headersize;
+ if (parser->model != SMART && parser->model != SMARTAPNEA && parser->model != SMARTAIR) {
+ p += 4;
+ }
}
- unsigned int volume = 0, workpressure = 0;
- unsigned int tankoffset = 0;
+ unsigned int metric = parser->model == GENIUS ? p[0x34] : parser->settings & 0x0100;
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
dc_tank_t *tank = (dc_tank_t *) value;
@@ -371,9 +632,11 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
if (value) {
switch (type) {
case DC_FIELD_DIVETIME:
- if (parser->model == SMARTAPNEA) {
+ if (parser->model == GENIUS) {
+ *((unsigned int *) value) = parser->nsamples * parser->interval;
+ } else if (parser->model == SMARTAPNEA) {
*((unsigned int *) value) = array_uint16_le (p + 0x24);
- } else if (parser->mode == FREEDIVE) {
+ } else if (parser->mode == ICONHD_FREEDIVE) {
unsigned int divetime = 0;
unsigned int offset = 4;
for (unsigned int i = 0; i < parser->nsamples; ++i) {
@@ -386,9 +649,11 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
}
break;
case DC_FIELD_MAXDEPTH:
- if (parser->model == SMARTAPNEA)
+ if (parser->model == GENIUS)
+ *((double *) value) = array_uint16_le (p + 0x22) / 10.0;
+ else if (parser->model == SMARTAPNEA)
*((double *) value) = array_uint16_le (p + 0x3A) / 10.0;
- else if (parser->mode == FREEDIVE)
+ else if (parser->mode == ICONHD_FREEDIVE)
*((double *) value) = array_uint16_le (p + 0x1A) / 10.0;
else
*((double *) value) = array_uint16_le (p + 0x00) / 10.0;
@@ -397,31 +662,28 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
*((unsigned int *) value) = parser->ngasmixes;
break;
case DC_FIELD_GASMIX:
- gasmix->oxygen = parser->oxygen[flags] / 100.0;
- gasmix->helium = 0.0;
+ gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
+ gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
case DC_FIELD_TANK_COUNT:
*((unsigned int *) value) = parser->ntanks;
break;
case DC_FIELD_TANK:
- tankoffset = (parser->model == ICONHDNET) ? 0x58 : 0x5C;
- volume = array_uint16_le (p + tankoffset + 0x0C + flags * 8 + 0);
- workpressure = array_uint16_le (p + tankoffset + 0x0C + flags * 8 + 2);
- if (parser->settings & 0x0100) {
+ if (metric) {
tank->type = DC_TANKVOLUME_METRIC;
- tank->volume = volume;
- tank->workpressure = workpressure;
+ tank->volume = parser->tank[flags].volume;
+ tank->workpressure = parser->tank[flags].workpressure;
} else {
- if (workpressure == 0)
+ if (parser->tank[flags].workpressure == 0)
return DC_STATUS_DATAFORMAT;
tank->type = DC_TANKVOLUME_IMPERIAL;
- tank->volume = volume * CUFT * 1000.0;
- tank->volume /= workpressure * PSI / ATM;
- tank->workpressure = workpressure * PSI / BAR;
+ tank->volume = parser->tank[flags].volume * CUFT * 1000.0;
+ tank->volume /= parser->tank[flags].workpressure * PSI / ATM;
+ tank->workpressure = parser->tank[flags].workpressure * PSI / BAR;
}
- tank->beginpressure = array_uint16_le (p + tankoffset + flags * 4 + 0) / 100.0;
- tank->endpressure = array_uint16_le (p + tankoffset + flags * 4 + 2) / 100.0;
+ tank->beginpressure = parser->tank[flags].beginpressure / 100.0;
+ tank->endpressure = parser->tank[flags].endpressure / 100.0;
if (flags < parser->ngasmixes) {
tank->gasmix = flags;
} else {
@@ -429,16 +691,35 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
}
break;
case DC_FIELD_ATMOSPHERIC:
- // Pressure (1/8 millibar)
- if (parser->model == SMARTAPNEA)
+ if (parser->model == GENIUS)
+ *((double *) value) = array_uint16_le (p + 0x3E) / 1000.0;
+ else if (parser->model == SMARTAPNEA)
*((double *) value) = array_uint16_le (p + 0x38) / 1000.0;
- else if (parser->mode == FREEDIVE)
+ else if (parser->mode == ICONHD_FREEDIVE)
*((double *) value) = array_uint16_le (p + 0x18) / 1000.0;
else
*((double *) value) = array_uint16_le (p + 0x22) / 8000.0;
break;
case DC_FIELD_SALINITY:
- if (parser->model == SMARTAPNEA) {
+ if (parser->model == GENIUS) {
+ unsigned int salinity = (parser->settings >> 5) & 0x03;
+ switch (salinity) {
+ case WATER_FRESH:
+ water->type = DC_WATER_FRESH;
+ water->density = 0.0;
+ break;
+ case WATER_SALT:
+ water->type = DC_WATER_SALT;
+ water->density = 0.0;
+ break;
+ case WATER_EN13319:
+ water->type = DC_WATER_SALT;
+ water->density = MSW / GRAVITY;
+ break;
+ default:
+ return DC_STATUS_DATAFORMAT;
+ }
+ } else if (parser->model == SMARTAPNEA) {
unsigned int salinity = parser->settings & 0x003F;
if (salinity == 0) {
water->type = DC_WATER_FRESH;
@@ -456,35 +737,58 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
}
break;
case DC_FIELD_TEMPERATURE_MINIMUM:
- if (parser->model == SMARTAPNEA)
+ if (parser->model == GENIUS)
+ *((double *) value) = (signed short) array_uint16_le (p + 0x28) / 10.0;
+ else if (parser->model == SMARTAPNEA)
*((double *) value) = (signed short) array_uint16_le (p + 0x3E) / 10.0;
- else if (parser->mode == FREEDIVE)
+ else if (parser->mode == ICONHD_FREEDIVE)
*((double *) value) = (signed short) array_uint16_le (p + 0x1C) / 10.0;
else
*((double *) value) = (signed short) array_uint16_le (p + 0x42) / 10.0;
break;
case DC_FIELD_TEMPERATURE_MAXIMUM:
- if (parser->model == SMARTAPNEA)
+ if (parser->model == GENIUS)
+ *((double *) value) = (signed short) array_uint16_le (p + 0x26) / 10.0;
+ else if (parser->model == SMARTAPNEA)
*((double *) value) = (signed short) array_uint16_le (p + 0x3C) / 10.0;
- else if (parser->mode == FREEDIVE)
+ else if (parser->mode == ICONHD_FREEDIVE)
*((double *) value) = (signed short) array_uint16_le (p + 0x1E) / 10.0;
else
*((double *) value) = (signed short) array_uint16_le (p + 0x44) / 10.0;
break;
case DC_FIELD_DIVEMODE:
- switch (parser->mode) {
- case AIR:
- case NITROX:
- *((dc_divemode_t *) value) = DC_DIVEMODE_OC;
- break;
- case GAUGE:
- *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE;
- break;
- case FREEDIVE:
- *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE;
- break;
- default:
- return DC_STATUS_DATAFORMAT;
+ if (parser->model == GENIUS) {
+ switch (parser->mode) {
+ case GENIUS_AIR:
+ case GENIUS_NITROX_SINGLE:
+ case GENIUS_NITROX_MULTI:
+ case GENIUS_TRIMIX:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_OC;
+ break;
+ case GENIUS_GAUGE:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE;
+ break;
+ case GENIUS_FREEDIVE:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE;
+ break;
+ default:
+ return DC_STATUS_DATAFORMAT;
+ }
+ } else {
+ switch (parser->mode) {
+ case ICONHD_AIR:
+ case ICONHD_NITROX:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_OC;
+ break;
+ case ICONHD_GAUGE:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE;
+ break;
+ case ICONHD_FREEDIVE:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE;
+ break;
+ default:
+ return DC_STATUS_DATAFORMAT;
+ }
}
break;
default:
@@ -519,8 +823,43 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
// Previous gas mix - initialize with impossible value
unsigned int gasmix_previous = 0xFFFFFFFF;
- unsigned int time = 0;
+ unsigned int isairintegrated = (parser->model == ICONHDNET || parser->model == QUADAIR || parser->model == SMARTAIR || parser->model == GENIUS);
+
unsigned int offset = 4;
+ unsigned int marker = 0;
+ if (parser->model == GENIUS) {
+ // Skip the dive header.
+ data += parser->headersize;
+
+ // Check the profile type and version.
+ unsigned int type = array_uint16_le (data);
+ unsigned int major = data[2];
+ unsigned int minor = data[3];
+ if (type != 0 || major != 2 || minor != 0) {
+ 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) {
dc_sample_value_t sample = {0};
@@ -555,7 +894,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
offset += 2 * parser->samplerate;
}
- } else if (parser->mode == FREEDIVE) {
+ } else if (parser->model != GENIUS && 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);
@@ -581,23 +920,39 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
offset += parser->samplesize;
nsamples++;
} else {
+ unsigned int depth = 0, temperature = 0;
+ unsigned int gasmix = 0, misc = 0, alarms = 0;
+ if (parser->model == GENIUS) {
+ if (!mares_genius_isvalid (data + offset, DPRS_SIZE, DPRS_TYPE)) {
+ ERROR (abstract->context, "Invalid DPRS record.");
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ depth = array_uint16_le (data + offset + marker + 0);
+ temperature = array_uint16_le (data + offset + marker + 4);
+ alarms = array_uint32_le (data + offset + marker + 0x0C);
+ misc = array_uint32_le (data + offset + marker + 0x14);
+ gasmix = (misc >> 6) & 0xF;
+ } else {
+ depth = array_uint16_le (data + offset + 0);
+ temperature = array_uint16_le (data + offset + 2) & 0x0FFF;
+ 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).
- unsigned int depth = array_uint16_le (data + offset + 0);
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
// Temperature (1/10 °C).
- unsigned int temperature = array_uint16_le (data + offset + 2) & 0x0FFF;
sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
// Current gas mix
- unsigned int gasmix = (data[offset + 3] & 0xF0) >> 4;
if (parser->ngasmixes > 0) {
if (gasmix >= parser->ngasmixes) {
ERROR (abstract->context, "Invalid gas mix index.");
@@ -610,14 +965,61 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
}
}
+ if (parser->model == GENIUS) {
+ // Deco stop / NDL.
+ unsigned int decostop = (misc >> 18) & 0x01;
+ unsigned int decodepth = (misc >> 19) & 0x7F;
+ 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 = array_uint16_le (data + offset + marker + 0x0A) * 60;
+ 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);
+ }
+ }
+ }
+
offset += parser->samplesize;
nsamples++;
// Some extra data.
- if ((parser->model == ICONHDNET || parser->model == QUADAIR || parser->model == SMARTAIR) &&
- (nsamples % 4) == 0) {
+ if (isairintegrated && (nsamples % 4) == 0) {
+ if (parser->model == GENIUS && !mares_genius_isvalid (data + offset, AIRS_SIZE, AIRS_TYPE)) {
+ ERROR (abstract->context, "Invalid AIRS record.");
+ return DC_STATUS_DATAFORMAT;
+ }
+
// Pressure (1/100 bar).
- unsigned int pressure = array_uint16_le(data + offset);
+ unsigned int pressure = array_uint16_le(data + offset + marker + 0);
if (gasmix < parser->ntanks) {
sample.pressure.tank = gasmix;
sample.pressure.value = pressure / 100.0;
@@ -626,10 +1028,19 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
WARNING (abstract->context, "Invalid tank with non-zero pressure.");
}
- offset += 8;
+ offset += (parser->model == GENIUS) ? AIRS_SIZE : 8;
}
}
}
+ if (parser->model == GENIUS) {
+ // 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;
+ }
+
return DC_STATUS_SUCCESS;
}
diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c
index 6c0009a..de7f303 100644
--- a/src/oceanic_atom2_parser.c
+++ b/src/oceanic_atom2_parser.c
@@ -736,7 +736,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
parser->model == GEO || parser->model == GEO20 ||
parser->model == MANTA || parser->model == I300 ||
parser->model == I200 || parser->model == I100 ||
- parser->model == I300C || TALIS) {
+ parser->model == I300C || parser->model == TALIS) {
have_pressure = 0;
}