Merge branch 'genius'

This commit is contained in:
Jef Driesen 2019-06-26 15:43:36 +02:00
commit 334105322c
3 changed files with 774 additions and 141 deletions

View File

@ -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},

View File

@ -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);
}
}

View File

@ -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;
}