Merge branch 'eonsteel'
This commit is contained in:
commit
5629efad94
@ -125,43 +125,49 @@ static void put_le32(unsigned int val, unsigned char *p)
|
|||||||
p[3] = val >> 24;
|
p[3] = val >> 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int receive_data(suunto_eonsteel_device_t *eon, unsigned char *buffer, int size)
|
/*
|
||||||
|
* Get a single 64-byte packet from the dive computer. This handles packet
|
||||||
|
* logging and any obvious packet-level errors, and returns the payload of
|
||||||
|
* packet.
|
||||||
|
*
|
||||||
|
* The two first bytes of the packet are packet-level metadata: the report
|
||||||
|
* type (always 0x3f), and then the size of the valid data in the packet.
|
||||||
|
*
|
||||||
|
* The maximum payload is 62 bytes.
|
||||||
|
*/
|
||||||
|
#define PACKET_SIZE 64
|
||||||
|
static int receive_packet(suunto_eonsteel_device_t *eon, unsigned char *buffer, int size)
|
||||||
{
|
{
|
||||||
const int InEndpoint = 0x82;
|
|
||||||
unsigned char buf[64];
|
unsigned char buf[64];
|
||||||
int ret = 0;
|
const int InEndpoint = 0x82;
|
||||||
|
int rc, transferred, len;
|
||||||
|
|
||||||
for (;;) {
|
/* 5000 = 5s timeout */
|
||||||
int rc, transferred, len;
|
rc = libusb_interrupt_transfer(eon->handle, InEndpoint, buf, PACKET_SIZE, &transferred, 5000);
|
||||||
|
if (rc) {
|
||||||
rc = libusb_interrupt_transfer(eon->handle, InEndpoint, buf, sizeof(buf), &transferred, 5000);
|
ERROR(eon->base.context, "read interrupt transfer failed (%s)", libusb_error_name(rc));
|
||||||
if (rc || transferred != sizeof(buf)) {
|
return -1;
|
||||||
ERROR(eon->base.context, "incomplete read interrupt transfer");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
// dump every incoming packet?
|
|
||||||
HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf, transferred);
|
|
||||||
if (buf[0] != 0x3f) {
|
|
||||||
ERROR(eon->base.context, "read interrupt transfer returns wrong report type");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len = buf[1];
|
|
||||||
if (len > sizeof(buf)-2) {
|
|
||||||
ERROR(eon->base.context, "read interrupt transfer reports short length");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (len > size) {
|
|
||||||
ERROR(eon->base.context, "read interrupt transfer reports excessive length");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memcpy(buffer+ret, buf+2, len);
|
|
||||||
size -= len;
|
|
||||||
ret += len;
|
|
||||||
if (len < sizeof(buf)-2)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (transferred != PACKET_SIZE) {
|
||||||
return ret;
|
ERROR(eon->base.context, "incomplete read interrupt transfer (got %d, expected %d)", transferred, PACKET_SIZE);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (buf[0] != 0x3f) {
|
||||||
|
ERROR(eon->base.context, "read interrupt transfer returns wrong report type (%d)", buf[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
len = buf[1];
|
||||||
|
if (len > PACKET_SIZE-2) {
|
||||||
|
ERROR(eon->base.context, "read interrupt transfer reports bad length (%d)", len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (len > size) {
|
||||||
|
ERROR(eon->base.context, "receive_packet result buffer too small - truncating");
|
||||||
|
len = size;
|
||||||
|
}
|
||||||
|
HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf+2, len);
|
||||||
|
memcpy(buffer, buf+2, len);
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int send_cmd(suunto_eonsteel_device_t *eon,
|
static int send_cmd(suunto_eonsteel_device_t *eon,
|
||||||
@ -210,10 +216,67 @@ static int send_cmd(suunto_eonsteel_device_t *eon,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// dump every outgoing packet?
|
// dump every outgoing packet?
|
||||||
HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "cmd", buf, sizeof(buf));
|
HEXDUMP (eon->base.context, DC_LOGLEVEL_DEBUG, "cmd", buf+2, len+12);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct eon_hdr {
|
||||||
|
unsigned short cmd;
|
||||||
|
unsigned int magic;
|
||||||
|
unsigned short seq;
|
||||||
|
unsigned int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int receive_header(suunto_eonsteel_device_t *eon, struct eon_hdr *hdr, unsigned char *buffer, int size)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
unsigned char header[64];
|
||||||
|
|
||||||
|
ret = receive_packet(eon, header, sizeof(header));
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
if (ret < 12) {
|
||||||
|
ERROR(eon->base.context, "short reply packet (%d)", ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unpack the 12-byte header */
|
||||||
|
hdr->cmd = array_uint16_le(header);
|
||||||
|
hdr->magic = array_uint32_le(header+2);
|
||||||
|
hdr->seq = array_uint16_le(header+6);
|
||||||
|
hdr->len = array_uint32_le(header+8);
|
||||||
|
|
||||||
|
ret -= 12;
|
||||||
|
if (ret > size) {
|
||||||
|
ERROR(eon->base.context, "receive_header result data buffer too small (%d vs %d)", ret, size);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(buffer, header+12, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int receive_data(suunto_eonsteel_device_t *eon, unsigned char *buffer, int size)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
while (size > 0) {
|
||||||
|
int len;
|
||||||
|
|
||||||
|
len = receive_packet(eon, buffer + ret, size);
|
||||||
|
if (len < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
size -= len;
|
||||||
|
ret += len;
|
||||||
|
|
||||||
|
/* Was it not a full packet of data? We're done, regardless of expectations */
|
||||||
|
if (len < PACKET_SIZE-2)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send a command, receive a reply
|
* Send a command, receive a reply
|
||||||
*
|
*
|
||||||
@ -233,42 +296,51 @@ static int send_receive(suunto_eonsteel_device_t *eon,
|
|||||||
unsigned int len_out, const unsigned char *out,
|
unsigned int len_out, const unsigned char *out,
|
||||||
unsigned int len_in, unsigned char *in)
|
unsigned int len_in, unsigned char *in)
|
||||||
{
|
{
|
||||||
int len, actual;
|
int len, actual, max;
|
||||||
unsigned char buf[2048];
|
unsigned char buf[2048];
|
||||||
|
struct eon_hdr hdr;
|
||||||
|
|
||||||
if (send_cmd(eon, cmd, len_out, out) < 0)
|
if (send_cmd(eon, cmd, len_out, out) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
len = receive_data(eon, buf, sizeof(buf));
|
|
||||||
if (len < 10) {
|
/* Get the header and the first part of the data */
|
||||||
ERROR(eon->base.context, "short command reply (%d)", len);
|
len = receive_header(eon, &hdr, in, len_in);
|
||||||
|
if (len < 0)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
if (array_uint16_le(buf) != cmd) {
|
/* Verify the header data */
|
||||||
|
if (hdr.cmd != cmd) {
|
||||||
ERROR(eon->base.context, "command reply doesn't match command");
|
ERROR(eon->base.context, "command reply doesn't match command");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (array_uint32_le(buf+2) != eon->magic + 5) {
|
if (hdr.magic != eon->magic + 5) {
|
||||||
ERROR(eon->base.context, "command reply doesn't match magic (got %08x, expected %08x)", array_uint32_le(buf+2), eon->magic + 5);
|
ERROR(eon->base.context, "command reply doesn't match magic (got %08x, expected %08x)", hdr.magic, eon->magic + 5);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (array_uint16_le(buf+6) != eon->seq) {
|
if (hdr.seq != eon->seq) {
|
||||||
ERROR(eon->base.context, "command reply doesn't match sequence number");
|
ERROR(eon->base.context, "command reply doesn't match sequence number");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
actual = array_uint32_le(buf+8);
|
actual = hdr.len;
|
||||||
if (actual + 12 != len) {
|
if (actual < len) {
|
||||||
ERROR(eon->base.context, "command reply length mismatch (got %d, claimed %d)", len-12, actual);
|
ERROR(eon->base.context, "command reply length mismatch (got %d, claimed %d)", len, actual);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (len_in < actual) {
|
if (actual > len_in) {
|
||||||
ERROR(eon->base.context, "command reply returned too much data (got %d, had %d)", actual, len_in);
|
ERROR(eon->base.context, "command reply too big for result buffer - truncating");
|
||||||
|
actual = len_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the rest of the data */
|
||||||
|
len += receive_data(eon, in + len, actual - len);
|
||||||
|
if (len != actual) {
|
||||||
|
ERROR(eon->base.context, "command reply returned unexpected amoutn of data (got %d, expected %d)", len, actual);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Successful command - increment sequence number
|
// Successful command - increment sequence number
|
||||||
eon->seq++;
|
eon->seq++;
|
||||||
memcpy(in, buf+12, actual);
|
return len;
|
||||||
return actual;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buffer_t *buf)
|
static int read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buffer_t *buf)
|
||||||
@ -452,6 +524,7 @@ static int initialize_eonsteel(suunto_eonsteel_device_t *eon)
|
|||||||
const int InEndpoint = 0x82;
|
const int InEndpoint = 0x82;
|
||||||
const unsigned char init[] = {0x02, 0x00, 0x2a, 0x00};
|
const unsigned char init[] = {0x02, 0x00, 0x2a, 0x00};
|
||||||
unsigned char buf[64];
|
unsigned char buf[64];
|
||||||
|
struct eon_hdr hdr;
|
||||||
|
|
||||||
/* Get rid of any pending stale input first */
|
/* Get rid of any pending stale input first */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@ -468,13 +541,13 @@ static int initialize_eonsteel(suunto_eonsteel_device_t *eon)
|
|||||||
ERROR(eon->base.context, "Failed to send initialization command");
|
ERROR(eon->base.context, "Failed to send initialization command");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (receive_data(eon, buf, sizeof(buf)) < 0) {
|
if (receive_header(eon, &hdr, buf, sizeof(buf)) < 0) {
|
||||||
ERROR(eon->base.context, "Failed to receive initial reply");
|
ERROR(eon->base.context, "Failed to receive initial reply");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't ask
|
// Don't ask
|
||||||
eon->magic = 0x00000005 | (buf[4] << 16) | (buf[5] << 24);
|
eon->magic = (hdr.magic & 0xffff0000) | 0x0005;
|
||||||
// Increment the sequence number for every command sent
|
// Increment the sequence number for every command sent
|
||||||
eon->seq++;
|
eon->seq++;
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include <libdivecomputer/suunto_eonsteel.h>
|
#include <libdivecomputer/suunto_eonsteel.h>
|
||||||
|
|
||||||
@ -28,8 +29,41 @@
|
|||||||
#include "parser-private.h"
|
#include "parser-private.h"
|
||||||
#include "array.h"
|
#include "array.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define C_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
|
||||||
|
|
||||||
|
enum eon_sample {
|
||||||
|
ES_none = 0,
|
||||||
|
ES_dtime, // duint16,precision=3 (time delta in ms)
|
||||||
|
ES_depth, // uint16,precision=2,nillable=65535 (depth in cm)
|
||||||
|
ES_temp, // int16,precision=2,nillable=-3000 (temp in deci-Celsius)
|
||||||
|
ES_ndl, // int16,nillable=-1 (ndl in minutes)
|
||||||
|
ES_ceiling, // uint16,precision=2,nillable=65535 (ceiling in cm)
|
||||||
|
ES_tts, // uint16,nillable=65535 (time to surface)
|
||||||
|
ES_heading, // uint16,precision=4,nillable=65535 (heading in degrees)
|
||||||
|
ES_abspressure, // uint16,precision=0,nillable=65535 (abs presure in centibar)
|
||||||
|
ES_gastime, // int16,nillable=-1 (remaining gas time in minutes)
|
||||||
|
ES_ventilation, // uint16,precision=6,nillable=65535 ("x/6000000,x"? No idea)
|
||||||
|
ES_gasnr, // uint8
|
||||||
|
ES_pressure, // uint16,nillable=65535 (cylinder pressure in centibar)
|
||||||
|
ES_state,
|
||||||
|
ES_state_active,
|
||||||
|
ES_notify,
|
||||||
|
ES_notify_active,
|
||||||
|
ES_warning,
|
||||||
|
ES_warning_active,
|
||||||
|
ES_alarm,
|
||||||
|
ES_alarm_active,
|
||||||
|
ES_gasswitch, // uint16
|
||||||
|
ES_bookmark,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EON_MAX_GROUP 16
|
||||||
|
|
||||||
struct type_desc {
|
struct type_desc {
|
||||||
const char *desc, *format, *mod;
|
const char *desc, *format, *mod;
|
||||||
|
unsigned int size;
|
||||||
|
enum eon_sample type[EON_MAX_GROUP];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAXTYPE 512
|
#define MAXTYPE 512
|
||||||
@ -53,6 +87,166 @@ typedef struct suunto_eonsteel_parser_t {
|
|||||||
|
|
||||||
typedef int (*eon_data_cb_t)(unsigned short type, const struct type_desc *desc, const unsigned char *data, int len, void *user);
|
typedef int (*eon_data_cb_t)(unsigned short type, const struct type_desc *desc, const unsigned char *data, int len, void *user);
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
const char *name;
|
||||||
|
enum eon_sample type;
|
||||||
|
} type_translation[] = {
|
||||||
|
{ "Depth", ES_depth },
|
||||||
|
{ "Temperature", ES_temp },
|
||||||
|
{ "NoDecTime", ES_ndl },
|
||||||
|
{ "Ceiling", ES_ceiling },
|
||||||
|
{ "TimeToSurface", ES_tts },
|
||||||
|
{ "Heading", ES_heading },
|
||||||
|
{ "DeviceInternalAbsPressure", ES_abspressure },
|
||||||
|
{ "GasTime", ES_gastime },
|
||||||
|
{ "Ventilation", ES_ventilation },
|
||||||
|
{ "Cylinders+Cylinder.GasNumber", ES_gasnr },
|
||||||
|
{ "Cylinders.Cylinder.Pressure", ES_pressure },
|
||||||
|
{ "Events+State.Type", ES_state },
|
||||||
|
{ "Events.State.Active", ES_state_active },
|
||||||
|
{ "Events+Notify.Type", ES_notify },
|
||||||
|
{ "Events.Notify.Active", ES_notify_active },
|
||||||
|
{ "Events+Warning.Type", ES_warning },
|
||||||
|
{ "Events.Warning.Active", ES_warning_active },
|
||||||
|
{ "Events+Alarm.Type", ES_alarm },
|
||||||
|
{ "Events.Alarm.Active", ES_alarm_active },
|
||||||
|
{ "Events.Bookmark.Name", ES_bookmark },
|
||||||
|
{ "Events.GasSwitch.GasNumber", ES_gasswitch },
|
||||||
|
{ "Events.DiveTimer.Active", ES_none },
|
||||||
|
{ "Events.DiveTimer.Time", ES_none },
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum eon_sample lookup_descriptor_type(suunto_eonsteel_parser_t *eon, struct type_desc *desc)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const char *name = desc->desc;
|
||||||
|
|
||||||
|
// Not a sample type? Skip it
|
||||||
|
if (strncmp(name, "sml.DeviceLog.Samples", 21))
|
||||||
|
return ES_none;
|
||||||
|
|
||||||
|
// Skip the common base
|
||||||
|
name += 21;
|
||||||
|
|
||||||
|
// We have a "+Sample.Time", which starts a new
|
||||||
|
// sample and contains the time delta
|
||||||
|
if (!strcmp(name, "+Sample.Time"))
|
||||||
|
return ES_dtime;
|
||||||
|
|
||||||
|
// .. the rest should start with ".Sample."
|
||||||
|
if (strncmp(name, ".Sample.", 8))
|
||||||
|
return ES_none;
|
||||||
|
|
||||||
|
// Skip the ".Sample."
|
||||||
|
name += 8;
|
||||||
|
|
||||||
|
// .. and look it up in the table of sample type strings
|
||||||
|
for (i = 0; i < C_ARRAY_SIZE(type_translation); i++) {
|
||||||
|
if (!strcmp(name, type_translation[i].name))
|
||||||
|
return type_translation[i].type;
|
||||||
|
}
|
||||||
|
return ES_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lookup_descriptor_size(suunto_eonsteel_parser_t *eon, struct type_desc *desc)
|
||||||
|
{
|
||||||
|
const char *format = desc->format;
|
||||||
|
unsigned char c;
|
||||||
|
|
||||||
|
if (!format)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!strncmp(format, "bool", 4))
|
||||||
|
return 1;
|
||||||
|
if (!strncmp(format, "enum", 4))
|
||||||
|
return 1;
|
||||||
|
if (!strncmp(format, "utf8", 4))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// find the byte size (eg "float32" -> 4 bytes)
|
||||||
|
while ((c = *format) != 0) {
|
||||||
|
if (isdigit(c))
|
||||||
|
return atoi(format)/8;
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fill_in_group_details(suunto_eonsteel_parser_t *eon, struct type_desc *desc)
|
||||||
|
{
|
||||||
|
int subtype = 0;
|
||||||
|
const char *grp = desc->desc;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
struct type_desc *base;
|
||||||
|
char *end;
|
||||||
|
long index;
|
||||||
|
|
||||||
|
index = strtol(grp, &end, 10);
|
||||||
|
if (index < 0 || index > MAXTYPE || end == grp) {
|
||||||
|
ERROR(eon->base.context, "Group type descriptor '%s' does not parse", desc->desc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
base = eon->type_desc + index;
|
||||||
|
if (!base->desc) {
|
||||||
|
ERROR(eon->base.context, "Group type descriptor '%s' has undescribed index %d", desc->desc, index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!base->size) {
|
||||||
|
ERROR(eon->base.context, "Group type descriptor '%s' uses unsized sub-entry '%s'", desc->desc, base->desc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!base->type[0]) {
|
||||||
|
ERROR(eon->base.context, "Group type descriptor '%s' has non-enumerated sub-entry '%s'", desc->desc, base->desc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (base->type[1]) {
|
||||||
|
ERROR(eon->base.context, "Group type descriptor '%s' has a recursive group sub-entry '%s'", desc->desc, base->desc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (subtype >= EON_MAX_GROUP-1) {
|
||||||
|
ERROR(eon->base.context, "Group type descriptor '%s' has too many sub-entries", desc->desc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
desc->size += base->size;
|
||||||
|
desc->type[subtype++] = base->type[0];
|
||||||
|
switch (*end) {
|
||||||
|
case 0:
|
||||||
|
return 0;
|
||||||
|
case ',':
|
||||||
|
grp = end+1;
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
ERROR(eon->base.context, "Group type descriptor '%s' has unparseable index %d", desc->desc, index);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here we cache descriptor data so that we don't have
|
||||||
|
* to re-parse the string all the time. That way we can
|
||||||
|
* do it just once per type.
|
||||||
|
*
|
||||||
|
* Right now we only bother with the sample descriptors,
|
||||||
|
* which all start with "sml.DeviceLog.Samples" (for the
|
||||||
|
* base types) or are "GRP" types that are a group of said
|
||||||
|
* types and are a set of numbers.
|
||||||
|
*/
|
||||||
|
static int fill_in_desc_details(suunto_eonsteel_parser_t *eon, struct type_desc *desc)
|
||||||
|
{
|
||||||
|
if (!desc->desc)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (isdigit(desc->desc[0]))
|
||||||
|
return fill_in_group_details(eon, desc);
|
||||||
|
|
||||||
|
desc->size = lookup_descriptor_size(eon, desc);
|
||||||
|
desc->type[0] = lookup_descriptor_type(eon, desc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
desc_free (struct type_desc desc[], unsigned int count)
|
desc_free (struct type_desc desc[], unsigned int count)
|
||||||
{
|
{
|
||||||
@ -68,7 +262,7 @@ static int record_type(suunto_eonsteel_parser_t *eon, unsigned short type, const
|
|||||||
struct type_desc desc;
|
struct type_desc desc;
|
||||||
const char *next;
|
const char *next;
|
||||||
|
|
||||||
desc.desc = desc.format = desc.mod = NULL;
|
memset(&desc, 0, sizeof(desc));
|
||||||
do {
|
do {
|
||||||
int len;
|
int len;
|
||||||
char *p;
|
char *p;
|
||||||
@ -79,6 +273,8 @@ static int record_type(suunto_eonsteel_parser_t *eon, unsigned short type, const
|
|||||||
next++;
|
next++;
|
||||||
} else {
|
} else {
|
||||||
len = strlen(name);
|
len = strlen(name);
|
||||||
|
if (!len)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len < 5 || name[0] != '<' || name[4] != '>') {
|
if (len < 5 || name[0] != '<' || name[4] != '>') {
|
||||||
@ -124,6 +320,8 @@ static int record_type(suunto_eonsteel_parser_t *eon, unsigned short type, const
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fill_in_desc_details(eon, &desc);
|
||||||
|
|
||||||
desc_free(eon->type_desc + type, 1);
|
desc_free(eon->type_desc + type, 1);
|
||||||
eon->type_desc[type] = desc;
|
eon->type_desc[type] = desc;
|
||||||
return 0;
|
return 0;
|
||||||
@ -228,6 +426,11 @@ struct sample_data {
|
|||||||
unsigned int time;
|
unsigned int time;
|
||||||
unsigned char state_type, notify_type;
|
unsigned char state_type, notify_type;
|
||||||
unsigned char warning_type, alarm_type;
|
unsigned char warning_type, alarm_type;
|
||||||
|
|
||||||
|
/* We gather up deco and cylinder pressure information */
|
||||||
|
int gasnr;
|
||||||
|
int tts, ndl;
|
||||||
|
double ceiling;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sample_time(struct sample_data *info, unsigned short time_delta)
|
static void sample_time(struct sample_data *info, unsigned short time_delta)
|
||||||
@ -261,32 +464,81 @@ static void sample_temp(struct sample_data *info, short temp)
|
|||||||
if (info->callback) info->callback(DC_SAMPLE_TEMPERATURE, sample, info->userdata);
|
if (info->callback) info->callback(DC_SAMPLE_TEMPERATURE, sample, info->userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sample_deco(struct sample_data *info, short ndl, unsigned short tts, unsigned ceiling)
|
static void sample_ndl(struct sample_data *info, short ndl)
|
||||||
{
|
{
|
||||||
dc_sample_value_t sample = {0};
|
dc_sample_value_t sample = {0};
|
||||||
|
|
||||||
/* Are we in deco? */
|
info->ndl = ndl;
|
||||||
if (ndl < 0) {
|
if (ndl < 0)
|
||||||
sample.deco.type = DC_DECO_DECOSTOP;
|
return;
|
||||||
if (tts != 0xffff)
|
|
||||||
sample.deco.time = tts;
|
sample.deco.type = DC_DECO_NDL;
|
||||||
if (ceiling != 0xffff)
|
sample.deco.time = ndl;
|
||||||
sample.deco.depth = ceiling / 100.0;
|
|
||||||
} else {
|
|
||||||
sample.deco.type = DC_DECO_NDL;
|
|
||||||
sample.deco.time = ndl;
|
|
||||||
}
|
|
||||||
if (info->callback) info->callback(DC_SAMPLE_DECO, sample, info->userdata);
|
if (info->callback) info->callback(DC_SAMPLE_DECO, sample, info->userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sample_cylinder_pressure(struct sample_data *info, unsigned char idx, unsigned short pressure)
|
static void sample_tts(struct sample_data *info, unsigned short tts)
|
||||||
|
{
|
||||||
|
if (tts != 0xffff)
|
||||||
|
info->tts = tts;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sample_ceiling(struct sample_data *info, unsigned short ceiling)
|
||||||
|
{
|
||||||
|
if (ceiling != 0xffff)
|
||||||
|
info->ceiling = ceiling / 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sample_heading(struct sample_data *info, unsigned short heading)
|
||||||
|
{
|
||||||
|
dc_sample_value_t sample = {0};
|
||||||
|
|
||||||
|
if (heading == 0xffff)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sample.event.type = SAMPLE_EVENT_HEADING;
|
||||||
|
sample.event.value = heading;
|
||||||
|
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sample_abspressure(struct sample_data *info, unsigned short pressure)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sample_gastime(struct sample_data *info, short gastime)
|
||||||
|
{
|
||||||
|
dc_sample_value_t sample = {0};
|
||||||
|
|
||||||
|
if (gastime < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Hmm. We have no good way to report airtime remaining
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Per-sample "ventilation" data.
|
||||||
|
*
|
||||||
|
* It's described as:
|
||||||
|
* - "uint16,precision=6,nillable=65535"
|
||||||
|
* - "x/6000000,x"
|
||||||
|
*/
|
||||||
|
static void sample_ventilation(struct sample_data *info, unsigned short unk)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sample_gasnr(struct sample_data *info, unsigned char idx)
|
||||||
|
{
|
||||||
|
info->gasnr = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sample_pressure(struct sample_data *info, unsigned short pressure)
|
||||||
{
|
{
|
||||||
dc_sample_value_t sample = {0};
|
dc_sample_value_t sample = {0};
|
||||||
|
|
||||||
if (pressure == 0xffff)
|
if (pressure == 0xffff)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sample.pressure.tank = idx-1;
|
sample.pressure.tank = info->gasnr-1;
|
||||||
sample.pressure.value = pressure / 100.0;
|
sample.pressure.value = pressure / 100.0;
|
||||||
if (info->callback) info->callback(DC_SAMPLE_PRESSURE, sample, info->userdata);
|
if (info->callback) info->callback(DC_SAMPLE_PRESSURE, sample, info->userdata);
|
||||||
}
|
}
|
||||||
@ -336,6 +588,8 @@ static void sample_gas_switch_event(struct sample_data *info, unsigned short idx
|
|||||||
* 3=Dive Active
|
* 3=Dive Active
|
||||||
* 4=Surface Calculation
|
* 4=Surface Calculation
|
||||||
* 5=Tank pressure available
|
* 5=Tank pressure available
|
||||||
|
*
|
||||||
|
* FIXME! This needs to parse the actual type descriptor enum
|
||||||
*/
|
*/
|
||||||
static void sample_event_state_type(struct sample_data *info, unsigned char type)
|
static void sample_event_state_type(struct sample_data *info, unsigned char type)
|
||||||
{
|
{
|
||||||
@ -362,6 +616,7 @@ static void sample_event_notify_type(struct sample_data *info, unsigned char typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME! This needs to parse the actual type descriptor enum
|
||||||
static void sample_event_notify_value(struct sample_data *info, unsigned char value)
|
static void sample_event_notify_value(struct sample_data *info, unsigned char value)
|
||||||
{
|
{
|
||||||
dc_sample_value_t sample = {0};
|
dc_sample_value_t sample = {0};
|
||||||
@ -439,6 +694,7 @@ static void sample_event_alarm_type(struct sample_data *info, unsigned char type
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME! This needs to parse the actual type descriptor enum
|
||||||
static void sample_event_alarm_value(struct sample_data *info, unsigned char value)
|
static void sample_event_alarm_value(struct sample_data *info, unsigned char value)
|
||||||
{
|
{
|
||||||
dc_sample_value_t sample = {0};
|
dc_sample_value_t sample = {0};
|
||||||
@ -463,58 +719,142 @@ static void sample_event_alarm_value(struct sample_data *info, unsigned char val
|
|||||||
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int handle_sample_type(struct sample_data *info, enum eon_sample type, const unsigned char *data)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case ES_dtime:
|
||||||
|
sample_time(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ES_depth:
|
||||||
|
sample_depth(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ES_temp:
|
||||||
|
sample_temp(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ES_ndl:
|
||||||
|
sample_ndl(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ES_ceiling:
|
||||||
|
sample_ceiling(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ES_tts:
|
||||||
|
sample_tts(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ES_heading:
|
||||||
|
sample_heading(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ES_abspressure:
|
||||||
|
sample_abspressure(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ES_gastime:
|
||||||
|
sample_gastime(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ES_ventilation:
|
||||||
|
sample_ventilation(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ES_gasnr:
|
||||||
|
sample_gasnr(info, *data);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case ES_pressure:
|
||||||
|
sample_pressure(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ES_state:
|
||||||
|
sample_event_state_type(info, data[0]);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case ES_state_active:
|
||||||
|
sample_event_state_value(info, data[0]);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case ES_notify:
|
||||||
|
sample_event_notify_type(info, data[0]);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case ES_notify_active:
|
||||||
|
sample_event_notify_value(info, data[0]);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case ES_warning:
|
||||||
|
sample_event_warning_type(info, data[0]);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case ES_warning_active:
|
||||||
|
sample_event_warning_value(info, data[0]);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case ES_alarm:
|
||||||
|
sample_event_alarm_type(info, data[0]);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case ES_alarm_active:
|
||||||
|
sample_event_alarm_value(info, data[0]);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case ES_bookmark:
|
||||||
|
sample_bookmark_event(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case ES_gasswitch:
|
||||||
|
sample_gas_switch_event(info, array_uint16_le(data));
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int traverse_samples(unsigned short type, const struct type_desc *desc, const unsigned char *data, int len, void *user)
|
static int traverse_samples(unsigned short type, const struct type_desc *desc, const unsigned char *data, int len, void *user)
|
||||||
{
|
{
|
||||||
struct sample_data *info = (struct sample_data *) user;
|
struct sample_data *info = (struct sample_data *) user;
|
||||||
|
suunto_eonsteel_parser_t *eon = info->eon;
|
||||||
|
int i, used = 0;
|
||||||
|
|
||||||
switch (type) {
|
if (desc->size > len)
|
||||||
case 0x0001: // group: time in first word, depth in second
|
ERROR(eon->base.context, "Got %d bytes of data for '%s' that wants %d bytes", len, desc->desc, desc->size);
|
||||||
sample_time(info, array_uint16_le(data));
|
|
||||||
sample_depth(info, array_uint16_le(data+2));
|
info->ndl = -1;
|
||||||
sample_temp(info, array_uint16_le(data+4));
|
info->tts = 0;
|
||||||
sample_deco(info, array_uint16_le(data+8), array_uint16_le(data+10), array_uint16_le(data+12));
|
info->ceiling = 0.0;
|
||||||
break;
|
|
||||||
case 0x0002: // time in first word
|
for (i = 0; i < EON_MAX_GROUP; i++) {
|
||||||
sample_time(info, array_uint16_le(data));
|
enum eon_sample type = desc->type[i];
|
||||||
break;
|
int bytes = handle_sample_type(info, type, data);
|
||||||
case 0x0003: // depth in first word
|
|
||||||
sample_depth(info, array_uint16_le(data));
|
if (!bytes)
|
||||||
break;
|
break;
|
||||||
case 0x000a: // cylinder idx in first byte, pressure in next word
|
if (bytes > len) {
|
||||||
sample_cylinder_pressure(info, data[0], array_uint16_le(data+1));
|
ERROR(eon->base.context, "Wanted %d bytes of data, only had %d bytes ('%s' idx %d)", bytes, len, desc->desc, i);
|
||||||
break;
|
break;
|
||||||
case 0x0013:
|
}
|
||||||
sample_event_state_type(info, data[0]);
|
data += bytes;
|
||||||
break;
|
len -= bytes;
|
||||||
case 0x0014:
|
used += bytes;
|
||||||
sample_event_state_value(info, data[0]);
|
|
||||||
break;
|
|
||||||
case 0x0015:
|
|
||||||
sample_event_notify_type(info, data[0]);
|
|
||||||
break;
|
|
||||||
case 0x0016:
|
|
||||||
sample_event_notify_value(info, data[0]);
|
|
||||||
break;
|
|
||||||
case 0x0017:
|
|
||||||
sample_event_warning_type(info, data[0]);
|
|
||||||
break;
|
|
||||||
case 0x0018:
|
|
||||||
sample_event_warning_value(info, data[0]);
|
|
||||||
break;
|
|
||||||
case 0x0019:
|
|
||||||
sample_event_alarm_type(info, data[0]);
|
|
||||||
break;
|
|
||||||
case 0x001a:
|
|
||||||
sample_event_alarm_value(info, data[0]);
|
|
||||||
break;
|
|
||||||
case 0x001c:
|
|
||||||
sample_bookmark_event(info, array_uint16_le(data));
|
|
||||||
break;
|
|
||||||
case 0x001d:
|
|
||||||
sample_gas_switch_event(info, array_uint16_le(data));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (info->ndl < 0 && (info->tts || info->ceiling)) {
|
||||||
|
dc_sample_value_t sample = {0};
|
||||||
|
|
||||||
|
sample.deco.type = DC_DECO_DECOSTOP;
|
||||||
|
sample.deco.time = info->tts;
|
||||||
|
sample.deco.depth = info->ceiling;
|
||||||
|
if (info->callback) info->callback(DC_SAMPLE_DECO, sample, info->userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn if there are left-over bytes for something we did use part of
|
||||||
|
if (used && len)
|
||||||
|
ERROR(eon->base.context, "Entry for '%s' had %d bytes, only used %d", desc->desc, len+used, used);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,57 +943,218 @@ static void set_depth_field(suunto_eonsteel_parser_t *eon, unsigned short d)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// gas type: 0=Off,1=Primary,2=?,3=Diluent
|
// new gas:
|
||||||
static void add_gas_type(suunto_eonsteel_parser_t *eon, unsigned char type)
|
// "sml.DeviceLog.Header.Diving.Gases+Gas.State"
|
||||||
|
//
|
||||||
|
// We eventually need to parse the descriptor for that 'enum type'.
|
||||||
|
// Two versions so far:
|
||||||
|
// "enum:0=Off,1=Primary,2=?,3=Diluent"
|
||||||
|
// "enum:0=Off,1=Primary,3=Diluent,4=Oxygen"
|
||||||
|
static int add_gas_type(suunto_eonsteel_parser_t *eon, const struct type_desc *desc, unsigned char type)
|
||||||
{
|
{
|
||||||
if (eon->cache.ngases < MAXGASES)
|
if (eon->cache.ngases < MAXGASES)
|
||||||
eon->cache.ngases++;
|
eon->cache.ngases++;
|
||||||
eon->cache.initialized |= 1 << DC_FIELD_GASMIX_COUNT;
|
eon->cache.initialized |= 1 << DC_FIELD_GASMIX_COUNT;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "sml.DeviceLog.Header.Diving.Gases.Gas.Oxygen"
|
||||||
// O2 percentage as a byte
|
// O2 percentage as a byte
|
||||||
static void add_gas_o2(suunto_eonsteel_parser_t *eon, unsigned char o2)
|
static int add_gas_o2(suunto_eonsteel_parser_t *eon, unsigned char o2)
|
||||||
{
|
{
|
||||||
int idx = eon->cache.ngases-1;
|
int idx = eon->cache.ngases-1;
|
||||||
if (idx >= 0)
|
if (idx >= 0)
|
||||||
eon->cache.gasmix[idx].oxygen = o2 / 100.0;
|
eon->cache.gasmix[idx].oxygen = o2 / 100.0;
|
||||||
eon->cache.initialized |= 1 << DC_FIELD_GASMIX;
|
eon->cache.initialized |= 1 << DC_FIELD_GASMIX;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "sml.DeviceLog.Header.Diving.Gases.Gas.Helium"
|
||||||
// He percentage as a byte
|
// He percentage as a byte
|
||||||
static void add_gas_he(suunto_eonsteel_parser_t *eon, unsigned char he)
|
static int add_gas_he(suunto_eonsteel_parser_t *eon, unsigned char he)
|
||||||
{
|
{
|
||||||
int idx = eon->cache.ngases-1;
|
int idx = eon->cache.ngases-1;
|
||||||
if (idx >= 0)
|
if (idx >= 0)
|
||||||
eon->cache.gasmix[idx].helium = he / 100.0;
|
eon->cache.gasmix[idx].helium = he / 100.0;
|
||||||
eon->cache.initialized |= 1 << DC_FIELD_GASMIX;
|
eon->cache.initialized |= 1 << DC_FIELD_GASMIX;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float get_le32_float(const unsigned char *src)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
unsigned int val;
|
||||||
|
float result;
|
||||||
|
} u;
|
||||||
|
|
||||||
|
u.val = array_uint32_le(src);
|
||||||
|
return u.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Device" fields are all utf8:
|
||||||
|
// Info.BatteryAtEnd
|
||||||
|
// Info.BatteryAtStart
|
||||||
|
// Info.BSL
|
||||||
|
// Info.HW
|
||||||
|
// Info.SW
|
||||||
|
// Name
|
||||||
|
// SerialNumber
|
||||||
|
static int traverse_device_fields(suunto_eonsteel_parser_t *eon, const struct type_desc *desc,
|
||||||
|
const unsigned char *data, int len)
|
||||||
|
{
|
||||||
|
const char *name = desc->desc + strlen("sml.DeviceLog.Device.");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "sml.DeviceLog.Header.Diving."
|
||||||
|
//
|
||||||
|
// Gases+Gas.State (enum:0=Off,1=Primary,3=Diluent,4=Oxygen)
|
||||||
|
// Gases.Gas.Oxygen (uint8,precision=2)
|
||||||
|
// Gases.Gas.Helium (uint8,precision=2)
|
||||||
|
// Gases.Gas.PO2 (uint32)
|
||||||
|
// Gases.Gas.TransmitterID (utf8)
|
||||||
|
// Gases.Gas.TankSize (float32,precision=5)
|
||||||
|
// Gases.Gas.TankFillPressure (float32,precision=0)
|
||||||
|
// Gases.Gas.StartPressure (float32,precision=0)
|
||||||
|
// Gases.Gas.EndPressure (float32,precision=0)
|
||||||
|
// Gases.Gas.TransmitterStartBatteryCharge (int8,precision=2)
|
||||||
|
// Gases.Gas.TransmitterEndBatteryCharge (int8,precision=2)
|
||||||
|
// SurfaceTime (uint32)
|
||||||
|
// NumberInSeries (uint32)
|
||||||
|
// Algorithm (utf8)
|
||||||
|
// SurfacePressure (uint32)
|
||||||
|
// Conservatism (int8)
|
||||||
|
// Altitude (uint16)
|
||||||
|
// AlgorithmTransitionDepth (uint8)
|
||||||
|
// DaysInSeries (uint32)
|
||||||
|
// PreviousDiveDepth (float32,precision=2)
|
||||||
|
// StartTissue.CNS (float32,precision=3)
|
||||||
|
// StartTissue.OTU (float32)
|
||||||
|
// StartTissue.OLF (float32,precision=3)
|
||||||
|
// StartTissue.Nitrogen+Pressure (uint32)
|
||||||
|
// StartTissue.Helium+Pressure (uint32)
|
||||||
|
// StartTissue.RgbmNitrogen (float32,precision=3)
|
||||||
|
// StartTissue.RgbmHelium (float32,precision=3)
|
||||||
|
// DiveMode (utf8)
|
||||||
|
// AlgorithmBottomTime (uint32)
|
||||||
|
// AlgorithmAscentTime (uint32)
|
||||||
|
// AlgorithmBottomMixture.Oxygen (uint8,precision=2)
|
||||||
|
// AlgorithmBottomMixture.Helium (uint8,precision=2)
|
||||||
|
// DesaturationTime (uint32)
|
||||||
|
// EndTissue.CNS (float32,precision=3)
|
||||||
|
// EndTissue.OTU (float32)
|
||||||
|
// EndTissue.OLF (float32,precision=3)
|
||||||
|
// EndTissue.Nitrogen+Pressure (uint32)
|
||||||
|
// EndTissue.Helium+Pressure (uint32)
|
||||||
|
// EndTissue.RgbmNitrogen (float32,precision=3)
|
||||||
|
// EndTissue.RgbmHelium (float32,precision=3)
|
||||||
|
static int traverse_diving_fields(suunto_eonsteel_parser_t *eon, const struct type_desc *desc,
|
||||||
|
const unsigned char *data, int len)
|
||||||
|
{
|
||||||
|
const char *name = desc->desc + strlen("sml.DeviceLog.Header.Diving.");
|
||||||
|
|
||||||
|
if (!strcmp(name, "Gases+Gas.State"))
|
||||||
|
return add_gas_type(eon, desc, data[0]);
|
||||||
|
|
||||||
|
if (!strcmp(name, "Gases.Gas.Oxygen"))
|
||||||
|
return add_gas_o2(eon, data[0]);
|
||||||
|
|
||||||
|
if (!strcmp(name, "Gases.Gas.Helium"))
|
||||||
|
return add_gas_he(eon, data[0]);
|
||||||
|
|
||||||
|
if (!strcmp(name, "SurfacePressure")) {
|
||||||
|
unsigned int pressure = array_uint32_le(data); // in SI units - Pascal
|
||||||
|
eon->cache.surface_pressure = pressure / 100000.0; // bar
|
||||||
|
eon->cache.initialized |= 1 << DC_FIELD_ATMOSPHERIC;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Header" fields are:
|
||||||
|
// Activity (utf8)
|
||||||
|
// DateTime (utf8)
|
||||||
|
// Depth.Avg (float32,precision=2)
|
||||||
|
// Depth.Max (float32,precision=2)
|
||||||
|
// Diving.*
|
||||||
|
// Duration (uint32)
|
||||||
|
// PauseDuration (uint32)
|
||||||
|
// SampleInterval (uint8)
|
||||||
|
static int traverse_header_fields(suunto_eonsteel_parser_t *eon, const struct type_desc *desc,
|
||||||
|
const unsigned char *data, int len)
|
||||||
|
{
|
||||||
|
const char *name = desc->desc + strlen("sml.DeviceLog.Header.");
|
||||||
|
|
||||||
|
if (!strncmp(name, "Diving.", 7))
|
||||||
|
return traverse_diving_fields(eon, desc, data, len);
|
||||||
|
|
||||||
|
if (!strcmp(name, "Depth.Max")) {
|
||||||
|
double d = get_le32_float(data);
|
||||||
|
if (d > eon->cache.maxdepth)
|
||||||
|
eon->cache.maxdepth = d;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int traverse_dynamic_fields(suunto_eonsteel_parser_t *eon, const struct type_desc *desc, const unsigned char *data, int len)
|
||||||
|
{
|
||||||
|
const char *name = desc->desc;
|
||||||
|
|
||||||
|
if (!strncmp(name, "sml.", 4)) {
|
||||||
|
name += 4;
|
||||||
|
if (!strncmp(name, "DeviceLog.", 10)) {
|
||||||
|
name += 10;
|
||||||
|
if (!strncmp(name, "Device.", 7))
|
||||||
|
return traverse_device_fields(eon, desc, data, len);
|
||||||
|
if (!strncmp(name, "Header.", 7)) {
|
||||||
|
return traverse_header_fields(eon, desc, data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a simplified sample parser that only parses the depth and time
|
||||||
|
* samples. It also depends on the GRP entries always starting with time/depth,
|
||||||
|
* and just stops on anything else.
|
||||||
|
*/
|
||||||
|
static int traverse_sample_fields(suunto_eonsteel_parser_t *eon, const struct type_desc *desc, const unsigned char *data, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < EON_MAX_GROUP; i++) {
|
||||||
|
enum eon_sample type = desc->type[i];
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case ES_dtime:
|
||||||
|
add_time_field(eon, array_uint16_le(data));
|
||||||
|
data += 2;
|
||||||
|
continue;
|
||||||
|
case ES_depth:
|
||||||
|
set_depth_field(eon, array_uint16_le(data));
|
||||||
|
data += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int traverse_fields(unsigned short type, const struct type_desc *desc, const unsigned char *data, int len, void *user)
|
static int traverse_fields(unsigned short type, const struct type_desc *desc, const unsigned char *data, int len, void *user)
|
||||||
{
|
{
|
||||||
suunto_eonsteel_parser_t *eon = (suunto_eonsteel_parser_t *) user;
|
suunto_eonsteel_parser_t *eon = (suunto_eonsteel_parser_t *) user;
|
||||||
|
|
||||||
switch (type) {
|
// Sample type? Do basic maxdepth and time parsing
|
||||||
case 0x0001: // group: time in first word, depth in second
|
if (desc->type[0])
|
||||||
add_time_field(eon, array_uint16_le(data));
|
traverse_sample_fields(eon, desc, data, len);
|
||||||
set_depth_field(eon, array_uint16_le(data+2));
|
else
|
||||||
break;
|
traverse_dynamic_fields(eon, desc, data, len);
|
||||||
case 0x0002: // time in first word
|
|
||||||
add_time_field(eon, array_uint16_le(data));
|
|
||||||
break;
|
|
||||||
case 0x0003: // depth in first word
|
|
||||||
set_depth_field(eon, array_uint16_le(data));
|
|
||||||
break;
|
|
||||||
case 0x000d: // gas state in first byte
|
|
||||||
add_gas_type(eon, data[0]);
|
|
||||||
break;
|
|
||||||
case 0x000e: // Oxygen percentage in first byte
|
|
||||||
add_gas_o2(eon, data[0]);
|
|
||||||
break;
|
|
||||||
case 0x000f: // Helium percentage in first byte
|
|
||||||
add_gas_he(eon, data[0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user