Compare commits

...

13 Commits

Author SHA1 Message Date
Dirk Hohndel
b5f53eeb1f Merge git://github.com/libdivecomputer/libdivecomputer into NG-test 2018-04-19 07:59:50 -07:00
Linus Torvalds
5a0f603770 Shearwater: add extended information parsing
This adds the string field interface to the Shearwater family of dive
computers.

That includes proper serial number formatting, but it also has a lot of
new fields for battery information (both the dive computer itself and
the transmitter) but also deco model information.

Much of the deco model cases come from Anton Lundin in the original
subsurface branch, and Dirk Hohndel added the battery type and serial
number and firmware version data.  And I ended up massaging it even in
that original branch, so it blamed me for all these lines even back
there.

The sign-offs from Dirk and Anton are from the original commits.

Signed-off-by: Anton Lundin <glance@acc.umu.se>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-17 12:28:33 -07:00
Linus Torvalds
6e71b174e0 Suunto EON Steel/Core: add extended information parsing
This adds the string field interface to the Suunto EON Steel and EON
Core.

This is actually a big deal, because it gets rid of all the ad-hoc
string parsing, and actually just uses the strings that the EON Steel
events and warnings natively use.

It also reports the severity of the notification/warning/alarm, so that
Subsurface can then use the proper icon.  An event isn't just an event,
there's a big difference between a warning and just a notification.

It also fills in the tank information data for closed-circuit cylinder
use.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-17 12:28:33 -07:00
Dirk Hohndel
b0d72cd74c Suunto D9 family: add extended information parsing
This adds the string field interface to the Suunto D9 family.

It's really just the proper serial number handling.  From Dirk's
original commit:

 "We have the correct firmware in the devinfo, but that's the firmware
  the dive computer is on NOW, not necessarily the firmware it was using
  when recording the dive"

so thus just serial number.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-17 12:28:33 -07:00
Dirk Hohndel
72c99a6dcb Oceanic Atom2: add extended string information parsing
This adds the string field interface to the Oceanic Atom2 family,
including the proper serial number handling.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-17 12:28:33 -07:00
Dirk Hohndel
9a38a1ac6d Heinrichs Weikamp OSTC: add extended information parsing
This adds the string field interface to the HW OSTC family, including
the proper serial number handling.

The deco model information was done by Anton Lundin in the original
subsurface branch, and the salinity, serial number, battery voltage and
desat information was added by Dirk Hohndel.  Jan Mulder added the
battery percentage.

[ The sign-offs have been taken from the original commits in that old
  subsurface branch, and I'm marking Dirk as the main author because on
  the whole most of the lines come from him  - Linus ]

Signed-off-by: Anton Lundin <glance@acc.umu.se>
Signed-off-by: Jan Mulder <jlmulder@xs4all.nl>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-17 12:28:33 -07:00
Linus Torvalds
a6637442b7 Make dc_parser_new() pass in the serial number to +dc_parser_new_internal
The libdivecomputer serial number handling is very very messy.

There are multiple issues that make it messy:

 - it's not actually figured out at parse-time, it's figured out at
   download time and passed up through the DC_EVENT_DEVINFO as part of
   the devinfo structure.

 - it's passed around as an "unsigned in" in the devinfo structure,
   which is entirely useless to anybody but libdivecomputer, since a
   serial number isn't actually a number, but a string, and the format
   of the string depends on the dive computer.

 - it is *not* passed to the parser, so the parser can't do a better job
   at it later.

But it turns out that the sane "create new parser" helper function does
actually get it, as part of the "devinfo" that is passed to it.  So as
long as you use that sane interface, we can now pass it in to the actual
parser creation, and then the dive computer parsers that want to do a
reasonable job of actually generating a real serial number string can
now save it off and do so.

This just adds the infrastructure to make this possible.  I'll do the
dive computers one by one.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-17 12:28:33 -07:00
Linus Torvalds
0ae143e27f Shearwater Petrel: make the hardware ID decoding a bit easier to read
Dirk seems to have some documentation about the different ID's, plus it
just makes sense to order the switch statement by number.

This is partly based on Dirk's original commit to do the different model
numbers, with various changes over time due to merge conflict
resolution.  Dirk's sign-off comes from Dirks commit in the original
subsurface branch.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-17 12:28:33 -07:00
Dirk Hohndel
2d3b25cf8e Atomics Cobalt: use the new DC string fields
Not a lot of fields, but give the serial number in the proper format,
and other version information (Software version and bootloader version).
And the Nofly time that the dive computer reports.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-17 12:28:33 -07:00
Linus Torvalds
bd1999134f Add subsurface-specific cylinder descriptor extension
This extends the libdivecomputer notion of "dc_tankvolume_t" to not just
have the tank volume type (imperial or metric), but be a "dc_tankinfo_t"
that shows other information about the cylinder.

The imperial-vs-metric data remains the same two values:

 1 - metric
 2 - imperial

but instead of being an enumeration of volume types, it is extended to a
bitmap of tank information, and the other bits currently are

 4 - CC diluent cylinder
 8 - CC O2 cylinder

with possible future extensions (bailout gas, perhaps).

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-17 12:28:33 -07:00
Linus Torvalds
927d272e0c Add subsurface-specific DC field extension: descriptor/value strings
The default libdivecomputer fields are good for structured data that has
a well-defined format, like the cylinder information, or the temperature
data.

But it is entirely useless for miscellaneous divecomputer-specific
information, where there is no standard way of representing the data
across different kinds of dive computers.

Examples of this include simple things like deco calculation algorithm
(what kind of Buehlmann, gradient factor information or is it some
vendor-specific mode?) and even something as trivial as a serial number.

No, serial numbers aren't numbers. They are strings. Really.

But this also includes much more complex data that is really specific to
a particular dive computer or family: what the battery status is for the
dive computer or the wireless transmitters it is connected to (sometimes
it's a voltage, sometimes it's a percentage, sometimes it's just "good"
or "marginal").

It also includes random incidental information like firmware version
numbers (again, these are strings, not numbers, despite the name) or
dive mode and personal adjustment information.

So allow the dive computer to just give "extra information" in the form
of an array of { key, value } string pairs.  For my Perdix AI the
information could be

  { "Serial", "370d1f24" }
  { "FW Version", "44" }
  { "Deco model", "GF 40/85" }
  { "Battery type", "3.6V Saft" }
  { "Battery at end", "3.4 V" }

and for my EON Steel with three wireless transmitters connected it can
look like this:

  { "Serial", "1742104730" }
  { "FW Version", "1.6.5" }
  { "HW Version", "70.3.0" }
  { "Battery at start", "Charge: 83%, Voltage: 4.012V" }
  { "Deco algorithm", "Suunto Fused RGBM" }
  { "Personal Adjustment", "P-2" }
  { "Battery at end", "Charge: 79%, Voltage: 3.977V" }
  { "Dive Mode", "Trimix" }
  { "Desaturation Time", "7:53" }
  { "Transmitter ID", "1519107801" }
  { "Transmitter Battery at start", "87 %" }
  { "Transmitter Battery at end", "87 %" }
  { "Transmitter ID", "1550110028" }
  { "Transmitter Battery at start", "100 %" }
  { "Transmitter Battery at end", "100 %" }
  { "Transmitter ID", "1719102387" }
  { "Transmitter Battery at start", "100 %" }
  { "Transmitter Battery at end", "100 %" }

so this data is inherently unstructured and dependent on the dive
computer, but quite relevant to the diver.  Subsurface shows this in the
"Extra Info" panel for each dive computer.

Also teach the example output-xml code about the new string field
extension.  That example output-xml code was written by Anton Lundin in
the old Subsurface branch, and signed-off-by Dirk.  The sign-offs here
are taken from that original work.

Signed-off-by: Anton Lundin <glance@acc.umu.se>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-17 12:28:33 -07:00
Linus Torvalds
820b3c0ff5 Add subsurface-specific event extension: event strings and severity
We _really_ find the standard libdivecomputer event enumeration much too
inflexible and not giving us enough useful information.  This is
particularly noticeable with the Suunto EON Steel/Core, where there are
no fixed event enumerations, but instead the dive computer literally
gives you event strings.

Do the same thing in the libdivecomputer interface: allow an event of
type SAMPLE_EVENT_STRING which instead of the useless "value" gives an
actual string describing the event.

Also, extend the "flags" field to have not just a NONE/BEGIN/END marker,
but a severity level. The severity level is 3 bits, so 0-7, with the meaning being

 0 - 'no severity info'
 1 - state change (so 'surface' event or similar - don't even show it by default)
 2 - notification (informational, eg "safety stop", "tank change")
 3 - warning ("ascent speed")
 4 - alarm (some actual dive violation).
 5-7: future expansion?

Think of 0 as "legacy - missing information", 1 as "internal DC thing",
and 2-4 as (green-yellow-red).

This makes it possible for the dive computer back-end to give the user
actual useful information for events.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-17 12:28:33 -07:00
Linus Torvalds
ee802410e3 Add subsurface-specific build setup
This changes the dc_version_suffix to show that this is the subsurface
'next generation' branch, and does minor tweaks to the build system: we
add the 'build' subdirectory to the .gitignore branch because that's
where we typically do our builds, and we tweak the default compiler
warning flags to not be as annoying.

No real semantic changes.

This is partially based off patches in the original Subsurface-branch by
Dirk Hohndel (configure.ac) and Jan Mulder (gitignore).  The sign-offs
for those come from those patches:

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Jan Mulder <jlmulder@xs4all.nl>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2018-04-17 12:28:33 -07:00
18 changed files with 627 additions and 142 deletions

2
.gitignore vendored
View File

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

View File

@ -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.

View File

@ -400,6 +400,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);

View File

@ -62,9 +62,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 +96,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 +165,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 +201,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 +226,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;

View File

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

View File

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

View File

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

View File

@ -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) {
@ -550,6 +593,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;
} }

View File

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

View File

@ -20,6 +20,8 @@
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libdivecomputer/units.h> #include <libdivecomputer/units.h>
@ -102,6 +104,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;
@ -131,7 +134,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;
@ -176,6 +179,7 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
parser->headersize = 5 * PAGESIZE; parser->headersize = 5 * PAGESIZE;
} }
parser->serial = serial;
parser->cached = 0; parser->cached = 0;
parser->header = 0; parser->header = 0;
parser->footer = 0; parser->footer = 0;
@ -366,6 +370,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)
@ -502,6 +507,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) {
@ -557,6 +565,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;
} }

View File

@ -64,7 +64,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 +87,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);
@ -120,7 +120,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:
@ -133,11 +133,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:
@ -150,10 +150,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);
@ -183,7 +183,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);
} }
@ -191,7 +193,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);
} }

View File

@ -212,25 +212,37 @@ 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;
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.

View File

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

View File

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

View File

@ -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>
@ -47,6 +54,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
@ -68,7 +76,11 @@ 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;
/* 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 +124,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 +152,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 +177,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);
} }
@ -219,6 +234,146 @@ 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)
{
switch (data[67]) {
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[68]);
break;
case 2:
add_string_fmt(parser, "Deco model", "VPM-B/GFS +%u %u%%", data[68], data[85]);
break;
default:
add_string_fmt(parser, "Deco model", "Unknown model %d", data[67]);
}
}
static void
add_battery_type(shearwater_predator_parser_t *parser, const unsigned char *data)
{
if (parser->logversion < 7)
return;
switch (data[120]) {
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[120]);
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)
@ -243,6 +398,9 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
unsigned int logversion = 6; unsigned int logversion = 6;
if (data[127] > 6) if (data[127] > 6)
logversion = data[127]; logversion = data[127];
INFO(abstract->context, "Shearwater log version %u\n", logversion);
memset(parser->strings, 0, sizeof(parser->strings));
// 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 || array_uint16_be (data + size - footersize) == 0xFFFD) {
@ -262,6 +420,9 @@ 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;
// Transmitter battery levels
unsigned int t1_battery = 0, t2_battery = 0;
unsigned int offset = headersize; unsigned int offset = headersize;
unsigned int length = size - footersize; unsigned int length = size - footersize;
while (offset < length) { while (offset < length) {
@ -304,6 +465,13 @@ 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);
t2_battery |= battery_state(data + offset + 19);
}
offset += parser->samplesize; offset += parser->samplesize;
} }
@ -348,6 +516,13 @@ 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);
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,6 +549,7 @@ 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;
if (value) { if (value) {
@ -409,6 +585,15 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ
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;
} }
@ -440,6 +625,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
unsigned int time = 0; unsigned int time = 0;
unsigned int offset = parser->headersize; unsigned int offset = parser->headersize;
unsigned int length = size - parser->footersize; unsigned int length = size - parser->footersize;
while (offset < length) { while (offset < length) {
dc_sample_value_t sample = {0}; dc_sample_value_t sample = {0};
@ -590,6 +776,5 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
offset += parser->samplesize; offset += parser->samplesize;
} }
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }

View File

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

View File

@ -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 mode; unsigned int mode;
@ -231,7 +233,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;
@ -247,6 +249,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->mode = AIR; parser->mode = AIR;
parser->ngasmixes = 0; parser->ngasmixes = 0;
@ -326,6 +329,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)
@ -341,6 +345,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) {
@ -388,6 +395,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;
} }

View File

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