Compare commits
59 Commits
Subsurface
...
teric
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
348387c6f6 | ||
|
|
ee7c14ecc3 | ||
|
|
d0a3336c82 | ||
|
|
437cc3e0cc | ||
|
|
9379004b2d | ||
|
|
34785f55ff | ||
|
|
0cbcc0518c | ||
|
|
f80024ed59 | ||
|
|
12b90c693a | ||
|
|
ec0029c4ce | ||
|
|
a9c582e26f | ||
|
|
c1c0303e04 | ||
|
|
7779bdf581 | ||
|
|
416bf35977 | ||
|
|
e02037bf84 | ||
|
|
ac25976258 | ||
|
|
41303bbc70 | ||
|
|
8f7e29e1f9 | ||
|
|
740222d216 | ||
|
|
4a43392c78 | ||
|
|
86540206db | ||
|
|
f6ea5f514a | ||
|
|
fe2a43e798 | ||
|
|
63cd80c560 | ||
|
|
22a96bf395 | ||
|
|
6a6e60c9bb | ||
|
|
994efff75a | ||
|
|
6b7c269c9c | ||
|
|
c8e52081cd | ||
|
|
00a90e2822 | ||
|
|
6d470d8430 | ||
|
|
3dbe5353f5 | ||
|
|
6d53e31cba | ||
|
|
bc2ba57302 | ||
|
|
b65b2318f1 | ||
|
|
2c7479ad1c | ||
|
|
a726a38cbb | ||
|
|
8f790b52e4 | ||
|
|
8735156e89 | ||
|
|
bb985eedbb | ||
|
|
8f4945dc1e | ||
|
|
02560a7e7f | ||
|
|
5255ba5448 | ||
|
|
503d934c19 | ||
|
|
902dbf4d6d | ||
|
|
e0761561e9 | ||
|
|
d264349676 | ||
|
|
e97a47cca5 | ||
|
|
14490a462a | ||
|
|
97c8bb908e | ||
|
|
f248a95d64 | ||
|
|
df1e97c471 | ||
|
|
a38d640df4 | ||
|
|
6c51ace384 | ||
|
|
db08a534bf | ||
|
|
167848aa59 | ||
|
|
362fe3f936 | ||
|
|
49f89d2205 | ||
|
|
063041ddca |
2
.gitignore
vendored
2
.gitignore
vendored
@ -82,3 +82,5 @@ Makefile.in
|
|||||||
/src/libdivecomputer.la
|
/src/libdivecomputer.la
|
||||||
/src/libdivecomputer.rc
|
/src/libdivecomputer.rc
|
||||||
/src/revision.h
|
/src/revision.h
|
||||||
|
|
||||||
|
/build
|
||||||
|
|||||||
10
configure.ac
10
configure.ac
@ -2,7 +2,7 @@
|
|||||||
m4_define([dc_version_major],[0])
|
m4_define([dc_version_major],[0])
|
||||||
m4_define([dc_version_minor],[7])
|
m4_define([dc_version_minor],[7])
|
||||||
m4_define([dc_version_micro],[0])
|
m4_define([dc_version_micro],[0])
|
||||||
m4_define([dc_version_suffix],[devel])
|
m4_define([dc_version_suffix],[devel-Subsurface-NG])
|
||||||
m4_define([dc_version],dc_version_major.dc_version_minor.dc_version_micro[]m4_ifset([dc_version_suffix],-[dc_version_suffix]))
|
m4_define([dc_version],dc_version_major.dc_version_minor.dc_version_micro[]m4_ifset([dc_version_suffix],-[dc_version_suffix]))
|
||||||
|
|
||||||
# Libtool versioning.
|
# Libtool versioning.
|
||||||
@ -175,19 +175,21 @@ AC_CHECK_FUNCS([getopt_long])
|
|||||||
|
|
||||||
# Checks for supported compiler options.
|
# Checks for supported compiler options.
|
||||||
AX_APPEND_COMPILE_FLAGS([ \
|
AX_APPEND_COMPILE_FLAGS([ \
|
||||||
-pedantic \
|
|
||||||
-Wall \
|
-Wall \
|
||||||
-Wextra \
|
|
||||||
-Wshadow \
|
-Wshadow \
|
||||||
-Wrestrict \
|
-Wrestrict \
|
||||||
-Wformat=2 \
|
-Wformat=2 \
|
||||||
-Wwrite-strings \
|
-Wwrite-strings \
|
||||||
-Wcast-qual \
|
|
||||||
-Wpointer-arith \
|
-Wpointer-arith \
|
||||||
-Wstrict-prototypes \
|
-Wstrict-prototypes \
|
||||||
-Wmissing-prototypes \
|
-Wmissing-prototypes \
|
||||||
-Wmissing-declarations \
|
-Wmissing-declarations \
|
||||||
-Wno-unused-parameter \
|
-Wno-unused-parameter \
|
||||||
|
-Wno-unused-function \
|
||||||
|
-Wno-unused-variable \
|
||||||
|
-Wno-unused-but-set-variable \
|
||||||
|
-Wno-pointer-sign \
|
||||||
|
-Wno-shadow \
|
||||||
])
|
])
|
||||||
|
|
||||||
# Windows specific compiler options.
|
# Windows specific compiler options.
|
||||||
|
|||||||
@ -89,6 +89,7 @@ static const backend_table_t g_backends[] = {
|
|||||||
{"idive", DC_FAMILY_DIVESYSTEM_IDIVE, 0x03},
|
{"idive", DC_FAMILY_DIVESYSTEM_IDIVE, 0x03},
|
||||||
{"cochran", DC_FAMILY_COCHRAN_COMMANDER, 0},
|
{"cochran", DC_FAMILY_COCHRAN_COMMANDER, 0},
|
||||||
{"divecomputereu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0},
|
{"divecomputereu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0},
|
||||||
|
{"descentmk1", DC_FAMILY_GARMIN, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const transport_table_t g_transports[] = {
|
static const transport_table_t g_transports[] = {
|
||||||
@ -98,6 +99,7 @@ static const transport_table_t g_transports[] = {
|
|||||||
{"irda", DC_TRANSPORT_IRDA},
|
{"irda", DC_TRANSPORT_IRDA},
|
||||||
{"bluetooth", DC_TRANSPORT_BLUETOOTH},
|
{"bluetooth", DC_TRANSPORT_BLUETOOTH},
|
||||||
{"ble", DC_TRANSPORT_BLE},
|
{"ble", DC_TRANSPORT_BLE},
|
||||||
|
{"usbstorage",DC_TRANSPORT_USBSTORAGE},
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
@ -536,6 +538,8 @@ dctool_iostream_open (dc_iostream_t **iostream, dc_context_t *context, dc_descri
|
|||||||
return dctool_irda_open (iostream, context, descriptor, devname);
|
return dctool_irda_open (iostream, context, descriptor, devname);
|
||||||
case DC_TRANSPORT_BLUETOOTH:
|
case DC_TRANSPORT_BLUETOOTH:
|
||||||
return dctool_bluetooth_open (iostream, context, descriptor, devname);
|
return dctool_bluetooth_open (iostream, context, descriptor, devname);
|
||||||
|
case DC_TRANSPORT_USBSTORAGE:
|
||||||
|
return dc_usb_storage_open (iostream, context, devname);
|
||||||
default:
|
default:
|
||||||
return DC_STATUS_UNSUPPORTED;
|
return DC_STATUS_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -414,6 +414,24 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
|
|||||||
convert_pressure(atmospheric, output->units));
|
convert_pressure(atmospheric, output->units));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ("Parsing strings.\n");
|
||||||
|
int idx;
|
||||||
|
for (idx = 0; idx < 100; idx++) {
|
||||||
|
dc_field_string_t str = { NULL };
|
||||||
|
status = dc_parser_get_field(parser, DC_FIELD_STRING, idx, &str);
|
||||||
|
if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) {
|
||||||
|
ERROR ("Error parsing strings");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (status == DC_STATUS_UNSUPPORTED)
|
||||||
|
break;
|
||||||
|
if (!str.desc || !str.value)
|
||||||
|
break;
|
||||||
|
fprintf (output->ostream, "<extradata key='%s' value='%s' />\n",
|
||||||
|
str.desc, str.value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the sample data.
|
// Parse the sample data.
|
||||||
message ("Parsing the sample data.\n");
|
message ("Parsing the sample data.\n");
|
||||||
status = dc_parser_samples_foreach (parser, sample_cb, &sampledata);
|
status = dc_parser_samples_foreach (parser, sample_cb, &sampledata);
|
||||||
|
|||||||
@ -48,9 +48,13 @@ typedef enum dc_transport_t {
|
|||||||
DC_TRANSPORT_USBHID = (1 << 2),
|
DC_TRANSPORT_USBHID = (1 << 2),
|
||||||
DC_TRANSPORT_IRDA = (1 << 3),
|
DC_TRANSPORT_IRDA = (1 << 3),
|
||||||
DC_TRANSPORT_BLUETOOTH = (1 << 4),
|
DC_TRANSPORT_BLUETOOTH = (1 << 4),
|
||||||
DC_TRANSPORT_BLE = (1 << 5)
|
DC_TRANSPORT_BLE = (1 << 5),
|
||||||
|
DC_TRANSPORT_USBSTORAGE= (1 << 6),
|
||||||
} dc_transport_t;
|
} dc_transport_t;
|
||||||
|
|
||||||
|
// Idiotic enums can't be queried
|
||||||
|
#define DC_TRANSPORT_USBSTORAGE DC_TRANSPORT_USBSTORAGE
|
||||||
|
|
||||||
typedef enum dc_family_t {
|
typedef enum dc_family_t {
|
||||||
DC_FAMILY_NULL = 0,
|
DC_FAMILY_NULL = 0,
|
||||||
/* Suunto */
|
/* Suunto */
|
||||||
@ -103,6 +107,8 @@ typedef enum dc_family_t {
|
|||||||
DC_FAMILY_COCHRAN_COMMANDER = (14 << 16),
|
DC_FAMILY_COCHRAN_COMMANDER = (14 << 16),
|
||||||
/* Tecdiving */
|
/* Tecdiving */
|
||||||
DC_FAMILY_TECDIVING_DIVECOMPUTEREU = (15 << 16),
|
DC_FAMILY_TECDIVING_DIVECOMPUTEREU = (15 << 16),
|
||||||
|
/* Garmin */
|
||||||
|
DC_FAMILY_GARMIN = (16 << 16),
|
||||||
} dc_family_t;
|
} dc_family_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@ -283,6 +283,9 @@ dc_iostream_sleep (dc_iostream_t *iostream, unsigned int milliseconds);
|
|||||||
dc_status_t
|
dc_status_t
|
||||||
dc_iostream_close (dc_iostream_t *iostream);
|
dc_iostream_close (dc_iostream_t *iostream);
|
||||||
|
|
||||||
|
dc_status_t
|
||||||
|
dc_usb_storage_open (dc_iostream_t **out, dc_context_t *context, const char *name);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
|||||||
@ -46,9 +46,13 @@ typedef enum dc_sample_type_t {
|
|||||||
DC_SAMPLE_PPO2,
|
DC_SAMPLE_PPO2,
|
||||||
DC_SAMPLE_CNS,
|
DC_SAMPLE_CNS,
|
||||||
DC_SAMPLE_DECO,
|
DC_SAMPLE_DECO,
|
||||||
DC_SAMPLE_GASMIX
|
DC_SAMPLE_GASMIX,
|
||||||
|
DC_SAMPLE_TTS, // time to surface in seconds
|
||||||
} dc_sample_type_t;
|
} dc_sample_type_t;
|
||||||
|
|
||||||
|
// Make it easy to test support compile-time with "#ifdef DC_SAMPLE_TTS"
|
||||||
|
#define DC_SAMPLE_TTS DC_SAMPLE_TTS
|
||||||
|
|
||||||
typedef enum dc_field_type_t {
|
typedef enum dc_field_type_t {
|
||||||
DC_FIELD_DIVETIME,
|
DC_FIELD_DIVETIME,
|
||||||
DC_FIELD_MAXDEPTH,
|
DC_FIELD_MAXDEPTH,
|
||||||
@ -62,9 +66,13 @@ typedef enum dc_field_type_t {
|
|||||||
DC_FIELD_TEMPERATURE_MAXIMUM,
|
DC_FIELD_TEMPERATURE_MAXIMUM,
|
||||||
DC_FIELD_TANK_COUNT,
|
DC_FIELD_TANK_COUNT,
|
||||||
DC_FIELD_TANK,
|
DC_FIELD_TANK,
|
||||||
DC_FIELD_DIVEMODE
|
DC_FIELD_DIVEMODE,
|
||||||
|
DC_FIELD_STRING,
|
||||||
} dc_field_type_t;
|
} dc_field_type_t;
|
||||||
|
|
||||||
|
// Make it easy to test support compile-time with "#ifdef DC_FIELD_STRING"
|
||||||
|
#define DC_FIELD_STRING DC_FIELD_STRING
|
||||||
|
|
||||||
typedef enum parser_sample_event_t {
|
typedef enum parser_sample_event_t {
|
||||||
SAMPLE_EVENT_NONE,
|
SAMPLE_EVENT_NONE,
|
||||||
SAMPLE_EVENT_DECOSTOP,
|
SAMPLE_EVENT_DECOSTOP,
|
||||||
@ -92,17 +100,30 @@ typedef enum parser_sample_event_t {
|
|||||||
SAMPLE_EVENT_HEADING,
|
SAMPLE_EVENT_HEADING,
|
||||||
SAMPLE_EVENT_TISSUELEVEL,
|
SAMPLE_EVENT_TISSUELEVEL,
|
||||||
SAMPLE_EVENT_GASCHANGE2, /* Deprecated: replaced by DC_SAMPLE_GASMIX. */
|
SAMPLE_EVENT_GASCHANGE2, /* Deprecated: replaced by DC_SAMPLE_GASMIX. */
|
||||||
|
SAMPLE_EVENT_STRING,
|
||||||
} parser_sample_event_t;
|
} parser_sample_event_t;
|
||||||
|
|
||||||
|
/* To let the compile know we have this */
|
||||||
|
#define SAMPLE_EVENT_STRING SAMPLE_EVENT_STRING
|
||||||
|
|
||||||
/* For backwards compatibility */
|
/* For backwards compatibility */
|
||||||
#define SAMPLE_EVENT_UNKNOWN SAMPLE_EVENT_FLOOR
|
#define SAMPLE_EVENT_UNKNOWN SAMPLE_EVENT_FLOOR
|
||||||
|
|
||||||
typedef enum parser_sample_flags_t {
|
typedef enum parser_sample_flags_t {
|
||||||
SAMPLE_FLAGS_NONE = 0,
|
SAMPLE_FLAGS_NONE = 0,
|
||||||
SAMPLE_FLAGS_BEGIN = (1 << 0),
|
SAMPLE_FLAGS_BEGIN = (1 << 0),
|
||||||
SAMPLE_FLAGS_END = (1 << 1)
|
SAMPLE_FLAGS_END = (1 << 1),
|
||||||
|
SAMPLE_FLAGS_SEVERITY_MASK = (7 << 2),
|
||||||
} parser_sample_flags_t;
|
} parser_sample_flags_t;
|
||||||
|
|
||||||
|
#define SAMPLE_FLAGS_SEVERITY_SHIFT 2
|
||||||
|
|
||||||
|
#define SAMPLE_FLAGS_SEVERITY_MISSING (0 << SAMPLE_FLAGS_SEVERITY_SHIFT)
|
||||||
|
#define SAMPLE_FLAGS_SEVERITY_STATE (1 << SAMPLE_FLAGS_SEVERITY_SHIFT)
|
||||||
|
#define SAMPLE_FLAGS_SEVERITY_INFO (2 << SAMPLE_FLAGS_SEVERITY_SHIFT)
|
||||||
|
#define SAMPLE_FLAGS_SEVERITY_WARN (3 << SAMPLE_FLAGS_SEVERITY_SHIFT)
|
||||||
|
#define SAMPLE_FLAGS_SEVERITY_ALARM (4 << SAMPLE_FLAGS_SEVERITY_SHIFT)
|
||||||
|
|
||||||
typedef enum parser_sample_vendor_t {
|
typedef enum parser_sample_vendor_t {
|
||||||
SAMPLE_VENDOR_NONE,
|
SAMPLE_VENDOR_NONE,
|
||||||
SAMPLE_VENDOR_UWATEC_ALADIN,
|
SAMPLE_VENDOR_UWATEC_ALADIN,
|
||||||
@ -148,11 +169,16 @@ typedef struct dc_gasmix_t {
|
|||||||
|
|
||||||
#define DC_GASMIX_UNKNOWN 0xFFFFFFFF
|
#define DC_GASMIX_UNKNOWN 0xFFFFFFFF
|
||||||
|
|
||||||
typedef enum dc_tankvolume_t {
|
typedef unsigned int dc_tankinfo_t;
|
||||||
DC_TANKVOLUME_NONE,
|
#define DC_TANKINFO_METRIC 1
|
||||||
DC_TANKVOLUME_METRIC,
|
#define DC_TANKINFO_IMPERIAL 2
|
||||||
DC_TANKVOLUME_IMPERIAL,
|
#define DC_TANKINFO_CC_DILUENT 4
|
||||||
} dc_tankvolume_t;
|
#define DC_TANKINFO_CC_O2 8
|
||||||
|
|
||||||
|
// For backwards compatibility
|
||||||
|
#define DC_TANKVOLUME_NONE 0
|
||||||
|
#define DC_TANKVOLUME_METRIC DC_TANKINFO_METRIC
|
||||||
|
#define DC_TANKVOLUME_IMPERIAL DC_TANKINFO_IMPERIAL
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tank volume
|
* Tank volume
|
||||||
@ -179,13 +205,18 @@ typedef enum dc_tankvolume_t {
|
|||||||
|
|
||||||
typedef struct dc_tank_t {
|
typedef struct dc_tank_t {
|
||||||
unsigned int gasmix; /* Gas mix index, or DC_GASMIX_UNKNOWN */
|
unsigned int gasmix; /* Gas mix index, or DC_GASMIX_UNKNOWN */
|
||||||
dc_tankvolume_t type; /* Tank type */
|
dc_tankinfo_t type; /* Tank type - metric/imperial and oc/cc */
|
||||||
double volume; /* Volume (liter) */
|
double volume; /* Volume (liter) */
|
||||||
double workpressure; /* Work pressure (bar) */
|
double workpressure; /* Work pressure (bar) */
|
||||||
double beginpressure; /* Begin pressure (bar) */
|
double beginpressure; /* Begin pressure (bar) */
|
||||||
double endpressure; /* End pressure (bar) */
|
double endpressure; /* End pressure (bar) */
|
||||||
} dc_tank_t;
|
} dc_tank_t;
|
||||||
|
|
||||||
|
typedef struct dc_field_string_t {
|
||||||
|
const char *desc;
|
||||||
|
const char *value;
|
||||||
|
} dc_field_string_t;
|
||||||
|
|
||||||
typedef union dc_sample_value_t {
|
typedef union dc_sample_value_t {
|
||||||
unsigned int time;
|
unsigned int time;
|
||||||
double depth;
|
double depth;
|
||||||
@ -199,6 +230,7 @@ typedef union dc_sample_value_t {
|
|||||||
unsigned int time;
|
unsigned int time;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
unsigned int value;
|
unsigned int value;
|
||||||
|
const char *name;
|
||||||
} event;
|
} event;
|
||||||
unsigned int rbt;
|
unsigned int rbt;
|
||||||
unsigned int heartbeat;
|
unsigned int heartbeat;
|
||||||
|
|||||||
@ -490,6 +490,14 @@
|
|||||||
RelativePath="..\src\tecdiving_divecomputereu_parser.c"
|
RelativePath="..\src\tecdiving_divecomputereu_parser.c"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\src\garmin.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\src\garmin_parser.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\src\timer.c"
|
RelativePath="..\src\timer.c"
|
||||||
>
|
>
|
||||||
@ -828,6 +836,10 @@
|
|||||||
RelativePath="..\src\tecdiving_divecomputereu.h"
|
RelativePath="..\src\tecdiving_divecomputereu.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\src\garmin.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\src\timer.h"
|
RelativePath="..\src\timer.h"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -71,10 +71,12 @@ libdivecomputer_la_SOURCES = \
|
|||||||
buffer.c \
|
buffer.c \
|
||||||
cochran_commander.h cochran_commander.c cochran_commander_parser.c \
|
cochran_commander.h cochran_commander.c cochran_commander_parser.c \
|
||||||
tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \
|
tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \
|
||||||
|
garmin.h garmin.c garmin_parser.c \
|
||||||
socket.h socket.c \
|
socket.h socket.c \
|
||||||
irda.c \
|
irda.c \
|
||||||
usbhid.c \
|
usbhid.c \
|
||||||
bluetooth.c \
|
bluetooth.c \
|
||||||
|
usb_storage.c \
|
||||||
custom.c
|
custom.c
|
||||||
|
|
||||||
if OS_WIN32
|
if OS_WIN32
|
||||||
|
|||||||
@ -20,6 +20,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <libdivecomputer/units.h>
|
#include <libdivecomputer/units.h>
|
||||||
|
|
||||||
@ -129,6 +135,9 @@ atomics_cobalt_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dateti
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define BUFLEN 16
|
||||||
|
|
||||||
|
|
||||||
static dc_status_t
|
static dc_status_t
|
||||||
atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||||
{
|
{
|
||||||
@ -143,6 +152,9 @@ atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
|
|||||||
dc_tank_t *tank = (dc_tank_t *) value;
|
dc_tank_t *tank = (dc_tank_t *) value;
|
||||||
|
|
||||||
double atmospheric = 0.0;
|
double atmospheric = 0.0;
|
||||||
|
char buf[BUFLEN];
|
||||||
|
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||||
|
|
||||||
if (parser->atmospheric)
|
if (parser->atmospheric)
|
||||||
atmospheric = parser->atmospheric;
|
atmospheric = parser->atmospheric;
|
||||||
else
|
else
|
||||||
@ -208,6 +220,29 @@ atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
|
|||||||
return DC_STATUS_DATAFORMAT;
|
return DC_STATUS_DATAFORMAT;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case DC_FIELD_STRING:
|
||||||
|
switch(flags) {
|
||||||
|
case 0: // Serialnr
|
||||||
|
string->desc = "Serial";
|
||||||
|
snprintf(buf, BUFLEN, "%c%c%c%c-%c%c%c%c", p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11]);
|
||||||
|
break;
|
||||||
|
case 1: // Program Version
|
||||||
|
string->desc = "Program Version";
|
||||||
|
snprintf(buf, BUFLEN, "%.2f", array_uint16_le(p + 30) / 100.0);
|
||||||
|
break;
|
||||||
|
case 2: // Boot Version
|
||||||
|
string->desc = "Boot Version";
|
||||||
|
snprintf(buf, BUFLEN, "%.2f", array_uint16_le(p + 32) / 100.0);
|
||||||
|
break;
|
||||||
|
case 3: // Nofly
|
||||||
|
string->desc = "NoFly Time";
|
||||||
|
snprintf(buf, BUFLEN, "%0u:%02u", p[0x52], p[0x53]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return DC_STATUS_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
string->value = strdup(buf);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return DC_STATUS_UNSUPPORTED;
|
return DC_STATUS_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -334,6 +334,7 @@ dc_context_get_transports (dc_context_t *context)
|
|||||||
#elif defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
#elif defined(HAVE_LIBUSB) && !defined(__APPLE__)
|
||||||
| DC_TRANSPORT_USBHID
|
| DC_TRANSPORT_USBHID
|
||||||
#endif
|
#endif
|
||||||
|
| DC_TRANSPORT_USBSTORAGE
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#ifdef HAVE_AF_IRDA_H
|
#ifdef HAVE_AF_IRDA_H
|
||||||
| DC_TRANSPORT_IRDA
|
| DC_TRANSPORT_IRDA
|
||||||
|
|||||||
@ -34,6 +34,7 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata);
|
|||||||
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata);
|
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata);
|
||||||
static int dc_filter_hw (dc_transport_t transport, const void *userdata);
|
static int dc_filter_hw (dc_transport_t transport, const void *userdata);
|
||||||
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata);
|
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata);
|
||||||
|
static int dc_filter_garmin (dc_transport_t transport, const void *userdata);
|
||||||
|
|
||||||
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
|
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
|
||||||
|
|
||||||
@ -215,7 +216,7 @@ static const dc_descriptor_t g_descriptors[] = {
|
|||||||
{"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556, DC_TRANSPORT_SERIAL, NULL},
|
{"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Oceanic", "VTX", DC_FAMILY_OCEANIC_ATOM2, 0x4557, DC_TRANSPORT_SERIAL, NULL},
|
{"Oceanic", "VTX", DC_FAMILY_OCEANIC_ATOM2, 0x4557, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559, DC_TRANSPORT_SERIAL, NULL},
|
{"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A, DC_TRANSPORT_SERIAL, NULL},
|
{"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, NULL},
|
||||||
{"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641, DC_TRANSPORT_SERIAL, NULL},
|
{"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642, DC_TRANSPORT_SERIAL, NULL},
|
{"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646, DC_TRANSPORT_SERIAL, NULL},
|
{"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646, DC_TRANSPORT_SERIAL, NULL},
|
||||||
@ -238,16 +239,16 @@ static const dc_descriptor_t g_descriptors[] = {
|
|||||||
{"Mares", "Airlab", DC_FAMILY_MARES_DARWIN , 1, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Airlab", DC_FAMILY_MARES_DARWIN , 1, DC_TRANSPORT_SERIAL, NULL},
|
||||||
/* Mares Icon HD */
|
/* Mares Icon HD */
|
||||||
{"Mares", "Matrix", DC_FAMILY_MARES_ICONHD , 0x0F, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Matrix", DC_FAMILY_MARES_ICONHD , 0x0F, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Mares", "Smart", DC_FAMILY_MARES_ICONHD , 0x000010, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Smart", DC_FAMILY_MARES_ICONHD , 0x000010, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL},
|
||||||
{"Mares", "Smart Apnea", DC_FAMILY_MARES_ICONHD , 0x010010, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Smart Apnea", DC_FAMILY_MARES_ICONHD , 0x010010, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Mares", "Icon HD", DC_FAMILY_MARES_ICONHD , 0x14, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Icon HD", DC_FAMILY_MARES_ICONHD , 0x14, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Mares", "Icon HD Net Ready", DC_FAMILY_MARES_ICONHD , 0x15, DC_TRANSPORT_SERIAL, NULL},
|
{"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, NULL},
|
{"Mares", "Puck Pro", DC_FAMILY_MARES_ICONHD , 0x18, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL},
|
||||||
{"Mares", "Nemo Wide 2", DC_FAMILY_MARES_ICONHD , 0x19, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Nemo Wide 2", DC_FAMILY_MARES_ICONHD , 0x19, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Mares", "Puck 2", DC_FAMILY_MARES_ICONHD , 0x1F, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Puck 2", DC_FAMILY_MARES_ICONHD , 0x1F, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Mares", "Quad Air", DC_FAMILY_MARES_ICONHD , 0x23, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Quad Air", DC_FAMILY_MARES_ICONHD , 0x23, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL},
|
||||||
{"Mares", "Smart Air", DC_FAMILY_MARES_ICONHD , 0x24, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Smart Air", DC_FAMILY_MARES_ICONHD , 0x24, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL},
|
||||||
{"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL},
|
||||||
/* Heinrichs Weikamp */
|
/* Heinrichs Weikamp */
|
||||||
{"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, DC_TRANSPORT_SERIAL, NULL},
|
{"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, DC_TRANSPORT_SERIAL, NULL},
|
{"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, DC_TRANSPORT_SERIAL, NULL},
|
||||||
@ -291,6 +292,7 @@ static const dc_descriptor_t g_descriptors[] = {
|
|||||||
{"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_shearwater},
|
{"Shearwater", "Perdix", DC_FAMILY_SHEARWATER_PETREL, 5, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_shearwater},
|
||||||
{"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6, DC_TRANSPORT_BLE, dc_filter_shearwater},
|
{"Shearwater", "Perdix AI", DC_FAMILY_SHEARWATER_PETREL, 6, DC_TRANSPORT_BLE, dc_filter_shearwater},
|
||||||
{"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7, DC_TRANSPORT_BLE, dc_filter_shearwater},
|
{"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7, DC_TRANSPORT_BLE, dc_filter_shearwater},
|
||||||
|
{"Shearwater", "Teric", DC_FAMILY_SHEARWATER_PETREL, 8, DC_TRANSPORT_BLE, dc_filter_shearwater},
|
||||||
/* Dive Rite NiTek Q */
|
/* Dive Rite NiTek Q */
|
||||||
{"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, DC_TRANSPORT_SERIAL, NULL},
|
{"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, DC_TRANSPORT_SERIAL, NULL},
|
||||||
/* Citizen Hyper Aqualand */
|
/* Citizen Hyper Aqualand */
|
||||||
@ -328,6 +330,8 @@ static const dc_descriptor_t g_descriptors[] = {
|
|||||||
{"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5, DC_TRANSPORT_SERIAL, NULL},
|
{"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5, DC_TRANSPORT_SERIAL, NULL},
|
||||||
/* Tecdiving DiveComputer.eu */
|
/* Tecdiving DiveComputer.eu */
|
||||||
{"Tecdiving", "DiveComputer.eu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_tecdiving},
|
{"Tecdiving", "DiveComputer.eu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_tecdiving},
|
||||||
|
/* Garmin */
|
||||||
|
{"Garmin", "Descent Mk1", DC_FAMILY_GARMIN, 2859, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -467,6 +471,19 @@ static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dc_filter_garmin (dc_transport_t transport, const void *userdata)
|
||||||
|
{
|
||||||
|
static const dc_usb_desc_t usbhid[] = {
|
||||||
|
{0x091e, 0x2b2b}, // Garmin Descent Mk1
|
||||||
|
};
|
||||||
|
|
||||||
|
if (transport == DC_TRANSPORT_USBSTORAGE) {
|
||||||
|
return dc_filter_internal_usb ((const dc_usb_desc_t *) userdata, usbhid, C_ARRAY_SIZE(usbhid));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
dc_descriptor_iterator (dc_iterator_t **out)
|
dc_descriptor_iterator (dc_iterator_t **out)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -56,6 +56,7 @@
|
|||||||
#include "divesystem_idive.h"
|
#include "divesystem_idive.h"
|
||||||
#include "cochran_commander.h"
|
#include "cochran_commander.h"
|
||||||
#include "tecdiving_divecomputereu.h"
|
#include "tecdiving_divecomputereu.h"
|
||||||
|
#include "garmin.h"
|
||||||
|
|
||||||
#include "device-private.h"
|
#include "device-private.h"
|
||||||
#include "context-private.h"
|
#include "context-private.h"
|
||||||
@ -207,6 +208,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
|
|||||||
case DC_FAMILY_TECDIVING_DIVECOMPUTEREU:
|
case DC_FAMILY_TECDIVING_DIVECOMPUTEREU:
|
||||||
rc = tecdiving_divecomputereu_device_open (&device, context, iostream);
|
rc = tecdiving_divecomputereu_device_open (&device, context, iostream);
|
||||||
break;
|
break;
|
||||||
|
case DC_FAMILY_GARMIN:
|
||||||
|
rc = garmin_device_open (&device, context, iostream);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return DC_STATUS_INVALIDARGS;
|
return DC_STATUS_INVALIDARGS;
|
||||||
}
|
}
|
||||||
|
|||||||
309
src/garmin.c
Normal file
309
src/garmin.c
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
/*
|
||||||
|
* Garmin Descent Mk1 USB storage downloading
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Linus Torvalds
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "garmin.h"
|
||||||
|
#include "context-private.h"
|
||||||
|
#include "device-private.h"
|
||||||
|
#include "array.h"
|
||||||
|
|
||||||
|
typedef struct garmin_device_t {
|
||||||
|
dc_device_t base;
|
||||||
|
dc_iostream_t *iostream;
|
||||||
|
unsigned char fingerprint[FIT_NAME_SIZE];
|
||||||
|
} garmin_device_t;
|
||||||
|
|
||||||
|
static dc_status_t garmin_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
|
||||||
|
static dc_status_t garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
|
||||||
|
static dc_status_t garmin_device_close (dc_device_t *abstract);
|
||||||
|
|
||||||
|
static const dc_device_vtable_t garmin_device_vtable = {
|
||||||
|
sizeof(garmin_device_t),
|
||||||
|
DC_FAMILY_GARMIN,
|
||||||
|
garmin_device_set_fingerprint, /* set_fingerprint */
|
||||||
|
NULL, /* read */
|
||||||
|
NULL, /* write */
|
||||||
|
NULL, /* dump */
|
||||||
|
garmin_device_foreach, /* foreach */
|
||||||
|
NULL, /* timesync */
|
||||||
|
garmin_device_close, /* close */
|
||||||
|
};
|
||||||
|
|
||||||
|
dc_status_t
|
||||||
|
garmin_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
|
||||||
|
{
|
||||||
|
dc_status_t status = DC_STATUS_SUCCESS;
|
||||||
|
garmin_device_t *device = NULL;
|
||||||
|
|
||||||
|
if (out == NULL)
|
||||||
|
return DC_STATUS_INVALIDARGS;
|
||||||
|
|
||||||
|
// Allocate memory.
|
||||||
|
device = (garmin_device_t *) dc_device_allocate (context, &garmin_device_vtable);
|
||||||
|
if (device == NULL) {
|
||||||
|
ERROR (context, "Failed to allocate memory.");
|
||||||
|
return DC_STATUS_NOMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default values.
|
||||||
|
device->iostream = iostream;
|
||||||
|
memset(device->fingerprint, 0, sizeof(device->fingerprint));
|
||||||
|
|
||||||
|
*out = (dc_device_t *) device;
|
||||||
|
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
garmin_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
|
||||||
|
{
|
||||||
|
garmin_device_t *device = (garmin_device_t *)abstract;
|
||||||
|
|
||||||
|
if (size && size != sizeof (device->fingerprint))
|
||||||
|
return DC_STATUS_INVALIDARGS;
|
||||||
|
|
||||||
|
if (size)
|
||||||
|
memcpy (device->fingerprint, data, sizeof (device->fingerprint));
|
||||||
|
else
|
||||||
|
memset (device->fingerprint, 0, sizeof (device->fingerprint));
|
||||||
|
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
garmin_device_close (dc_device_t *abstract)
|
||||||
|
{
|
||||||
|
dc_status_t status = DC_STATUS_SUCCESS;
|
||||||
|
garmin_device_t *device = (garmin_device_t *) abstract;
|
||||||
|
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct file_list {
|
||||||
|
int nr, allocated;
|
||||||
|
struct fit_name *array;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int name_cmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
// Sort reverse string ordering (newest first), so use 'b,a'
|
||||||
|
return strcmp(b,a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the FIT file list and sort it.
|
||||||
|
*
|
||||||
|
* Return number of files found.
|
||||||
|
*/
|
||||||
|
static int get_file_list(DIR *dir, struct file_list *files)
|
||||||
|
{
|
||||||
|
struct dirent *de;
|
||||||
|
|
||||||
|
while ((de = readdir(dir)) != NULL) {
|
||||||
|
int len = strlen(de->d_name);
|
||||||
|
|
||||||
|
if (len != FIT_NAME_SIZE-1)
|
||||||
|
continue;
|
||||||
|
if (strncasecmp(de->d_name + len - 4, ".FIT", 4))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (files->nr == files->allocated) {
|
||||||
|
struct fit_name *array;
|
||||||
|
int n = 3*(files->allocated + 8)/2;
|
||||||
|
size_t new_size;
|
||||||
|
|
||||||
|
new_size = n * sizeof(array[0]);
|
||||||
|
array = realloc(files->array, new_size);
|
||||||
|
if (!array)
|
||||||
|
return DC_STATUS_NOMEMORY;
|
||||||
|
|
||||||
|
files->array = array;
|
||||||
|
files->allocated = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(files->array + files->nr++, de->d_name, FIT_NAME_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort(files->array, files->nr, sizeof(struct fit_name), name_cmp);
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
read_file(char *pathname, int pathlen, const char *name, dc_buffer_t *file)
|
||||||
|
{
|
||||||
|
int fd, rc;
|
||||||
|
|
||||||
|
pathname[pathlen] = '/';
|
||||||
|
memcpy(pathname+pathlen+1, name, FIT_NAME_SIZE);
|
||||||
|
fd = open(pathname, O_RDONLY);
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
|
||||||
|
rc = DC_STATUS_SUCCESS;
|
||||||
|
for (;;) {
|
||||||
|
char buffer[4096];
|
||||||
|
int n;
|
||||||
|
|
||||||
|
n = read(fd, buffer, sizeof(buffer));
|
||||||
|
if (!n)
|
||||||
|
break;
|
||||||
|
if (n > 0) {
|
||||||
|
dc_buffer_append(file, buffer, n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rc = DC_STATUS_IO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
|
||||||
|
{
|
||||||
|
dc_status_t status = DC_STATUS_SUCCESS;
|
||||||
|
garmin_device_t *device = (garmin_device_t *) abstract;
|
||||||
|
dc_parser_t *parser;
|
||||||
|
char pathname[PATH_MAX];
|
||||||
|
size_t pathlen;
|
||||||
|
struct file_list files = { 0, 0, NULL };
|
||||||
|
dc_buffer_t *file;
|
||||||
|
DIR *dir;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
// Read the directory name from the iostream
|
||||||
|
rc = dc_iostream_read(device->iostream, &pathname, sizeof(pathname), &pathlen);
|
||||||
|
if (rc != DC_STATUS_SUCCESS)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
// The actual dives are under the "Garmin/Activity/" directory
|
||||||
|
// as FIT files, with names like "2018-08-20-10-23-30.fit".
|
||||||
|
// Make sure our buffer is big enough.
|
||||||
|
if (pathlen + strlen("/Garmin/Activity/") + FIT_NAME_SIZE + 2 > PATH_MAX)
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
|
||||||
|
if (pathlen && pathname[pathlen-1] != '/')
|
||||||
|
pathname[pathlen++] = '/';
|
||||||
|
strcpy(pathname + pathlen, "Garmin/Activity");
|
||||||
|
pathlen += strlen("Garmin/Activity");
|
||||||
|
|
||||||
|
dir = opendir(pathname);
|
||||||
|
if (!dir)
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
|
||||||
|
// Get the list of FIT files
|
||||||
|
rc = get_file_list(dir, &files);
|
||||||
|
closedir(dir);
|
||||||
|
if (rc != DC_STATUS_SUCCESS || !files.nr) {
|
||||||
|
free(files.array);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can we find the fingerprint entry?
|
||||||
|
for (int i = 0; i < files.nr; i++) {
|
||||||
|
const char *name = files.array[i].name;
|
||||||
|
|
||||||
|
if (memcmp(name, device->fingerprint, sizeof (device->fingerprint)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Found fingerprint, just cut the array short here
|
||||||
|
files.nr = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable progress notifications.
|
||||||
|
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
|
||||||
|
progress.maximum = files.nr;
|
||||||
|
progress.current = 0;
|
||||||
|
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||||
|
|
||||||
|
file = dc_buffer_new (16384);
|
||||||
|
if (file == NULL) {
|
||||||
|
ERROR (abstract->context, "Insufficient buffer space available.");
|
||||||
|
free(files.array);
|
||||||
|
return DC_STATUS_NOMEMORY;
|
||||||
|
}
|
||||||
|
if ((rc = garmin_parser_create(&parser, abstract->context) != DC_STATUS_SUCCESS)) {
|
||||||
|
ERROR (abstract->context, "Failed to create parser for dive verification.");
|
||||||
|
free(files.array);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
dc_event_devinfo_t devinfo;
|
||||||
|
dc_event_devinfo_t *devinfo_p = &devinfo;
|
||||||
|
for (int i = 0; i < files.nr; i++) {
|
||||||
|
const char *name = files.array[i].name;
|
||||||
|
const unsigned char *data;
|
||||||
|
unsigned int size;
|
||||||
|
short is_dive = 0;
|
||||||
|
|
||||||
|
if (device_is_cancelled(abstract)) {
|
||||||
|
status = DC_STATUS_CANCELLED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the membuffer, read the data
|
||||||
|
dc_buffer_clear(file);
|
||||||
|
dc_buffer_append(file, name, FIT_NAME_SIZE);
|
||||||
|
|
||||||
|
status = read_file(pathname, pathlen, name, file);
|
||||||
|
if (status != DC_STATUS_SUCCESS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
data = dc_buffer_get_data(file);
|
||||||
|
size = dc_buffer_get_size(file);
|
||||||
|
|
||||||
|
is_dive = garmin_parser_is_dive(parser, data, size, devinfo_p);
|
||||||
|
if (devinfo_p) {
|
||||||
|
// first time we came through here, let's emit the
|
||||||
|
// devinfo and vendor events
|
||||||
|
device_event_emit (abstract, DC_EVENT_DEVINFO, devinfo_p);
|
||||||
|
devinfo_p = NULL;
|
||||||
|
}
|
||||||
|
if (!is_dive) {
|
||||||
|
DEBUG (abstract->context, "decided %s isn't a dive.", name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback && !callback(data, size, name, FIT_NAME_SIZE, userdata))
|
||||||
|
break;
|
||||||
|
|
||||||
|
progress.current++;
|
||||||
|
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(files.array);
|
||||||
|
dc_parser_destroy(parser);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
59
src/garmin.h
Normal file
59
src/garmin.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Garmin Descent Mk1
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Linus Torvalds
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GARMIN_H
|
||||||
|
#define GARMIN_H
|
||||||
|
|
||||||
|
#include <libdivecomputer/context.h>
|
||||||
|
#include <libdivecomputer/iostream.h>
|
||||||
|
#include <libdivecomputer/device.h>
|
||||||
|
#include <libdivecomputer/parser.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
dc_status_t
|
||||||
|
garmin_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
||||||
|
|
||||||
|
dc_status_t
|
||||||
|
garmin_parser_create (dc_parser_t **parser, dc_context_t *context);
|
||||||
|
|
||||||
|
// we need to be able to call into the parser to check if the
|
||||||
|
// files that we find are actual dives
|
||||||
|
int
|
||||||
|
garmin_parser_is_dive (dc_parser_t *abstract, const unsigned char *data, unsigned int size, dc_event_devinfo_t *devinfo_p);
|
||||||
|
|
||||||
|
// The dive names are of the form "2018-08-20-10-23-30.fit"
|
||||||
|
// With the terminating zero, that's 24 bytes.
|
||||||
|
//
|
||||||
|
// We use this as the fingerprint, but it ends up being a
|
||||||
|
// special fixed header in the parser data too.
|
||||||
|
#define FIT_NAME_SIZE 24
|
||||||
|
|
||||||
|
struct fit_name {
|
||||||
|
char name[FIT_NAME_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
#endif /* GARMIN_H */
|
||||||
1363
src/garmin_parser.c
Normal file
1363
src/garmin_parser.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -36,7 +36,7 @@ dc_status_t
|
|||||||
hw_ostc_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
hw_ostc_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
hw_ostc_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int hwos);
|
hw_ostc_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int serial, unsigned int hwos);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ dc_status_t
|
|||||||
hw_ostc3_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
hw_ostc3_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model);
|
hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int serial, unsigned int model);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "libdivecomputer/units.h"
|
#include "libdivecomputer/units.h"
|
||||||
|
|
||||||
@ -61,8 +67,14 @@
|
|||||||
#define OSTC3_APNEA 3
|
#define OSTC3_APNEA 3
|
||||||
#define OSTC3_PSCR 4
|
#define OSTC3_PSCR 4
|
||||||
|
|
||||||
|
#define OSTC3_ZHL16 0
|
||||||
|
#define OSTC3_ZHL16_GF 1
|
||||||
|
#define OSTC4_VPM 2
|
||||||
|
|
||||||
#define OSTC4 0x3B
|
#define OSTC4 0x3B
|
||||||
|
|
||||||
|
#define UNSUPPORTED 0xFFFFFFFF
|
||||||
|
|
||||||
typedef struct hw_ostc_sample_info_t {
|
typedef struct hw_ostc_sample_info_t {
|
||||||
unsigned int type;
|
unsigned int type;
|
||||||
unsigned int divisor;
|
unsigned int divisor;
|
||||||
@ -78,7 +90,13 @@ typedef struct hw_ostc_layout_t {
|
|||||||
unsigned int salinity;
|
unsigned int salinity;
|
||||||
unsigned int duration;
|
unsigned int duration;
|
||||||
unsigned int temperature;
|
unsigned int temperature;
|
||||||
|
unsigned int battery;
|
||||||
|
unsigned int desat;
|
||||||
unsigned int firmware;
|
unsigned int firmware;
|
||||||
|
unsigned int deco_info1;
|
||||||
|
unsigned int deco_info2;
|
||||||
|
unsigned int decomode;
|
||||||
|
unsigned int battery_percentage;
|
||||||
} hw_ostc_layout_t;
|
} hw_ostc_layout_t;
|
||||||
|
|
||||||
typedef struct hw_ostc_gasmix_t {
|
typedef struct hw_ostc_gasmix_t {
|
||||||
@ -90,6 +108,7 @@ typedef struct hw_ostc_parser_t {
|
|||||||
dc_parser_t base;
|
dc_parser_t base;
|
||||||
unsigned int hwos;
|
unsigned int hwos;
|
||||||
unsigned int model;
|
unsigned int model;
|
||||||
|
unsigned int serial;
|
||||||
// Cached fields.
|
// Cached fields.
|
||||||
unsigned int cached;
|
unsigned int cached;
|
||||||
unsigned int version;
|
unsigned int version;
|
||||||
@ -127,7 +146,13 @@ static const hw_ostc_layout_t hw_ostc_layout_ostc = {
|
|||||||
43, /* salinity */
|
43, /* salinity */
|
||||||
47, /* duration */
|
47, /* duration */
|
||||||
13, /* temperature */
|
13, /* temperature */
|
||||||
|
34, /* battery volt after dive */
|
||||||
|
17, /* desat */
|
||||||
32, /* firmware */
|
32, /* firmware */
|
||||||
|
49, /* deco_info1 */
|
||||||
|
50, /* deco_info1 */
|
||||||
|
51, /* decomode */
|
||||||
|
0, /* battery percentage TBD */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const hw_ostc_layout_t hw_ostc_layout_frog = {
|
static const hw_ostc_layout_t hw_ostc_layout_frog = {
|
||||||
@ -139,7 +164,13 @@ static const hw_ostc_layout_t hw_ostc_layout_frog = {
|
|||||||
43, /* salinity */
|
43, /* salinity */
|
||||||
47, /* duration */
|
47, /* duration */
|
||||||
19, /* temperature */
|
19, /* temperature */
|
||||||
|
34, /* battery volt after dive */
|
||||||
|
23, /* desat */
|
||||||
32, /* firmware */
|
32, /* firmware */
|
||||||
|
49, /* deco_info1 */
|
||||||
|
50, /* deco_info2 */
|
||||||
|
51, /* decomode */
|
||||||
|
0, /* battery percentage TBD */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const hw_ostc_layout_t hw_ostc_layout_ostc3 = {
|
static const hw_ostc_layout_t hw_ostc_layout_ostc3 = {
|
||||||
@ -151,7 +182,13 @@ static const hw_ostc_layout_t hw_ostc_layout_ostc3 = {
|
|||||||
70, /* salinity */
|
70, /* salinity */
|
||||||
75, /* duration */
|
75, /* duration */
|
||||||
22, /* temperature */
|
22, /* temperature */
|
||||||
|
50, /* battery volt after dive */
|
||||||
|
26, /* desat */
|
||||||
48, /* firmware */
|
48, /* firmware */
|
||||||
|
77, /* deco_info1 */
|
||||||
|
78, /* deco_info2 */
|
||||||
|
79, /* decomode */
|
||||||
|
59, /* battery percentage */
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned int
|
static unsigned int
|
||||||
@ -251,7 +288,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The first fixed setpoint is the initial setpoint in CCR mode.
|
// The first fixed setpoint is the initial setpoint in CCR mode.
|
||||||
if (data[82] == OSTC3_CC) {
|
if (data[82] == OSTC3_CC) {
|
||||||
initial_setpoint = data[60];
|
initial_setpoint = data[60];
|
||||||
}
|
}
|
||||||
// Initial CNS
|
// Initial CNS
|
||||||
@ -294,7 +331,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static dc_status_t
|
static dc_status_t
|
||||||
hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsigned int hwos, unsigned int model)
|
hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsigned int serial, unsigned int hwos, unsigned int model)
|
||||||
{
|
{
|
||||||
hw_ostc_parser_t *parser = NULL;
|
hw_ostc_parser_t *parser = NULL;
|
||||||
|
|
||||||
@ -324,6 +361,7 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
|
|||||||
parser->gasmix[i].oxygen = 0;
|
parser->gasmix[i].oxygen = 0;
|
||||||
parser->gasmix[i].helium = 0;
|
parser->gasmix[i].helium = 0;
|
||||||
}
|
}
|
||||||
|
parser->serial = serial;
|
||||||
|
|
||||||
*out = (dc_parser_t *) parser;
|
*out = (dc_parser_t *) parser;
|
||||||
|
|
||||||
@ -332,15 +370,15 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
|
|||||||
|
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
hw_ostc_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int hwos)
|
hw_ostc_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int serial, unsigned int hwos)
|
||||||
{
|
{
|
||||||
return hw_ostc_parser_create_internal (out, context, hwos, 0);
|
return hw_ostc_parser_create_internal (out, context, serial, hwos, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
|
hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int serial, unsigned int model)
|
||||||
{
|
{
|
||||||
return hw_ostc_parser_create_internal (out, context, 1, model);
|
return hw_ostc_parser_create_internal (out, context, serial, 1, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
static dc_status_t
|
static dc_status_t
|
||||||
@ -428,6 +466,8 @@ hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
|||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define BUFLEN 32
|
||||||
|
|
||||||
static dc_status_t
|
static dc_status_t
|
||||||
hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||||
{
|
{
|
||||||
@ -452,9 +492,12 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
|
|||||||
|
|
||||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||||
|
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||||
|
|
||||||
unsigned int salinity = data[layout->salinity];
|
unsigned int salinity = data[layout->salinity];
|
||||||
if (version == 0x23 || version == 0x24)
|
if (version == 0x23 || version == 0x24)
|
||||||
salinity += 100;
|
salinity += 100;
|
||||||
|
char buf[BUFLEN];
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -552,6 +595,79 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
|
|||||||
return DC_STATUS_UNSUPPORTED;
|
return DC_STATUS_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case DC_FIELD_STRING:
|
||||||
|
switch(flags) {
|
||||||
|
case 0: /* serial */
|
||||||
|
string->desc = "Serial";
|
||||||
|
snprintf(buf, BUFLEN, "%u", parser->serial);
|
||||||
|
break;
|
||||||
|
case 1: /* battery */
|
||||||
|
string->desc = "Battery at end";
|
||||||
|
unsigned int percentage = (unsigned int) data[layout->battery_percentage];
|
||||||
|
if (percentage != 0xFF && (version == 0x23 || version == 0x24)) {
|
||||||
|
percentage = percentage>100? 100: percentage;
|
||||||
|
snprintf(buf, BUFLEN, "%.2fV, %u%% remaining",
|
||||||
|
array_uint16_le (data + layout->battery) / 1000.0,
|
||||||
|
percentage);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, BUFLEN, "%.2fV", array_uint16_le (data + layout->battery) / 1000.0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: /* desat */
|
||||||
|
string->desc = "Desat time";
|
||||||
|
snprintf(buf, BUFLEN, "%0u:%02u", array_uint16_le (data + layout->desat) / 60,
|
||||||
|
array_uint16_le (data + layout->desat) % 60);
|
||||||
|
break;
|
||||||
|
case 3: /* firmware */
|
||||||
|
string->desc = "FW Version";
|
||||||
|
/* OSTC4 stores firmware as XXXX XYYY YYZZ ZZZB, -> X.Y.Z beta? */
|
||||||
|
if (parser->model == OSTC4) {
|
||||||
|
int firmwareOnDevice = array_uint16_le (data + layout->firmware);
|
||||||
|
unsigned char X = 0, Y = 0, Z = 0, beta = 0;
|
||||||
|
X = (firmwareOnDevice & 0xF800) >> 11;
|
||||||
|
Y = (firmwareOnDevice & 0x07C0) >> 6;
|
||||||
|
Z = (firmwareOnDevice & 0x003E) >> 1;
|
||||||
|
beta = firmwareOnDevice & 0x0001;
|
||||||
|
|
||||||
|
snprintf(buf, BUFLEN, "%u.%u.%u%s\n", X, Y, Z, beta? "beta": "");
|
||||||
|
} else {
|
||||||
|
snprintf(buf, BUFLEN, "%0u.%02u", data[layout->firmware], data[layout->firmware + 1]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: /* Deco model */
|
||||||
|
string->desc = "Deco model";
|
||||||
|
if (((version == 0x23 || version == 0x24) && data[layout->decomode] == OSTC3_ZHL16) ||
|
||||||
|
(version == 0x22 && data[layout->decomode] == FROG_ZHL16) ||
|
||||||
|
(version == 0x21 && (data[layout->decomode] == OSTC_ZHL16_OC || data[layout->decomode] == OSTC_ZHL16_CC)))
|
||||||
|
strncpy(buf, "ZH-L16", BUFLEN);
|
||||||
|
else if (((version == 0x23 || version == 0x24) && data[layout->decomode] == OSTC3_ZHL16_GF) ||
|
||||||
|
(version == 0x22 && data[layout->decomode] == FROG_ZHL16_GF) ||
|
||||||
|
(version == 0x21 && (data[layout->decomode] == OSTC_ZHL16_OC_GF || data[layout->decomode] == OSTC_ZHL16_CC_GF)))
|
||||||
|
strncpy(buf, "ZH-L16-GF", BUFLEN);
|
||||||
|
else if (((version == 0x24) && data[layout->decomode] == OSTC4_VPM))
|
||||||
|
strncpy(buf, "VPM", BUFLEN);
|
||||||
|
else
|
||||||
|
return DC_STATUS_DATAFORMAT;
|
||||||
|
break;
|
||||||
|
case 5: /* Deco model info */
|
||||||
|
string->desc = "Deco model info";
|
||||||
|
if (((version == 0x23 || version == 0x24) && data[layout->decomode] == OSTC3_ZHL16) ||
|
||||||
|
(version == 0x22 && data[layout->decomode] == FROG_ZHL16) ||
|
||||||
|
(version == 0x21 && (data[layout->decomode] == OSTC_ZHL16_OC || data[layout->decomode] == OSTC_ZHL16_CC)))
|
||||||
|
snprintf(buf, BUFLEN, "Saturation %u, Desaturation %u", layout->deco_info1, layout->deco_info2);
|
||||||
|
else if (((version == 0x23 || version == 0x24) && data[layout->decomode] == OSTC3_ZHL16_GF) ||
|
||||||
|
(version == 0x22 && data[layout->decomode] == FROG_ZHL16_GF) ||
|
||||||
|
(version == 0x21 && (data[layout->decomode] == OSTC_ZHL16_OC_GF || data[layout->decomode] == OSTC_ZHL16_CC_GF)))
|
||||||
|
snprintf(buf, BUFLEN, "GF %u/%u", data[layout->deco_info1], data[layout->deco_info2]);
|
||||||
|
else
|
||||||
|
return DC_STATUS_DATAFORMAT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return DC_STATUS_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
string->value = strdup(buf);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return DC_STATUS_UNSUPPORTED;
|
return DC_STATUS_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,6 +75,8 @@ dc_usbhid_device_free
|
|||||||
dc_usbhid_iterator_new
|
dc_usbhid_iterator_new
|
||||||
dc_usbhid_open
|
dc_usbhid_open
|
||||||
|
|
||||||
|
dc_usb_storage_open
|
||||||
|
|
||||||
dc_custom_open
|
dc_custom_open
|
||||||
|
|
||||||
dc_parser_new
|
dc_parser_new
|
||||||
|
|||||||
@ -37,7 +37,7 @@ dc_status_t
|
|||||||
oceanic_atom2_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
|
oceanic_atom2_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
oceanic_atom2_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
|
oceanic_atom2_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model, unsigned int serial);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <libdivecomputer/units.h>
|
#include <libdivecomputer/units.h>
|
||||||
|
|
||||||
@ -103,6 +105,7 @@ struct oceanic_atom2_parser_t {
|
|||||||
unsigned int model;
|
unsigned int model;
|
||||||
unsigned int headersize;
|
unsigned int headersize;
|
||||||
unsigned int footersize;
|
unsigned int footersize;
|
||||||
|
unsigned int serial;
|
||||||
// Cached fields.
|
// Cached fields.
|
||||||
unsigned int cached;
|
unsigned int cached;
|
||||||
unsigned int header;
|
unsigned int header;
|
||||||
@ -132,7 +135,7 @@ static const dc_parser_vtable_t oceanic_atom2_parser_vtable = {
|
|||||||
|
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
|
oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial)
|
||||||
{
|
{
|
||||||
oceanic_atom2_parser_t *parser = NULL;
|
oceanic_atom2_parser_t *parser = NULL;
|
||||||
|
|
||||||
@ -179,6 +182,7 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
|
|||||||
parser->headersize = 3 * PAGESIZE;
|
parser->headersize = 3 * PAGESIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parser->serial = serial;
|
||||||
parser->cached = 0;
|
parser->cached = 0;
|
||||||
parser->header = 0;
|
parser->header = 0;
|
||||||
parser->footer = 0;
|
parser->footer = 0;
|
||||||
@ -370,6 +374,7 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim
|
|||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define BUF_LEN 16
|
||||||
|
|
||||||
static dc_status_t
|
static dc_status_t
|
||||||
oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser)
|
oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser)
|
||||||
@ -506,6 +511,9 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns
|
|||||||
|
|
||||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||||
|
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||||
|
|
||||||
|
char buf[BUF_LEN];
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -561,6 +569,17 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns
|
|||||||
return DC_STATUS_DATAFORMAT;
|
return DC_STATUS_DATAFORMAT;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case DC_FIELD_STRING:
|
||||||
|
switch(flags) {
|
||||||
|
case 0: /* Serial */
|
||||||
|
string->desc = "Serial";
|
||||||
|
snprintf(buf, BUF_LEN, "%06u", parser->serial);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return DC_STATUS_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
string->value = strdup(buf);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return DC_STATUS_UNSUPPORTED;
|
return DC_STATUS_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/parser.c
26
src/parser.c
@ -56,6 +56,7 @@
|
|||||||
#include "divesystem_idive.h"
|
#include "divesystem_idive.h"
|
||||||
#include "cochran_commander.h"
|
#include "cochran_commander.h"
|
||||||
#include "tecdiving_divecomputereu.h"
|
#include "tecdiving_divecomputereu.h"
|
||||||
|
#include "garmin.h"
|
||||||
|
|
||||||
#include "context-private.h"
|
#include "context-private.h"
|
||||||
#include "parser-private.h"
|
#include "parser-private.h"
|
||||||
@ -64,7 +65,7 @@
|
|||||||
#define REACTPROWHITE 0x4354
|
#define REACTPROWHITE 0x4354
|
||||||
|
|
||||||
static dc_status_t
|
static dc_status_t
|
||||||
dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t family, unsigned int model, unsigned int devtime, dc_ticks_t systime)
|
dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t family, unsigned int model, unsigned int serial, unsigned int devtime, dc_ticks_t systime)
|
||||||
{
|
{
|
||||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||||
dc_parser_t *parser = NULL;
|
dc_parser_t *parser = NULL;
|
||||||
@ -87,7 +88,7 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
|||||||
break;
|
break;
|
||||||
case DC_FAMILY_SUUNTO_VYPER2:
|
case DC_FAMILY_SUUNTO_VYPER2:
|
||||||
case DC_FAMILY_SUUNTO_D9:
|
case DC_FAMILY_SUUNTO_D9:
|
||||||
rc = suunto_d9_parser_create (&parser, context, model);
|
rc = suunto_d9_parser_create (&parser, context, model, serial);
|
||||||
break;
|
break;
|
||||||
case DC_FAMILY_SUUNTO_EONSTEEL:
|
case DC_FAMILY_SUUNTO_EONSTEEL:
|
||||||
rc = suunto_eonsteel_parser_create(&parser, context, model);
|
rc = suunto_eonsteel_parser_create(&parser, context, model);
|
||||||
@ -118,7 +119,7 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
|||||||
if (model == REACTPROWHITE)
|
if (model == REACTPROWHITE)
|
||||||
rc = oceanic_veo250_parser_create (&parser, context, model);
|
rc = oceanic_veo250_parser_create (&parser, context, model);
|
||||||
else
|
else
|
||||||
rc = oceanic_atom2_parser_create (&parser, context, model);
|
rc = oceanic_atom2_parser_create (&parser, context, model, serial);
|
||||||
break;
|
break;
|
||||||
case DC_FAMILY_MARES_NEMO:
|
case DC_FAMILY_MARES_NEMO:
|
||||||
case DC_FAMILY_MARES_PUCK:
|
case DC_FAMILY_MARES_PUCK:
|
||||||
@ -131,11 +132,11 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
|||||||
rc = mares_iconhd_parser_create (&parser, context, model);
|
rc = mares_iconhd_parser_create (&parser, context, model);
|
||||||
break;
|
break;
|
||||||
case DC_FAMILY_HW_OSTC:
|
case DC_FAMILY_HW_OSTC:
|
||||||
rc = hw_ostc_parser_create (&parser, context, 0);
|
rc = hw_ostc_parser_create (&parser, context, serial, 0);
|
||||||
break;
|
break;
|
||||||
case DC_FAMILY_HW_FROG:
|
case DC_FAMILY_HW_FROG:
|
||||||
case DC_FAMILY_HW_OSTC3:
|
case DC_FAMILY_HW_OSTC3:
|
||||||
rc = hw_ostc3_parser_create (&parser, context, model);
|
rc = hw_ostc3_parser_create (&parser, context, serial, model);
|
||||||
break;
|
break;
|
||||||
case DC_FAMILY_CRESSI_EDY:
|
case DC_FAMILY_CRESSI_EDY:
|
||||||
case DC_FAMILY_ZEAGLE_N2ITION3:
|
case DC_FAMILY_ZEAGLE_N2ITION3:
|
||||||
@ -148,10 +149,10 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
|||||||
rc = atomics_cobalt_parser_create (&parser, context);
|
rc = atomics_cobalt_parser_create (&parser, context);
|
||||||
break;
|
break;
|
||||||
case DC_FAMILY_SHEARWATER_PREDATOR:
|
case DC_FAMILY_SHEARWATER_PREDATOR:
|
||||||
rc = shearwater_predator_parser_create (&parser, context, model);
|
rc = shearwater_predator_parser_create (&parser, context, model, serial);
|
||||||
break;
|
break;
|
||||||
case DC_FAMILY_SHEARWATER_PETREL:
|
case DC_FAMILY_SHEARWATER_PETREL:
|
||||||
rc = shearwater_petrel_parser_create (&parser, context, model);
|
rc = shearwater_petrel_parser_create (&parser, context, model, serial);
|
||||||
break;
|
break;
|
||||||
case DC_FAMILY_DIVERITE_NITEKQ:
|
case DC_FAMILY_DIVERITE_NITEKQ:
|
||||||
rc = diverite_nitekq_parser_create (&parser, context);
|
rc = diverite_nitekq_parser_create (&parser, context);
|
||||||
@ -168,6 +169,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
|||||||
case DC_FAMILY_TECDIVING_DIVECOMPUTEREU:
|
case DC_FAMILY_TECDIVING_DIVECOMPUTEREU:
|
||||||
rc = tecdiving_divecomputereu_parser_create (&parser, context);
|
rc = tecdiving_divecomputereu_parser_create (&parser, context);
|
||||||
break;
|
break;
|
||||||
|
case DC_FAMILY_GARMIN:
|
||||||
|
rc = garmin_parser_create (&parser, context);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return DC_STATUS_INVALIDARGS;
|
return DC_STATUS_INVALIDARGS;
|
||||||
}
|
}
|
||||||
@ -184,7 +188,9 @@ dc_parser_new (dc_parser_t **out, dc_device_t *device)
|
|||||||
return DC_STATUS_INVALIDARGS;
|
return DC_STATUS_INVALIDARGS;
|
||||||
|
|
||||||
return dc_parser_new_internal (out, device->context,
|
return dc_parser_new_internal (out, device->context,
|
||||||
dc_device_get_type (device), device->devinfo.model,
|
dc_device_get_type (device),
|
||||||
|
device->devinfo.model,
|
||||||
|
device->devinfo.serial,
|
||||||
device->clock.devtime, device->clock.systime);
|
device->clock.devtime, device->clock.systime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +198,9 @@ dc_status_t
|
|||||||
dc_parser_new2 (dc_parser_t **out, dc_context_t *context, dc_descriptor_t *descriptor, unsigned int devtime, dc_ticks_t systime)
|
dc_parser_new2 (dc_parser_t **out, dc_context_t *context, dc_descriptor_t *descriptor, unsigned int devtime, dc_ticks_t systime)
|
||||||
{
|
{
|
||||||
return dc_parser_new_internal (out, context,
|
return dc_parser_new_internal (out, context,
|
||||||
dc_descriptor_get_type (descriptor), dc_descriptor_get_model (descriptor),
|
dc_descriptor_get_type (descriptor),
|
||||||
|
dc_descriptor_get_model (descriptor),
|
||||||
|
0,
|
||||||
devtime, systime);
|
devtime, systime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -316,6 +316,27 @@ done:
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dc_status_t
|
||||||
|
shearwater_common_command (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize)
|
||||||
|
{
|
||||||
|
dc_status_t status = DC_STATUS_SUCCESS;
|
||||||
|
dc_device_t *abstract = (dc_device_t *) device;
|
||||||
|
|
||||||
|
if (isize > SZ_PACKET)
|
||||||
|
return DC_STATUS_INVALIDARGS;
|
||||||
|
|
||||||
|
if (device_is_cancelled (abstract))
|
||||||
|
return DC_STATUS_CANCELLED;
|
||||||
|
|
||||||
|
// Send the command packet.
|
||||||
|
status = shearwater_common_slip_write (device, input, isize);
|
||||||
|
if (status != DC_STATUS_SUCCESS)
|
||||||
|
ERROR (abstract->context, "Failed to send the command packet.");
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
shearwater_common_transfer (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize, unsigned int *actual)
|
shearwater_common_transfer (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize, unsigned int *actual)
|
||||||
@ -379,7 +400,6 @@ shearwater_common_transfer (shearwater_common_device_t *device, const unsigned c
|
|||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buffer, unsigned int address, unsigned int size, unsigned int compression, dc_event_progress_t *progress)
|
shearwater_common_download (shearwater_common_device_t *device, dc_buffer_t *buffer, unsigned int address, unsigned int size, unsigned int compression, dc_event_progress_t *progress)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -32,6 +32,7 @@ extern "C" {
|
|||||||
|
|
||||||
#define ID_SERIAL 0x8010
|
#define ID_SERIAL 0x8010
|
||||||
#define ID_FIRMWARE 0x8011
|
#define ID_FIRMWARE 0x8011
|
||||||
|
#define ID_RDBI 0x8021
|
||||||
#define ID_HARDWARE 0x8050
|
#define ID_HARDWARE 0x8050
|
||||||
|
|
||||||
#define PREDATOR 2
|
#define PREDATOR 2
|
||||||
@ -40,6 +41,7 @@ extern "C" {
|
|||||||
#define PERDIX 5
|
#define PERDIX 5
|
||||||
#define PERDIXAI 6
|
#define PERDIXAI 6
|
||||||
#define NERD2 7
|
#define NERD2 7
|
||||||
|
#define TERIC 8
|
||||||
|
|
||||||
#define NSTEPS 10000
|
#define NSTEPS 10000
|
||||||
#define STEP(i,n) ((NSTEPS * (i) + (n) / 2) / (n))
|
#define STEP(i,n) ((NSTEPS * (i) + (n) / 2) / (n))
|
||||||
@ -52,6 +54,9 @@ typedef struct shearwater_common_device_t {
|
|||||||
dc_status_t
|
dc_status_t
|
||||||
shearwater_common_setup (shearwater_common_device_t *device, dc_context_t *context, dc_iostream_t *iostream);
|
shearwater_common_setup (shearwater_common_device_t *device, dc_context_t *context, dc_iostream_t *iostream);
|
||||||
|
|
||||||
|
dc_status_t
|
||||||
|
shearwater_common_command (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize);
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
shearwater_common_transfer (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize, unsigned int *actual);
|
shearwater_common_transfer (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize, unsigned int *actual);
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,6 @@
|
|||||||
#define MANIFEST_ADDR 0xE0000000
|
#define MANIFEST_ADDR 0xE0000000
|
||||||
#define MANIFEST_SIZE 0x600
|
#define MANIFEST_SIZE 0x600
|
||||||
|
|
||||||
#define DIVE_ADDR 0xC0000000
|
|
||||||
#define DIVE_SIZE 0xFFFFFF
|
#define DIVE_SIZE 0xFFFFFF
|
||||||
|
|
||||||
#define RECORD_SIZE 0x20
|
#define RECORD_SIZE 0x20
|
||||||
@ -212,25 +211,40 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
|||||||
unsigned int hardware = array_uint_be (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer));
|
unsigned int hardware = array_uint_be (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer));
|
||||||
unsigned int model = 0;
|
unsigned int model = 0;
|
||||||
switch (hardware) {
|
switch (hardware) {
|
||||||
case 0x0808: // Petrel 2
|
case 0x0101:
|
||||||
case 0x0909: // Petrel 1
|
case 0x0202:
|
||||||
case 0x0B0B: // Petrel 1 (newer hardware)
|
model = PREDATOR;
|
||||||
model = PETREL;
|
|
||||||
break;
|
break;
|
||||||
|
case 0x0606:
|
||||||
case 0x0A0A: // Nerd 1
|
case 0x0A0A: // Nerd 1
|
||||||
model = NERD;
|
model = NERD;
|
||||||
break;
|
break;
|
||||||
case 0x0E0D: // Nerd 2
|
case 0x0E0D: // Nerd 2
|
||||||
model = NERD2;
|
model = NERD2;
|
||||||
break;
|
break;
|
||||||
case 0x0707:
|
case 0x0404:
|
||||||
|
case 0x0909: // Petrel 1
|
||||||
|
case 0x0B0B: // Petrel 1 (newer hardware)
|
||||||
|
model = PETREL;
|
||||||
|
break;
|
||||||
|
case 0x0505:
|
||||||
|
case 0x0808: // Petrel 2
|
||||||
|
model = PETREL;
|
||||||
|
break;
|
||||||
|
case 0x0707: // documentation list 0C0D for both Perdix and Perdix AI :-(
|
||||||
model = PERDIX;
|
model = PERDIX;
|
||||||
break;
|
break;
|
||||||
|
case 0x0C0C:
|
||||||
case 0x0C0D:
|
case 0x0C0D:
|
||||||
|
case 0x0D0D:
|
||||||
model = PERDIXAI;
|
model = PERDIXAI;
|
||||||
break;
|
break;
|
||||||
|
case 0x0F0F:
|
||||||
|
model = TERIC;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
WARNING (abstract->context, "Unknown hardware type %04x.", hardware);
|
model = PETREL;
|
||||||
|
WARNING (abstract->context, "Unknown hardware type %04x. Assuming Petrel.", hardware);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit a device info event.
|
// Emit a device info event.
|
||||||
@ -240,6 +254,39 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
|||||||
devinfo.serial = array_uint32_be (serial);
|
devinfo.serial = array_uint32_be (serial);
|
||||||
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
|
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
|
||||||
|
|
||||||
|
// Read the logbook type
|
||||||
|
rc = shearwater_common_identifier (&device->base, buffer, ID_RDBI);
|
||||||
|
if (rc != DC_STATUS_SUCCESS) {
|
||||||
|
ERROR (abstract->context, "Failed to read the logbook type.");
|
||||||
|
dc_buffer_free (buffer);
|
||||||
|
dc_buffer_free (manifests);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
unsigned int base_addr = array_uint_be (dc_buffer_get_data (buffer), dc_buffer_get_size (buffer));
|
||||||
|
INFO(abstract->context, "RDBI command completed with %d bytes, evaluated as %08x", dc_buffer_get_size (buffer), base_addr);
|
||||||
|
base_addr &= 0xFF000000u;
|
||||||
|
switch (base_addr) {
|
||||||
|
case 0xDD000000: // Predator or Predator-Like Format
|
||||||
|
// on a Predator, use the old format, otherwise use the Predator-Like Format (what we called Petrel so far)
|
||||||
|
if (model != PREDATOR)
|
||||||
|
base_addr = 0xC0000000u;
|
||||||
|
break;
|
||||||
|
case 0x90000000: // some firmware versions supported an earlier version of PNF without final record
|
||||||
|
// use the Predator-Like Format instead
|
||||||
|
base_addr = 0xC0000000u;
|
||||||
|
break;
|
||||||
|
case 0x80000000: // new Petrel Native Format with final record
|
||||||
|
// that's the correct address
|
||||||
|
break;
|
||||||
|
default: // unknown format
|
||||||
|
// use the defaults for the models
|
||||||
|
if (model >= TERIC)
|
||||||
|
base_addr = 0x80000000u;
|
||||||
|
else
|
||||||
|
base_addr = 0xC0000000u;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the manifest pages
|
||||||
while (1) {
|
while (1) {
|
||||||
// Update the progress state.
|
// Update the progress state.
|
||||||
// Assume the worst case scenario of a full manifest, and adjust the
|
// Assume the worst case scenario of a full manifest, and adjust the
|
||||||
@ -263,11 +310,17 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
|||||||
unsigned int size = dc_buffer_get_size (buffer);
|
unsigned int size = dc_buffer_get_size (buffer);
|
||||||
|
|
||||||
// Process the records in the manifest.
|
// Process the records in the manifest.
|
||||||
unsigned int count = 0;
|
unsigned int count = 0, deleted = 0;
|
||||||
unsigned int offset = 0;
|
unsigned int offset = 0;
|
||||||
while (offset < size) {
|
while (offset < size) {
|
||||||
// Check for a valid dive header.
|
// Check for a valid dive header.
|
||||||
unsigned int header = array_uint16_be (data + offset);
|
unsigned int header = array_uint16_be (data + offset);
|
||||||
|
if (header == 0x5A23) {
|
||||||
|
// this is a deleted dive; keep looking
|
||||||
|
offset += RECORD_SIZE;
|
||||||
|
deleted++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (header != 0xA5C4)
|
if (header != 0xA5C4)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -281,7 +334,7 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
|||||||
|
|
||||||
// Update the progress state.
|
// Update the progress state.
|
||||||
current += 1;
|
current += 1;
|
||||||
maximum -= RECORD_COUNT - count;
|
maximum -= RECORD_COUNT - count - deleted;
|
||||||
|
|
||||||
// Append the manifest records to the main buffer.
|
// Append the manifest records to the main buffer.
|
||||||
if (!dc_buffer_append (manifests, data, count * RECORD_SIZE)) {
|
if (!dc_buffer_append (manifests, data, count * RECORD_SIZE)) {
|
||||||
@ -292,7 +345,7 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stop downloading manifest if there are no more records.
|
// Stop downloading manifest if there are no more records.
|
||||||
if (count != RECORD_COUNT)
|
if (count + deleted != RECORD_COUNT)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,13 +360,18 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
|||||||
|
|
||||||
unsigned int offset = 0;
|
unsigned int offset = 0;
|
||||||
while (offset < size) {
|
while (offset < size) {
|
||||||
|
// skip deleted dives
|
||||||
|
if (array_uint16_be(data + offset) == 0x5A23) {
|
||||||
|
offset += RECORD_SIZE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Get the address of the dive.
|
// Get the address of the dive.
|
||||||
unsigned int address = array_uint32_be (data + offset + 20);
|
unsigned int address = array_uint32_be (data + offset + 20);
|
||||||
|
|
||||||
// Download the dive.
|
// Download the dive.
|
||||||
progress.current = NSTEPS * current;
|
progress.current = NSTEPS * current;
|
||||||
progress.maximum = NSTEPS * maximum;
|
progress.maximum = NSTEPS * maximum;
|
||||||
rc = shearwater_common_download (&device->base, buffer, DIVE_ADDR + address, DIVE_SIZE, 1, &progress);
|
rc = shearwater_common_download (&device->base, buffer, base_addr + address, DIVE_SIZE, 1, &progress);
|
||||||
if (rc != DC_STATUS_SUCCESS) {
|
if (rc != DC_STATUS_SUCCESS) {
|
||||||
ERROR (abstract->context, "Failed to download the dive.");
|
ERROR (abstract->context, "Failed to download the dive.");
|
||||||
dc_buffer_free (buffer);
|
dc_buffer_free (buffer);
|
||||||
@ -331,6 +389,11 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
|||||||
|
|
||||||
offset += RECORD_SIZE;
|
offset += RECORD_SIZE;
|
||||||
}
|
}
|
||||||
|
// send the "graceful exit" instruction
|
||||||
|
// I don't think we care about the return value of this call - this is just trying to be nice to the device
|
||||||
|
unsigned char command[4] = { 0x2e, 0x90, 0x20, 0x00 };
|
||||||
|
rc = shearwater_common_command(&device->base, command, 4);
|
||||||
|
DEBUG(abstract->context, "Sent graceful exit command, rc=%d", rc);
|
||||||
|
|
||||||
// Update and emit a progress event.
|
// Update and emit a progress event.
|
||||||
progress.current = NSTEPS * current;
|
progress.current = NSTEPS * current;
|
||||||
|
|||||||
@ -35,7 +35,7 @@ dc_status_t
|
|||||||
shearwater_petrel_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
shearwater_petrel_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
shearwater_petrel_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
|
shearwater_petrel_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model, unsigned int serial);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,7 @@ dc_status_t
|
|||||||
shearwater_predator_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
shearwater_predator_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
shearwater_predator_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
|
shearwater_predator_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model, unsigned int serial);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <libdivecomputer/units.h>
|
#include <libdivecomputer/units.h>
|
||||||
|
|
||||||
@ -33,6 +40,30 @@
|
|||||||
dc_parser_isinstance((parser), &shearwater_predator_parser_vtable) || \
|
dc_parser_isinstance((parser), &shearwater_predator_parser_vtable) || \
|
||||||
dc_parser_isinstance((parser), &shearwater_petrel_parser_vtable))
|
dc_parser_isinstance((parser), &shearwater_petrel_parser_vtable))
|
||||||
|
|
||||||
|
// Petrel Native Format constants
|
||||||
|
#define PNF_BLOCKSIZE 0x20
|
||||||
|
#define LOG_RECORD_DIVE_SAMPLE 0x01
|
||||||
|
#define LOG_RECORD_FREEDIVE_SAMPLE 0x02
|
||||||
|
#define LOG_RECORD_OPENING_0 0x10
|
||||||
|
#define LOG_RECORD_OPENING_1 0x11
|
||||||
|
#define LOG_RECORD_OPENING_2 0x12
|
||||||
|
#define LOG_RECORD_OPENING_3 0x13
|
||||||
|
#define LOG_RECORD_OPENING_4 0x14
|
||||||
|
#define LOG_RECORD_OPENING_5 0x15
|
||||||
|
#define LOG_RECORD_OPENING_6 0x16
|
||||||
|
#define LOG_RECORD_OPENING_7 0x17
|
||||||
|
#define LOG_RECORD_CLOSING_0 0x20
|
||||||
|
#define LOG_RECORD_CLOSING_1 0x21
|
||||||
|
#define LOG_RECORD_CLOSING_2 0x22
|
||||||
|
#define LOG_RECORD_CLOSING_3 0x23
|
||||||
|
#define LOG_RECORD_CLOSING_4 0x24
|
||||||
|
#define LOG_RECORD_CLOSING_5 0x25
|
||||||
|
#define LOG_RECORD_CLOSING_6 0x26
|
||||||
|
#define LOG_RECORD_CLOSING_7 0x27
|
||||||
|
#define LOG_RECORD_FINAL 0xFF
|
||||||
|
#define NUM_BLOCK_IDS 0x28
|
||||||
|
|
||||||
|
// constant for the older Predator and Predator-like formats
|
||||||
#define SZ_BLOCK 0x80
|
#define SZ_BLOCK 0x80
|
||||||
#define SZ_SAMPLE_PREDATOR 0x10
|
#define SZ_SAMPLE_PREDATOR 0x10
|
||||||
#define SZ_SAMPLE_PETREL 0x20
|
#define SZ_SAMPLE_PETREL 0x20
|
||||||
@ -47,6 +78,7 @@
|
|||||||
#define IMPERIAL 1
|
#define IMPERIAL 1
|
||||||
|
|
||||||
#define NGASMIXES 10
|
#define NGASMIXES 10
|
||||||
|
#define MAXSTRINGS 32
|
||||||
|
|
||||||
#define PREDATOR 2
|
#define PREDATOR 2
|
||||||
#define PETREL 3
|
#define PETREL 3
|
||||||
@ -57,6 +89,7 @@ struct shearwater_predator_parser_t {
|
|||||||
dc_parser_t base;
|
dc_parser_t base;
|
||||||
unsigned int model;
|
unsigned int model;
|
||||||
unsigned int petrel;
|
unsigned int petrel;
|
||||||
|
unsigned int pnf;
|
||||||
unsigned int samplesize;
|
unsigned int samplesize;
|
||||||
// Cached fields.
|
// Cached fields.
|
||||||
unsigned int cached;
|
unsigned int cached;
|
||||||
@ -68,7 +101,14 @@ struct shearwater_predator_parser_t {
|
|||||||
unsigned int helium[NGASMIXES];
|
unsigned int helium[NGASMIXES];
|
||||||
unsigned int calibrated;
|
unsigned int calibrated;
|
||||||
double calibration[3];
|
double calibration[3];
|
||||||
|
unsigned int serial;
|
||||||
dc_divemode_t mode;
|
dc_divemode_t mode;
|
||||||
|
|
||||||
|
/* Block addresses for PNF */
|
||||||
|
unsigned int block_offset[NUM_BLOCK_IDS];
|
||||||
|
|
||||||
|
/* String fields */
|
||||||
|
dc_field_string_t strings[MAXSTRINGS];
|
||||||
};
|
};
|
||||||
|
|
||||||
static dc_status_t shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
static dc_status_t shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||||
@ -112,7 +152,7 @@ shearwater_predator_find_gasmix (shearwater_predator_parser_t *parser, unsigned
|
|||||||
|
|
||||||
|
|
||||||
static dc_status_t
|
static dc_status_t
|
||||||
shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int petrel)
|
shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial, unsigned int petrel)
|
||||||
{
|
{
|
||||||
shearwater_predator_parser_t *parser = NULL;
|
shearwater_predator_parser_t *parser = NULL;
|
||||||
const dc_parser_vtable_t *vtable = NULL;
|
const dc_parser_vtable_t *vtable = NULL;
|
||||||
@ -140,6 +180,9 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig
|
|||||||
parser->model = model;
|
parser->model = model;
|
||||||
parser->petrel = petrel;
|
parser->petrel = petrel;
|
||||||
parser->samplesize = samplesize;
|
parser->samplesize = samplesize;
|
||||||
|
parser->serial = serial;
|
||||||
|
|
||||||
|
// Set the default values.
|
||||||
parser->cached = 0;
|
parser->cached = 0;
|
||||||
parser->logversion = 0;
|
parser->logversion = 0;
|
||||||
parser->headersize = 0;
|
parser->headersize = 0;
|
||||||
@ -162,16 +205,16 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig
|
|||||||
|
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
|
shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial)
|
||||||
{
|
{
|
||||||
return shearwater_common_parser_create (out, context, model, 0);
|
return shearwater_common_parser_create (out, context, model, serial, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
shearwater_petrel_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
|
shearwater_petrel_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial)
|
||||||
{
|
{
|
||||||
return shearwater_common_parser_create (out, context, model, 1);
|
return shearwater_common_parser_create (out, context, model, serial, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -205,6 +248,7 @@ shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *d
|
|||||||
{
|
{
|
||||||
const unsigned char *data = abstract->data;
|
const unsigned char *data = abstract->data;
|
||||||
unsigned int size = abstract->size;
|
unsigned int size = abstract->size;
|
||||||
|
shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract;
|
||||||
|
|
||||||
if (size < 2 * SZ_BLOCK)
|
if (size < 2 * SZ_BLOCK)
|
||||||
return DC_STATUS_DATAFORMAT;
|
return DC_STATUS_DATAFORMAT;
|
||||||
@ -219,6 +263,150 @@ shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *d
|
|||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These string cache interfaces should be some generic
|
||||||
|
* library rather than copied for all the dive computers.
|
||||||
|
*
|
||||||
|
* This is just copied from the EON Steel code.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
add_string(shearwater_predator_parser_t *parser, const char *desc, const char *value)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAXSTRINGS; i++) {
|
||||||
|
dc_field_string_t *str = parser->strings+i;
|
||||||
|
if (str->desc)
|
||||||
|
continue;
|
||||||
|
str->desc = desc;
|
||||||
|
str->value = strdup(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_string_fmt(shearwater_predator_parser_t *parser, const char *desc, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We ignore the return value from vsnprintf, and we
|
||||||
|
* always NUL-terminate the destination buffer ourselves.
|
||||||
|
*
|
||||||
|
* That way we don't have to worry about random bad legacy
|
||||||
|
* implementations.
|
||||||
|
*/
|
||||||
|
va_start(ap, fmt);
|
||||||
|
buffer[sizeof(buffer)-1] = 0;
|
||||||
|
(void) vsnprintf(buffer, sizeof(buffer)-1, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return add_string(parser, desc, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Battery state is a big-endian word:
|
||||||
|
//
|
||||||
|
// ffff = not paired / no comms for 90 s
|
||||||
|
// fffe = no comms for 30 s
|
||||||
|
//
|
||||||
|
// Otherwise:
|
||||||
|
// - top four bits are battery state (0 - normal, 1 - critical, 2 - warning)
|
||||||
|
// - bottom 12 bits are pressure in 2 psi increments (0..8k psi)
|
||||||
|
//
|
||||||
|
// This returns the state as a bitmask (so you can see all states it had
|
||||||
|
// during the dive). Note that we currently do not report pairing and
|
||||||
|
// communication lapses. Todo?
|
||||||
|
static unsigned int
|
||||||
|
battery_state(const unsigned char *data)
|
||||||
|
{
|
||||||
|
unsigned int pressure = array_uint16_be(data);
|
||||||
|
unsigned int state;
|
||||||
|
|
||||||
|
if ((pressure & 0xFFF0) == 0xFFF0)
|
||||||
|
return 0;
|
||||||
|
state = pressure >> 12;
|
||||||
|
if (state > 2)
|
||||||
|
return 0;
|
||||||
|
return 1u << state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the battery state
|
||||||
|
//
|
||||||
|
// NOTE! Right now it only shows the most serious bit
|
||||||
|
// but the code is set up so that we could perhaps
|
||||||
|
// indicate that the battery is on the edge (ie it
|
||||||
|
// reported both "normal" _and_ "warning" during the
|
||||||
|
// dive - maybe that would be a "starting to warn")
|
||||||
|
//
|
||||||
|
// We could also report unpaired and comm errors.
|
||||||
|
static void
|
||||||
|
add_battery_info(shearwater_predator_parser_t *parser, const char *desc, unsigned int state)
|
||||||
|
{
|
||||||
|
if (state >= 1 && state <= 7) {
|
||||||
|
static const char *states[8] = {
|
||||||
|
"", // 000 - No state bits, not used
|
||||||
|
"normal", // 001 - only normal
|
||||||
|
"critical", // 010 - only critical
|
||||||
|
"critical", // 011 - both normal and critical
|
||||||
|
"warning", // 100 - only warning
|
||||||
|
"warning", // 101 - normal and warning
|
||||||
|
"critical", // 110 - warning and critical
|
||||||
|
"critical", // 111 - normal, warning and critical
|
||||||
|
};
|
||||||
|
add_string(parser, desc, states[state]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_deco_model(shearwater_predator_parser_t *parser, const unsigned char *data)
|
||||||
|
{
|
||||||
|
unsigned int idx_deco_model = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_2] + 18 : 67;
|
||||||
|
unsigned int idx_gfs = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_3] + 5 : 85;
|
||||||
|
|
||||||
|
switch (data[idx_deco_model]) {
|
||||||
|
case 0:
|
||||||
|
add_string_fmt(parser, "Deco model", "GF %u/%u", data[4], data[5]);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
add_string_fmt(parser, "Deco model", "VPM-B +%u", data[idx_deco_model + 1]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
add_string_fmt(parser, "Deco model", "VPM-B/GFS +%u %u%%", data[idx_deco_model + 1], data[idx_gfs]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
add_string_fmt(parser, "Deco model", "Unknown model %d", data[idx_deco_model]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_battery_type(shearwater_predator_parser_t *parser, const unsigned char *data)
|
||||||
|
{
|
||||||
|
if (parser->logversion < 7)
|
||||||
|
return;
|
||||||
|
|
||||||
|
unsigned int idx_battery_type = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_4] + 9 : 120;
|
||||||
|
switch (data[idx_battery_type]) {
|
||||||
|
case 1:
|
||||||
|
add_string(parser, "Battery type", "1.5V Alkaline");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
add_string(parser, "Battery type", "1.5V Lithium");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
add_string(parser, "Battery type", "1.2V NiMH");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
add_string(parser, "Battery type", "3.6V Saft");
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
add_string(parser, "Battery type", "3.7V Li-Ion");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
add_string_fmt(parser, "Battery type", "unknown type %d", data[idx_battery_type]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static dc_status_t
|
static dc_status_t
|
||||||
shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||||
@ -231,21 +419,66 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
|||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the log formats are very similar - but the Petrel Native Format (PNF)
|
||||||
|
// is organized differently. There everything is in 32 byte (PNF_BLOCKSIZE) blocks
|
||||||
|
// and the offsets of various fields are different. It still seems to make sense
|
||||||
|
// to just all parse it in one place
|
||||||
|
|
||||||
|
// header and footer are concepts of the Predator and Predator-like formats
|
||||||
unsigned int headersize = SZ_BLOCK;
|
unsigned int headersize = SZ_BLOCK;
|
||||||
unsigned int footersize = SZ_BLOCK;
|
unsigned int footersize = SZ_BLOCK;
|
||||||
|
|
||||||
if (size < headersize + footersize) {
|
if (size < headersize + footersize) {
|
||||||
ERROR (abstract->context, "Invalid data length.");
|
ERROR (abstract->context, "Invalid data length.");
|
||||||
return DC_STATUS_DATAFORMAT;
|
return DC_STATUS_DATAFORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remember if this is a Petrel Native Format download
|
||||||
|
// if yes, we need different ways to access the various data fields
|
||||||
|
// for samples it's simple, they are just offset by one (so we can use pnf as offset)
|
||||||
|
// for header and footer data it's more complicated because of the new block structure
|
||||||
|
unsigned int pnf = parser->pnf = data[0] == 0x10 ? 1 : 0;
|
||||||
|
|
||||||
|
// sanity check on the log format
|
||||||
|
// is this a Predator-like or Petrel-native (or Teric style) log?
|
||||||
|
if (parser->petrel == 0 && pnf) {
|
||||||
|
ERROR (abstract->context, "This is a Petrel-native log, but we claim this is a Predator");
|
||||||
|
return DC_STATUS_DATAFORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset (parser->block_offset, 0, NUM_BLOCK_IDS * sizeof(unsigned int));
|
||||||
|
if (pnf) {
|
||||||
|
// find the offsets of the various header and footer blocks
|
||||||
|
int i = 0, j = 0;
|
||||||
|
while (i < size) {
|
||||||
|
for (j = LOG_RECORD_OPENING_0; j < NUM_BLOCK_IDS; j++) {
|
||||||
|
if (data[i] == j)
|
||||||
|
parser->block_offset[j] = i;
|
||||||
|
if (j == LOG_RECORD_OPENING_7)
|
||||||
|
j = LOG_RECORD_CLOSING_0 - 1;
|
||||||
|
}
|
||||||
|
i += PNF_BLOCKSIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// there is a small risk we are taking here... if the log were damaged and one or
|
||||||
|
// more of the blocks were missing, we'll default to looking into block 0 and
|
||||||
|
// report bogus data. This may be worth testing for?
|
||||||
|
|
||||||
// Log versions before 6 weren't reliably stored in the data, but
|
// Log versions before 6 weren't reliably stored in the data, but
|
||||||
// 6 is also the oldest version that we assume in our code
|
// 6 is also the oldest version that we assume in our code
|
||||||
unsigned int logversion = 6;
|
unsigned int logversion = 6;
|
||||||
if (data[127] > 6)
|
if (!pnf && data[127] > 6)
|
||||||
logversion = data[127];
|
logversion = data[127];
|
||||||
|
if (pnf)
|
||||||
|
logversion = data[parser->block_offset[LOG_RECORD_OPENING_4] + 16];
|
||||||
|
|
||||||
|
INFO(abstract->context, "Shearwater log version %u\n", logversion);
|
||||||
|
|
||||||
|
memset(parser->strings, 0, sizeof(parser->strings));
|
||||||
|
add_string_fmt(parser, "Logversion", "%d%s", logversion, pnf ? "(PNF)" : "");
|
||||||
|
|
||||||
// Adjust the footersize for the final block.
|
// Adjust the footersize for the final block.
|
||||||
if (parser->petrel || array_uint16_be (data + size - footersize) == 0xFFFD) {
|
if (parser->petrel == 1 || array_uint16_be (data + size - footersize) == 0xFFFD) {
|
||||||
footersize += SZ_BLOCK;
|
footersize += SZ_BLOCK;
|
||||||
if (size < headersize + footersize) {
|
if (size < headersize + footersize) {
|
||||||
ERROR (abstract->context, "Invalid data length.");
|
ERROR (abstract->context, "Invalid data length.");
|
||||||
@ -253,6 +486,14 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if this is logversion 9 or higher, make sure this isn't a freedive, as we can't parse that
|
||||||
|
if (logversion > 9 && pnf) {
|
||||||
|
if (data[parser->block_offset[LOG_RECORD_OPENING_5] + 25] == LOG_RECORD_FREEDIVE_SAMPLE) {
|
||||||
|
ERROR (abstract->context, "Cannot parse freedive samples");
|
||||||
|
return DC_STATUS_DATAFORMAT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Default dive mode.
|
// Default dive mode.
|
||||||
dc_divemode_t mode = DC_DIVEMODE_OC;
|
dc_divemode_t mode = DC_DIVEMODE_OC;
|
||||||
|
|
||||||
@ -262,9 +503,19 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
|||||||
unsigned int helium[NGASMIXES] = {0};
|
unsigned int helium[NGASMIXES] = {0};
|
||||||
unsigned int o2_previous = 0, he_previous = 0;
|
unsigned int o2_previous = 0, he_previous = 0;
|
||||||
|
|
||||||
unsigned int offset = headersize;
|
// Transmitter battery levels
|
||||||
unsigned int length = size - footersize;
|
unsigned int t1_battery = 0, t2_battery = 0;
|
||||||
|
|
||||||
|
// the indices in the sample block are offset by 1 in PNF
|
||||||
|
unsigned int offset = pnf ? 0 : headersize;
|
||||||
|
unsigned int length = pnf ? size : size - footersize;
|
||||||
|
|
||||||
while (offset < length) {
|
while (offset < length) {
|
||||||
|
// Ignore blocks that aren't dive samples
|
||||||
|
if (pnf && data[offset] != LOG_RECORD_DIVE_SAMPLE) {
|
||||||
|
offset += parser->samplesize;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Ignore empty samples.
|
// Ignore empty samples.
|
||||||
if (array_isequal (data + offset, parser->samplesize, 0x00)) {
|
if (array_isequal (data + offset, parser->samplesize, 0x00)) {
|
||||||
offset += parser->samplesize;
|
offset += parser->samplesize;
|
||||||
@ -272,14 +523,14 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Status flags.
|
// Status flags.
|
||||||
unsigned int status = data[offset + 11];
|
unsigned int status = data[offset + 11 + pnf];
|
||||||
if ((status & OC) == 0) {
|
if ((status & OC) == 0) {
|
||||||
mode = DC_DIVEMODE_CCR;
|
mode = DC_DIVEMODE_CCR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gaschange.
|
// Gaschange.
|
||||||
unsigned int o2 = data[offset + 7];
|
unsigned int o2 = data[offset + 7 + pnf];
|
||||||
unsigned int he = data[offset + 8];
|
unsigned int he = data[offset + 8 + pnf];
|
||||||
if (o2 != o2_previous || he != he_previous) {
|
if (o2 != o2_previous || he != he_previous) {
|
||||||
// Find the gasmix in the list.
|
// Find the gasmix in the list.
|
||||||
unsigned int idx = 0;
|
unsigned int idx = 0;
|
||||||
@ -304,13 +555,27 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
|||||||
he_previous = he;
|
he_previous = he;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transmitter battery levels
|
||||||
|
if (logversion >= 7) {
|
||||||
|
// T1 at offset 27, T2 at offset 19
|
||||||
|
t1_battery |= battery_state(data + offset + 27 + pnf);
|
||||||
|
t2_battery |= battery_state(data + offset + 19 + pnf);
|
||||||
|
}
|
||||||
|
|
||||||
offset += parser->samplesize;
|
offset += parser->samplesize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for header and footer indices we use a variable base that is set to the
|
||||||
|
// correct value based on the log type
|
||||||
|
unsigned int base = 0;
|
||||||
|
|
||||||
// Cache sensor calibration for later use
|
// Cache sensor calibration for later use
|
||||||
unsigned int nsensors = 0, ndefaults = 0;
|
unsigned int nsensors = 0, ndefaults = 0;
|
||||||
|
|
||||||
|
// calibration value for sensors
|
||||||
|
base = pnf ? parser->block_offset[LOG_RECORD_OPENING_3] + 7 : 87;
|
||||||
for (size_t i = 0; i < 3; ++i) {
|
for (size_t i = 0; i < 3; ++i) {
|
||||||
unsigned int calibration = array_uint16_be(data + 87 + i * 2);
|
unsigned int calibration = array_uint16_be(data + base + i * 2);
|
||||||
parser->calibration[i] = calibration / 100000.0;
|
parser->calibration[i] = calibration / 100000.0;
|
||||||
if (parser->model == PREDATOR) {
|
if (parser->model == PREDATOR) {
|
||||||
// The Predator expects the mV output of the cells to be
|
// The Predator expects the mV output of the cells to be
|
||||||
@ -319,7 +584,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
|||||||
// sensors lines up and matches the average.
|
// sensors lines up and matches the average.
|
||||||
parser->calibration[i] *= 2.2;
|
parser->calibration[i] *= 2.2;
|
||||||
}
|
}
|
||||||
if (data[86] & (1 << i)) {
|
if (data[base - 1] & (1 << i)) {
|
||||||
if (calibration == 2100) {
|
if (calibration == 2100) {
|
||||||
ndefaults++;
|
ndefaults++;
|
||||||
}
|
}
|
||||||
@ -334,8 +599,12 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
|||||||
// uncalibrated).
|
// uncalibrated).
|
||||||
WARNING (abstract->context, "Disabled all O2 sensors due to a default calibration value.");
|
WARNING (abstract->context, "Disabled all O2 sensors due to a default calibration value.");
|
||||||
parser->calibrated = 0;
|
parser->calibrated = 0;
|
||||||
|
if (mode != DC_DIVEMODE_OC)
|
||||||
|
add_string(parser, "PPO2 source", "voted/averaged");
|
||||||
} else {
|
} else {
|
||||||
parser->calibrated = data[86];
|
parser->calibrated = data[base - 1];
|
||||||
|
if (mode != DC_DIVEMODE_OC)
|
||||||
|
add_string(parser, "PPO2 source", "cells");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache the data for later use.
|
// Cache the data for later use.
|
||||||
@ -348,6 +617,14 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
|||||||
parser->helium[i] = helium[i];
|
parser->helium[i] = helium[i];
|
||||||
}
|
}
|
||||||
parser->mode = mode;
|
parser->mode = mode;
|
||||||
|
add_string_fmt(parser, "Serial", "%08x", parser->serial);
|
||||||
|
// bytes 1-31 are identical in all formats
|
||||||
|
add_string_fmt(parser, "FW Version", "%2x", data[19]);
|
||||||
|
add_deco_model(parser, data);
|
||||||
|
add_battery_type(parser, data);
|
||||||
|
add_string_fmt(parser, "Battery at end", "%.1f V", data[9] / 10.0);
|
||||||
|
add_battery_info(parser, "T1 battery", t1_battery);
|
||||||
|
add_battery_info(parser, "T2 battery", t2_battery);
|
||||||
parser->cached = 1;
|
parser->cached = 1;
|
||||||
|
|
||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
@ -374,18 +651,29 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ
|
|||||||
|
|
||||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||||
|
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||||
unsigned int density = 0;
|
unsigned int density = 0;
|
||||||
|
|
||||||
|
// the first 32 bytes of the footer and closing block 0 are identical
|
||||||
|
unsigned int block_start = parser->pnf ? parser->block_offset[LOG_RECORD_CLOSING_0] : footer;
|
||||||
if (value) {
|
if (value) {
|
||||||
|
unsigned int idx;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case DC_FIELD_DIVETIME:
|
case DC_FIELD_DIVETIME:
|
||||||
*((unsigned int *) value) = array_uint16_be (data + footer + 6) * 60;
|
// FIXME: this is wrong based on the documentation I received
|
||||||
|
// it should be a 3 byte value in offsets 6-8 that is dive length in seconds
|
||||||
|
*((unsigned int *) value) = array_uint16_be (data + block_start + 6) * 60;
|
||||||
break;
|
break;
|
||||||
case DC_FIELD_MAXDEPTH:
|
case DC_FIELD_MAXDEPTH:
|
||||||
if (units == IMPERIAL)
|
if (units == IMPERIAL)
|
||||||
*((double *) value) = array_uint16_be (data + footer + 4) * FEET;
|
*((double *) value) = array_uint16_be (data + block_start + 4) * FEET;
|
||||||
else
|
else
|
||||||
*((double *) value) = array_uint16_be (data + footer + 4);
|
*((double *) value) = array_uint16_be (data + block_start + 4);
|
||||||
|
// according to the documentation this should have been in tenth of a meter
|
||||||
|
// before, but the existing code for the Predator-like format didn't have
|
||||||
|
// that adjustment, so let's just do that for PNF (where we definitely need it).
|
||||||
|
if (parser->pnf)
|
||||||
|
*((double *)value) /= 10.0;
|
||||||
break;
|
break;
|
||||||
case DC_FIELD_GASMIX_COUNT:
|
case DC_FIELD_GASMIX_COUNT:
|
||||||
*((unsigned int *) value) = parser->ngasmixes;
|
*((unsigned int *) value) = parser->ngasmixes;
|
||||||
@ -396,7 +684,8 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ
|
|||||||
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
|
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
|
||||||
break;
|
break;
|
||||||
case DC_FIELD_SALINITY:
|
case DC_FIELD_SALINITY:
|
||||||
density = array_uint16_be (data + 83);
|
idx = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_3] + 3 : 83;
|
||||||
|
density = array_uint16_be (data + idx);
|
||||||
if (density == 1000)
|
if (density == 1000)
|
||||||
water->type = DC_WATER_FRESH;
|
water->type = DC_WATER_FRESH;
|
||||||
else
|
else
|
||||||
@ -404,11 +693,21 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ
|
|||||||
water->density = density;
|
water->density = density;
|
||||||
break;
|
break;
|
||||||
case DC_FIELD_ATMOSPHERIC:
|
case DC_FIELD_ATMOSPHERIC:
|
||||||
|
idx = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_1] + 16 : 47;
|
||||||
*((double *) value) = array_uint16_be (data + 47) / 1000.0;
|
*((double *) value) = array_uint16_be (data + 47) / 1000.0;
|
||||||
break;
|
break;
|
||||||
case DC_FIELD_DIVEMODE:
|
case DC_FIELD_DIVEMODE:
|
||||||
*((dc_divemode_t *) value) = parser->mode;
|
*((dc_divemode_t *) value) = parser->mode;
|
||||||
break;
|
break;
|
||||||
|
case DC_FIELD_STRING:
|
||||||
|
if (flags < MAXSTRINGS) {
|
||||||
|
dc_field_string_t *p = parser->strings + flags;
|
||||||
|
if (p->desc) {
|
||||||
|
*string = *p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DC_STATUS_UNSUPPORTED;
|
||||||
default:
|
default:
|
||||||
return DC_STATUS_UNSUPPORTED;
|
return DC_STATUS_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
@ -438,11 +737,27 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
|||||||
unsigned int o2_previous = 0, he_previous = 0;
|
unsigned int o2_previous = 0, he_previous = 0;
|
||||||
|
|
||||||
unsigned int time = 0;
|
unsigned int time = 0;
|
||||||
unsigned int offset = parser->headersize;
|
unsigned int pnf = parser->pnf;
|
||||||
unsigned int length = size - parser->footersize;
|
unsigned int offset = pnf ? 0 : parser->headersize;
|
||||||
|
unsigned int length = pnf ? size : size - parser->footersize;
|
||||||
|
unsigned int time_increment = 10;
|
||||||
|
|
||||||
|
// the time increment is now given in ms. not sure how we'll deal with that since all we do is full seconds
|
||||||
|
if (pnf && parser->logversion >= 9)
|
||||||
|
time_increment = array_uint16_be (data + parser->block_offset[LOG_RECORD_OPENING_5] + 23) / 1000;
|
||||||
|
|
||||||
while (offset < length) {
|
while (offset < length) {
|
||||||
dc_sample_value_t sample = {0};
|
dc_sample_value_t sample = {0};
|
||||||
|
|
||||||
|
// stop parsing if we see the end block
|
||||||
|
if (pnf && data[offset] == LOG_RECORD_FINAL && data[offset + 1] == 0xFD)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Ignore blocks that aren't dive samples
|
||||||
|
if (pnf && data[offset] != LOG_RECORD_DIVE_SAMPLE) {
|
||||||
|
offset += parser->samplesize;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Ignore empty samples.
|
// Ignore empty samples.
|
||||||
if (array_isequal (data + offset, parser->samplesize, 0x00)) {
|
if (array_isequal (data + offset, parser->samplesize, 0x00)) {
|
||||||
offset += parser->samplesize;
|
offset += parser->samplesize;
|
||||||
@ -450,12 +765,12 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Time (seconds).
|
// Time (seconds).
|
||||||
time += 10;
|
time += time_increment;
|
||||||
sample.time = time;
|
sample.time = time;
|
||||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||||
|
|
||||||
// Depth (1/10 m or ft).
|
// Depth (1/10 m or ft).
|
||||||
unsigned int depth = array_uint16_be (data + offset);
|
unsigned int depth = array_uint16_be (data + pnf + offset);
|
||||||
if (units == IMPERIAL)
|
if (units == IMPERIAL)
|
||||||
sample.depth = depth * FEET / 10.0;
|
sample.depth = depth * FEET / 10.0;
|
||||||
else
|
else
|
||||||
@ -463,7 +778,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
|||||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||||
|
|
||||||
// Temperature (°C or °F).
|
// Temperature (°C or °F).
|
||||||
int temperature = (signed char) data[offset + 13];
|
int temperature = (signed char) data[offset + pnf + 13];
|
||||||
if (temperature < 0) {
|
if (temperature < 0) {
|
||||||
// Fix negative temperatures.
|
// Fix negative temperatures.
|
||||||
temperature += 102;
|
temperature += 102;
|
||||||
@ -478,30 +793,31 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
|||||||
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
|
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
|
||||||
|
|
||||||
// Status flags.
|
// Status flags.
|
||||||
unsigned int status = data[offset + 11];
|
unsigned int status = data[offset + pnf + 11];
|
||||||
|
|
||||||
if ((status & OC) == 0) {
|
if ((status & OC) == 0) {
|
||||||
// PPO2
|
// PPO2
|
||||||
if ((status & PPO2_EXTERNAL) == 0) {
|
if ((status & PPO2_EXTERNAL) == 0) {
|
||||||
#ifdef SENSOR_AVERAGE
|
if (!parser->calibrated) {
|
||||||
sample.ppo2 = data[offset + 6] / 100.0;
|
sample.ppo2 = data[offset + pnf + 6] / 100.0;
|
||||||
if (callback) callback (DC_SAMPLE_PPO2, sample, userdata);
|
if (callback) callback (DC_SAMPLE_PPO2, sample, userdata);
|
||||||
#else
|
} else {
|
||||||
sample.ppo2 = data[offset + 12] * parser->calibration[0];
|
sample.ppo2 = data[offset + pnf + 12] * parser->calibration[0];
|
||||||
if (callback && (parser->calibrated & 0x01)) callback (DC_SAMPLE_PPO2, sample, userdata);
|
if (callback && (parser->calibrated & 0x01)) callback (DC_SAMPLE_PPO2, sample, userdata);
|
||||||
|
|
||||||
sample.ppo2 = data[offset + 14] * parser->calibration[1];
|
sample.ppo2 = data[offset + pnf + 14] * parser->calibration[1];
|
||||||
if (callback && (parser->calibrated & 0x02)) callback (DC_SAMPLE_PPO2, sample, userdata);
|
if (callback && (parser->calibrated & 0x02)) callback (DC_SAMPLE_PPO2, sample, userdata);
|
||||||
|
|
||||||
sample.ppo2 = data[offset + 15] * parser->calibration[2];
|
sample.ppo2 = data[offset + pnf + 15] * parser->calibration[2];
|
||||||
if (callback && (parser->calibrated & 0x04)) callback (DC_SAMPLE_PPO2, sample, userdata);
|
if (callback && (parser->calibrated & 0x04)) callback (DC_SAMPLE_PPO2, sample, userdata);
|
||||||
#endif
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setpoint
|
// Setpoint
|
||||||
if (parser->petrel) {
|
if (parser->petrel) {
|
||||||
sample.setpoint = data[offset + 18] / 100.0;
|
sample.setpoint = data[offset + pnf + 18] / 100.0;
|
||||||
} else {
|
} else {
|
||||||
|
// this will only ever be called for the actual Predator, so no adjustment needed for PNF
|
||||||
if (status & SETPOINT_HIGH) {
|
if (status & SETPOINT_HIGH) {
|
||||||
sample.setpoint = data[18] / 100.0;
|
sample.setpoint = data[18] / 100.0;
|
||||||
} else {
|
} else {
|
||||||
@ -513,13 +829,13 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
|||||||
|
|
||||||
// CNS
|
// CNS
|
||||||
if (parser->petrel) {
|
if (parser->petrel) {
|
||||||
sample.cns = data[offset + 22] / 100.0;
|
sample.cns = data[offset + pnf + 22] / 100.0;
|
||||||
if (callback) callback (DC_SAMPLE_CNS, sample, userdata);
|
if (callback) callback (DC_SAMPLE_CNS, sample, userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gaschange.
|
// Gaschange.
|
||||||
unsigned int o2 = data[offset + 7];
|
unsigned int o2 = data[offset + pnf + 7];
|
||||||
unsigned int he = data[offset + 8];
|
unsigned int he = data[offset + pnf + 8];
|
||||||
if (o2 != o2_previous || he != he_previous) {
|
if (o2 != o2_previous || he != he_previous) {
|
||||||
unsigned int idx = shearwater_predator_find_gasmix (parser, o2, he);
|
unsigned int idx = shearwater_predator_find_gasmix (parser, o2, he);
|
||||||
if (idx >= parser->ngasmixes) {
|
if (idx >= parser->ngasmixes) {
|
||||||
@ -534,7 +850,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Deco stop / NDL.
|
// Deco stop / NDL.
|
||||||
unsigned int decostop = array_uint16_be (data + offset + 2);
|
unsigned int decostop = array_uint16_be (data + offset + pnf + 2);
|
||||||
if (decostop) {
|
if (decostop) {
|
||||||
sample.deco.type = DC_DECO_DECOSTOP;
|
sample.deco.type = DC_DECO_DECOSTOP;
|
||||||
if (units == IMPERIAL)
|
if (units == IMPERIAL)
|
||||||
@ -545,7 +861,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
|||||||
sample.deco.type = DC_DECO_NDL;
|
sample.deco.type = DC_DECO_NDL;
|
||||||
sample.deco.depth = 0.0;
|
sample.deco.depth = 0.0;
|
||||||
}
|
}
|
||||||
sample.deco.time = data[offset + 9] * 60;
|
sample.deco.time = data[offset + pnf + 9] * 60;
|
||||||
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
|
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
|
||||||
|
|
||||||
// for logversion 7 and newer (introduced for Perdix AI)
|
// for logversion 7 and newer (introduced for Perdix AI)
|
||||||
@ -560,14 +876,14 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
|||||||
// For regular values, the top 4 bits contain the battery
|
// For regular values, the top 4 bits contain the battery
|
||||||
// level (0=normal, 1=critical, 2=warning), and the lower 12
|
// level (0=normal, 1=critical, 2=warning), and the lower 12
|
||||||
// bits the tank pressure in units of 2 psi.
|
// bits the tank pressure in units of 2 psi.
|
||||||
unsigned int pressure = array_uint16_be (data + offset + 27);
|
unsigned int pressure = array_uint16_be (data + offset + pnf + 27);
|
||||||
if (pressure < 0xFFF0) {
|
if (pressure < 0xFFF0) {
|
||||||
pressure &= 0x0FFF;
|
pressure &= 0x0FFF;
|
||||||
sample.pressure.tank = 0;
|
sample.pressure.tank = 0;
|
||||||
sample.pressure.value = pressure * 2 * PSI / BAR;
|
sample.pressure.value = pressure * 2 * PSI / BAR;
|
||||||
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
|
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
|
||||||
}
|
}
|
||||||
pressure = array_uint16_be (data + offset + 19);
|
pressure = array_uint16_be (data + offset + pnf + 19);
|
||||||
if (pressure < 0xFFF0) {
|
if (pressure < 0xFFF0) {
|
||||||
pressure &= 0x0FFF;
|
pressure &= 0x0FFF;
|
||||||
sample.pressure.tank = 1;
|
sample.pressure.tank = 1;
|
||||||
@ -582,14 +898,13 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
|||||||
// 0xFD Not available in current mode
|
// 0xFD Not available in current mode
|
||||||
// 0xFC Not available because of DECO
|
// 0xFC Not available because of DECO
|
||||||
// 0xFB Tank size or max pressure haven’t been set up
|
// 0xFB Tank size or max pressure haven’t been set up
|
||||||
if (data[offset + 21] < 0xF0) {
|
if (data[offset + pnf + 21] < 0xF0) {
|
||||||
sample.rbt = data[offset + 21];
|
sample.rbt = data[offset + pnf + 21];
|
||||||
if (callback) callback (DC_SAMPLE_RBT, sample, userdata);
|
if (callback) callback (DC_SAMPLE_RBT, sample, userdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += parser->samplesize;
|
offset += parser->samplesize;
|
||||||
}
|
}
|
||||||
|
|
||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ dc_status_t
|
|||||||
suunto_d9_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
|
suunto_d9_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
suunto_d9_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
|
suunto_d9_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model, unsigned int serial);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h> // memcmp
|
#include <string.h> // memcmp, strdup
|
||||||
|
#include <stdio.h> // snprintf
|
||||||
|
|
||||||
#include "suunto_d9.h"
|
#include "suunto_d9.h"
|
||||||
#include "context-private.h"
|
#include "context-private.h"
|
||||||
@ -72,6 +73,7 @@ typedef struct suunto_d9_parser_t suunto_d9_parser_t;
|
|||||||
struct suunto_d9_parser_t {
|
struct suunto_d9_parser_t {
|
||||||
dc_parser_t base;
|
dc_parser_t base;
|
||||||
unsigned int model;
|
unsigned int model;
|
||||||
|
unsigned int serial;
|
||||||
// Cached fields.
|
// Cached fields.
|
||||||
unsigned int cached;
|
unsigned int cached;
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
@ -233,7 +235,7 @@ suunto_d9_parser_cache (suunto_d9_parser_t *parser)
|
|||||||
}
|
}
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
suunto_d9_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
|
suunto_d9_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial)
|
||||||
{
|
{
|
||||||
suunto_d9_parser_t *parser = NULL;
|
suunto_d9_parser_t *parser = NULL;
|
||||||
|
|
||||||
@ -249,6 +251,7 @@ suunto_d9_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int
|
|||||||
|
|
||||||
// Set the default values.
|
// Set the default values.
|
||||||
parser->model = model;
|
parser->model = model;
|
||||||
|
parser->serial = serial;
|
||||||
parser->cached = 0;
|
parser->cached = 0;
|
||||||
parser->id = 0;
|
parser->id = 0;
|
||||||
parser->mode = AIR;
|
parser->mode = AIR;
|
||||||
@ -330,6 +333,7 @@ suunto_d9_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
|||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define BUFLEN 16
|
||||||
|
|
||||||
static dc_status_t
|
static dc_status_t
|
||||||
suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||||
@ -345,6 +349,9 @@ suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigne
|
|||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||||
|
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||||
|
|
||||||
|
char buf[BUFLEN];
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -392,6 +399,17 @@ suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigne
|
|||||||
return DC_STATUS_DATAFORMAT;
|
return DC_STATUS_DATAFORMAT;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case DC_FIELD_STRING:
|
||||||
|
switch (flags) {
|
||||||
|
case 0: /* serial */
|
||||||
|
string->desc = "Serial";
|
||||||
|
snprintf(buf, BUFLEN, "%08u", parser->serial);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return DC_STATUS_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
string->value = strdup(buf);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return DC_STATUS_UNSUPPORTED;
|
return DC_STATUS_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,8 +21,16 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
/* Wow. MSC is truly crap */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#define vsnprintf _vsnprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "suunto_eonsteel.h"
|
#include "suunto_eonsteel.h"
|
||||||
#include "context-private.h"
|
#include "context-private.h"
|
||||||
@ -71,6 +79,7 @@ struct type_desc {
|
|||||||
|
|
||||||
#define MAXTYPE 512
|
#define MAXTYPE 512
|
||||||
#define MAXGASES 16
|
#define MAXGASES 16
|
||||||
|
#define MAXSTRINGS 32
|
||||||
|
|
||||||
typedef struct suunto_eonsteel_parser_t {
|
typedef struct suunto_eonsteel_parser_t {
|
||||||
dc_parser_t base;
|
dc_parser_t base;
|
||||||
@ -89,7 +98,8 @@ typedef struct suunto_eonsteel_parser_t {
|
|||||||
double lowsetpoint;
|
double lowsetpoint;
|
||||||
double highsetpoint;
|
double highsetpoint;
|
||||||
double customsetpoint;
|
double customsetpoint;
|
||||||
dc_tankvolume_t tankinfo[MAXGASES];
|
dc_field_string_t strings[MAXSTRINGS];
|
||||||
|
dc_tankinfo_t tankinfo[MAXGASES];
|
||||||
double tanksize[MAXGASES];
|
double tanksize[MAXGASES];
|
||||||
double tankworkingpressure[MAXGASES];
|
double tankworkingpressure[MAXGASES];
|
||||||
} cache;
|
} cache;
|
||||||
@ -97,11 +107,6 @@ 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);
|
||||||
|
|
||||||
typedef struct eon_event_t {
|
|
||||||
const char *name;
|
|
||||||
parser_sample_event_t type;
|
|
||||||
} eon_event_t;
|
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
enum eon_sample type;
|
enum eon_sample type;
|
||||||
@ -167,16 +172,6 @@ static enum eon_sample lookup_descriptor_type(suunto_eonsteel_parser_t *eon, str
|
|||||||
return ES_none;
|
return ES_none;
|
||||||
}
|
}
|
||||||
|
|
||||||
static parser_sample_event_t lookup_event(const char *name, const eon_event_t events[], size_t n)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
|
||||||
if (!strcasecmp(name, events[i].name))
|
|
||||||
return events[i].type;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SAMPLE_EVENT_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *desc_type_name(enum eon_sample type)
|
static const char *desc_type_name(enum eon_sample type)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -681,26 +676,17 @@ static void sample_event_state_type(const struct type_desc *desc, struct sample_
|
|||||||
static void sample_event_state_value(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
static void sample_event_state_value(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
||||||
{
|
{
|
||||||
dc_sample_value_t sample = {0};
|
dc_sample_value_t sample = {0};
|
||||||
static const eon_event_t states[] = {
|
|
||||||
{"Wet Outside", SAMPLE_EVENT_NONE},
|
|
||||||
{"Below Wet Activation Depth", SAMPLE_EVENT_NONE},
|
|
||||||
{"Below Surface", SAMPLE_EVENT_NONE},
|
|
||||||
{"Dive Active", SAMPLE_EVENT_NONE},
|
|
||||||
{"Surface Calculation", SAMPLE_EVENT_NONE},
|
|
||||||
{"Tank pressure available", SAMPLE_EVENT_NONE},
|
|
||||||
{"Closed Circuit Mode", SAMPLE_EVENT_NONE},
|
|
||||||
};
|
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
name = info->state_type;
|
name = info->state_type;
|
||||||
if (!name)
|
if (!name)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sample.event.type = lookup_event(name, states, C_ARRAY_SIZE(states));
|
sample.event.type = SAMPLE_EVENT_STRING;
|
||||||
if (sample.event.type == SAMPLE_EVENT_NONE)
|
sample.event.name = name;
|
||||||
return;
|
|
||||||
|
|
||||||
sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END;
|
sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END;
|
||||||
|
sample.event.flags |= 1 << SAMPLE_FLAGS_SEVERITY_SHIFT;
|
||||||
|
|
||||||
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -712,25 +698,6 @@ static void sample_event_notify_type(const struct type_desc *desc, struct sample
|
|||||||
|
|
||||||
static void sample_event_notify_value(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
static void sample_event_notify_value(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
||||||
{
|
{
|
||||||
static const eon_event_t notifications[] = {
|
|
||||||
{"NoFly Time", SAMPLE_EVENT_NONE},
|
|
||||||
{"Depth", SAMPLE_EVENT_NONE},
|
|
||||||
{"Surface Time", SAMPLE_EVENT_NONE},
|
|
||||||
{"Tissue Level", SAMPLE_EVENT_TISSUELEVEL},
|
|
||||||
{"Deco", SAMPLE_EVENT_NONE},
|
|
||||||
{"Deco Window", SAMPLE_EVENT_NONE},
|
|
||||||
{"Safety Stop Ahead", SAMPLE_EVENT_NONE},
|
|
||||||
{"Safety Stop", SAMPLE_EVENT_SAFETYSTOP},
|
|
||||||
{"Safety Stop Broken", SAMPLE_EVENT_CEILING_SAFETYSTOP},
|
|
||||||
{"Deep Stop Ahead", SAMPLE_EVENT_NONE},
|
|
||||||
{"Deep Stop", SAMPLE_EVENT_DEEPSTOP},
|
|
||||||
{"Dive Time", SAMPLE_EVENT_DIVETIME},
|
|
||||||
{"Gas Available", SAMPLE_EVENT_NONE},
|
|
||||||
{"SetPoint Switch", SAMPLE_EVENT_NONE},
|
|
||||||
{"Diluent Hypoxia", SAMPLE_EVENT_NONE},
|
|
||||||
{"Air Time", SAMPLE_EVENT_NONE},
|
|
||||||
{"Tank Pressure", SAMPLE_EVENT_NONE},
|
|
||||||
};
|
|
||||||
dc_sample_value_t sample = {0};
|
dc_sample_value_t sample = {0};
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
@ -738,11 +705,11 @@ static void sample_event_notify_value(const struct type_desc *desc, struct sampl
|
|||||||
if (!name)
|
if (!name)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sample.event.type = lookup_event(name, notifications, C_ARRAY_SIZE(notifications));
|
sample.event.type = SAMPLE_EVENT_STRING;
|
||||||
if (sample.event.type == SAMPLE_EVENT_NONE)
|
sample.event.name = name;
|
||||||
return;
|
|
||||||
|
|
||||||
sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END;
|
sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END;
|
||||||
|
sample.event.flags |= 2 << SAMPLE_FLAGS_SEVERITY_SHIFT;
|
||||||
|
|
||||||
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -755,22 +722,6 @@ static void sample_event_warning_type(const struct type_desc *desc, struct sampl
|
|||||||
|
|
||||||
static void sample_event_warning_value(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
static void sample_event_warning_value(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
||||||
{
|
{
|
||||||
static const eon_event_t warnings[] = {
|
|
||||||
{"ICD Penalty", SAMPLE_EVENT_NONE},
|
|
||||||
{"Deep Stop Penalty", SAMPLE_EVENT_VIOLATION},
|
|
||||||
{"Mandatory Safety Stop", SAMPLE_EVENT_SAFETYSTOP_MANDATORY},
|
|
||||||
{"OTU250", SAMPLE_EVENT_NONE},
|
|
||||||
{"OTU300", SAMPLE_EVENT_NONE},
|
|
||||||
{"CNS80%", SAMPLE_EVENT_NONE},
|
|
||||||
{"CNS100%", SAMPLE_EVENT_NONE},
|
|
||||||
{"Max.Depth", SAMPLE_EVENT_MAXDEPTH},
|
|
||||||
{"Air Time", SAMPLE_EVENT_AIRTIME},
|
|
||||||
{"Tank Pressure", SAMPLE_EVENT_NONE},
|
|
||||||
{"Safety Stop Broken", SAMPLE_EVENT_CEILING_SAFETYSTOP},
|
|
||||||
{"Deep Stop Broken", SAMPLE_EVENT_CEILING_SAFETYSTOP},
|
|
||||||
{"Ceiling Broken", SAMPLE_EVENT_CEILING},
|
|
||||||
{"PO2 High", SAMPLE_EVENT_PO2},
|
|
||||||
};
|
|
||||||
dc_sample_value_t sample = {0};
|
dc_sample_value_t sample = {0};
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
@ -778,11 +729,11 @@ static void sample_event_warning_value(const struct type_desc *desc, struct samp
|
|||||||
if (!name)
|
if (!name)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sample.event.type = lookup_event(name, warnings, C_ARRAY_SIZE(warnings));
|
sample.event.type = SAMPLE_EVENT_STRING;
|
||||||
if (sample.event.type == SAMPLE_EVENT_NONE)
|
sample.event.name = name;
|
||||||
return;
|
|
||||||
|
|
||||||
sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END;
|
sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END;
|
||||||
|
sample.event.flags |= 3 << SAMPLE_FLAGS_SEVERITY_SHIFT;
|
||||||
|
|
||||||
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -795,27 +746,18 @@ static void sample_event_alarm_type(const struct type_desc *desc, struct sample_
|
|||||||
|
|
||||||
static void sample_event_alarm_value(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
static void sample_event_alarm_value(const struct type_desc *desc, struct sample_data *info, unsigned char value)
|
||||||
{
|
{
|
||||||
static const eon_event_t alarms[] = {
|
|
||||||
{"Mandatory Safety Stop Broken", SAMPLE_EVENT_CEILING_SAFETYSTOP},
|
|
||||||
{"Ascent Speed", SAMPLE_EVENT_ASCENT},
|
|
||||||
{"Diluent Hyperoxia", SAMPLE_EVENT_NONE},
|
|
||||||
{"Violated Deep Stop", SAMPLE_EVENT_VIOLATION},
|
|
||||||
{"Ceiling Broken", SAMPLE_EVENT_CEILING},
|
|
||||||
{"PO2 High", SAMPLE_EVENT_PO2},
|
|
||||||
{"PO2 Low", SAMPLE_EVENT_PO2},
|
|
||||||
};
|
|
||||||
dc_sample_value_t sample = {0};
|
|
||||||
const char *name;
|
const char *name;
|
||||||
|
dc_sample_value_t sample = {0};
|
||||||
|
|
||||||
name = info->alarm_type;
|
name = info->alarm_type;
|
||||||
if (!name)
|
if (!name)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sample.event.type = lookup_event(name, alarms, C_ARRAY_SIZE(alarms));
|
sample.event.type = SAMPLE_EVENT_STRING;
|
||||||
if (sample.event.type == SAMPLE_EVENT_NONE)
|
sample.event.name = name;
|
||||||
return;
|
|
||||||
|
|
||||||
sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END;
|
sample.event.flags = value ? SAMPLE_FLAGS_BEGIN : SAMPLE_FLAGS_END;
|
||||||
|
sample.event.flags |= 4 << SAMPLE_FLAGS_SEVERITY_SHIFT;
|
||||||
|
|
||||||
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
if (info->callback) info->callback(DC_SAMPLE_EVENT, sample, info->userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1026,6 +968,19 @@ suunto_eonsteel_parser_samples_foreach(dc_parser_t *abstract, dc_sample_callback
|
|||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static dc_status_t get_string_field(suunto_eonsteel_parser_t *eon, unsigned idx, dc_field_string_t *value)
|
||||||
|
{
|
||||||
|
if (idx < MAXSTRINGS) {
|
||||||
|
dc_field_string_t *res = eon->cache.strings+idx;
|
||||||
|
if (res->desc && res->value) {
|
||||||
|
*value = *res;
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return DC_STATUS_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
// Ugly define thing makes the code much easier to read
|
// Ugly define thing makes the code much easier to read
|
||||||
// I'd love to use __typeof__, but that's a gcc'ism
|
// I'd love to use __typeof__, but that's a gcc'ism
|
||||||
#define field_value(p, set) \
|
#define field_value(p, set) \
|
||||||
@ -1094,11 +1049,13 @@ suunto_eonsteel_parser_get_field(dc_parser_t *parser, dc_field_type_t type, unsi
|
|||||||
* We need to have workpressure and a valid tank. In that case,
|
* We need to have workpressure and a valid tank. In that case,
|
||||||
* a fractional tank size implies imperial.
|
* a fractional tank size implies imperial.
|
||||||
*/
|
*/
|
||||||
if (tank->workpressure && (tank->type == DC_TANKVOLUME_METRIC)) {
|
if (tank->workpressure && (tank->type & DC_TANKINFO_METRIC)) {
|
||||||
if (fabs(tank->volume - rint(tank->volume)) > 0.001)
|
if (fabs(tank->volume - rint(tank->volume)) > 0.001)
|
||||||
tank->type = DC_TANKVOLUME_IMPERIAL;
|
tank->type += DC_TANKINFO_IMPERIAL - DC_TANKINFO_METRIC;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case DC_FIELD_STRING:
|
||||||
|
return get_string_field(eon, flags, (dc_field_string_t *)value);
|
||||||
default:
|
default:
|
||||||
return DC_STATUS_UNSUPPORTED;
|
return DC_STATUS_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
@ -1149,7 +1106,7 @@ static void set_depth_field(suunto_eonsteel_parser_t *eon, unsigned short d)
|
|||||||
// "enum:0=Off,1=Primary,2=?,3=Diluent"
|
// "enum:0=Off,1=Primary,2=?,3=Diluent"
|
||||||
// "enum:0=Off,1=Primary,3=Diluent,4=Oxygen"
|
// "enum:0=Off,1=Primary,3=Diluent,4=Oxygen"
|
||||||
//
|
//
|
||||||
// We turn that into the DC_TANKVOLUME data here, but
|
// We turn that into the DC_TANKINFO data here, but
|
||||||
// initially consider all non-off tanks to me METRIC.
|
// initially consider all non-off tanks to me METRIC.
|
||||||
//
|
//
|
||||||
// We may later turn the METRIC tank size into IMPERIAL if we
|
// We may later turn the METRIC tank size into IMPERIAL if we
|
||||||
@ -1157,7 +1114,7 @@ static void set_depth_field(suunto_eonsteel_parser_t *eon, unsigned short d)
|
|||||||
static int add_gas_type(suunto_eonsteel_parser_t *eon, const struct type_desc *desc, unsigned char type)
|
static int add_gas_type(suunto_eonsteel_parser_t *eon, const struct type_desc *desc, unsigned char type)
|
||||||
{
|
{
|
||||||
int idx = eon->cache.ngases;
|
int idx = eon->cache.ngases;
|
||||||
dc_tankvolume_t tankinfo = DC_TANKVOLUME_METRIC;
|
dc_tankinfo_t tankinfo = DC_TANKINFO_METRIC;
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
if (idx >= MAXGASES)
|
if (idx >= MAXGASES)
|
||||||
@ -1168,9 +1125,9 @@ static int add_gas_type(suunto_eonsteel_parser_t *eon, const struct type_desc *d
|
|||||||
if (!name)
|
if (!name)
|
||||||
DEBUG(eon->base.context, "Unable to look up gas type %u in %s", type, desc->format);
|
DEBUG(eon->base.context, "Unable to look up gas type %u in %s", type, desc->format);
|
||||||
else if (!strcasecmp(name, "Diluent"))
|
else if (!strcasecmp(name, "Diluent"))
|
||||||
;
|
tankinfo |= DC_TANKINFO_CC_DILUENT;
|
||||||
else if (!strcasecmp(name, "Oxygen"))
|
else if (!strcasecmp(name, "Oxygen"))
|
||||||
;
|
tankinfo |= DC_TANKINFO_CC_O2;
|
||||||
else if (!strcasecmp(name, "None"))
|
else if (!strcasecmp(name, "None"))
|
||||||
tankinfo = DC_TANKVOLUME_NONE;
|
tankinfo = DC_TANKVOLUME_NONE;
|
||||||
else if (strcasecmp(name, "Primary"))
|
else if (strcasecmp(name, "Primary"))
|
||||||
@ -1223,6 +1180,42 @@ static int add_gas_workpressure(suunto_eonsteel_parser_t *eon, float wp)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int add_string(suunto_eonsteel_parser_t *eon, const char *desc, const char *value)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
eon->cache.initialized |= 1 << DC_FIELD_STRING;
|
||||||
|
for (i = 0; i < MAXSTRINGS; i++) {
|
||||||
|
dc_field_string_t *str = eon->cache.strings+i;
|
||||||
|
if (str->desc)
|
||||||
|
continue;
|
||||||
|
str->desc = desc;
|
||||||
|
str->value = strdup(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_string_fmt(suunto_eonsteel_parser_t *eon, const char *desc, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We ignore the return value from vsnprintf, and we
|
||||||
|
* always NUL-terminate the destination buffer ourselves.
|
||||||
|
*
|
||||||
|
* That way we don't have to worry about random bad legacy
|
||||||
|
* implementations.
|
||||||
|
*/
|
||||||
|
va_start(ap, fmt);
|
||||||
|
buffer[sizeof(buffer)-1] = 0;
|
||||||
|
(void) vsnprintf(buffer, sizeof(buffer)-1, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return add_string(eon, desc, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
static float get_le32_float(const unsigned char *src)
|
static float get_le32_float(const unsigned char *src)
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
@ -1246,7 +1239,16 @@ static int traverse_device_fields(suunto_eonsteel_parser_t *eon, const struct ty
|
|||||||
const unsigned char *data, int len)
|
const unsigned char *data, int len)
|
||||||
{
|
{
|
||||||
const char *name = desc->desc + strlen("sml.DeviceLog.Device.");
|
const char *name = desc->desc + strlen("sml.DeviceLog.Device.");
|
||||||
|
if (!strcmp(name, "SerialNumber"))
|
||||||
|
return add_string(eon, "Serial", data);
|
||||||
|
if (!strcmp(name, "Info.HW"))
|
||||||
|
return add_string(eon, "HW Version", data);
|
||||||
|
if (!strcmp(name, "Info.SW"))
|
||||||
|
return add_string(eon, "FW Version", data);
|
||||||
|
if (!strcmp(name, "Info.BatteryAtStart"))
|
||||||
|
return add_string(eon, "Battery at start", data);
|
||||||
|
if (!strcmp(name, "Info.BatteryAtEnd"))
|
||||||
|
return add_string(eon, "Battery at end", data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1277,12 +1279,30 @@ static int traverse_gas_fields(suunto_eonsteel_parser_t *eon, const struct type_
|
|||||||
if (!strcmp(name, ".Gas.Helium"))
|
if (!strcmp(name, ".Gas.Helium"))
|
||||||
return add_gas_he(eon, data[0]);
|
return add_gas_he(eon, data[0]);
|
||||||
|
|
||||||
|
if (!strcmp(name, ".Gas.TransmitterID"))
|
||||||
|
return add_string(eon, "Transmitter ID", data);
|
||||||
|
|
||||||
if (!strcmp(name, ".Gas.TankSize"))
|
if (!strcmp(name, ".Gas.TankSize"))
|
||||||
return add_gas_size(eon, get_le32_float(data));
|
return add_gas_size(eon, get_le32_float(data));
|
||||||
|
|
||||||
if (!strcmp(name, ".Gas.TankFillPressure"))
|
if (!strcmp(name, ".Gas.TankFillPressure"))
|
||||||
return add_gas_workpressure(eon, get_le32_float(data));
|
return add_gas_workpressure(eon, get_le32_float(data));
|
||||||
|
|
||||||
|
// There is a bug with older transmitters, where the transmitter
|
||||||
|
// battery charge returns zero. Rather than returning that bogus
|
||||||
|
// data, just don't return any battery charge information at all.
|
||||||
|
//
|
||||||
|
// Make sure to add all non-battery-charge field checks above this
|
||||||
|
// test, so that it doesn't trigger for anything else.
|
||||||
|
if (!data[0])
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!strcmp(name, ".Gas.TransmitterStartBatteryCharge"))
|
||||||
|
return add_string_fmt(eon, "Transmitter Battery at start", "%d %%", data[0]);
|
||||||
|
|
||||||
|
if (!strcmp(name, ".Gas.TransmitterEndBatteryCharge"))
|
||||||
|
return add_string_fmt(eon, "Transmitter Battery at end", "%d %%", data[0]);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1339,12 +1359,22 @@ static int traverse_diving_fields(suunto_eonsteel_parser_t *eon, const struct ty
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp(name, "Algorithm"))
|
||||||
|
return add_string(eon, "Deco algorithm", data);
|
||||||
|
|
||||||
if (!strcmp(name, "DiveMode")) {
|
if (!strcmp(name, "DiveMode")) {
|
||||||
if (!strncmp((const char *)data, "CCR", 3)) {
|
if (!strncmp((const char *)data, "CCR", 3)) {
|
||||||
eon->cache.divemode = DC_DIVEMODE_CCR;
|
eon->cache.divemode = DC_DIVEMODE_CCR;
|
||||||
eon->cache.initialized |= 1 << DC_FIELD_DIVEMODE;
|
eon->cache.initialized |= 1 << DC_FIELD_DIVEMODE;
|
||||||
}
|
}
|
||||||
return 0;
|
return add_string(eon, "Dive Mode", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Signed byte of conservatism (-2 .. +2) */
|
||||||
|
if (!strcmp(name, "Conservatism")) {
|
||||||
|
int val = *(signed char *)data;
|
||||||
|
|
||||||
|
return add_string_fmt(eon, "Personal Adjustment", "P%d", val);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(name, "LowSetPoint")) {
|
if (!strcmp(name, "LowSetPoint")) {
|
||||||
@ -1359,6 +1389,18 @@ static int traverse_diving_fields(suunto_eonsteel_parser_t *eon, const struct ty
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Time recoded in seconds.
|
||||||
|
// Let's just agree to ignore seconds
|
||||||
|
if (!strcmp(name, "DesaturationTime")) {
|
||||||
|
unsigned int time = array_uint32_le(data) / 60;
|
||||||
|
return add_string_fmt(eon, "Desaturation Time", "%d:%02d", time / 60, time % 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(name, "SurfaceTime")) {
|
||||||
|
unsigned int time = array_uint32_le(data) / 60;
|
||||||
|
return add_string_fmt(eon, "Surface Time", "%d:%02d", time / 60, time % 60);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1385,6 +1427,8 @@ static int traverse_header_fields(suunto_eonsteel_parser_t *eon, const struct ty
|
|||||||
eon->cache.maxdepth = d;
|
eon->cache.maxdepth = d;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (!strcmp(name, "DateTime"))
|
||||||
|
return add_string(eon, "Dive ID", data);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1428,6 +1472,8 @@ static int traverse_sample_fields(suunto_eonsteel_parser_t *eon, const struct ty
|
|||||||
set_depth_field(eon, array_uint16_le(data));
|
set_depth_field(eon, array_uint16_le(data));
|
||||||
data += 2;
|
data += 2;
|
||||||
continue;
|
continue;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
109
src/usb_storage.c
Normal file
109
src/usb_storage.c
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Dummy "stream" operations for USB storage
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Linus Torvalds
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "common-private.h"
|
||||||
|
#include "context-private.h"
|
||||||
|
#include "iostream-private.h"
|
||||||
|
#include "iterator-private.h"
|
||||||
|
#include "descriptor-private.h"
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
// Fake "device" that just contains the directory name that
|
||||||
|
// you can read out of the iostream. All the actual IO is
|
||||||
|
// up to you.
|
||||||
|
typedef struct dc_usbstorage_t {
|
||||||
|
dc_iostream_t base;
|
||||||
|
char pathname[PATH_MAX];
|
||||||
|
} dc_usbstorage_t;
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
dc_usb_storage_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual);
|
||||||
|
|
||||||
|
static const dc_iostream_vtable_t dc_usbstorage_vtable = {
|
||||||
|
sizeof(dc_usbstorage_t),
|
||||||
|
NULL, /* set_timeout */
|
||||||
|
NULL, /* set_latency */
|
||||||
|
NULL, /* set_break */
|
||||||
|
NULL, /* set_dtr */
|
||||||
|
NULL, /* set_rts */
|
||||||
|
NULL, /* get_lines */
|
||||||
|
NULL, /* get_available */
|
||||||
|
NULL, /* configure */
|
||||||
|
dc_usb_storage_read, /* read */
|
||||||
|
NULL, /* write */
|
||||||
|
NULL, /* flush */
|
||||||
|
NULL, /* purge */
|
||||||
|
NULL, /* sleep */
|
||||||
|
NULL, /* close */
|
||||||
|
};
|
||||||
|
|
||||||
|
dc_status_t
|
||||||
|
dc_usb_storage_open (dc_iostream_t **out, dc_context_t *context, const char *name)
|
||||||
|
{
|
||||||
|
dc_usbstorage_t *device = NULL;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (out == NULL || name == NULL)
|
||||||
|
return DC_STATUS_INVALIDARGS;
|
||||||
|
|
||||||
|
INFO (context, "Open: name=%s", name);
|
||||||
|
if (stat(name, &st) < 0 || !S_ISDIR(st.st_mode))
|
||||||
|
return DC_STATUS_NODEVICE;
|
||||||
|
|
||||||
|
// Allocate memory.
|
||||||
|
device = (dc_usbstorage_t *) dc_iostream_allocate (context, &dc_usbstorage_vtable, DC_TRANSPORT_USBSTORAGE);
|
||||||
|
if (device == NULL) {
|
||||||
|
SYSERROR (context, ENOMEM);
|
||||||
|
return DC_STATUS_NOMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(device->pathname, name, PATH_MAX);
|
||||||
|
device->pathname[PATH_MAX-1] = 0;
|
||||||
|
|
||||||
|
*out = (dc_iostream_t *) device;
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
dc_usb_storage_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual)
|
||||||
|
{
|
||||||
|
dc_usbstorage_t *device = (dc_usbstorage_t *) iostream;
|
||||||
|
size_t len = strlen(device->pathname);
|
||||||
|
|
||||||
|
if (size <= len)
|
||||||
|
return DC_STATUS_IO;
|
||||||
|
memcpy(data, device->pathname, len+1);
|
||||||
|
if (actual)
|
||||||
|
*actual = len;
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user