Merge git://github.com/libdivecomputer/libdivecomputer
Merge upstream libdivecomputer updates from Jef: - Support the new versions of the Mares Genius with more memory - misc small fixes (Pelagic/atom2 tank pressure fix, proper error codes, license info for AES code) * git://github.com/libdivecomputer/libdivecomputer: Fix the tank pressure reporting Return the correct error code Add support for the Mares Genius Simplify the detection of air integrated models Refactor the gas mix and tank parsing Refactor the date/time parsing Replace the header offset with the header size Use symbolic constants for the commands Add license information to the AES code Update the udev rules
This commit is contained in:
commit
1bbd386959
@ -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"
|
||||
|
||||
24
src/aes.c
24
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 <http://unlicense.org/>
|
||||
|
||||
This is an implementation of the AES128 algorithm, specifically ECB and CBC mode.
|
||||
|
||||
|
||||
26
src/aes.h
26
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 <http://unlicense.org/>
|
||||
*/
|
||||
#ifndef _AES_H_
|
||||
#define _AES_H_
|
||||
|
||||
|
||||
@ -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},
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 NGASMIXES_ICONHD 3
|
||||
#define NGASMIXES_GENIUS 5
|
||||
#define NGASMIXES NGASMIXES_GENIUS
|
||||
|
||||
#define NTANKS_ICONHD NGASMIXES_ICONHD
|
||||
#define NTANKS_GENIUS NGASMIXES_GENIUS
|
||||
#define NTANKS NGASMIXES
|
||||
|
||||
#define AIR 0
|
||||
#define GAUGE 1
|
||||
#define NITROX 2
|
||||
#define FREEDIVE 3
|
||||
#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) {
|
||||
// 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->mode == ICONHD_FREEDIVE) {
|
||||
p += 0x20;
|
||||
} else {
|
||||
p += 2;
|
||||
}
|
||||
} else if (parser->model == SMARTAPNEA) {
|
||||
p += 0x40;
|
||||
} else if (parser->model == SMARTAIR) {
|
||||
p += 2;
|
||||
} else {
|
||||
p += 6;
|
||||
}
|
||||
|
||||
if (datetime) {
|
||||
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;
|
||||
// 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,36 +737,59 @@ 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:
|
||||
if (parser->model == GENIUS) {
|
||||
switch (parser->mode) {
|
||||
case AIR:
|
||||
case NITROX:
|
||||
case GENIUS_AIR:
|
||||
case GENIUS_NITROX_SINGLE:
|
||||
case GENIUS_NITROX_MULTI:
|
||||
case GENIUS_TRIMIX:
|
||||
*((dc_divemode_t *) value) = DC_DIVEMODE_OC;
|
||||
break;
|
||||
case GAUGE:
|
||||
case GENIUS_GAUGE:
|
||||
*((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE;
|
||||
break;
|
||||
case FREEDIVE:
|
||||
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:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user