Merge branch 'genius'
This commit is contained in:
commit
334105322c
@ -252,6 +252,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},
|
||||
|
||||
@ -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 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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user