Merge branch 'master' of https://github.com/libdivecomputer/libdivecomputer into Subsurface-DS9
Merge upstream updates from Jef Driesen: - Deepblu Cosmiq+ support has been merged upstream - Oceans S1 support has been merged upstream - Various new models supported: Cressi Donatello, Scubapro G2 TEK, new Excursion v6+ firmware. - misc core changes, most notably supporting a new annoying specialized binary format for "decomode", because Jef still can't deal with strings. - lots of small details * https://github.com/libdivecomputer/libdivecomputer: (58 commits) Keep open-circuit and diluent gas mixes separately Parse some extra gas mix information Limit the index to the fixed gas mixes Handle dives without a valid gas mix more explicit Ignore all gas mixes for freedives Always include all gas mixes defined in the header Add support for the new Excursion v6+ firmware Add support for the HP CCR tank pressure Use the correct field for the setpoint sample Add support for the Oceans S1 Add support for the Deepblu Cosmiq+ Add missing functions for accessing big/little endian values Move the snprintf functions to the platform module Repeat the handshake every few packets Enable big page support Remove the model number from the vtpro struct Add the model number to the version table Move all model numbers to the common header Remove a duplicated include statement Add support for the 300bar pressure sensor ...
This commit is contained in:
commit
7efedfbb2b
18
.github/workflows/build.yml
vendored
18
.github/workflows/build.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
CC: ${{ matrix.compiler }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get install libbluetooth-dev libusb-1.0-0-dev
|
||||
- run: autoreconf --install --force
|
||||
@ -30,7 +30,7 @@ jobs:
|
||||
run: |
|
||||
make install DESTDIR=$PWD/artifacts
|
||||
tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ github.job }}-${{ matrix.compiler }}
|
||||
path: ${{ github.job }}-${{ matrix.compiler }}.tar.gz
|
||||
@ -50,7 +50,7 @@ jobs:
|
||||
CC: ${{ matrix.compiler }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: brew install autoconf automake libtool hidapi libusb
|
||||
- run: autoreconf --install --force
|
||||
@ -61,7 +61,7 @@ jobs:
|
||||
run: |
|
||||
make install DESTDIR=$PWD/artifacts
|
||||
tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ github.job }}-${{ matrix.compiler }}
|
||||
path: ${{ github.job }}-${{ matrix.compiler }}.tar.gz
|
||||
@ -78,12 +78,12 @@ jobs:
|
||||
arch: [i686, x86_64]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get install gcc-mingw-w64 binutils-mingw-w64 mingw-w64-tools
|
||||
- name: Install libusb
|
||||
env:
|
||||
LIBUSB_VERSION: 1.0.24
|
||||
LIBUSB_VERSION: 1.0.26
|
||||
run: |
|
||||
wget -c https://github.com/libusb/libusb/archive/refs/tags/v${LIBUSB_VERSION}.tar.gz
|
||||
tar xzf v${LIBUSB_VERSION}.tar.gz
|
||||
@ -95,13 +95,13 @@ jobs:
|
||||
popd
|
||||
- name: Install hidapi
|
||||
env:
|
||||
HIDAPI_VERSION: 0.10.1
|
||||
HIDAPI_VERSION: 0.12.0
|
||||
run: |
|
||||
wget -c https://github.com/libusb/hidapi/archive/refs/tags/hidapi-${HIDAPI_VERSION}.tar.gz
|
||||
tar xzf hidapi-${HIDAPI_VERSION}.tar.gz
|
||||
pushd hidapi-hidapi-${HIDAPI_VERSION}
|
||||
autoreconf --install --force
|
||||
./configure --host=${{ matrix.arch }}-w64-mingw32 --prefix=/usr
|
||||
./configure --host=${{ matrix.arch }}-w64-mingw32 --prefix=/usr LDFLAGS='-static-libgcc'
|
||||
make
|
||||
make install DESTDIR=$PWD/../artifacts
|
||||
popd
|
||||
@ -118,7 +118,7 @@ jobs:
|
||||
run: |
|
||||
make install DESTDIR=$PWD/artifacts
|
||||
tar -czf ${{ github.job }}-${{ matrix.arch }}.tar.gz -C artifacts usr
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ github.job }}-${{ matrix.arch }}
|
||||
path: ${{ github.job }}-${{ matrix.arch }}.tar.gz
|
||||
|
||||
32
.github/workflows/release.yml
vendored
32
.github/workflows/release.yml
vendored
@ -9,19 +9,13 @@ jobs:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Version number
|
||||
id: version
|
||||
run: |
|
||||
VERSION="${GITHUB_REF/refs\/tags\/v/}"
|
||||
if [ "${VERSION}" = "${VERSION%%-*}" ]; then
|
||||
PRERELEASE=false
|
||||
else
|
||||
PRERELEASE=true
|
||||
fi
|
||||
echo ::set-output name=version::${VERSION}
|
||||
echo ::set-output name=prerelease::${PRERELEASE}
|
||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build distribution tarball
|
||||
id: build
|
||||
@ -41,21 +35,13 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- uses: actions/create-release@v1
|
||||
- name: Create Github release
|
||||
id: release
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
if [ "${VERSION}" != "${VERSION%%-*}" ]; then
|
||||
PRERELEASE="-p"
|
||||
fi
|
||||
gh release create ${PRERELEASE} "${{ github.ref }}" "libdivecomputer-${VERSION}.tar.gz"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
prerelease: ${{ steps.version.outputs.prerelease }}
|
||||
|
||||
- uses: actions/upload-release-asset@v1
|
||||
id: upload
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.release.outputs.upload_url }}
|
||||
asset_path: libdivecomputer-${{ steps.version.outputs.version }}.tar.gz
|
||||
asset_name: libdivecomputer-${{ steps.version.outputs.version }}.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
@ -120,12 +120,12 @@ AS_IF([test "x$with_libmtp" != "xno"], [
|
||||
AC_ARG_WITH([hidapi],
|
||||
[AS_HELP_STRING([--without-hidapi],
|
||||
[Build without the hidapi library])],
|
||||
[], [with_hidapi=auto])
|
||||
[], [with_hidapi=hidapi])
|
||||
AS_IF([test "x$with_hidapi" != "xno"], [
|
||||
PKG_CHECK_MODULES([HIDAPI], [hidapi], [have_hidapi=yes], [have_hidapi=no])
|
||||
PKG_CHECK_MODULES([HIDAPI], [$with_hidapi], [have_hidapi=yes], [have_hidapi=no])
|
||||
AS_IF([test "x$have_hidapi" = "xyes"], [
|
||||
AC_DEFINE([HAVE_HIDAPI], [1], [hidapi library])
|
||||
DEPENDENCIES="$DEPENDENCIES hidapi"
|
||||
DEPENDENCIES="$DEPENDENCIES $with_hidapi"
|
||||
])
|
||||
])
|
||||
|
||||
@ -170,7 +170,7 @@ AC_CHECK_HEADERS([sys/socket.h linux/types.h linux/irda.h], , , [
|
||||
# Checks for header files.
|
||||
AC_CHECK_HEADERS([linux/serial.h])
|
||||
AC_CHECK_HEADERS([IOKit/serial/ioss.h])
|
||||
AC_CHECK_HEADERS([getopt.h])
|
||||
AC_CHECK_HEADERS([unistd.h getopt.h])
|
||||
AC_CHECK_HEADERS([sys/param.h])
|
||||
AC_CHECK_HEADERS([pthread.h])
|
||||
AC_CHECK_HEADERS([mach/mach_time.h])
|
||||
|
||||
@ -1,26 +1,35 @@
|
||||
# Atomic Aquatics Cobalt
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="0471", ATTR{idProduct}=="0888", GROUP="plugdev"
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0471", ATTRS{idProduct}=="0888", GROUP="plugdev"
|
||||
|
||||
# Suunto EON Steel
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0030", GROUP="plugdev"
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1493", ATTRS{idProduct}=="0030", GROUP="plugdev"
|
||||
|
||||
# Suunto EON Core
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0033", GROUP="plugdev"
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1493", ATTRS{idProduct}=="0033", GROUP="plugdev"
|
||||
|
||||
# Suunto D5
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0035", GROUP="plugdev"
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1493", ATTRS{idProduct}=="0035", GROUP="plugdev"
|
||||
|
||||
# Suunto EON Steel Black
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0036", GROUP="plugdev"
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1493", ATTRS{idProduct}=="0036", GROUP="plugdev"
|
||||
|
||||
# Scubapro G2
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="2e6c", ATTR{idProduct}=="3201", GROUP="plugdev"
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2e6c", ATTRS{idProduct}=="3201", GROUP="plugdev"
|
||||
|
||||
# Scubapro G2 Console
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="2e6c", ATTR{idProduct}=="3211", GROUP="plugdev"
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2e6c", ATTRS{idProduct}=="3211", GROUP="plugdev"
|
||||
|
||||
# Scubapro G2 HUD
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="2e6c", ATTR{idProduct}=="4201", GROUP="plugdev"
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2e6c", ATTRS{idProduct}=="4201", GROUP="plugdev"
|
||||
|
||||
# Scubapro Aladin Square
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="c251", ATTR{idProduct}=="2006", GROUP="plugdev"
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="c251", ATTRS{idProduct}=="2006", GROUP="plugdev"
|
||||
|
||||
@ -96,11 +96,11 @@ static const backend_table_t g_backends[] = {
|
||||
{"sp2", DC_FAMILY_SPORASUB_SP2, 0},
|
||||
{"excursion", DC_FAMILY_DEEPSIX_EXCURSION, 0},
|
||||
{"screen", DC_FAMILY_SEAC_SCREEN, 0},
|
||||
{"cosmiq", DC_FAMILY_DEEPBLU_COSMIQ, 0},
|
||||
{"s1", DC_FAMILY_OCEANS_S1, 0},
|
||||
|
||||
// Not merged upstream yet
|
||||
{"descentmk1", DC_FAMILY_GARMIN, 0},
|
||||
{"cosmiq", DC_FAMILY_DEEPBLU, 0},
|
||||
{"oceans", DC_FAMILY_OCEANS_S1, 0},
|
||||
};
|
||||
|
||||
static const transport_table_t g_transports[] = {
|
||||
|
||||
@ -24,10 +24,12 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
@ -24,9 +24,11 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
@ -24,9 +24,11 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
@ -24,8 +24,10 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
@ -24,8 +24,10 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
@ -24,8 +24,10 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
@ -24,9 +24,11 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
@ -24,8 +24,10 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
@ -24,8 +24,10 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
@ -24,8 +24,10 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
@ -24,8 +24,10 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
@ -24,8 +24,10 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
@ -386,6 +386,30 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
|
||||
names[divemode]);
|
||||
}
|
||||
|
||||
// Parse the deco model.
|
||||
message ("Parsing the deco model.\n");
|
||||
dc_decomodel_t decomodel = {DC_DECOMODEL_NONE};
|
||||
status = dc_parser_get_field (parser, DC_FIELD_DECOMODEL, 0, &decomodel);
|
||||
if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) {
|
||||
ERROR ("Error parsing the deco model.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (status != DC_STATUS_UNSUPPORTED) {
|
||||
const char *names[] = {"none", "buhlmann", "vpm", "rgbm", "dciem"};
|
||||
fprintf (output->ostream, "<decomodel>%s</decomodel>\n",
|
||||
names[decomodel.type]);
|
||||
if (decomodel.type == DC_DECOMODEL_BUHLMANN &&
|
||||
(decomodel.params.gf.low != 0 || decomodel.params.gf.high != 0)) {
|
||||
fprintf (output->ostream, "<gf>%u/%u</gf>\n",
|
||||
decomodel.params.gf.low, decomodel.params.gf.high);
|
||||
}
|
||||
if (decomodel.conservatism) {
|
||||
fprintf (output->ostream, "<conservatism>%d</conservatism>\n",
|
||||
decomodel.conservatism);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the salinity.
|
||||
message ("Parsing the salinity.\n");
|
||||
dc_salinity_t salinity = {DC_WATER_FRESH, 0.0};
|
||||
|
||||
@ -26,6 +26,12 @@
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#define strcasecmp _stricmp
|
||||
#define strncasecmp _strnicmp
|
||||
#endif
|
||||
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
#define FUNCTION __func__
|
||||
#else
|
||||
|
||||
@ -118,14 +118,14 @@ typedef enum dc_family_t {
|
||||
DC_FAMILY_DEEPSIX_EXCURSION = (19 << 16),
|
||||
/* Seac Screen */
|
||||
DC_FAMILY_SEAC_SCREEN = (20 << 16),
|
||||
/* Deepblu Cosmiq */
|
||||
DC_FAMILY_DEEPBLU_COSMIQ = (21 << 16),
|
||||
/* Oceans S1 */
|
||||
DC_FAMILY_OCEANS_S1 = (22 << 16),
|
||||
|
||||
// Not merged upstream yet
|
||||
/* Garmin */
|
||||
DC_FAMILY_GARMIN = (100 << 16),
|
||||
/* Deepblu */
|
||||
DC_FAMILY_DEEPBLU = (101 << 16),
|
||||
/* Oceans S1 */
|
||||
DC_FAMILY_OCEANS_S1 = (102 << 16),
|
||||
} dc_family_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@ -67,6 +67,7 @@ typedef enum dc_field_type_t {
|
||||
DC_FIELD_TANK_COUNT,
|
||||
DC_FIELD_TANK,
|
||||
DC_FIELD_DIVEMODE,
|
||||
DC_FIELD_DECOMODEL,
|
||||
DC_FIELD_STRING,
|
||||
} dc_field_type_t;
|
||||
|
||||
@ -223,6 +224,43 @@ typedef struct dc_tank_t {
|
||||
double endpressure; /* End pressure (bar) */
|
||||
} dc_tank_t;
|
||||
|
||||
typedef enum dc_decomodel_type_t {
|
||||
DC_DECOMODEL_NONE,
|
||||
DC_DECOMODEL_BUHLMANN,
|
||||
DC_DECOMODEL_VPM,
|
||||
DC_DECOMODEL_RGBM,
|
||||
DC_DECOMODEL_DCIEM,
|
||||
} dc_decomodel_type_t;
|
||||
|
||||
/*
|
||||
* Decompression model
|
||||
*
|
||||
* The type field contains the decompression algorithm.
|
||||
*
|
||||
* The (optional) conservatism field contains the personal adjustment
|
||||
* setting of the algorithm. The exact interpretation depends on the
|
||||
* dive computer, but the default value (zero) will typically correspond
|
||||
* to the neutral setting, while a positive value is more conservative
|
||||
* and a negative value more aggressive.
|
||||
*
|
||||
* The (optional) params field contains the parameters of the algorithm:
|
||||
*
|
||||
* DC_DECOMODEL_BUHLMANN: The Gradient Factor (GF) parameters low and
|
||||
* high. For a pure Buhlmann algorithm (e.g. without GF enabled), both
|
||||
* values are 100. If GF are enabled, but the actual parameter values
|
||||
* are not available from the dive computer, both values are zero.
|
||||
*/
|
||||
typedef struct dc_decomodel_t {
|
||||
dc_decomodel_type_t type;
|
||||
int conservatism;
|
||||
union {
|
||||
struct {
|
||||
unsigned int high;
|
||||
unsigned int low;
|
||||
} gf;
|
||||
} params;
|
||||
} dc_decomodel_t;
|
||||
|
||||
typedef struct dc_field_string_t {
|
||||
const char *desc;
|
||||
const char *value;
|
||||
@ -275,6 +313,15 @@ dc_parser_new2 (dc_parser_t **parser, dc_context_t *context, dc_descriptor_t *de
|
||||
dc_family_t
|
||||
dc_parser_get_type (dc_parser_t *parser);
|
||||
|
||||
dc_status_t
|
||||
dc_parser_set_clock (dc_parser_t *parser, unsigned int devtime, dc_ticks_t systime);
|
||||
|
||||
dc_status_t
|
||||
dc_parser_set_atmospheric (dc_parser_t *parser, double atmospheric);
|
||||
|
||||
dc_status_t
|
||||
dc_parser_set_density (dc_parser_t *parser, double density);
|
||||
|
||||
dc_status_t
|
||||
dc_parser_set_data (dc_parser_t *parser, const unsigned char *data, unsigned int size);
|
||||
|
||||
|
||||
@ -182,6 +182,8 @@
|
||||
<ClCompile Include="..\src\cressi_leonardo_parser.c" />
|
||||
<ClCompile Include="..\src\custom.c" />
|
||||
<ClCompile Include="..\src\datetime.c" />
|
||||
<ClCompile Include="..\src\deepblu_cosmiq.c" />
|
||||
<ClCompile Include="..\src\deepblu_cosmiq_parser.c" />
|
||||
<ClCompile Include="..\src\deepsix_excursion.c" />
|
||||
<ClCompile Include="..\src\deepsix_excursion_parser.c" />
|
||||
<ClCompile Include="..\src\descriptor.c" />
|
||||
@ -217,6 +219,9 @@
|
||||
<ClCompile Include="..\src\oceanic_veo250_parser.c" />
|
||||
<ClCompile Include="..\src\oceanic_vtpro.c" />
|
||||
<ClCompile Include="..\src\oceanic_vtpro_parser.c" />
|
||||
<ClCompile Include="..\src\oceans_s1.c" />
|
||||
<ClCompile Include="..\src\oceans_s1_common.c" />
|
||||
<ClCompile Include="..\src\oceans_s1_parser.c" />
|
||||
<ClCompile Include="..\src\parser.c" />
|
||||
<ClCompile Include="..\src\platform.c" />
|
||||
<ClCompile Include="..\src\rbstream.c" />
|
||||
@ -308,6 +313,7 @@
|
||||
<ClInclude Include="..\src\cressi_edy.h" />
|
||||
<ClInclude Include="..\src\cressi_goa.h" />
|
||||
<ClInclude Include="..\src\cressi_leonardo.h" />
|
||||
<ClInclude Include="..\src\deepblu_cosmiq.h" />
|
||||
<ClInclude Include="..\src\deepsix_excursion.h" />
|
||||
<ClInclude Include="..\src\descriptor-private.h" />
|
||||
<ClInclude Include="..\src\device-private.h" />
|
||||
@ -330,6 +336,8 @@
|
||||
<ClInclude Include="..\src\oceanic_common.h" />
|
||||
<ClInclude Include="..\src\oceanic_veo250.h" />
|
||||
<ClInclude Include="..\src\oceanic_vtpro.h" />
|
||||
<ClInclude Include="..\src\oceans_s1.h" />
|
||||
<ClInclude Include="..\src\oceans_s1_common.h" />
|
||||
<ClInclude Include="..\src\parser-private.h" />
|
||||
<ClInclude Include="..\src\platform.h" />
|
||||
<ClInclude Include="..\src\rbstream.h" />
|
||||
|
||||
@ -77,6 +77,9 @@ libdivecomputer_la_SOURCES = \
|
||||
sporasub_sp2.h sporasub_sp2.c sporasub_sp2_parser.c \
|
||||
deepsix_excursion.h deepsix_excursion.c deepsix_excursion_parser.c \
|
||||
seac_screen.h seac_screen.c seac_screen_parser.c \
|
||||
deepblu_cosmiq.h deepblu_cosmiq.c deepblu_cosmiq_parser.c \
|
||||
oceans_s1_common.h oceans_s1_common.c \
|
||||
oceans_s1.h oceans_s1.c oceans_s1_parser.c \
|
||||
socket.h socket.c \
|
||||
irda.c \
|
||||
usb.c \
|
||||
@ -88,9 +91,7 @@ libdivecomputer_la_SOURCES = \
|
||||
libdivecomputer_la_SOURCES += \
|
||||
usb_storage.c \
|
||||
field-cache.h field-cache.c \
|
||||
garmin.h garmin.c garmin_parser.c \
|
||||
deepblu.h deepblu.c deepblu_parser.c \
|
||||
oceans_s1.h oceans_s1.c oceans_s1_parser.c
|
||||
garmin.h garmin.c garmin_parser.c
|
||||
|
||||
if OS_WIN32
|
||||
libdivecomputer_la_SOURCES += serial_win32.c
|
||||
|
||||
132
src/array.c
132
src/array.c
@ -208,6 +208,32 @@ array_uint_le (const unsigned char data[], unsigned int n)
|
||||
return value;
|
||||
}
|
||||
|
||||
unsigned long long
|
||||
array_uint64_be (const unsigned char data[])
|
||||
{
|
||||
return ((unsigned long long) data[0] << 56) |
|
||||
((unsigned long long) data[1] << 48) |
|
||||
((unsigned long long) data[2] << 40) |
|
||||
((unsigned long long) data[3] << 32) |
|
||||
((unsigned long long) data[4] << 24) |
|
||||
((unsigned long long) data[5] << 16) |
|
||||
((unsigned long long) data[6] << 8) |
|
||||
((unsigned long long) data[7] << 0);
|
||||
}
|
||||
|
||||
unsigned long long
|
||||
array_uint64_le (const unsigned char data[])
|
||||
{
|
||||
return ((unsigned long long) data[0] << 0) |
|
||||
((unsigned long long) data[1] << 8) |
|
||||
((unsigned long long) data[2] << 16) |
|
||||
((unsigned long long) data[3] << 24) |
|
||||
((unsigned long long) data[4] << 32) |
|
||||
((unsigned long long) data[5] << 40) |
|
||||
((unsigned long long) data[6] << 48) |
|
||||
((unsigned long long) data[7] << 56);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
array_uint32_be (const unsigned char data[])
|
||||
{
|
||||
@ -237,17 +263,6 @@ array_uint32_word_be (const unsigned char data[])
|
||||
((unsigned int) data[3] << 16);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
array_uint32_le_set (unsigned char data[], const unsigned int input)
|
||||
{
|
||||
data[0] = input & 0xFF;
|
||||
data[1] = (input >> 8) & 0xFF;
|
||||
data[2] = (input >> 16) & 0xFF;
|
||||
data[3] = (input >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
array_uint24_be (const unsigned char data[])
|
||||
{
|
||||
@ -256,16 +271,6 @@ array_uint24_be (const unsigned char data[])
|
||||
((unsigned int) data[2] << 0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
array_uint24_be_set (unsigned char data[], const unsigned int input)
|
||||
{
|
||||
data[0] = (input >> 16) & 0xFF;
|
||||
data[1] = (input >> 8) & 0xFF;
|
||||
data[2] = input & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
unsigned int
|
||||
array_uint24_le (const unsigned char data[])
|
||||
{
|
||||
@ -289,8 +294,93 @@ array_uint16_le (const unsigned char data[])
|
||||
((unsigned int) data[1] << 8);
|
||||
}
|
||||
|
||||
void
|
||||
array_uint64_be_set (unsigned char data[], const unsigned long long input)
|
||||
{
|
||||
data[0] = (input >> 56) & 0xFF;
|
||||
data[1] = (input >> 48) & 0xFF;
|
||||
data[2] = (input >> 40) & 0xFF;
|
||||
data[3] = (input >> 32) & 0xFF;
|
||||
data[4] = (input >> 24) & 0xFF;
|
||||
data[5] = (input >> 16) & 0xFF;
|
||||
data[6] = (input >> 8) & 0xFF;
|
||||
data[7] = (input ) & 0xFF;
|
||||
}
|
||||
|
||||
void
|
||||
array_uint64_le_set (unsigned char data[], const unsigned long long input)
|
||||
{
|
||||
data[0] = (input ) & 0xFF;
|
||||
data[1] = (input >> 8) & 0xFF;
|
||||
data[2] = (input >> 16) & 0xFF;
|
||||
data[3] = (input >> 24) & 0xFF;
|
||||
data[4] = (input >> 32) & 0xFF;
|
||||
data[5] = (input >> 40) & 0xFF;
|
||||
data[6] = (input >> 48) & 0xFF;
|
||||
data[7] = (input >> 56) & 0xFF;
|
||||
}
|
||||
|
||||
void
|
||||
array_uint32_be_set (unsigned char data[], const unsigned int input)
|
||||
{
|
||||
data[0] = (input >> 24) & 0xFF;
|
||||
data[1] = (input >> 16) & 0xFF;
|
||||
data[2] = (input >> 8) & 0xFF;
|
||||
data[3] = (input ) & 0xFF;
|
||||
}
|
||||
|
||||
void
|
||||
array_uint32_le_set (unsigned char data[], const unsigned int input)
|
||||
{
|
||||
data[0] = (input ) & 0xFF;
|
||||
data[1] = (input >> 8) & 0xFF;
|
||||
data[2] = (input >> 16) & 0xFF;
|
||||
data[3] = (input >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
void
|
||||
array_uint24_be_set (unsigned char data[], const unsigned int input)
|
||||
{
|
||||
data[0] = (input >> 16) & 0xFF;
|
||||
data[1] = (input >> 8) & 0xFF;
|
||||
data[2] = (input ) & 0xFF;
|
||||
}
|
||||
|
||||
void
|
||||
array_uint24_le_set (unsigned char data[], const unsigned int input)
|
||||
{
|
||||
data[0] = (input ) & 0xFF;
|
||||
data[1] = (input >> 8) & 0xFF;
|
||||
data[2] = (input >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
void
|
||||
array_uint16_be_set (unsigned char data[], const unsigned short input)
|
||||
{
|
||||
data[0] = (input >> 8) & 0xFF;
|
||||
data[1] = (input ) & 0xFF;
|
||||
}
|
||||
|
||||
void
|
||||
array_uint16_le_set (unsigned char data[], const unsigned short input)
|
||||
{
|
||||
data[0] = (input ) & 0xFF;
|
||||
data[1] = (input >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
unsigned char
|
||||
bcd2dec (unsigned char value)
|
||||
{
|
||||
return ((value >> 4) & 0x0f) * 10 + (value & 0x0f);
|
||||
}
|
||||
|
||||
unsigned char
|
||||
dec2bcd (unsigned char value)
|
||||
{
|
||||
if (value >= 100)
|
||||
return 0;
|
||||
|
||||
unsigned char hi = value / 10;
|
||||
unsigned char lo = value % 10;
|
||||
return (hi << 4) | lo;
|
||||
}
|
||||
|
||||
39
src/array.h
39
src/array.h
@ -66,6 +66,12 @@ array_uint_be (const unsigned char data[], unsigned int n);
|
||||
unsigned int
|
||||
array_uint_le (const unsigned char data[], unsigned int n);
|
||||
|
||||
unsigned long long
|
||||
array_uint64_be (const unsigned char data[]);
|
||||
|
||||
unsigned long long
|
||||
array_uint64_le (const unsigned char data[]);
|
||||
|
||||
unsigned int
|
||||
array_uint32_be (const unsigned char data[]);
|
||||
|
||||
@ -75,15 +81,9 @@ array_uint32_le (const unsigned char data[]);
|
||||
unsigned int
|
||||
array_uint32_word_be (const unsigned char data[]);
|
||||
|
||||
void
|
||||
array_uint32_le_set (unsigned char data[], const unsigned int input);
|
||||
|
||||
unsigned int
|
||||
array_uint24_be (const unsigned char data[]);
|
||||
|
||||
void
|
||||
array_uint24_be_set (unsigned char data[], const unsigned int input);
|
||||
|
||||
unsigned int
|
||||
array_uint24_le (const unsigned char data[]);
|
||||
|
||||
@ -93,9 +93,36 @@ array_uint16_be (const unsigned char data[]);
|
||||
unsigned short
|
||||
array_uint16_le (const unsigned char data[]);
|
||||
|
||||
void
|
||||
array_uint64_be_set (unsigned char data[], const unsigned long long input);
|
||||
|
||||
void
|
||||
array_uint64_le_set (unsigned char data[], const unsigned long long input);
|
||||
|
||||
void
|
||||
array_uint32_be_set (unsigned char data[], const unsigned int input);
|
||||
|
||||
void
|
||||
array_uint32_le_set (unsigned char data[], const unsigned int input);
|
||||
|
||||
void
|
||||
array_uint24_be_set (unsigned char data[], const unsigned int input);
|
||||
|
||||
void
|
||||
array_uint24_le_set (unsigned char data[], const unsigned int input);
|
||||
|
||||
void
|
||||
array_uint16_be_set (unsigned char data[], const unsigned short input);
|
||||
|
||||
void
|
||||
array_uint16_le_set (unsigned char data[], const unsigned short input);
|
||||
|
||||
unsigned char
|
||||
bcd2dec (unsigned char value);
|
||||
|
||||
unsigned char
|
||||
dec2bcd (unsigned char value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@ -50,6 +50,7 @@ struct atomics_cobalt_parser_t {
|
||||
};
|
||||
|
||||
static dc_status_t atomics_cobalt_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
static dc_status_t atomics_cobalt_parser_set_density (dc_parser_t *abstract, double density);
|
||||
static dc_status_t atomics_cobalt_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
|
||||
static dc_status_t atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
|
||||
static dc_status_t atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
|
||||
@ -58,6 +59,9 @@ static const dc_parser_vtable_t atomics_cobalt_parser_vtable = {
|
||||
sizeof(atomics_cobalt_parser_t),
|
||||
DC_FAMILY_ATOMICS_COBALT,
|
||||
atomics_cobalt_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
atomics_cobalt_parser_set_density, /* set_density */
|
||||
atomics_cobalt_parser_get_datetime, /* datetime */
|
||||
atomics_cobalt_parser_get_field, /* fields */
|
||||
atomics_cobalt_parser_samples_foreach, /* samples_foreach */
|
||||
@ -81,7 +85,7 @@ atomics_cobalt_parser_create (dc_parser_t **out, dc_context_t *context)
|
||||
}
|
||||
|
||||
// Set the default values.
|
||||
parser->hydrostatic = 1025.0 * GRAVITY;
|
||||
parser->hydrostatic = DEF_DENSITY_SALT * GRAVITY;
|
||||
|
||||
*out = (dc_parser_t*) parser;
|
||||
|
||||
@ -110,6 +114,17 @@ atomics_cobalt_parser_set_calibration (dc_parser_t *abstract, double atmospheric
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
atomics_cobalt_parser_set_density (dc_parser_t *abstract, double density)
|
||||
{
|
||||
atomics_cobalt_parser_t *parser = (atomics_cobalt_parser_t *) abstract;
|
||||
|
||||
parser->hydrostatic = density * GRAVITY;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
atomics_cobalt_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
{
|
||||
|
||||
@ -24,7 +24,6 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h> // malloc, free
|
||||
#include <stdio.h>
|
||||
|
||||
#include "socket.h"
|
||||
|
||||
@ -231,7 +230,7 @@ dc_bluetooth_addr2str(dc_bluetooth_address_t address, char *str, size_t size)
|
||||
if (str == NULL || size < DC_BLUETOOTH_SIZE)
|
||||
return NULL;
|
||||
|
||||
int n = snprintf(str, size, "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
int n = dc_platform_snprintf(str, size, "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
(unsigned char)((address >> 40) & 0xFF),
|
||||
(unsigned char)((address >> 32) & 0xFF),
|
||||
(unsigned char)((address >> 24) & 0xFF),
|
||||
|
||||
@ -45,6 +45,9 @@ static const dc_parser_vtable_t citizen_aqualand_parser_vtable = {
|
||||
sizeof(citizen_aqualand_parser_t),
|
||||
DC_FAMILY_CITIZEN_AQUALAND,
|
||||
citizen_aqualand_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
citizen_aqualand_parser_get_datetime, /* datetime */
|
||||
citizen_aqualand_parser_get_field, /* fields */
|
||||
citizen_aqualand_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -108,6 +108,9 @@ static const dc_parser_vtable_t cochran_commander_parser_vtable = {
|
||||
sizeof(cochran_commander_parser_t),
|
||||
DC_FAMILY_COCHRAN_COMMANDER,
|
||||
cochran_commander_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
cochran_commander_parser_get_datetime, /* datetime */
|
||||
cochran_commander_parser_get_field, /* fields */
|
||||
cochran_commander_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -28,6 +28,8 @@
|
||||
|
||||
#include <libdivecomputer/context.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
@ -40,12 +42,6 @@ extern "C" {
|
||||
#define FUNCTION __FUNCTION__
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define ATTR_FORMAT_PRINTF(a,b) __attribute__((format(printf, a, b)))
|
||||
#else
|
||||
#define ATTR_FORMAT_PRINTF(a,b)
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_LOGGING
|
||||
#define HEXDUMP(context, loglevel, prefix, data, size) dc_context_hexdump (context, loglevel, __FILE__, __LINE__, FUNCTION, prefix, data, size)
|
||||
#define SYSERROR(context, errcode) dc_context_syserror (context, DC_LOGLEVEL_ERROR, __FILE__, __LINE__, FUNCTION, errcode)
|
||||
@ -63,7 +59,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
dc_status_t
|
||||
dc_context_log (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *format, ...) ATTR_FORMAT_PRINTF(6, 7);
|
||||
dc_context_log (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *format, ...) DC_ATTR_FORMAT_PRINTF(6, 7);
|
||||
|
||||
dc_status_t
|
||||
dc_context_syserror (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, int errcode);
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
@ -36,6 +35,7 @@
|
||||
#endif
|
||||
|
||||
#include "context-private.h"
|
||||
#include "platform.h"
|
||||
#include "timer.h"
|
||||
|
||||
struct dc_context_t {
|
||||
@ -49,55 +49,6 @@ struct dc_context_t {
|
||||
};
|
||||
|
||||
#ifdef ENABLE_LOGGING
|
||||
/*
|
||||
* A wrapper for the vsnprintf function, which will always null terminate the
|
||||
* string and returns a negative value if the destination buffer is too small.
|
||||
*/
|
||||
static int
|
||||
l_vsnprintf (char *str, size_t size, const char *format, va_list ap)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (size == 0)
|
||||
return -1;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/*
|
||||
* The non-standard vsnprintf implementation provided by MSVC doesn't null
|
||||
* terminate the string and returns a negative value if the destination
|
||||
* buffer is too small.
|
||||
*/
|
||||
n = _vsnprintf (str, size - 1, format, ap);
|
||||
if (n == size - 1 || n < 0)
|
||||
str[size - 1] = 0;
|
||||
#else
|
||||
/*
|
||||
* The C99 vsnprintf function will always null terminate the string. If the
|
||||
* destination buffer is too small, the return value is the number of
|
||||
* characters that would have been written if the buffer had been large
|
||||
* enough.
|
||||
*/
|
||||
n = vsnprintf (str, size, format, ap);
|
||||
if (n >= 0 && (size_t) n >= size)
|
||||
n = -1;
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
l_snprintf (char *str, size_t size, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int n;
|
||||
|
||||
va_start (ap, format);
|
||||
n = l_vsnprintf (str, size, format, ap);
|
||||
va_end (ap);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
l_hexdump (char *str, size_t size, const unsigned char data[], size_t n)
|
||||
{
|
||||
@ -244,7 +195,7 @@ dc_context_log (dc_context_t *context, dc_loglevel_t loglevel, const char *file,
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
va_start (ap, format);
|
||||
l_vsnprintf (context->msg, sizeof (context->msg), format, ap);
|
||||
dc_platform_vsnprintf (context->msg, sizeof (context->msg), format, ap);
|
||||
va_end (ap);
|
||||
|
||||
context->logfunc (context, loglevel, file, line, function, context->msg, context->userdata);
|
||||
@ -310,7 +261,7 @@ dc_context_hexdump (dc_context_t *context, dc_loglevel_t loglevel, const char *f
|
||||
if (context->logfunc == NULL)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
n = l_snprintf (context->msg, sizeof (context->msg), "%s: size=%u, data=", prefix, size);
|
||||
n = dc_platform_snprintf (context->msg, sizeof (context->msg), "%s: size=%u, data=", prefix, size);
|
||||
|
||||
if (n >= 0) {
|
||||
n = l_hexdump (context->msg + n, sizeof (context->msg) - n, data, size);
|
||||
|
||||
@ -47,6 +47,9 @@ static const dc_parser_vtable_t cressi_edy_parser_vtable = {
|
||||
sizeof(cressi_edy_parser_t),
|
||||
DC_FAMILY_CRESSI_EDY,
|
||||
cressi_edy_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
cressi_edy_parser_get_datetime, /* datetime */
|
||||
cressi_edy_parser_get_field, /* fields */
|
||||
cressi_edy_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -29,9 +29,6 @@
|
||||
#define ISINSTANCE(parser) dc_device_isinstance((parser), &cressi_goa_parser_vtable)
|
||||
|
||||
#define SZ_HEADER 23
|
||||
#define SZ_HEADER_SCUBA 0x61
|
||||
#define SZ_HEADER_FREEDIVE 0x2B
|
||||
#define SZ_HEADER_GAUGE 0x2D
|
||||
|
||||
#define DEPTH 0
|
||||
#define DEPTH2 1
|
||||
@ -45,16 +42,26 @@
|
||||
|
||||
#define NGASMIXES 2
|
||||
|
||||
#define UNDEFINED 0xFFFFFFFF
|
||||
|
||||
typedef struct cressi_goa_parser_t cressi_goa_parser_t;
|
||||
|
||||
struct cressi_goa_parser_t {
|
||||
dc_parser_t base;
|
||||
unsigned int model;
|
||||
// Cached fields.
|
||||
unsigned int cached;
|
||||
double maxdepth;
|
||||
};
|
||||
|
||||
typedef struct cressi_goa_layout_t {
|
||||
unsigned int headersize;
|
||||
unsigned int datetime;
|
||||
unsigned int divetime;
|
||||
unsigned int gasmix;
|
||||
unsigned int atmospheric;
|
||||
unsigned int maxdepth;
|
||||
unsigned int avgdepth;
|
||||
unsigned int temperature;
|
||||
} cressi_goa_layout_t;
|
||||
|
||||
static dc_status_t cressi_goa_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
static dc_status_t cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
|
||||
static dc_status_t cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
|
||||
@ -64,17 +71,60 @@ static const dc_parser_vtable_t cressi_goa_parser_vtable = {
|
||||
sizeof(cressi_goa_parser_t),
|
||||
DC_FAMILY_CRESSI_GOA,
|
||||
cressi_goa_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
cressi_goa_parser_get_datetime, /* datetime */
|
||||
cressi_goa_parser_get_field, /* fields */
|
||||
cressi_goa_parser_samples_foreach, /* samples_foreach */
|
||||
NULL /* destroy */
|
||||
};
|
||||
|
||||
static const unsigned int headersizes[] = {
|
||||
SZ_HEADER_SCUBA,
|
||||
SZ_HEADER_SCUBA,
|
||||
SZ_HEADER_FREEDIVE,
|
||||
SZ_HEADER_GAUGE,
|
||||
static const cressi_goa_layout_t layouts[] = {
|
||||
/* SCUBA */
|
||||
{
|
||||
0x61, /* headersize */
|
||||
0x11, /* datetime */
|
||||
0x19, /* divetime */
|
||||
0x1F, /* gasmix */
|
||||
0x23, /* atmospheric */
|
||||
0x4E, /* maxdepth */
|
||||
0x50, /* avgdepth */
|
||||
0x52, /* temperature */
|
||||
},
|
||||
/* NITROX */
|
||||
{
|
||||
0x61, /* headersize */
|
||||
0x11, /* datetime */
|
||||
0x19, /* divetime */
|
||||
0x1F, /* gasmix */
|
||||
0x23, /* atmospheric */
|
||||
0x4E, /* maxdepth */
|
||||
0x50, /* avgdepth */
|
||||
0x52, /* temperature */
|
||||
},
|
||||
/* FREEDIVE */
|
||||
{
|
||||
0x2B, /* headersize */
|
||||
0x11, /* datetime */
|
||||
0x19, /* divetime */
|
||||
UNDEFINED, /* gasmix */
|
||||
UNDEFINED, /* atmospheric */
|
||||
0x1C, /* maxdepth */
|
||||
UNDEFINED, /* avgdepth */
|
||||
0x1E, /* temperature */
|
||||
},
|
||||
/* GAUGE */
|
||||
{
|
||||
0x2D, /* headersize */
|
||||
0x11, /* datetime */
|
||||
0x19, /* divetime */
|
||||
UNDEFINED, /* gasmix */
|
||||
0x1B, /* atmospheric */
|
||||
0x1D, /* maxdepth */
|
||||
0x1F, /* avgdepth */
|
||||
0x21, /* temperature */
|
||||
},
|
||||
};
|
||||
|
||||
dc_status_t
|
||||
@ -93,8 +143,6 @@ cressi_goa_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int
|
||||
}
|
||||
|
||||
parser->model = model;
|
||||
parser->cached = 0;
|
||||
parser->maxdepth = 0.0;
|
||||
|
||||
*out = (dc_parser_t*) parser;
|
||||
|
||||
@ -104,12 +152,6 @@ cressi_goa_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int
|
||||
static dc_status_t
|
||||
cressi_goa_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
|
||||
{
|
||||
cressi_goa_parser_t *parser = (cressi_goa_parser_t *) abstract;
|
||||
|
||||
// Reset the cache.
|
||||
parser->cached = 0;
|
||||
parser->maxdepth = 0.0;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -123,15 +165,16 @@ cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int divemode = data[2];
|
||||
if (divemode >= C_ARRAY_SIZE(headersizes)) {
|
||||
if (divemode >= C_ARRAY_SIZE(layouts)) {
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned int headersize = headersizes[divemode];
|
||||
if (size < headersize)
|
||||
const cressi_goa_layout_t *layout = &layouts[divemode];
|
||||
|
||||
if (size < layout->headersize)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
const unsigned char *p = abstract->data + 0x11;
|
||||
const unsigned char *p = abstract->data + layout->datetime;
|
||||
|
||||
if (datetime) {
|
||||
datetime->year = array_uint16_le(p);
|
||||
@ -149,7 +192,6 @@ cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
static dc_status_t
|
||||
cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||
{
|
||||
cressi_goa_parser_t *parser = (cressi_goa_parser_t *) abstract;
|
||||
const unsigned char *data = abstract->data;
|
||||
unsigned int size = abstract->size;
|
||||
|
||||
@ -157,29 +199,19 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int divemode = data[2];
|
||||
if (divemode >= C_ARRAY_SIZE(headersizes)) {
|
||||
if (divemode >= C_ARRAY_SIZE(layouts)) {
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned int headersize = headersizes[divemode];
|
||||
if (size < headersize)
|
||||
const cressi_goa_layout_t *layout = &layouts[divemode];
|
||||
|
||||
if (size < layout->headersize)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
if (!parser->cached) {
|
||||
sample_statistics_t statistics = SAMPLE_STATISTICS_INITIALIZER;
|
||||
dc_status_t rc = cressi_goa_parser_samples_foreach (
|
||||
abstract, sample_statistics_cb, &statistics);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
parser->cached = 1;
|
||||
parser->maxdepth = statistics.maxdepth;
|
||||
}
|
||||
|
||||
unsigned int ngasmixes = 0;
|
||||
if (divemode == SCUBA || divemode == NITROX) {
|
||||
if (layout->gasmix != UNDEFINED) {
|
||||
for (unsigned int i = 0; i < NGASMIXES; ++i) {
|
||||
if (data[0x20 + 2 * i] == 0)
|
||||
if (data[layout->gasmix + 2 * i + 1] == 0)
|
||||
break;
|
||||
ngasmixes++;
|
||||
}
|
||||
@ -190,17 +222,36 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign
|
||||
if (value) {
|
||||
switch (type) {
|
||||
case DC_FIELD_DIVETIME:
|
||||
*((unsigned int *) value) = array_uint16_le (data + 0x19);
|
||||
if (layout->divetime == UNDEFINED)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
*((unsigned int *) value) = array_uint16_le (data + layout->divetime);
|
||||
break;
|
||||
case DC_FIELD_MAXDEPTH:
|
||||
*((double *) value) = parser->maxdepth;
|
||||
if (layout->maxdepth == UNDEFINED)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
*((double *) value) = array_uint16_le (data + layout->maxdepth) / 10.0;
|
||||
break;
|
||||
case DC_FIELD_AVGDEPTH:
|
||||
if (layout->avgdepth == UNDEFINED)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
*((double *) value) = array_uint16_le (data + layout->avgdepth) / 10.0;
|
||||
break;
|
||||
case DC_FIELD_TEMPERATURE_MINIMUM:
|
||||
if (layout->temperature == UNDEFINED)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
*((double *) value) = array_uint16_le (data + layout->temperature) / 10.0;
|
||||
break;
|
||||
case DC_FIELD_ATMOSPHERIC:
|
||||
if (layout->atmospheric == UNDEFINED)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
*((double *) value) = array_uint16_le (data + layout->atmospheric) / 1000.0;
|
||||
break;
|
||||
case DC_FIELD_GASMIX_COUNT:
|
||||
*((unsigned int *) value) = ngasmixes;
|
||||
break;
|
||||
case DC_FIELD_GASMIX:
|
||||
gasmix->helium = 0.0;
|
||||
gasmix->oxygen = data[0x20 + 2 * flags] / 100.0;
|
||||
gasmix->oxygen = data[layout->gasmix + 2 * flags + 1] / 100.0;
|
||||
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
|
||||
break;
|
||||
case DC_FIELD_DIVEMODE:
|
||||
@ -237,12 +288,13 @@ cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int divemode = data[2];
|
||||
if (divemode >= C_ARRAY_SIZE(headersizes)) {
|
||||
if (divemode >= C_ARRAY_SIZE(layouts)) {
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned int headersize = headersizes[divemode];
|
||||
if (size < headersize)
|
||||
const cressi_goa_layout_t *layout = &layouts[divemode];
|
||||
|
||||
if (size < layout->headersize)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int interval = divemode == FREEDIVE ? 2 : 5;
|
||||
@ -254,7 +306,7 @@ cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
|
||||
unsigned int have_temperature = 0;
|
||||
unsigned int complete = 0;
|
||||
|
||||
unsigned int offset = headersize;
|
||||
unsigned int offset = layout->headersize;
|
||||
while (offset + 2 <= size) {
|
||||
dc_sample_value_t sample = {0};
|
||||
|
||||
|
||||
@ -48,6 +48,9 @@ static const dc_parser_vtable_t cressi_leonardo_parser_vtable = {
|
||||
sizeof(cressi_leonardo_parser_t),
|
||||
DC_FAMILY_CRESSI_LEONARDO,
|
||||
cressi_leonardo_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
cressi_leonardo_parser_get_datetime, /* datetime */
|
||||
cressi_leonardo_parser_get_field, /* fields */
|
||||
cressi_leonardo_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
500
src/deepblu.c
500
src/deepblu.c
@ -1,500 +0,0 @@
|
||||
/*
|
||||
* Deepblu Cosmiq+ downloading
|
||||
*
|
||||
* Copyright (C) 2019 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "deepblu.h"
|
||||
#include "context-private.h"
|
||||
#include "device-private.h"
|
||||
#include "array.h"
|
||||
|
||||
// "Write state"
|
||||
#define CMD_SETTIME 0x20 // Send 6 byte date-time, get single-byte 00x00 ack
|
||||
#define CMD_23 0x23 // Send 00/01 byte, get ack back? Some metric/imperial setting?
|
||||
|
||||
// "Read dives"?
|
||||
#define CMD_GETDIVENR 0x40 // Send empty byte, get single-byte number of dives back
|
||||
#define CMD_GETDIVE 0x41 // Send dive number (1-nr) byte, get dive stat length byte back
|
||||
#define RSP_DIVESTAT 0x42 // .. followed by packets of dive stat for that dive of that length
|
||||
#define CMD_GETPROFILE 0x43 // Send dive number (1-nr) byte, get dive profile length BE word back
|
||||
#define RSP_DIVEPROF 0x44 // .. followed by packets of dive profile of that length
|
||||
|
||||
// "Read state"
|
||||
#define CMD_GETTIME 0x50 // Send empty byte, get six-byte bcd date-time back
|
||||
#define CMD_51 0x51 // Send empty byte, get four bytes back (03 dc 00 e3)
|
||||
#define CMD_52 0x52 // Send empty byte, get two bytes back (bf 8d)
|
||||
#define CMD_53 0x53 // Send empty byte, get six bytes back (0e 81 00 03 00 00)
|
||||
#define CMD_54 0x54 // Send empty byte, get byte back (00)
|
||||
#define CMD_55 0x55 // Send empty byte, get byte back (00)
|
||||
#define CMD_56 0x56 // Send empty byte, get byte back (00)
|
||||
#define CMD_57 0x57 // Send empty byte, get byte back (00)
|
||||
#define CMD_58 0x58 // Send empty byte, get byte back (52)
|
||||
#define CMD_59 0x59 // Send empty byte, get six bytes back (00 00 07 00 00 00)
|
||||
// (00 00 00 00 00 00)
|
||||
#define CMD_5a 0x5a // Send empty byte, get six bytes back (23 1b 09 d8 37 c0)
|
||||
#define CMD_5b 0x5b // Send empty byte, get six bytes back (00 21 00 14 00 01)
|
||||
// (00 00 00 14 00 01)
|
||||
#define CMD_5c 0x5c // Send empty byte, get six bytes back (13 88 00 46 20 00)
|
||||
// (13 88 00 3c 15 00)
|
||||
#define CMD_5d 0x5d // Send empty byte, get six bytes back (19 00 23 0C 02 0E)
|
||||
// (14 14 14 0c 01 0e)
|
||||
#define CMD_5f 0x5f // Send empty byte, get six bytes back (00 00 07 00 00 00)
|
||||
|
||||
#define COSMIQ_HDR_SIZE 36
|
||||
|
||||
typedef struct deepblu_device_t {
|
||||
dc_device_t base;
|
||||
dc_iostream_t *iostream;
|
||||
unsigned char fingerprint[COSMIQ_HDR_SIZE];
|
||||
} deepblu_device_t;
|
||||
|
||||
static dc_status_t deepblu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
|
||||
static dc_status_t deepblu_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
|
||||
static dc_status_t deepblu_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime);
|
||||
static dc_status_t deepblu_device_close (dc_device_t *abstract);
|
||||
|
||||
static const dc_device_vtable_t deepblu_device_vtable = {
|
||||
sizeof(deepblu_device_t),
|
||||
DC_FAMILY_DEEPBLU,
|
||||
deepblu_device_set_fingerprint, /* set_fingerprint */
|
||||
NULL, /* read */
|
||||
NULL, /* write */
|
||||
NULL, /* dump */
|
||||
deepblu_device_foreach, /* foreach */
|
||||
deepblu_device_timesync, /* timesync */
|
||||
deepblu_device_close, /* close */
|
||||
};
|
||||
|
||||
// Maximum data in a packet. It's actually much
|
||||
// less than this, since BLE packets are small and
|
||||
// with the 7 bytes of headers and final newline
|
||||
// and the HEX encoding, the actual maximum is
|
||||
// just something like 6 bytes.
|
||||
//
|
||||
// But in theory the data could be done over
|
||||
// multiple packets. That doesn't seem to be
|
||||
// the case in anything I've seen so far.
|
||||
//
|
||||
// Pick something small and easy to use for
|
||||
// stack buffers.
|
||||
#define MAX_DATA 20
|
||||
|
||||
static char *
|
||||
write_hex_byte(unsigned char data, char *p)
|
||||
{
|
||||
static const char hex[16] = "0123456789ABCDEF";
|
||||
*p++ = hex[data >> 4];
|
||||
*p++ = hex[data & 0xf];
|
||||
return p;
|
||||
}
|
||||
|
||||
//
|
||||
// Send a cmd packet.
|
||||
//
|
||||
// The format of the cmd on the "wire" is:
|
||||
// - byte '#'
|
||||
// - HEX char of cmd
|
||||
// - HEX char two's complement modular sum of packet data (including cmd/size)
|
||||
// - HEX char size of data as encoded in HEX
|
||||
// - n * HEX char data
|
||||
// - byte '\n'
|
||||
// so you end up having 8 bytes of header/trailer overhead, and two bytes
|
||||
// for every byte of data sent due to the HEX encoding.
|
||||
//
|
||||
static dc_status_t
|
||||
deepblu_send_cmd(deepblu_device_t *device, const unsigned char cmd, const unsigned char data[], size_t size)
|
||||
{
|
||||
char buffer[8+2*MAX_DATA], *p;
|
||||
unsigned char csum;
|
||||
int i;
|
||||
|
||||
if (size > MAX_DATA)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
// Calculate packet csum
|
||||
csum = cmd + 2*size;
|
||||
for (i = 0; i < size; i++)
|
||||
csum += data[i];
|
||||
csum = -csum;
|
||||
|
||||
// Fill the data buffer
|
||||
p = buffer;
|
||||
*p++ = '#';
|
||||
p = write_hex_byte(cmd, p);
|
||||
p = write_hex_byte(csum, p);
|
||||
p = write_hex_byte(size*2, p);
|
||||
for (i = 0; i < size; i++)
|
||||
p = write_hex_byte(data[i], p);
|
||||
*p++ = '\n';
|
||||
|
||||
// .. and send it out
|
||||
return dc_iostream_write(device->iostream, buffer, p-buffer, NULL);
|
||||
}
|
||||
|
||||
//
|
||||
// Receive one 'line' of data
|
||||
//
|
||||
// The deepblu BLE protocol is ASCII line based and packetized.
|
||||
// Normally one packet is one line, but it looks like the Nordic
|
||||
// Semi BLE chip will sometimes send packets early (some internal
|
||||
// serial buffer timeout?) with incompete data.
|
||||
//
|
||||
// So read packets until you get newline.
|
||||
static dc_status_t
|
||||
deepblu_recv_line(deepblu_device_t *device, unsigned char *buf, size_t size)
|
||||
{
|
||||
while (1) {
|
||||
unsigned char buffer[20];
|
||||
size_t transferred = 0;
|
||||
dc_status_t status;
|
||||
|
||||
status = dc_iostream_read(device->iostream, buffer, sizeof(buffer), &transferred);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR(device->base.context, "Failed to receive Deepblu reply packet.");
|
||||
return status;
|
||||
}
|
||||
if (transferred > size) {
|
||||
ERROR(device->base.context, "Deepblu reply packet with too much data (got %zu, expected %zu)", transferred, size);
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
if (!transferred) {
|
||||
ERROR(device->base.context, "Empty Deepblu reply packet");
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
memcpy(buf, buffer, transferred);
|
||||
buf += transferred;
|
||||
size -= transferred;
|
||||
if (buf[-1] == '\n')
|
||||
break;
|
||||
}
|
||||
buf[-1] = 0;
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
hex_nibble(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
read_hex_byte(char *p)
|
||||
{
|
||||
// This is negative if either of the nibbles is invalid
|
||||
return (hex_nibble(p[0]) << 4) | hex_nibble(p[1]);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Receive a reply packet
|
||||
//
|
||||
// The reply packet has the same format as the cmd packet we
|
||||
// send, except the first byte is '$' instead of '#'.
|
||||
static dc_status_t
|
||||
deepblu_recv_data(deepblu_device_t *device, const unsigned char expected, unsigned char *buf, size_t size, size_t *received)
|
||||
{
|
||||
int len, i;
|
||||
dc_status_t status;
|
||||
char buffer[8+2*MAX_DATA];
|
||||
int cmd, csum, ndata;
|
||||
|
||||
status = deepblu_recv_line(device, buffer, sizeof(buffer));
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
// deepblu_recv_line() always zero-terminates the result
|
||||
// if it returned success, and has removed the final newline.
|
||||
len = strlen(buffer);
|
||||
HEXDUMP(device->base.context, DC_LOGLEVEL_DEBUG, "rcv", buffer, len);
|
||||
|
||||
// A valid reply should always be at least 7 characters: the
|
||||
// initial '$' and the three header HEX bytes.
|
||||
if (len < 8 || buffer[0] != '$') {
|
||||
ERROR(device->base.context, "Invalid Deepblu reply packet");
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
|
||||
cmd = read_hex_byte(buffer+1);
|
||||
csum = read_hex_byte(buffer+3);
|
||||
ndata = read_hex_byte(buffer+5);
|
||||
if ((cmd | csum | ndata) < 0) {
|
||||
ERROR(device->base.context, "non-hex Deepblu reply packet header");
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
|
||||
// Verify the data length: it's the size of the HEX data,
|
||||
// and should also match the line length we got (the 7
|
||||
// is for the header data we already decoded above).
|
||||
if ((ndata & 1) || ndata != len - 7) {
|
||||
ERROR(device->base.context, "Deepblu reply packet data length does not match (claimed %d, got %d)", ndata, len-7);
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
|
||||
if (ndata >> 1 > size) {
|
||||
ERROR(device->base.context, "Deepblu reply packet too big for buffer (ndata=%d, size=%zu)", ndata, size);
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
|
||||
csum += cmd + ndata;
|
||||
|
||||
for (i = 7; i < len; i += 2) {
|
||||
int byte = read_hex_byte(buffer + i);
|
||||
if (byte < 0) {
|
||||
ERROR(device->base.context, "Deepblu reply packet data not valid hex");
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
*buf++ = byte;
|
||||
csum += byte;
|
||||
}
|
||||
|
||||
if (csum & 255) {
|
||||
ERROR(device->base.context, "Deepblu reply packet csum not valid (%x)", csum);
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
|
||||
*received = ndata >> 1;
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Common communication pattern: send a command, expect data back with the same
|
||||
// command byte.
|
||||
static dc_status_t
|
||||
deepblu_send_recv(deepblu_device_t *device, const unsigned char cmd,
|
||||
const unsigned char *data, size_t data_size,
|
||||
unsigned char *result, size_t result_size)
|
||||
{
|
||||
dc_status_t status;
|
||||
size_t got;
|
||||
|
||||
status = deepblu_send_cmd(device, cmd, data, data_size);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
status = deepblu_recv_data(device, cmd, result, result_size, &got);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
if (got != result_size) {
|
||||
ERROR(device->base.context, "Deepblu result size didn't match expected (expected %zu, got %zu)",
|
||||
result_size, got);
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_recv_bulk(deepblu_device_t *device, const unsigned char cmd, unsigned char *buf, size_t len)
|
||||
{
|
||||
while (len) {
|
||||
dc_status_t status;
|
||||
size_t got;
|
||||
|
||||
status = deepblu_recv_data(device, cmd, buf, len, &got);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
if (got > len) {
|
||||
ERROR(device->base.context, "Deepblu bulk receive overflow");
|
||||
return DC_STATUS_IO;
|
||||
}
|
||||
buf += got;
|
||||
len -= got;
|
||||
}
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
deepblu_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
|
||||
{
|
||||
deepblu_device_t *device;
|
||||
|
||||
if (out == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
// Allocate memory.
|
||||
device = (deepblu_device_t *) dc_device_allocate (context, &deepblu_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
|
||||
deepblu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
|
||||
{
|
||||
deepblu_device_t *device = (deepblu_device_t *)abstract;
|
||||
|
||||
HEXDUMP(device->base.context, DC_LOGLEVEL_DEBUG, "set_fingerprint", data, size);
|
||||
|
||||
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 unsigned char bcd(int val)
|
||||
{
|
||||
if (val >= 0 && val < 100) {
|
||||
int high = val / 10;
|
||||
int low = val % 10;
|
||||
return (high << 4) | low;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
{
|
||||
deepblu_device_t *device = (deepblu_device_t *)abstract;
|
||||
unsigned char result[1], data[6];
|
||||
dc_status_t status;
|
||||
size_t len;
|
||||
|
||||
data[0] = bcd(datetime->year - 2000);
|
||||
data[1] = bcd(datetime->month);
|
||||
data[2] = bcd(datetime->day);
|
||||
data[3] = bcd(datetime->hour);
|
||||
data[4] = bcd(datetime->minute);
|
||||
data[5] = bcd(datetime->second);
|
||||
|
||||
// Maybe also check that we received one zero byte (ack?)
|
||||
return deepblu_send_recv(device, CMD_SETTIME,
|
||||
data, sizeof(data),
|
||||
result, sizeof(result));
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_device_close (dc_device_t *abstract)
|
||||
{
|
||||
deepblu_device_t *device = (deepblu_device_t *) abstract;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static const char zero[MAX_DATA];
|
||||
|
||||
static dc_status_t
|
||||
deepblu_download_dive(deepblu_device_t *device, unsigned char nr, dc_dive_callback_t callback, void *userdata)
|
||||
{
|
||||
unsigned char header_len;
|
||||
unsigned char profilebytes[2];
|
||||
unsigned int profile_len;
|
||||
dc_status_t status;
|
||||
char header[256];
|
||||
unsigned char *profile;
|
||||
|
||||
status = deepblu_send_recv(device, CMD_GETDIVE, &nr, 1, &header_len, 1);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
status = deepblu_recv_bulk(device, RSP_DIVESTAT, header, header_len);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
memset(header + header_len, 0, 256 - header_len);
|
||||
|
||||
/* The header is the fingerprint. If we've already seen this header, we're done */
|
||||
if (memcmp(header, device->fingerprint, sizeof (device->fingerprint)) == 0)
|
||||
return DC_STATUS_DONE;
|
||||
|
||||
status = deepblu_send_recv(device, CMD_GETPROFILE, &nr, 1, profilebytes, sizeof(profilebytes));
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
profile_len = (profilebytes[0] << 8) | profilebytes[1];
|
||||
|
||||
profile = malloc(256 + profile_len);
|
||||
if (!profile) {
|
||||
ERROR (device->base.context, "Insufficient buffer space available.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
// We make the dive data be 256 bytes of header, followed by the profile data
|
||||
memcpy(profile, header, 256);
|
||||
|
||||
status = deepblu_recv_bulk(device, RSP_DIVEPROF, profile+256, profile_len);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (callback) {
|
||||
if (!callback(profile, profile_len+256, header, header_len, userdata))
|
||||
return DC_STATUS_DONE;
|
||||
}
|
||||
free(profile);
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
|
||||
{
|
||||
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
|
||||
deepblu_device_t *device = (deepblu_device_t *) abstract;
|
||||
unsigned char nrdives, val;
|
||||
dc_status_t status;
|
||||
int i;
|
||||
|
||||
val = 0;
|
||||
status = deepblu_send_recv(device, CMD_GETDIVENR, &val, 1, &nrdives, 1);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (!nrdives)
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
progress.maximum = nrdives;
|
||||
progress.current = 0;
|
||||
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
for (i = 1; i <= nrdives; i++) {
|
||||
if (device_is_cancelled(abstract)) {
|
||||
dc_status_set_error(&status, DC_STATUS_CANCELLED);
|
||||
break;
|
||||
}
|
||||
|
||||
status = deepblu_download_dive(device, i, callback, userdata);
|
||||
switch (status) {
|
||||
case DC_STATUS_DONE:
|
||||
i = nrdives;
|
||||
break;
|
||||
case DC_STATUS_SUCCESS:
|
||||
break;
|
||||
default:
|
||||
return status;
|
||||
}
|
||||
progress.current = i;
|
||||
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
548
src/deepblu_cosmiq.c
Normal file
548
src/deepblu_cosmiq.c
Normal file
@ -0,0 +1,548 @@
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2019 Linus Torvalds
|
||||
* Copyright (C) 2022 Jef Driesen
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "deepblu_cosmiq.h"
|
||||
#include "context-private.h"
|
||||
#include "device-private.h"
|
||||
#include "platform.h"
|
||||
#include "checksum.h"
|
||||
#include "array.h"
|
||||
|
||||
// Maximum data in a packet. It's actually much
|
||||
// less than this, since BLE packets are small and
|
||||
// with the 7 bytes of headers and final newline
|
||||
// and the HEX encoding, the actual maximum is
|
||||
// just something like 6 bytes.
|
||||
//
|
||||
// But in theory the data could be done over
|
||||
// multiple packets. That doesn't seem to be
|
||||
// the case in anything I've seen so far.
|
||||
//
|
||||
// Pick something small and easy to use for
|
||||
// stack buffers.
|
||||
#define MAX_DATA 20
|
||||
|
||||
#define SZ_HEADER 36
|
||||
|
||||
#define FP_OFFSET 6
|
||||
#define FP_SIZE 6
|
||||
|
||||
#define CMD_SET_DATETIME 0x20
|
||||
|
||||
#define CMD_DIVE_COUNT 0x40
|
||||
#define CMD_DIVE_HEADER 0x41
|
||||
#define CMD_DIVE_HEADER_DATA 0x42
|
||||
#define CMD_DIVE_PROFILE 0x43
|
||||
#define CMD_DIVE_PROFILE_DATA 0x44
|
||||
|
||||
#define CMD_SYSTEM_FW 0x58
|
||||
#define CMD_SYSTEM_MAC 0x5A
|
||||
|
||||
#define NSTEPS 1000
|
||||
#define STEP(i,n) (NSTEPS * (i) / (n))
|
||||
|
||||
typedef struct deepblu_cosmiq_device_t {
|
||||
dc_device_t base;
|
||||
dc_iostream_t *iostream;
|
||||
unsigned char fingerprint[FP_SIZE];
|
||||
} deepblu_cosmiq_device_t;
|
||||
|
||||
static dc_status_t deepblu_cosmiq_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
|
||||
static dc_status_t deepblu_cosmiq_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
|
||||
static dc_status_t deepblu_cosmiq_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime);
|
||||
|
||||
static const dc_device_vtable_t deepblu_cosmiq_device_vtable = {
|
||||
sizeof(deepblu_cosmiq_device_t),
|
||||
DC_FAMILY_DEEPBLU_COSMIQ,
|
||||
deepblu_cosmiq_device_set_fingerprint, /* set_fingerprint */
|
||||
NULL, /* read */
|
||||
NULL, /* write */
|
||||
NULL, /* dump */
|
||||
deepblu_cosmiq_device_foreach, /* foreach */
|
||||
deepblu_cosmiq_device_timesync, /* timesync */
|
||||
NULL, /* close */
|
||||
};
|
||||
|
||||
//
|
||||
// Send a cmd packet.
|
||||
//
|
||||
// The format of the cmd on the "wire" is:
|
||||
// - byte '#'
|
||||
// - HEX char of cmd
|
||||
// - HEX char two's complement modular sum of packet data (including cmd/size)
|
||||
// - HEX char size of data as encoded in HEX
|
||||
// - n * HEX char data
|
||||
// - byte '\n'
|
||||
// so you end up having 8 bytes of header/trailer overhead, and two bytes
|
||||
// for every byte of data sent due to the HEX encoding.
|
||||
//
|
||||
static dc_status_t
|
||||
deepblu_cosmiq_send (deepblu_cosmiq_device_t *device, const unsigned char cmd, const unsigned char data[], size_t size)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_device_t *abstract = (dc_device_t *) device;
|
||||
|
||||
if (size > MAX_DATA)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
if (device_is_cancelled (abstract))
|
||||
return DC_STATUS_CANCELLED;
|
||||
|
||||
// Build the packet.
|
||||
unsigned char csum = ~checksum_add_uint8 (data, size, cmd + 2 * size) + 1;
|
||||
unsigned char raw[3 + MAX_DATA] = {
|
||||
cmd,
|
||||
csum,
|
||||
2 * size
|
||||
};
|
||||
if (size) {
|
||||
memcpy (raw + 3, data, size);
|
||||
}
|
||||
unsigned char packet[1 + 2 * (3 + MAX_DATA) + 1] = {0};
|
||||
packet[0] = '#';
|
||||
array_convert_bin2hex (raw, 3 + size, packet + 1, 2 * (3 + size));
|
||||
packet[1 + 2 * (3 + size)] = '\n';
|
||||
|
||||
HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "cmd", raw, 3 + size);
|
||||
|
||||
// Send the command.
|
||||
status = dc_iostream_write (device->iostream, packet, 2 + 2 * (3 + size), NULL);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (device->base.context, "Failed to send the command.");
|
||||
return status;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Receive one 'line' of data
|
||||
//
|
||||
// The deepblu BLE protocol is ASCII line based and packetized.
|
||||
// Normally one packet is one line, but it looks like the Nordic
|
||||
// Semi BLE chip will sometimes send packets early (some internal
|
||||
// serial buffer timeout?) with incompete data.
|
||||
//
|
||||
// So read packets until you get newline.
|
||||
static dc_status_t
|
||||
deepblu_cosmiq_recv_line (deepblu_cosmiq_device_t *device, unsigned char data[], size_t size, size_t *actual)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
size_t nbytes = 0;
|
||||
|
||||
while (1) {
|
||||
unsigned char buffer[20] = {0};
|
||||
size_t transferred = 0;
|
||||
|
||||
status = dc_iostream_read (device->iostream, buffer, sizeof(buffer), &transferred);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (device->base.context, "Failed to receive the reply packet.");
|
||||
return status;
|
||||
}
|
||||
|
||||
if (transferred < 1) {
|
||||
ERROR (device->base.context, "Empty reply packet received.");
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
// Append the payload data to the output buffer. If the output
|
||||
// buffer is too small, the error is not reported immediately
|
||||
// but delayed until all packets have been received.
|
||||
if (nbytes < size) {
|
||||
size_t n = transferred;
|
||||
if (nbytes + n > size) {
|
||||
n = size - nbytes;
|
||||
}
|
||||
memcpy(data + nbytes, buffer, n);
|
||||
}
|
||||
nbytes += transferred;
|
||||
|
||||
// Last packet?
|
||||
if (buffer[transferred - 1] == '\n')
|
||||
break;
|
||||
}
|
||||
|
||||
// Verify the expected number of bytes.
|
||||
if (nbytes > size) {
|
||||
ERROR (device->base.context, "Unexpected number of bytes received (" DC_PRINTF_SIZE " " DC_PRINTF_SIZE ").", nbytes, size);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
if (actual)
|
||||
*actual = nbytes;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Receive a reply packet
|
||||
//
|
||||
// The reply packet has the same format as the cmd packet we
|
||||
// send, except the first byte is '$' instead of '#'.
|
||||
static dc_status_t
|
||||
deepblu_cosmiq_recv (deepblu_cosmiq_device_t *device, const unsigned char cmd, unsigned char data[], size_t size, size_t *actual)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
unsigned char packet[1 + 2 * (3 + MAX_DATA) + 1] = {0};
|
||||
|
||||
size_t transferred = 0;
|
||||
status = deepblu_cosmiq_recv_line (device, packet, sizeof(packet), &transferred);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (transferred < 8 || (transferred % 2) != 0) {
|
||||
ERROR (device->base.context, "Unexpected packet length (" DC_PRINTF_SIZE ").", transferred);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
if (packet[0] != '$' || packet[transferred - 1] != '\n') {
|
||||
ERROR (device->base.context, "Unexpected packet start/end byte.");
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
size_t length = transferred - 2;
|
||||
|
||||
unsigned char raw[3 + MAX_DATA] = {0};
|
||||
if (array_convert_hex2bin (packet + 1, length, raw, length / 2) != 0) {
|
||||
ERROR (device->base.context, "Unexpected packet data.");
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
length /= 2;
|
||||
|
||||
HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "rcv", raw, length);
|
||||
|
||||
unsigned char rsp = raw[0];
|
||||
if (rsp != cmd) {
|
||||
ERROR (device->base.context, "Unexpected packet command byte (%02x)", rsp);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
unsigned int n = raw[2];
|
||||
if ((n % 2) != 0 || n != transferred - 8) {
|
||||
ERROR (device->base.context, "Unexpected packet length (%u)", n);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
unsigned char csum = checksum_add_uint8 (raw, length, 0);
|
||||
if (csum != 0) {
|
||||
ERROR (device->base.context, "Unexpected packet checksum (%02x).", csum);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
length -= 3;
|
||||
|
||||
if (length > size) {
|
||||
ERROR (device->base.context, "Unexpected number of bytes received (" DC_PRINTF_SIZE " " DC_PRINTF_SIZE ").", length, size);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
memcpy (data, raw + 3, length);
|
||||
|
||||
if (actual)
|
||||
*actual = length;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_cosmiq_transfer (deepblu_cosmiq_device_t *device, const unsigned char cmd,
|
||||
const unsigned char input[], size_t isize,
|
||||
unsigned char output[], size_t osize)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
|
||||
status = deepblu_cosmiq_send (device, cmd, input, isize);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
size_t transferred = 0;
|
||||
status = deepblu_cosmiq_recv (device, cmd, output, osize, &transferred);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (transferred != osize) {
|
||||
ERROR (device->base.context, "Unexpected number of bytes received (" DC_PRINTF_SIZE " " DC_PRINTF_SIZE ").", osize, transferred);
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_cosmiq_recv_bulk (deepblu_cosmiq_device_t *device, dc_event_progress_t *progress, const unsigned char cmd, unsigned char data[], size_t size)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
dc_device_t *abstract = (dc_device_t *) device;
|
||||
|
||||
const unsigned int initial = progress ? progress->current : 0;
|
||||
|
||||
size_t nbytes = 0;
|
||||
while (nbytes < size) {
|
||||
size_t transferred = 0;
|
||||
status = deepblu_cosmiq_recv (device, cmd, data + nbytes, size - nbytes, &transferred);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
nbytes += transferred;
|
||||
|
||||
// Update and emit a progress event.
|
||||
if (progress) {
|
||||
progress->current = initial + STEP(nbytes, size);
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
|
||||
}
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
deepblu_cosmiq_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
deepblu_cosmiq_device_t *device = NULL;
|
||||
|
||||
if (out == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
// Allocate memory.
|
||||
device = (deepblu_cosmiq_device_t *) dc_device_allocate (context, &deepblu_cosmiq_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));
|
||||
|
||||
// Set the timeout for receiving data (1000ms).
|
||||
status = dc_iostream_set_timeout (device->iostream, 1000);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (context, "Failed to set the timeout.");
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
|
||||
|
||||
*out = (dc_device_t *) device;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
error_free:
|
||||
dc_device_deallocate ((dc_device_t *) device);
|
||||
return status;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_cosmiq_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
|
||||
{
|
||||
deepblu_cosmiq_device_t *device = (deepblu_cosmiq_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
|
||||
deepblu_cosmiq_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
deepblu_cosmiq_device_t *device = (deepblu_cosmiq_device_t *) abstract;
|
||||
const unsigned char zero = 0;
|
||||
|
||||
// Enable progress notifications.
|
||||
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
// Read the firmware version.
|
||||
unsigned char fw[1] = {0};
|
||||
status = deepblu_cosmiq_transfer (device, CMD_SYSTEM_FW, &zero, 1, fw, sizeof(fw));
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the firmware version.");
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
// Read the MAC address.
|
||||
unsigned char mac[6] = {0};
|
||||
status = deepblu_cosmiq_transfer (device, CMD_SYSTEM_MAC, &zero, 1, mac, sizeof(mac));
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the MAC address.");
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
// Emit a device info event.
|
||||
dc_event_devinfo_t devinfo;
|
||||
devinfo.model = 0;
|
||||
devinfo.firmware = fw[0] & 0x3F;
|
||||
devinfo.serial = array_uint32_le (mac);
|
||||
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
|
||||
|
||||
unsigned char ndives = 0;
|
||||
status = deepblu_cosmiq_transfer (device, CMD_DIVE_COUNT, &zero, 1, &ndives, 1);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the number of dives.");
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
// Update and emit a progress event.
|
||||
progress.current = (ndives == 0 ? 1 : 0) * NSTEPS;
|
||||
progress.maximum = (ndives + 1) * NSTEPS;
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
if (ndives == 0) {
|
||||
status = DC_STATUS_SUCCESS;
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
unsigned char *headers = malloc (ndives * SZ_HEADER);
|
||||
if (headers == NULL) {
|
||||
ERROR (abstract->context, "Failed to allocate memory.");
|
||||
status = DC_STATUS_NOMEMORY;
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
unsigned int count = 0;
|
||||
for (unsigned int i = 0; i < ndives; ++i) {
|
||||
unsigned char number = i + 1;
|
||||
|
||||
unsigned char len = 0;
|
||||
status = deepblu_cosmiq_transfer (device, CMD_DIVE_HEADER, &number, 1, &len, 1);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the dive header.");
|
||||
goto error_free_headers;
|
||||
}
|
||||
|
||||
if (len != SZ_HEADER) {
|
||||
status = DC_STATUS_PROTOCOL;
|
||||
goto error_free_headers;
|
||||
}
|
||||
|
||||
status = deepblu_cosmiq_recv_bulk (device, NULL, CMD_DIVE_HEADER_DATA, headers + i * SZ_HEADER, SZ_HEADER);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the dive header.");
|
||||
goto error_free_headers;
|
||||
}
|
||||
|
||||
// Update and emit a progress event.
|
||||
progress.current = STEP(i + 1, ndives);
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
if (memcmp (headers + i * SZ_HEADER + FP_OFFSET, device->fingerprint, sizeof (device->fingerprint)) == 0)
|
||||
break;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
// Update and emit a progress event.
|
||||
progress.current = 1 * NSTEPS;
|
||||
progress.maximum = (count + 1) * NSTEPS;
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
// Allocate memory for the dive.
|
||||
dc_buffer_t *dive = dc_buffer_new (4096);
|
||||
if (dive == NULL) {
|
||||
ERROR (abstract->context, "Failed to allocate memory.");
|
||||
status = DC_STATUS_NOMEMORY;
|
||||
goto error_free_headers;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < count; ++i) {
|
||||
unsigned char number = i + 1;
|
||||
|
||||
unsigned char rsp_len[2] = {0};
|
||||
status = deepblu_cosmiq_transfer (device, CMD_DIVE_PROFILE, &number, 1, rsp_len, sizeof(rsp_len));
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the dive profile.");
|
||||
goto error_free_dive;
|
||||
}
|
||||
|
||||
unsigned int len = array_uint16_be (rsp_len);
|
||||
|
||||
// Erase the buffer.
|
||||
dc_buffer_clear (dive);
|
||||
|
||||
// Allocate memory for the dive.
|
||||
if (!dc_buffer_resize (dive, len + SZ_HEADER)) {
|
||||
ERROR (abstract->context, "Failed to allocate memory.");
|
||||
status = DC_STATUS_NOMEMORY;
|
||||
goto error_free_dive;
|
||||
}
|
||||
|
||||
// Cache the pointer.
|
||||
unsigned char *data = dc_buffer_get_data (dive);
|
||||
|
||||
memcpy (data, headers + i * SZ_HEADER, SZ_HEADER);
|
||||
|
||||
status = deepblu_cosmiq_recv_bulk (device, &progress, CMD_DIVE_PROFILE_DATA, data + SZ_HEADER, len);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the dive profile.");
|
||||
goto error_free_dive;
|
||||
}
|
||||
|
||||
if (callback && !callback (data, len + SZ_HEADER, data + FP_OFFSET, FP_SIZE, userdata))
|
||||
break;
|
||||
}
|
||||
|
||||
error_free_dive:
|
||||
dc_buffer_free (dive);
|
||||
error_free_headers:
|
||||
free (headers);
|
||||
error_exit:
|
||||
return status;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_cosmiq_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
deepblu_cosmiq_device_t *device = (deepblu_cosmiq_device_t *) abstract;
|
||||
|
||||
if (datetime->year < 2000) {
|
||||
ERROR (abstract->context, "Invalid date/time value specified.");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
const unsigned char cmd[6] = {
|
||||
dec2bcd(datetime->year - 2000),
|
||||
dec2bcd(datetime->month),
|
||||
dec2bcd(datetime->day),
|
||||
dec2bcd(datetime->hour),
|
||||
dec2bcd(datetime->minute),
|
||||
dec2bcd(datetime->second)};
|
||||
unsigned char rsp[1] = {0};
|
||||
status = deepblu_cosmiq_transfer (device, CMD_SET_DATETIME,
|
||||
cmd, sizeof(cmd), rsp, sizeof(rsp));
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Deepblu Cosmiq+ downloading/parsing
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2018 Linus Torvalds
|
||||
* Copyright (C) 2022 Jef Driesen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -19,8 +20,8 @@
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef DEEPBLU_H
|
||||
#define DEEPBLU_H
|
||||
#ifndef DEEPBLU_COSMIQ_H
|
||||
#define DEEPBLU_COSMIQ_H
|
||||
|
||||
#include <libdivecomputer/context.h>
|
||||
#include <libdivecomputer/iostream.h>
|
||||
@ -32,12 +33,12 @@ extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
dc_status_t
|
||||
deepblu_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
||||
deepblu_cosmiq_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
|
||||
|
||||
dc_status_t
|
||||
deepblu_parser_create (dc_parser_t **parser, dc_context_t *context);
|
||||
deepblu_cosmiq_parser_create (dc_parser_t **parser, dc_context_t *context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* DEEPBLU_H */
|
||||
#endif /* DEEPBLU_COSMIQ_H */
|
||||
218
src/deepblu_cosmiq_parser.c
Normal file
218
src/deepblu_cosmiq_parser.c
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2019 Linus Torvalds
|
||||
* Copyright (C) 2022 Jef Driesen
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
|
||||
#include <libdivecomputer/units.h>
|
||||
|
||||
#include "deepblu_cosmiq.h"
|
||||
#include "context-private.h"
|
||||
#include "parser-private.h"
|
||||
#include "array.h"
|
||||
|
||||
#define SCUBA 2
|
||||
#define GAUGE 3
|
||||
#define FREEDIVE 4
|
||||
|
||||
#define SZ_HEADER 36
|
||||
#define SZ_SAMPLE 4
|
||||
|
||||
typedef struct deepblu_cosmiq_parser_t {
|
||||
dc_parser_t base;
|
||||
double hydrostatic;
|
||||
} deepblu_cosmiq_parser_t;
|
||||
|
||||
static dc_status_t deepblu_cosmiq_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
static dc_status_t deepblu_cosmiq_parser_set_density (dc_parser_t *abstract, double density);
|
||||
static dc_status_t deepblu_cosmiq_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
|
||||
static dc_status_t deepblu_cosmiq_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
|
||||
static dc_status_t deepblu_cosmiq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
|
||||
|
||||
static const dc_parser_vtable_t deepblu_cosmiq_parser_vtable = {
|
||||
sizeof(deepblu_cosmiq_parser_t),
|
||||
DC_FAMILY_DEEPBLU_COSMIQ,
|
||||
deepblu_cosmiq_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
deepblu_cosmiq_parser_set_density, /* set_density */
|
||||
deepblu_cosmiq_parser_get_datetime, /* datetime */
|
||||
deepblu_cosmiq_parser_get_field, /* fields */
|
||||
deepblu_cosmiq_parser_samples_foreach, /* samples_foreach */
|
||||
NULL /* destroy */
|
||||
};
|
||||
|
||||
dc_status_t
|
||||
deepblu_cosmiq_parser_create (dc_parser_t **out, dc_context_t *context)
|
||||
{
|
||||
deepblu_cosmiq_parser_t *parser = NULL;
|
||||
|
||||
if (out == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
// Allocate memory.
|
||||
parser = (deepblu_cosmiq_parser_t *) dc_parser_allocate (context, &deepblu_cosmiq_parser_vtable);
|
||||
if (parser == NULL) {
|
||||
ERROR (context, "Failed to allocate memory.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
// Set the default values.
|
||||
parser->hydrostatic = DEF_DENSITY_SALT * GRAVITY;
|
||||
|
||||
*out = (dc_parser_t *) parser;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_cosmiq_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
|
||||
{
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_cosmiq_parser_set_density (dc_parser_t *abstract, double density)
|
||||
{
|
||||
deepblu_cosmiq_parser_t *parser = (deepblu_cosmiq_parser_t *) abstract;
|
||||
|
||||
parser->hydrostatic = density * GRAVITY;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_cosmiq_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
{
|
||||
const unsigned char *data = abstract->data;
|
||||
unsigned int size = abstract->size;
|
||||
|
||||
if (size < SZ_HEADER)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
if (datetime) {
|
||||
datetime->year = array_uint16_le(data + 6);
|
||||
datetime->day = data[8];
|
||||
datetime->month = data[9];
|
||||
datetime->minute = data[10];
|
||||
datetime->hour = data[11];
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_cosmiq_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||
{
|
||||
deepblu_cosmiq_parser_t *parser = (deepblu_cosmiq_parser_t *) abstract;
|
||||
const unsigned char *data = abstract->data;
|
||||
unsigned int size = abstract->size;
|
||||
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
|
||||
if (size < SZ_HEADER)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int mode = data[2];
|
||||
unsigned int atmospheric = array_uint16_le (data + 4) & 0x1FFF;
|
||||
|
||||
if (value) {
|
||||
switch (type) {
|
||||
case DC_FIELD_DIVETIME:
|
||||
if (mode == SCUBA || mode == GAUGE)
|
||||
*((unsigned int *) value) = array_uint16_le(data + 12) * 60;
|
||||
else
|
||||
*((unsigned int *) value) = array_uint16_le(data + 12);
|
||||
break;
|
||||
case DC_FIELD_MAXDEPTH:
|
||||
*((double *) value) = (signed int) (array_uint16_le (data + 22) - atmospheric) * (BAR / 1000.0) / parser->hydrostatic;
|
||||
break;
|
||||
case DC_FIELD_GASMIX_COUNT:
|
||||
*((unsigned int *) value) = mode == SCUBA;
|
||||
break;
|
||||
case DC_FIELD_GASMIX:
|
||||
gasmix->oxygen = data[3] / 100.0;
|
||||
gasmix->helium = 0.0;
|
||||
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
|
||||
break;
|
||||
case DC_FIELD_DIVEMODE:
|
||||
switch (mode) {
|
||||
case SCUBA:
|
||||
*((dc_divemode_t *) value) = DC_DIVEMODE_OC;
|
||||
break;
|
||||
case GAUGE:
|
||||
*((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE;
|
||||
break;
|
||||
case FREEDIVE:
|
||||
*((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE;
|
||||
break;
|
||||
default:
|
||||
ERROR (abstract->context, "Unknown activity type '%02x'", mode);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_ATMOSPHERIC:
|
||||
*((double *) value) = atmospheric / 1000.0;
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_cosmiq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
deepblu_cosmiq_parser_t *parser = (deepblu_cosmiq_parser_t *) abstract;
|
||||
const unsigned char *data = abstract->data;
|
||||
unsigned int size = abstract->size;
|
||||
|
||||
if (size < SZ_HEADER)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int interval = data[26];
|
||||
unsigned int atmospheric = array_uint16_le (data + 4) & 0x1FFF;
|
||||
|
||||
unsigned int time = 0;
|
||||
unsigned int offset = SZ_HEADER;
|
||||
while (offset + SZ_SAMPLE <= size) {
|
||||
dc_sample_value_t sample = {0};
|
||||
unsigned int temperature = array_uint16_le(data + offset + 0);
|
||||
unsigned int depth = array_uint16_le(data + offset + 2);
|
||||
offset += SZ_SAMPLE;
|
||||
|
||||
time += interval;
|
||||
sample.time = time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
sample.depth = (signed int) (depth - atmospheric) * (BAR / 1000.0) / parser->hydrostatic;
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
|
||||
sample.temperature = temperature / 10.0;
|
||||
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
@ -1,245 +0,0 @@
|
||||
/*
|
||||
* Deeplu Cosmiq+ parsing
|
||||
*
|
||||
* Copyright (C) 2019 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "deepblu.h"
|
||||
#include "context-private.h"
|
||||
#include "parser-private.h"
|
||||
#include "array.h"
|
||||
#include "field-cache.h"
|
||||
|
||||
#define MAXFIELDS 128
|
||||
|
||||
struct msg_desc;
|
||||
|
||||
typedef struct deepblu_parser_t {
|
||||
dc_parser_t base;
|
||||
|
||||
dc_sample_callback_t callback;
|
||||
void *userdata;
|
||||
|
||||
// 20 sec for scuba, 1 sec for freedives
|
||||
int sample_interval;
|
||||
|
||||
// Common fields
|
||||
struct dc_field_cache cache;
|
||||
} deepblu_parser_t;
|
||||
|
||||
static dc_status_t deepblu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
static dc_status_t deepblu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
|
||||
static dc_status_t deepblu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
|
||||
static dc_status_t deepblu_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
|
||||
|
||||
static const dc_parser_vtable_t deepblu_parser_vtable = {
|
||||
sizeof(deepblu_parser_t),
|
||||
DC_FAMILY_DEEPBLU,
|
||||
deepblu_parser_set_data, /* set_data */
|
||||
deepblu_parser_get_datetime, /* datetime */
|
||||
deepblu_parser_get_field, /* fields */
|
||||
deepblu_parser_samples_foreach, /* samples_foreach */
|
||||
NULL /* destroy */
|
||||
};
|
||||
|
||||
dc_status_t
|
||||
deepblu_parser_create (dc_parser_t **out, dc_context_t *context)
|
||||
{
|
||||
deepblu_parser_t *parser = NULL;
|
||||
|
||||
if (out == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
// Allocate memory.
|
||||
parser = (deepblu_parser_t *) dc_parser_allocate (context, &deepblu_parser_vtable);
|
||||
if (parser == NULL) {
|
||||
ERROR (context, "Failed to allocate memory.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
*out = (dc_parser_t *) parser;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static double
|
||||
pressure_to_depth(unsigned int mbar)
|
||||
{
|
||||
// Specific weight of seawater (millibar to cm)
|
||||
const double specific_weight = 1.024 * 0.980665;
|
||||
|
||||
// Absolute pressure, subtract surface pressure
|
||||
if (mbar < 1013)
|
||||
return 0.0;
|
||||
mbar -= 1013;
|
||||
return mbar / specific_weight / 100.0;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
|
||||
{
|
||||
deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract;
|
||||
const unsigned char *hdr = data;
|
||||
const unsigned char *profile = data + 256;
|
||||
unsigned int divetime, maxpressure;
|
||||
dc_gasmix_t gasmix = {0, };
|
||||
|
||||
if (size < 256)
|
||||
return DC_STATUS_IO;
|
||||
|
||||
deepblu->callback = NULL;
|
||||
deepblu->userdata = NULL;
|
||||
memset(&deepblu->cache, 0, sizeof(deepblu->cache));
|
||||
|
||||
// LE16 at 0 is 'dive number'
|
||||
|
||||
// LE16 at 12 is the dive time
|
||||
// It's in seconds for freedives, minutes for scuba/gauge
|
||||
divetime = hdr[12] + 256*hdr[13];
|
||||
|
||||
// Byte at 2 is 'activity type' (2 = scuba, 3 = gauge, 4 = freedive)
|
||||
// Byte at 3 is O2 percentage
|
||||
switch (data[2]) {
|
||||
case 2:
|
||||
// SCUBA - divetime in minutes
|
||||
divetime *= 60;
|
||||
gasmix.oxygen = data[3] / 100.0;
|
||||
DC_ASSIGN_IDX(deepblu->cache, GASMIX, 0, gasmix);
|
||||
DC_ASSIGN_FIELD(deepblu->cache, GASMIX_COUNT, 1);
|
||||
DC_ASSIGN_FIELD(deepblu->cache, DIVEMODE, DC_DIVEMODE_OC);
|
||||
break;
|
||||
case 3:
|
||||
// GAUGE - divetime in minutes
|
||||
divetime *= 60;
|
||||
DC_ASSIGN_FIELD(deepblu->cache, DIVEMODE, DC_DIVEMODE_GAUGE);
|
||||
break;
|
||||
case 4:
|
||||
// FREEDIVE - divetime in seconds
|
||||
DC_ASSIGN_FIELD(deepblu->cache, DIVEMODE, DC_DIVEMODE_FREEDIVE);
|
||||
deepblu->sample_interval = 1;
|
||||
break;
|
||||
default:
|
||||
ERROR (abstract->context, "Deepblu: unknown activity type '%02x'", data[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
// Seems to be fixed at 20s for scuba, 1s for freedive
|
||||
deepblu->sample_interval = hdr[26];
|
||||
|
||||
maxpressure = hdr[22] + 256*hdr[23]; // Maxpressure in millibar
|
||||
|
||||
DC_ASSIGN_FIELD(deepblu->cache, DIVETIME, divetime);
|
||||
DC_ASSIGN_FIELD(deepblu->cache, MAXDEPTH, pressure_to_depth(maxpressure));
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// The layout of the header in the 'data' is
|
||||
// 0: LE16 dive number
|
||||
// 2: dive type byte?
|
||||
// 3: O2 percentage byte
|
||||
// 4: unknown
|
||||
// 5: unknown
|
||||
// 6: LE16 year
|
||||
// 8: day of month
|
||||
// 9: month
|
||||
// 10: minute
|
||||
// 11: hour
|
||||
// 12: LE16 dive time
|
||||
// 14: LE16 ??
|
||||
// 16: LE16 surface pressure?
|
||||
// 18: LE16 ??
|
||||
// 20: LE16 ??
|
||||
// 22: LE16 max depth pressure
|
||||
// 24: LE16 water temp
|
||||
// 26: LE16 ??
|
||||
// 28: LE16 ??
|
||||
// 30: LE16 ??
|
||||
// 32: LE16 ??
|
||||
// 34: LE16 ??
|
||||
static dc_status_t
|
||||
deepblu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
{
|
||||
deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract;
|
||||
const unsigned char *data = deepblu->base.data;
|
||||
int len = deepblu->base.size;
|
||||
|
||||
if (len < 256)
|
||||
return DC_STATUS_IO;
|
||||
datetime->year = data[6] + (data[7] << 8);
|
||||
datetime->day = data[8];
|
||||
datetime->month = data[9];
|
||||
datetime->minute = data[10];
|
||||
datetime->hour = data[11];
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||
{
|
||||
deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract;
|
||||
|
||||
if (!value)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
return dc_field_get(&deepblu->cache, type, flags, value);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract;
|
||||
const unsigned char *data = deepblu->base.data;
|
||||
int len = deepblu->base.size, i;
|
||||
|
||||
deepblu->callback = callback;
|
||||
deepblu->userdata = userdata;
|
||||
|
||||
// Skip the header information
|
||||
if (len < 256)
|
||||
return DC_STATUS_IO;
|
||||
data += 256;
|
||||
len -= 256;
|
||||
|
||||
// The rest should be samples every 20s with temperature and depth
|
||||
for (i = 0; i < len/4; i++) {
|
||||
dc_sample_value_t sample = {0};
|
||||
unsigned int temp = data[0]+256*data[1];
|
||||
unsigned int pressure = data[2]+256*data[3];
|
||||
|
||||
data += 4;
|
||||
sample.time = (i+1)*deepblu->sample_interval;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
sample.depth = pressure_to_depth(pressure);
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
|
||||
sample.temperature = temp / 10.0;
|
||||
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
@ -32,7 +32,8 @@
|
||||
|
||||
#define MAXPACKET 255
|
||||
|
||||
#define HEADERSIZE 156
|
||||
#define HEADERSIZE_MIN 128
|
||||
#define HEADERSIZE_V0 156
|
||||
|
||||
#define NSTEPS 1000
|
||||
#define STEP(i,n) (NSTEPS * (i) / (n))
|
||||
@ -58,6 +59,11 @@
|
||||
#define GRP_DIVE 0xC0
|
||||
#define CMD_DIVE_HEADER 0x02
|
||||
#define CMD_DIVE_PROFILE 0x03
|
||||
#define CMD_DIVE_COUNT 0x05
|
||||
#define CMD_DIVE_INDEX_LAST 0x06
|
||||
#define CMD_DIVE_INDEX_FIRST 0x07
|
||||
#define CMD_DIVE_INDEX_PREVIOUS 0x08
|
||||
#define CMD_DIVE_INDEX_NEXT 0x09
|
||||
|
||||
typedef struct deepsix_excursion_device_t {
|
||||
dc_device_t base;
|
||||
@ -163,7 +169,9 @@ deepsix_excursion_recv (deepsix_excursion_device_t *device, unsigned char grp, u
|
||||
return DC_STATUS_PROTOCOL;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
memcpy(data, packet + 4, len);
|
||||
}
|
||||
|
||||
if (actual)
|
||||
*actual = len;
|
||||
@ -214,8 +222,8 @@ deepsix_excursion_device_open (dc_device_t **out, dc_context_t *context, dc_iost
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
// Set the timeout for receiving data (1000ms).
|
||||
status = dc_iostream_set_timeout (device->iostream, 1000);
|
||||
// Set the timeout for receiving data (3000ms).
|
||||
status = dc_iostream_set_timeout (device->iostream, 3000);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (context, "Failed to set the timeout.");
|
||||
goto error_free;
|
||||
@ -298,20 +306,45 @@ deepsix_excursion_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
devinfo.serial = array_convert_str2num (rsp_serial + 3, sizeof(rsp_serial) - 3);
|
||||
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
|
||||
|
||||
// Firmware version 6+ uses the new commands.
|
||||
unsigned int fw6 = memcmp(rsp_software, "D01", 3) == 0 && rsp_software[4] >= '6';
|
||||
|
||||
unsigned int ndives = 0, last = 0;
|
||||
if (fw6) {
|
||||
// Read the number of dives.
|
||||
unsigned char rsp_ndives[2] = {0};
|
||||
status = deepsix_excursion_transfer (device, GRP_DIVE, CMD_DIVE_COUNT, DIR_READ, NULL, 0, rsp_ndives, sizeof(rsp_ndives), NULL);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the number of dives");
|
||||
return status;
|
||||
}
|
||||
|
||||
// Read the index of the last dive.
|
||||
const unsigned char cmd_index[2] = {0};
|
||||
unsigned char rsp_index[2] = {0};
|
||||
status = deepsix_excursion_transfer (device, GRP_INFO, CMD_INFO_LASTDIVE, DIR_READ, cmd_index, sizeof(cmd_index), rsp_index, sizeof(rsp_index), NULL);
|
||||
unsigned char rsp_last[4] = {0};
|
||||
status = deepsix_excursion_transfer (device, GRP_DIVE, CMD_DIVE_INDEX_LAST, DIR_READ, NULL, 0, rsp_last, sizeof(rsp_last), NULL);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the last dive index.");
|
||||
return status;
|
||||
}
|
||||
|
||||
// Calculate the number of dives.
|
||||
unsigned int ndives = array_uint16_le (rsp_index);
|
||||
ndives = array_uint16_le (rsp_ndives);
|
||||
last = array_uint16_le (rsp_last);
|
||||
} else {
|
||||
// Read the index of the last dive.
|
||||
const unsigned char cmd_last[2] = {0};
|
||||
unsigned char rsp_last[2] = {0};
|
||||
status = deepsix_excursion_transfer (device, GRP_INFO, CMD_INFO_LASTDIVE, DIR_READ, cmd_last, sizeof(cmd_last), rsp_last, sizeof(rsp_last), NULL);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the last dive index.");
|
||||
return status;
|
||||
}
|
||||
|
||||
ndives = last = array_uint16_le (rsp_last);
|
||||
}
|
||||
|
||||
// Update and emit a progress event.
|
||||
progress.maximum = ndives * NSTEPS;
|
||||
progress.current = 1 * NSTEPS;
|
||||
progress.maximum = (ndives + 1) * NSTEPS;
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
dc_buffer_t *buffer = dc_buffer_new(0);
|
||||
@ -320,32 +353,58 @@ deepsix_excursion_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
unsigned int number = last;
|
||||
for (unsigned int i = 0; i < ndives; ++i) {
|
||||
unsigned int number = ndives - i;
|
||||
if (fw6) {
|
||||
if (i > 0) {
|
||||
const unsigned char cmd_previous[] = {
|
||||
(number ) & 0xFF,
|
||||
(number >> 8) & 0xFF,
|
||||
(number >> 16) & 0xFF,
|
||||
(number >> 24) & 0xFF};
|
||||
unsigned char rsp_previous[4] = {0};
|
||||
status = deepsix_excursion_transfer (device, GRP_DIVE, CMD_DIVE_INDEX_PREVIOUS, DIR_READ,
|
||||
cmd_previous, sizeof(cmd_previous), rsp_previous, sizeof(rsp_previous), NULL);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the previous dive index");
|
||||
return status;
|
||||
}
|
||||
number = array_uint32_le (rsp_previous);
|
||||
}
|
||||
} else {
|
||||
number = ndives - i;
|
||||
}
|
||||
|
||||
unsigned int headersize = 0;
|
||||
const unsigned char cmd_header[] = {
|
||||
(number ) & 0xFF,
|
||||
(number >> 8) & 0xFF};
|
||||
unsigned char rsp_header[HEADERSIZE] = {0};
|
||||
status = deepsix_excursion_transfer (device, GRP_DIVE, CMD_DIVE_HEADER, DIR_READ, cmd_header, sizeof(cmd_header), rsp_header, sizeof(rsp_header), NULL);
|
||||
unsigned char rsp_header[MAXPACKET] = {0};
|
||||
status = deepsix_excursion_transfer (device, GRP_DIVE, CMD_DIVE_HEADER, DIR_READ,
|
||||
cmd_header, sizeof(cmd_header), rsp_header, sizeof(rsp_header), &headersize);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the dive header.");
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
if (headersize < HEADERSIZE_MIN || headersize != (fw6 ? rsp_header[2] : HEADERSIZE_V0)) {
|
||||
ERROR (abstract->context, "Unexpected size of the dive header (%u).", headersize);
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
if (memcmp(rsp_header + FP_OFFSET, device->fingerprint, sizeof(device->fingerprint)) == 0)
|
||||
break;
|
||||
|
||||
unsigned int length = array_uint32_le (rsp_header + 8);
|
||||
|
||||
// Update and emit a progress event.
|
||||
progress.current = i * NSTEPS + STEP(sizeof(rsp_header), sizeof(rsp_header) + length);
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
progress.current = (i + 1) * NSTEPS + STEP(headersize, headersize + length);
|
||||
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
dc_buffer_clear(buffer);
|
||||
dc_buffer_reserve(buffer, sizeof(rsp_header) + length);
|
||||
dc_buffer_reserve(buffer, headersize + length);
|
||||
|
||||
if (!dc_buffer_append(buffer, rsp_header, sizeof(rsp_header))) {
|
||||
if (!dc_buffer_append(buffer, rsp_header, headersize)) {
|
||||
ERROR (abstract->context, "Insufficient buffer space available.");
|
||||
status = DC_STATUS_NOMEMORY;
|
||||
goto error_free;
|
||||
@ -362,7 +421,8 @@ deepsix_excursion_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
(offset >> 16) & 0xFF,
|
||||
(offset >> 24) & 0xFF};
|
||||
unsigned char rsp_profile[MAXPACKET] = {0};
|
||||
status = deepsix_excursion_transfer (device, GRP_DIVE, CMD_DIVE_PROFILE, DIR_READ, cmd_profile, sizeof(cmd_profile), rsp_profile, sizeof(rsp_profile), &len);
|
||||
status = deepsix_excursion_transfer (device, GRP_DIVE, CMD_DIVE_PROFILE, DIR_READ,
|
||||
cmd_profile, sizeof(cmd_profile), rsp_profile, sizeof(rsp_profile), &len);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the dive profile.");
|
||||
goto error_free;
|
||||
@ -375,8 +435,8 @@ deepsix_excursion_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
}
|
||||
|
||||
// Update and emit a progress event.
|
||||
progress.current = i * NSTEPS + STEP(sizeof(rsp_header) + offset + n, sizeof(rsp_header) + length);
|
||||
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
|
||||
progress.current = (i + 1) * NSTEPS + STEP(headersize + offset + n, headersize + length);
|
||||
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
if (!dc_buffer_append(buffer, rsp_profile, n)) {
|
||||
ERROR (abstract->context, "Insufficient buffer space available.");
|
||||
@ -405,7 +465,7 @@ deepsix_excursion_device_timesync (dc_device_t *abstract, const dc_datetime_t *d
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
deepsix_excursion_device_t *device = (deepsix_excursion_device_t *) abstract;
|
||||
|
||||
if (datetime == NULL || datetime->year < 2000) {
|
||||
if (datetime->year < 2000) {
|
||||
ERROR (abstract->context, "Invalid date/time value specified.");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
@ -31,7 +31,11 @@
|
||||
#include "parser-private.h"
|
||||
#include "array.h"
|
||||
|
||||
#define HEADERSIZE 156
|
||||
#define HEADERSIZE_MIN 128
|
||||
|
||||
#define MAX_SAMPLES 7
|
||||
#define MAX_EVENTS 7
|
||||
#define MAX_GASMIXES 20
|
||||
|
||||
#define ALARM 0x0001
|
||||
#define TEMPERATURE 0x0002
|
||||
@ -39,27 +43,147 @@
|
||||
#define CEILING 0x0004
|
||||
#define CNS 0x0005
|
||||
|
||||
#define DENSITY 1024.0
|
||||
#define SAMPLE_TEMPERATURE 0
|
||||
#define SAMPLE_DECO_NDL 1
|
||||
#define SAMPLE_CNS 2
|
||||
|
||||
#define EVENT_CHANGE_GAS 7
|
||||
#define EVENT_ALARMS 8
|
||||
#define EVENT_CHANGE_SETPOINT 9
|
||||
#define EVENT_SAMPLES_MISSED 10
|
||||
#define EVENT_RESERVED 15
|
||||
|
||||
#define ALARM_ASCENTRATE 0
|
||||
#define ALARM_CEILING 1
|
||||
#define ALARM_PO2 2
|
||||
#define ALARM_MAXDEPTH 3
|
||||
#define ALARM_DIVETIME 4
|
||||
#define ALARM_CNS 5
|
||||
|
||||
#define DECOSTOP 0x02
|
||||
#define SAFETYSTOP 0x04
|
||||
|
||||
#define DENSITY 1024
|
||||
|
||||
#define UNDEFINED 0xFFFFFFFF
|
||||
|
||||
#define FWVERSION(major,minor) ( \
|
||||
((((major) + '0') & 0xFF) << 8) | \
|
||||
((minor) & 0xFF))
|
||||
|
||||
typedef struct deepsix_excursion_layout_t {
|
||||
unsigned int headersize;
|
||||
unsigned int version;
|
||||
unsigned int divemode;
|
||||
unsigned int samplerate;
|
||||
unsigned int salinity;
|
||||
unsigned int datetime;
|
||||
unsigned int divetime;
|
||||
unsigned int maxdepth;
|
||||
unsigned int temperature_min;
|
||||
unsigned int avgdepth;
|
||||
unsigned int firmware;
|
||||
unsigned int temperature_surf;
|
||||
unsigned int atmospheric;
|
||||
unsigned int gf;
|
||||
} deepsix_excursion_layout_t;
|
||||
|
||||
typedef struct deepsix_excursion_gasmix_t {
|
||||
unsigned int id;
|
||||
unsigned int oxygen;
|
||||
unsigned int helium;
|
||||
} deepsix_excursion_gasmix_t;
|
||||
|
||||
typedef struct deepsix_excursion_sample_info_t {
|
||||
unsigned int type;
|
||||
unsigned int divisor;
|
||||
unsigned int size;
|
||||
} deepsix_excursion_sample_info_t;
|
||||
|
||||
typedef struct deepsix_excursion_event_info_t {
|
||||
unsigned int type;
|
||||
unsigned int size;
|
||||
} deepsix_excursion_event_info_t;
|
||||
|
||||
typedef struct deepsix_excursion_parser_t {
|
||||
dc_parser_t base;
|
||||
unsigned int cached;
|
||||
unsigned int ngasmixes;
|
||||
deepsix_excursion_gasmix_t gasmix[MAX_GASMIXES];
|
||||
} deepsix_excursion_parser_t;
|
||||
|
||||
static dc_status_t deepsix_excursion_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
static dc_status_t deepsix_excursion_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
|
||||
static dc_status_t deepsix_excursion_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
|
||||
static dc_status_t deepsix_excursion_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
|
||||
static dc_status_t deepsix_excursion_parser_samples_foreach_v0 (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
|
||||
static dc_status_t deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
|
||||
|
||||
static const dc_parser_vtable_t deepsix_parser_vtable = {
|
||||
sizeof(deepsix_excursion_parser_t),
|
||||
DC_FAMILY_DEEPSIX_EXCURSION,
|
||||
deepsix_excursion_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
deepsix_excursion_parser_get_datetime, /* datetime */
|
||||
deepsix_excursion_parser_get_field, /* fields */
|
||||
deepsix_excursion_parser_samples_foreach, /* samples_foreach */
|
||||
NULL /* destroy */
|
||||
};
|
||||
|
||||
static const deepsix_excursion_layout_t deepsix_excursion_layout_v0 = {
|
||||
156,/* headersize */
|
||||
UNDEFINED, /* version */
|
||||
4, /* divemode */
|
||||
24, /* samplerate */
|
||||
UNDEFINED, /* salinity */
|
||||
12, /* datetime */
|
||||
20, /* divetime */
|
||||
28, /* maxdepth */
|
||||
32, /* temperature_min */
|
||||
UNDEFINED, /* avgdepth */
|
||||
48, /* firmware */
|
||||
UNDEFINED, /* temperature_surf */
|
||||
56, /* atmospheric */
|
||||
UNDEFINED, /* gf */
|
||||
};
|
||||
|
||||
static const deepsix_excursion_layout_t deepsix_excursion_layout_v1 = {
|
||||
129,/* headersize */
|
||||
3, /* version */
|
||||
4, /* divemode */
|
||||
5, /* samplerate */
|
||||
7, /* salinity */
|
||||
12, /* datetime */
|
||||
19, /* divetime */
|
||||
29, /* maxdepth */
|
||||
31, /* temperature_min */
|
||||
33, /* avgdepth */
|
||||
35, /* firmware */
|
||||
43, /* temperature_surf */
|
||||
45, /* atmospheric */
|
||||
127, /* gf */
|
||||
};
|
||||
|
||||
static double
|
||||
pressure_to_depth(unsigned int depth, unsigned int atmospheric, unsigned int density)
|
||||
{
|
||||
return ((signed int)(depth - atmospheric)) * (BAR / 1000.0) / (density * GRAVITY);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
deepsix_excursion_find_gasmix(deepsix_excursion_parser_t *parser, unsigned int o2, unsigned int he, unsigned int id)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
while (i < parser->ngasmixes) {
|
||||
if (o2 == parser->gasmix[i].oxygen && he == parser->gasmix[i].helium && id == parser->gasmix[i].id)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
deepsix_excursion_parser_create (dc_parser_t **out, dc_context_t *context)
|
||||
{
|
||||
@ -75,6 +199,15 @@ deepsix_excursion_parser_create (dc_parser_t **out, dc_context_t *context)
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
// Set the default values.
|
||||
parser->cached = 0;
|
||||
parser->ngasmixes = 0;
|
||||
for (unsigned int i = 0; i < MAX_GASMIXES; ++i) {
|
||||
parser->gasmix[i].id = 0;
|
||||
parser->gasmix[i].oxygen = 0;
|
||||
parser->gasmix[i].helium = 0;
|
||||
}
|
||||
|
||||
*out = (dc_parser_t *) parser;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
@ -83,6 +216,17 @@ deepsix_excursion_parser_create (dc_parser_t **out, dc_context_t *context)
|
||||
static dc_status_t
|
||||
deepsix_excursion_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
|
||||
{
|
||||
deepsix_excursion_parser_t *parser = (deepsix_excursion_parser_t *) abstract;
|
||||
|
||||
// Reset the cache.
|
||||
parser->cached = 0;
|
||||
parser->ngasmixes = 0;
|
||||
for (unsigned int i = 0; i < MAX_GASMIXES; ++i) {
|
||||
parser->gasmix[i].id = 0;
|
||||
parser->gasmix[i].oxygen = 0;
|
||||
parser->gasmix[i].helium = 0;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -92,18 +236,38 @@ deepsix_excursion_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dat
|
||||
const unsigned char *data = abstract->data;
|
||||
unsigned int size = abstract->size;
|
||||
|
||||
if (size < HEADERSIZE)
|
||||
if (size < HEADERSIZE_MIN)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int version = data[3];
|
||||
const deepsix_excursion_layout_t *layout = version == 0 ?
|
||||
&deepsix_excursion_layout_v0 : &deepsix_excursion_layout_v1;
|
||||
|
||||
if (size < layout->headersize)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int firmware = array_uint16_be (data + layout->firmware + 4);
|
||||
|
||||
const unsigned char *p = data + layout->datetime;
|
||||
|
||||
if (datetime) {
|
||||
datetime->year = data[12] + 2000;
|
||||
datetime->month = data[13];
|
||||
datetime->day = data[14];
|
||||
datetime->hour = data[15];
|
||||
datetime->minute = data[16];
|
||||
datetime->second = data[17];
|
||||
datetime->year = p[0] + 2000;
|
||||
datetime->month = p[1];
|
||||
datetime->day = p[2];
|
||||
datetime->hour = p[3];
|
||||
datetime->minute = p[4];
|
||||
datetime->second = p[5];
|
||||
|
||||
if (version == 0) {
|
||||
if (firmware >= FWVERSION(5, 'B')) {
|
||||
datetime->timezone = (p[6] - 12) * 3600;
|
||||
} else {
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
}
|
||||
} else {
|
||||
datetime->timezone = ((signed char) p[6]) * 900;
|
||||
}
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
@ -111,37 +275,74 @@ deepsix_excursion_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *dat
|
||||
static dc_status_t
|
||||
deepsix_excursion_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||
{
|
||||
deepsix_excursion_parser_t *parser = (deepsix_excursion_parser_t *) abstract;
|
||||
const unsigned char *data = abstract->data;
|
||||
unsigned int size = abstract->size;
|
||||
|
||||
if (size < HEADERSIZE)
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||
dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
|
||||
|
||||
if (size < HEADERSIZE_MIN)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int atmospheric = array_uint32_le(data + 56);
|
||||
unsigned int version = data[3];
|
||||
const deepsix_excursion_layout_t *layout = version == 0 ?
|
||||
&deepsix_excursion_layout_v0 : &deepsix_excursion_layout_v1;
|
||||
|
||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||
if (size < layout->headersize)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
if (version != 0 && !parser->cached) {
|
||||
dc_status_t rc = deepsix_excursion_parser_samples_foreach_v1(abstract, NULL, NULL);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
}
|
||||
|
||||
unsigned int atmospheric = array_uint16_le(data + layout->atmospheric);
|
||||
unsigned int density = DENSITY;
|
||||
if (layout->salinity != UNDEFINED) {
|
||||
density = 1000 + data[layout->salinity] * 10;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
switch (type) {
|
||||
case DC_FIELD_DIVETIME:
|
||||
*((unsigned int *) value) = array_uint32_le(data + 20);
|
||||
*((unsigned int *) value) = array_uint32_le(data + layout->divetime);
|
||||
break;
|
||||
case DC_FIELD_MAXDEPTH:
|
||||
*((double *) value) = (signed int)(array_uint32_le(data + 28) - atmospheric) * (BAR / 1000.0) / (DENSITY * GRAVITY);
|
||||
*((double *) value) = pressure_to_depth(array_uint16_le(data + layout->maxdepth), atmospheric, density);
|
||||
break;
|
||||
case DC_FIELD_AVGDEPTH:
|
||||
if (layout->avgdepth == UNDEFINED)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
*((double *) value) = pressure_to_depth(array_uint16_le(data + layout->avgdepth), atmospheric, density);
|
||||
break;
|
||||
case DC_FIELD_GASMIX_COUNT:
|
||||
*((unsigned int *) value) = parser->ngasmixes;
|
||||
break;
|
||||
case DC_FIELD_GASMIX:
|
||||
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
|
||||
gasmix->helium = parser->gasmix[flags].helium / 100.0;
|
||||
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
|
||||
break;
|
||||
case DC_FIELD_TEMPERATURE_MINIMUM:
|
||||
*((double *) value) = (signed int) array_uint32_le(data + 32) / 10.0;
|
||||
*((double *) value) = (signed int) array_uint16_le(data + layout->temperature_min) / 10.0;
|
||||
break;
|
||||
case DC_FIELD_TEMPERATURE_SURFACE:
|
||||
if (layout->temperature_surf == UNDEFINED)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
*((double *) value) = (signed int) array_uint16_le(data + layout->temperature_surf) / 10.0;
|
||||
break;
|
||||
case DC_FIELD_ATMOSPHERIC:
|
||||
*((double *) value) = atmospheric / 1000.0;
|
||||
break;
|
||||
case DC_FIELD_SALINITY:
|
||||
water->type = DC_WATER_SALT;
|
||||
water->density = DENSITY;
|
||||
water->type = (density == 1000) ? DC_WATER_FRESH : DC_WATER_SALT;
|
||||
water->density = density;
|
||||
break;
|
||||
case DC_FIELD_DIVEMODE:
|
||||
switch (array_uint32_le(data + 4)) {
|
||||
switch (data[layout->divemode]) {
|
||||
case 0:
|
||||
*((dc_divemode_t *) value) = DC_DIVEMODE_OC;
|
||||
break;
|
||||
@ -155,6 +356,17 @@ deepsix_excursion_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_DECOMODEL:
|
||||
decomodel->type = DC_DECOMODEL_BUHLMANN;
|
||||
decomodel->conservatism = 0;
|
||||
if (layout->gf != UNDEFINED) {
|
||||
decomodel->params.gf.low = data[layout->gf + 0];
|
||||
decomodel->params.gf.high = data[layout->gf + 1];
|
||||
} else {
|
||||
decomodel->params.gf.low = 0;
|
||||
decomodel->params.gf.high = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
@ -164,23 +376,24 @@ deepsix_excursion_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepsix_excursion_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
deepsix_excursion_parser_samples_foreach_v0 (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
const unsigned char *data = abstract->data;
|
||||
unsigned int size = abstract->size;
|
||||
const deepsix_excursion_layout_t *layout = &deepsix_excursion_layout_v0;
|
||||
|
||||
if (size < HEADERSIZE)
|
||||
if (size < layout->headersize)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
int firmware4c = memcmp(data + 48, "D01-4C", 6) == 0;
|
||||
int firmware4c = memcmp(data + layout->firmware, "D01-4C", 6) == 0;
|
||||
|
||||
unsigned int maxtype = firmware4c ? TEMPERATURE : CNS;
|
||||
|
||||
unsigned int interval = array_uint32_le(data + 24);
|
||||
unsigned int atmospheric = array_uint32_le(data + 56);
|
||||
unsigned int interval = array_uint32_le(data + layout->samplerate);
|
||||
unsigned int atmospheric = array_uint32_le(data + layout->atmospheric);
|
||||
|
||||
unsigned int time = 0;
|
||||
unsigned int offset = HEADERSIZE;
|
||||
unsigned int offset = layout->headersize;
|
||||
while (offset + 1 < size) {
|
||||
dc_sample_value_t sample = {0};
|
||||
|
||||
@ -213,7 +426,7 @@ deepsix_excursion_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb
|
||||
sample.time = time;
|
||||
if (callback) callback(DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
sample.depth = (signed int)(depth - atmospheric) * (BAR / 1000.0) / (DENSITY * GRAVITY);
|
||||
sample.depth = pressure_to_depth(depth, atmospheric, DENSITY);
|
||||
if (callback) callback(DC_SAMPLE_DEPTH, sample, userdata);
|
||||
}
|
||||
|
||||
@ -240,7 +453,7 @@ deepsix_excursion_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb
|
||||
unsigned int ceiling_time = array_uint16_le(data + offset + 6);
|
||||
} else if (type == CNS) {
|
||||
unsigned int cns = array_uint16_le(data + offset + 4);
|
||||
sample.cns = cns;
|
||||
sample.cns = cns / 100.0;
|
||||
if (callback) callback(DC_SAMPLE_CNS, sample, userdata);
|
||||
}
|
||||
|
||||
@ -249,3 +462,325 @@ deepsix_excursion_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callb
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
deepsix_excursion_parser_t *parser = (deepsix_excursion_parser_t *) abstract;
|
||||
const unsigned char *data = abstract->data;
|
||||
unsigned int size = abstract->size;
|
||||
const deepsix_excursion_layout_t *layout = &deepsix_excursion_layout_v1;
|
||||
|
||||
if (size < layout->headersize)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int headersize = data[2];
|
||||
if (headersize < layout->headersize)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int samplerate = data[layout->samplerate];
|
||||
unsigned int atmospheric = array_uint16_le(data + layout->atmospheric);
|
||||
unsigned int density = 1000 + data[layout->salinity] * 10;
|
||||
|
||||
unsigned int offset = headersize;
|
||||
if (offset + 1 > size) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned int nconfig = data[offset];
|
||||
if (nconfig > MAX_SAMPLES) {
|
||||
ERROR(abstract->context, "Too many sample descriptors (%u).", nconfig);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
offset += 1;
|
||||
|
||||
if (offset + 3 * nconfig > size) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
deepsix_excursion_sample_info_t sample_info[MAX_SAMPLES] = {{0}};
|
||||
for (unsigned int i = 0; i < nconfig; i++) {
|
||||
sample_info[i].type = data[offset + 3 * i + 0];
|
||||
sample_info[i].size = data[offset + 3 * i + 1];
|
||||
sample_info[i].divisor = data[offset + 3 * i + 2];
|
||||
|
||||
if (sample_info[i].divisor) {
|
||||
switch (sample_info[i].type) {
|
||||
case SAMPLE_CNS:
|
||||
case SAMPLE_TEMPERATURE:
|
||||
if (sample_info[i].size != 2) {
|
||||
ERROR(abstract->context, "Unexpected sample size (%u).", sample_info[i].size);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case SAMPLE_DECO_NDL:
|
||||
if (sample_info[i].size != 7) {
|
||||
ERROR(abstract->context, "Unexpected sample size (%u).", sample_info[i].size);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WARNING (abstract->context, "Unknown sample descriptor (%u %u %u).",
|
||||
sample_info[i].type, sample_info[i].size, sample_info[i].divisor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset += 3 * nconfig;
|
||||
|
||||
if (offset + 1 > size) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned int nevents = data[offset];
|
||||
if (nevents > MAX_EVENTS) {
|
||||
ERROR(abstract->context, "Too many event descriptors (%u).", nevents);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
offset += 1;
|
||||
|
||||
if (offset + 2 * nevents > size) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
deepsix_excursion_event_info_t event_info[MAX_EVENTS] = {{0}};
|
||||
for (unsigned int i = 0; i < nevents; i++) {
|
||||
event_info[i].type = data[offset + 2 * i];
|
||||
event_info[i].size = data[offset + 2 * i + 1];
|
||||
|
||||
switch (event_info[i].type) {
|
||||
case EVENT_ALARMS:
|
||||
if (event_info[i].size != 1) {
|
||||
ERROR(abstract->context, "Unexpected event size (%u).", event_info[i].size);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case EVENT_CHANGE_GAS:
|
||||
if (event_info[i].size != 3) {
|
||||
ERROR(abstract->context, "Unexpected event size (%u).", event_info[i].size);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case EVENT_SAMPLES_MISSED:
|
||||
if (event_info[i].size != 6) {
|
||||
ERROR(abstract->context, "Unexpected event size (%u).", event_info[i].size);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WARNING (abstract->context, "Unknown event descriptor (%u %u).",
|
||||
event_info[i].type, event_info[i].size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
offset += 2 * nevents;
|
||||
|
||||
unsigned int time = 0;
|
||||
unsigned int nsamples = 0;
|
||||
while (offset + 3 <= size) {
|
||||
dc_sample_value_t sample = {0};
|
||||
nsamples++;
|
||||
|
||||
// Time (seconds).
|
||||
time += samplerate;
|
||||
sample.time = time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
unsigned int depth = array_uint16_le (data + offset);
|
||||
sample.depth = pressure_to_depth(depth, atmospheric, density);
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
offset += 2;
|
||||
|
||||
// event info
|
||||
unsigned int length = data[offset];
|
||||
offset += 1;
|
||||
|
||||
if (offset + length > size) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
if (length) {
|
||||
if (length < 2) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned int events = array_uint16_le (data + offset);
|
||||
unsigned int event_offset = 2;
|
||||
|
||||
for (unsigned int i = 0; i < nevents; i++) {
|
||||
if ((events & (1 << event_info[i].type)) == 0)
|
||||
continue;
|
||||
|
||||
if (event_offset + event_info[i].size > length) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned int alarms = 0;
|
||||
unsigned int id = 0, o2 = 0, he = 0;
|
||||
unsigned int mix_idx = 0;
|
||||
unsigned int count = 0, timestamp = 0;
|
||||
switch (event_info[i].type) {
|
||||
case EVENT_ALARMS:
|
||||
alarms = data[offset + event_offset];
|
||||
for (unsigned int v = alarms, j = 0; v; v >>= 1, ++j) {
|
||||
if ((v & 1) == 0)
|
||||
continue;
|
||||
|
||||
sample.event.type = SAMPLE_EVENT_NONE;
|
||||
sample.event.time = 0;
|
||||
sample.event.flags = 0;
|
||||
sample.event.value = 0;
|
||||
switch (j) {
|
||||
case ALARM_ASCENTRATE:
|
||||
sample.event.type = SAMPLE_EVENT_ASCENT;
|
||||
break;
|
||||
case ALARM_CEILING:
|
||||
sample.event.type = SAMPLE_EVENT_CEILING;
|
||||
break;
|
||||
case ALARM_PO2:
|
||||
sample.event.type = SAMPLE_EVENT_PO2;
|
||||
break;
|
||||
case ALARM_MAXDEPTH:
|
||||
sample.event.type = SAMPLE_EVENT_MAXDEPTH;
|
||||
break;
|
||||
case ALARM_DIVETIME:
|
||||
sample.event.type = SAMPLE_EVENT_DIVETIME;
|
||||
break;
|
||||
case ALARM_CNS:
|
||||
break;
|
||||
default:
|
||||
WARNING (abstract->context, "Unknown event (%u).", j);
|
||||
break;
|
||||
}
|
||||
if (sample.event.type != SAMPLE_EVENT_NONE) {
|
||||
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EVENT_CHANGE_GAS:
|
||||
id = data[offset + event_offset];
|
||||
o2 = data[offset + event_offset + 1];
|
||||
he = data[offset + event_offset + 2];
|
||||
|
||||
mix_idx = deepsix_excursion_find_gasmix(parser, o2, he, id);
|
||||
if (mix_idx >= parser->ngasmixes) {
|
||||
if (mix_idx >= MAX_GASMIXES) {
|
||||
ERROR (abstract->context, "Maximum number of gas mixes reached.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
parser->gasmix[mix_idx].oxygen = o2;
|
||||
parser->gasmix[mix_idx].helium = he;
|
||||
parser->gasmix[mix_idx].id = id;
|
||||
parser->ngasmixes = mix_idx + 1;
|
||||
}
|
||||
|
||||
sample.gasmix = mix_idx;
|
||||
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
|
||||
break;
|
||||
case EVENT_SAMPLES_MISSED:
|
||||
count = array_uint16_le(data + offset + event_offset);
|
||||
timestamp = array_uint32_le(data + offset + event_offset + 2);
|
||||
if (timestamp < time) {
|
||||
ERROR (abstract->context, "Timestamp moved backwards (%u %u).", timestamp, time);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
nsamples += count;
|
||||
time = timestamp;
|
||||
break;
|
||||
default:
|
||||
WARNING (abstract->context, "Unknown event (%u %u).",
|
||||
event_info[i].type, event_info[i].size);
|
||||
break;
|
||||
}
|
||||
event_offset += event_info[i].size;
|
||||
}
|
||||
|
||||
// Skip remaining sample bytes (if any).
|
||||
if (event_offset < length) {
|
||||
WARNING (abstract->context, "Remaining %u bytes skipped.", length - event_offset);
|
||||
}
|
||||
offset += length;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < nconfig; ++i) {
|
||||
if (sample_info[i].divisor && (nsamples % sample_info[i].divisor) == 0) {
|
||||
if (offset + sample_info[i].size > size) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned int value = 0;
|
||||
unsigned int deco_flags = 0, deco_ndl_tts = 0;
|
||||
unsigned int deco_depth = 0, deco_time = 0;
|
||||
switch (sample_info[i].type) {
|
||||
case SAMPLE_TEMPERATURE:
|
||||
value = array_uint16_le(data + offset);
|
||||
sample.temperature = value / 10.0;
|
||||
if (callback) callback(DC_SAMPLE_TEMPERATURE, sample, userdata);
|
||||
break;
|
||||
case SAMPLE_CNS:
|
||||
value = array_uint16_le(data + offset);
|
||||
sample.cns = value / 10000.0;
|
||||
if (callback) callback (DC_SAMPLE_CNS, sample, userdata);
|
||||
break;
|
||||
case SAMPLE_DECO_NDL:
|
||||
deco_flags = data[offset];
|
||||
deco_ndl_tts = array_uint16_le(data + offset + 1);
|
||||
deco_depth = array_uint16_le(data + offset + 3);
|
||||
deco_time = array_uint16_le(data + offset + 5);
|
||||
if (deco_flags & DECOSTOP) {
|
||||
sample.deco.type = DC_DECO_DECOSTOP;
|
||||
sample.deco.depth = pressure_to_depth(deco_depth, atmospheric, density);
|
||||
sample.deco.time = deco_time;
|
||||
} else if (deco_flags & SAFETYSTOP) {
|
||||
sample.deco.type = DC_DECO_SAFETYSTOP;
|
||||
sample.deco.depth = pressure_to_depth(deco_depth, atmospheric, density);
|
||||
sample.deco.time = deco_time;
|
||||
} else {
|
||||
sample.deco.type = DC_DECO_NDL;
|
||||
sample.deco.depth = 0;
|
||||
sample.deco.time = deco_ndl_tts;
|
||||
}
|
||||
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
offset += sample_info[i].size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parser->cached = 1;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepsix_excursion_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
const unsigned char *data = abstract->data;
|
||||
unsigned int size = abstract->size;
|
||||
|
||||
if (size < HEADERSIZE_MIN)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
unsigned int version = data[3];
|
||||
|
||||
if (version == 0) {
|
||||
return deepsix_excursion_parser_samples_foreach_v0(abstract, callback, userdata);
|
||||
} else {
|
||||
return deepsix_excursion_parser_samples_foreach_v1(abstract, callback, userdata);
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,11 +63,11 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, vo
|
||||
static int dc_filter_mclean (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_atomic (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_deepsix (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_deepblu (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_oceans (dc_transport_t transport, const void *userdata, void *params);
|
||||
|
||||
// Not merged upstream yet
|
||||
static int dc_filter_garmin (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_deepblu (dc_transport_t transport, const void *userdata, void *params);
|
||||
static int dc_filter_oceans(dc_transport_t transport, const void *userdata, void *params);
|
||||
|
||||
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
|
||||
|
||||
@ -176,6 +176,7 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Scubapro", "Aladin A1", DC_FAMILY_UWATEC_SMART, 0x25, DC_TRANSPORT_BLE, dc_filter_uwatec},
|
||||
{"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_SMART, 0x26, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Scubapro", "Aladin A2", DC_FAMILY_UWATEC_SMART, 0x28, DC_TRANSPORT_BLE, dc_filter_uwatec},
|
||||
{"Scubapro", "G2 TEK", DC_FAMILY_UWATEC_SMART, 0x31, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
|
||||
{"Scubapro", "G2", DC_FAMILY_UWATEC_SMART, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
|
||||
{"Scubapro", "G2 Console", DC_FAMILY_UWATEC_SMART, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
|
||||
{"Scubapro", "G2 HUD", DC_FAMILY_UWATEC_SMART, 0x42, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
|
||||
@ -257,7 +258,7 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556, 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", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE, dc_filter_oceanic},
|
||||
{"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
|
||||
{"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641, 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},
|
||||
@ -276,6 +277,7 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Sherwood", "Beacon", DC_FAMILY_OCEANIC_ATOM2, 0x4742, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
|
||||
{"Aqualung", "i470TC", DC_FAMILY_OCEANIC_ATOM2, 0x4743, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
|
||||
{"Aqualung", "i200Cv2", DC_FAMILY_OCEANIC_ATOM2, 0x4749, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
|
||||
{"Oceanic", "Geo Air", DC_FAMILY_OCEANIC_ATOM2, 0x474B, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
|
||||
/* Mares Nemo */
|
||||
{"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL},
|
||||
@ -335,8 +337,9 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Cressi", "Newton", DC_FAMILY_CRESSI_LEONARDO, 5, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Cressi", "Drake", DC_FAMILY_CRESSI_LEONARDO, 6, DC_TRANSPORT_SERIAL, NULL},
|
||||
/* Cressi Goa */
|
||||
{"Cressi", "Cartesio", DC_FAMILY_CRESSI_GOA, 1, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL},
|
||||
{"Cressi", "Goa", DC_FAMILY_CRESSI_GOA, 2, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL},
|
||||
{"Cressi", "Cartesio", DC_FAMILY_CRESSI_GOA, 1, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Cressi", "Goa", DC_FAMILY_CRESSI_GOA, 2, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Cressi", "Donatello", DC_FAMILY_CRESSI_GOA, 4, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Cressi", "Michelangelo", DC_FAMILY_CRESSI_GOA, 5, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Cressi", "Neon", DC_FAMILY_CRESSI_GOA, 9, DC_TRANSPORT_SERIAL, NULL},
|
||||
/* Zeagle N2iTiON3 */
|
||||
@ -443,9 +446,14 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
{"Crest", "CR-4", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix},
|
||||
{"Genesis", "Centauri", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix},
|
||||
{"Tusa", "TC1", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix},
|
||||
{"Scorpena", "Alpha", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix},
|
||||
/* Seac Screen */
|
||||
{"Seac", "Screen", DC_FAMILY_SEAC_SCREEN, 0, DC_TRANSPORT_SERIAL, NULL},
|
||||
{"Seac", "Action", DC_FAMILY_SEAC_SCREEN, 0, DC_TRANSPORT_SERIAL, NULL},
|
||||
/* Deepblu Cosmiq */
|
||||
{"Deepblu", "Cosmiq+", DC_FAMILY_DEEPBLU_COSMIQ, 0, DC_TRANSPORT_BLE, dc_filter_deepblu},
|
||||
/* Oceans S1 */
|
||||
{"Oceans", "S1", DC_FAMILY_OCEANS_S1, 0, DC_TRANSPORT_BLE, dc_filter_oceans},
|
||||
|
||||
// Not merged upstream yet
|
||||
/* Garmin -- model numbers as defined in FIT format; USB product id is (0x4000 | model) */
|
||||
@ -453,10 +461,6 @@ static const dc_descriptor_t g_descriptors[] = {
|
||||
/* for the Mk2 we are using the model of the global model - the APAC model is 3702 */
|
||||
{"Garmin", "Descent Mk1", DC_FAMILY_GARMIN, 2859, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin},
|
||||
{"Garmin", "Descent Mk2/Mk2i", DC_FAMILY_GARMIN, 3258, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin},
|
||||
/* Deepblu */
|
||||
{"Deepblu", "Cosmiq+", DC_FAMILY_DEEPBLU, 0, DC_TRANSPORT_BLE, dc_filter_deepblu},
|
||||
/* Oceans S1 */
|
||||
{ "Oceans", "S1", DC_FAMILY_OCEANS_S1, 0, DC_TRANSPORT_BLE, dc_filter_oceans },
|
||||
};
|
||||
|
||||
static int
|
||||
@ -571,7 +575,7 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, voi
|
||||
"UWATEC Galileo Sol",
|
||||
};
|
||||
static const dc_usb_desc_t usbhid[] = {
|
||||
{0x2e6c, 0x3201}, // G2
|
||||
{0x2e6c, 0x3201}, // G2, G2 TEK
|
||||
{0x2e6c, 0x3211}, // G2 Console
|
||||
{0x2e6c, 0x4201}, // G2 HUD
|
||||
{0xc251, 0x2006}, // Aladin Square
|
||||
@ -582,6 +586,7 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, voi
|
||||
"HUD",
|
||||
"A1",
|
||||
"A2",
|
||||
"G2 TEK",
|
||||
};
|
||||
|
||||
if (transport == DC_TRANSPORT_IRDA) {
|
||||
@ -718,6 +723,7 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, vo
|
||||
0x4742, // Sherwood Beacon
|
||||
0x4743, // Aqualung i470TC
|
||||
0x4749, // Aqualung i200C (newer model)
|
||||
0x474B, // Oceanic Geo Air
|
||||
};
|
||||
|
||||
if (transport == DC_TRANSPORT_BLE) {
|
||||
@ -766,6 +772,7 @@ static int dc_filter_deepsix (dc_transport_t transport, const void *userdata, vo
|
||||
"Crest-CR4",
|
||||
"CENTAURI",
|
||||
"TC1",
|
||||
"ALPHA",
|
||||
};
|
||||
|
||||
if (transport == DC_TRANSPORT_BLE) {
|
||||
@ -775,20 +782,6 @@ static int dc_filter_deepsix (dc_transport_t transport, const void *userdata, vo
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Not merged upstream yet
|
||||
static int dc_filter_garmin (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const dc_usb_desc_t usbhid[] = {
|
||||
{0x091e, 0x2b2b}, // Garmin Descent Mk1
|
||||
};
|
||||
|
||||
if (transport == DC_TRANSPORT_USBSTORAGE) {
|
||||
return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usb);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_filter_deepblu (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const char * const bluetooth[] = {
|
||||
@ -802,14 +795,28 @@ static int dc_filter_deepblu (dc_transport_t transport, const void *userdata, vo
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dc_filter_oceans(dc_transport_t transport, const void* userdata, void *params)
|
||||
static int dc_filter_oceans (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const char* const ble[] = {
|
||||
static const char * const bluetooth[] = {
|
||||
"S1",
|
||||
};
|
||||
|
||||
if (transport == DC_TRANSPORT_BLE) {
|
||||
return DC_FILTER_INTERNAL(userdata, ble, 0, dc_match_prefix);
|
||||
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Not merged upstream yet
|
||||
static int dc_filter_garmin (dc_transport_t transport, const void *userdata, void *params)
|
||||
{
|
||||
static const dc_usb_desc_t usbhid[] = {
|
||||
{0x091e, 0x2b2b}, // Garmin Descent Mk1
|
||||
};
|
||||
|
||||
if (transport == DC_TRANSPORT_USBSTORAGE) {
|
||||
return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usb);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
19
src/device.c
19
src/device.c
@ -62,11 +62,11 @@
|
||||
#include "sporasub_sp2.h"
|
||||
#include "deepsix_excursion.h"
|
||||
#include "seac_screen.h"
|
||||
#include "deepblu_cosmiq.h"
|
||||
#include "oceans_s1.h"
|
||||
|
||||
// Not merged upstream yet
|
||||
#include "garmin.h"
|
||||
#include "deepblu.h"
|
||||
#include "oceans_s1.h"
|
||||
|
||||
#include "device-private.h"
|
||||
#include "context-private.h"
|
||||
@ -236,6 +236,12 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
|
||||
case DC_FAMILY_SEAC_SCREEN:
|
||||
rc = seac_screen_device_open (&device, context, iostream);
|
||||
break;
|
||||
case DC_FAMILY_DEEPBLU_COSMIQ:
|
||||
rc = deepblu_cosmiq_device_open (&device, context, iostream);
|
||||
break;
|
||||
case DC_FAMILY_OCEANS_S1:
|
||||
rc = oceans_s1_device_open (&device, context, iostream);
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
@ -243,12 +249,6 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
|
||||
case DC_FAMILY_GARMIN:
|
||||
rc = garmin_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
|
||||
break;
|
||||
case DC_FAMILY_DEEPBLU:
|
||||
rc = deepblu_device_open (&device, context, iostream);
|
||||
break;
|
||||
case DC_FAMILY_OCEANS_S1:
|
||||
rc = oceans_s1_device_open(&device, context, iostream);
|
||||
break;
|
||||
}
|
||||
|
||||
*out = device;
|
||||
@ -420,6 +420,9 @@ dc_device_timesync (dc_device_t *device, const dc_datetime_t *datetime)
|
||||
if (device->vtable->timesync == NULL)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
|
||||
if (datetime == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
return device->vtable->timesync (device, datetime);
|
||||
}
|
||||
|
||||
|
||||
@ -58,6 +58,9 @@ static const dc_parser_vtable_t diverite_nitekq_parser_vtable = {
|
||||
sizeof(diverite_nitekq_parser_t),
|
||||
DC_FAMILY_DIVERITE_NITEKQ,
|
||||
diverite_nitekq_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
diverite_nitekq_parser_get_datetime, /* datetime */
|
||||
diverite_nitekq_parser_get_field, /* fields */
|
||||
diverite_nitekq_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -608,11 +608,6 @@ divesystem_idive_device_timesync (dc_device_t *abstract, const dc_datetime_t *da
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (datetime == NULL) {
|
||||
ERROR (abstract->context, "Invalid parameter specified.");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
// Get the UTC timestamp.
|
||||
dc_ticks_t timestamp = dc_datetime_mktime(datetime);
|
||||
if (timestamp == -1) {
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#define ISINSTANCE(parser) dc_device_isinstance((parser), &divesystem_idive_parser_vtable)
|
||||
|
||||
#define ISIX3M(model) ((model) >= 0x21)
|
||||
#define ISIX3M2(model) ((model) >= 0x60 && (model) < 0x1000)
|
||||
|
||||
#define SZ_HEADER_IDIVE 0x32
|
||||
#define SZ_SAMPLE_IDIVE 0x2A
|
||||
@ -48,6 +49,15 @@
|
||||
#define FREEDIVE 4
|
||||
#define INVALID 0xFFFFFFFF
|
||||
|
||||
#define BUHLMANN 0
|
||||
#define VPM 1
|
||||
#define DUAL 2
|
||||
|
||||
#define IX3M2_BUHLMANN 0
|
||||
#define IX3M2_ZHL16B 1
|
||||
#define IX3M2_ZHL16C 2
|
||||
#define IX3M2_VPM 3
|
||||
|
||||
typedef struct divesystem_idive_parser_t divesystem_idive_parser_t;
|
||||
|
||||
typedef struct divesystem_idive_gasmix_t {
|
||||
@ -74,6 +84,9 @@ struct divesystem_idive_parser_t {
|
||||
unsigned int ntanks;
|
||||
divesystem_idive_gasmix_t gasmix[NGASMIXES];
|
||||
divesystem_idive_tank_t tank[NTANKS];
|
||||
unsigned int algorithm;
|
||||
unsigned int gf_low;
|
||||
unsigned int gf_high;
|
||||
};
|
||||
|
||||
static dc_status_t divesystem_idive_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
@ -85,6 +98,9 @@ static const dc_parser_vtable_t divesystem_idive_parser_vtable = {
|
||||
sizeof(divesystem_idive_parser_t),
|
||||
DC_FAMILY_DIVESYSTEM_IDIVE,
|
||||
divesystem_idive_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
divesystem_idive_parser_get_datetime, /* datetime */
|
||||
divesystem_idive_parser_get_field, /* fields */
|
||||
divesystem_idive_parser_samples_foreach, /* samples_foreach */
|
||||
@ -129,6 +145,10 @@ divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, unsign
|
||||
parser->tank[i].beginpressure = 0;
|
||||
parser->tank[i].endpressure = 0;
|
||||
}
|
||||
parser->algorithm = INVALID;
|
||||
parser->gf_low = INVALID;
|
||||
parser->gf_high = INVALID;
|
||||
|
||||
|
||||
*out = (dc_parser_t*) parser;
|
||||
|
||||
@ -157,6 +177,9 @@ divesystem_idive_parser_set_data (dc_parser_t *abstract, const unsigned char *da
|
||||
parser->tank[i].beginpressure = 0;
|
||||
parser->tank[i].endpressure = 0;
|
||||
}
|
||||
parser->algorithm = INVALID;
|
||||
parser->gf_low = INVALID;
|
||||
parser->gf_high = INVALID;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
@ -280,6 +303,7 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
dc_tank_t *tank = (dc_tank_t *) value;
|
||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||
dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
|
||||
|
||||
if (value) {
|
||||
switch (type) {
|
||||
@ -343,6 +367,46 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_DECOMODEL:
|
||||
if (parser->algorithm == INVALID)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
if (ISIX3M2(parser->model)) {
|
||||
switch (parser->algorithm) {
|
||||
case IX3M2_BUHLMANN:
|
||||
case IX3M2_ZHL16B:
|
||||
case IX3M2_ZHL16C:
|
||||
decomodel->type = DC_DECOMODEL_BUHLMANN;
|
||||
decomodel->conservatism = 0;
|
||||
decomodel->params.gf.low = parser->gf_low;
|
||||
decomodel->params.gf.high = parser->gf_high;
|
||||
break;
|
||||
case IX3M2_VPM:
|
||||
decomodel->type = DC_DECOMODEL_VPM;
|
||||
decomodel->conservatism = 0;
|
||||
break;
|
||||
default:
|
||||
ERROR (abstract->context, "Unknown deco algorithm %02x.", parser->algorithm);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
} else {
|
||||
switch (parser->algorithm) {
|
||||
case BUHLMANN:
|
||||
case DUAL:
|
||||
decomodel->type = DC_DECOMODEL_BUHLMANN;
|
||||
decomodel->conservatism = 0;
|
||||
decomodel->params.gf.low = parser->gf_low;
|
||||
decomodel->params.gf.high = parser->gf_high;
|
||||
break;
|
||||
case VPM:
|
||||
decomodel->type = DC_DECOMODEL_VPM;
|
||||
decomodel->conservatism = 0;
|
||||
break;
|
||||
default:
|
||||
ERROR (abstract->context, "Unknown deco algorithm %02x.", parser->algorithm);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
@ -371,6 +435,10 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
|
||||
unsigned int divemode = INVALID;
|
||||
unsigned int tank_previous = INVALID;
|
||||
unsigned int tank_idx = INVALID;
|
||||
unsigned int algorithm = INVALID;
|
||||
unsigned int algorithm_previous = INVALID;
|
||||
unsigned int gf_low = INVALID;
|
||||
unsigned int gf_high = INVALID;
|
||||
|
||||
unsigned int firmware = 0;
|
||||
unsigned int apos4 = 0;
|
||||
@ -433,6 +501,22 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
|
||||
divemode = mode;
|
||||
}
|
||||
|
||||
// Deco model
|
||||
unsigned int s_algorithm = data[offset + 14];
|
||||
unsigned int s_gf_high = data[offset + 15];
|
||||
unsigned int s_gf_low = data[offset + 16];
|
||||
if (s_algorithm != algorithm_previous) {
|
||||
if (algorithm_previous != INVALID) {
|
||||
WARNING (abstract->context, "Deco algorithm changed from %02x to %02x.", algorithm_previous, s_algorithm);
|
||||
}
|
||||
algorithm_previous = s_algorithm;
|
||||
}
|
||||
if (algorithm == INVALID) {
|
||||
algorithm = s_algorithm;
|
||||
gf_low = s_gf_low;
|
||||
gf_high = s_gf_high;
|
||||
}
|
||||
|
||||
// Setpoint
|
||||
if (mode == SCR || mode == CCR) {
|
||||
unsigned int setpoint = array_uint16_le (data + offset + 19);
|
||||
@ -501,6 +585,11 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
|
||||
unsigned int flags = data[offset + 47] & 0xF0;
|
||||
unsigned int pressure = data[offset + 49];
|
||||
|
||||
if (flags & 0x20) {
|
||||
// 300 bar transmitter.
|
||||
pressure *= 2;
|
||||
}
|
||||
|
||||
if (flags & 0x80) {
|
||||
// No active transmitter available
|
||||
} else if (flags & 0x40) {
|
||||
@ -560,6 +649,9 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
|
||||
parser->maxdepth = maxdepth;
|
||||
parser->divetime = time;
|
||||
parser->divemode = divemode;
|
||||
parser->algorithm = algorithm;
|
||||
parser->gf_low = gf_low;
|
||||
parser->gf_high = gf_high;
|
||||
parser->cached = 1;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -357,6 +357,9 @@ static const dc_parser_vtable_t garmin_parser_vtable = {
|
||||
sizeof(garmin_parser_t),
|
||||
DC_FAMILY_GARMIN,
|
||||
garmin_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
garmin_parser_get_datetime, /* datetime */
|
||||
garmin_parser_get_field, /* fields */
|
||||
garmin_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -473,11 +473,6 @@ hw_frog_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
{
|
||||
hw_frog_device_t *device = (hw_frog_device_t *) abstract;
|
||||
|
||||
if (datetime == NULL) {
|
||||
ERROR (abstract->context, "Invalid parameter specified.");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
// Send the command.
|
||||
unsigned char packet[6] = {
|
||||
datetime->hour, datetime->minute, datetime->second,
|
||||
|
||||
@ -348,11 +348,6 @@ hw_ostc_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
hw_ostc_device_t *device = (hw_ostc_device_t *) abstract;
|
||||
|
||||
if (datetime == NULL) {
|
||||
ERROR (abstract->context, "Invalid parameter specified.");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
// Send the command.
|
||||
dc_status_t rc = hw_ostc_send (device, 'b', 1);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
|
||||
146
src/hw_ostc3.c
146
src/hw_ostc3.c
@ -89,6 +89,19 @@
|
||||
#define NODELAY 0
|
||||
#define TIMEOUT 400
|
||||
|
||||
#define HDR_COMPACT_LENGTH 0 // 3 bytes
|
||||
#define HDR_COMPACT_SUMMARY 3 // 10 bytes
|
||||
#define HDR_COMPACT_NUMBER 13 // 2 bytes
|
||||
#define HDR_COMPACT_VERSION 15 // 1 byte
|
||||
|
||||
#define HDR_FULL_LENGTH 9 // 3 bytes
|
||||
#define HDR_FULL_SUMMARY 12 // 10 bytes
|
||||
#define HDR_FULL_NUMBER 80 // 2 bytes
|
||||
#define HDR_FULL_VERSION 8 // 1 byte
|
||||
|
||||
#define HDR_FULL_POINTERS 2 // 6 bytes
|
||||
#define HDR_FULL_FIRMWARE 48 // 2 bytes
|
||||
|
||||
typedef enum hw_ostc3_state_t {
|
||||
OPEN,
|
||||
DOWNLOAD,
|
||||
@ -116,6 +129,7 @@ typedef struct hw_ostc3_logbook_t {
|
||||
unsigned int profile;
|
||||
unsigned int fingerprint;
|
||||
unsigned int number;
|
||||
unsigned int version;
|
||||
} hw_ostc3_logbook_t;
|
||||
|
||||
typedef struct hw_ostc3_firmware_t {
|
||||
@ -155,16 +169,18 @@ static const dc_device_vtable_t hw_ostc3_device_vtable = {
|
||||
|
||||
static const hw_ostc3_logbook_t hw_ostc3_logbook_compact = {
|
||||
RB_LOGBOOK_SIZE_COMPACT, /* size */
|
||||
0, /* profile */
|
||||
3, /* fingerprint */
|
||||
13, /* number */
|
||||
HDR_COMPACT_LENGTH, /* profile */
|
||||
HDR_COMPACT_SUMMARY, /* fingerprint */
|
||||
HDR_COMPACT_NUMBER, /* number */
|
||||
HDR_COMPACT_VERSION, /* version */
|
||||
};
|
||||
|
||||
static const hw_ostc3_logbook_t hw_ostc3_logbook_full = {
|
||||
RB_LOGBOOK_SIZE_FULL, /* size */
|
||||
9, /* profile */
|
||||
12, /* fingerprint */
|
||||
80, /* number */
|
||||
HDR_FULL_LENGTH, /* profile */
|
||||
HDR_FULL_SUMMARY, /* fingerprint */
|
||||
HDR_FULL_NUMBER, /* number */
|
||||
HDR_FULL_VERSION, /* version */
|
||||
};
|
||||
|
||||
|
||||
@ -279,10 +295,15 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
|
||||
unsigned int isize,
|
||||
unsigned char output[],
|
||||
unsigned int osize,
|
||||
unsigned int *actual,
|
||||
unsigned int delay)
|
||||
{
|
||||
dc_device_t *abstract = (dc_device_t *) device;
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
unsigned int length = osize;
|
||||
|
||||
if (cmd == DIVE && length < RB_LOGBOOK_SIZE_FULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
if (device_is_cancelled (abstract))
|
||||
return DC_STATUS_CANCELLED;
|
||||
@ -345,13 +366,46 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
|
||||
}
|
||||
|
||||
if (output) {
|
||||
if (cmd == DIVE) {
|
||||
// Read the dive header.
|
||||
status = hw_ostc3_read (device, progress, output, RB_LOGBOOK_SIZE_FULL);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to receive the dive header.");
|
||||
return status;
|
||||
}
|
||||
|
||||
// When the hwOS firmware detects the dive profile is no longer
|
||||
// valid, it sends a modified dive header (with the begin/end
|
||||
// pointer fields reset to zero, and the length field reduced to 8
|
||||
// bytes), along with an empty dive profile. Detect this condition
|
||||
// and adjust the expected length.
|
||||
if (array_isequal (output + HDR_FULL_POINTERS, 6, 0x00) &&
|
||||
array_uint24_le (output + HDR_FULL_LENGTH) == 8 &&
|
||||
length > RB_LOGBOOK_SIZE_FULL + 5) {
|
||||
length = RB_LOGBOOK_SIZE_FULL + 5;
|
||||
}
|
||||
|
||||
// Read the dive profile.
|
||||
status = hw_ostc3_read (device, progress, output + RB_LOGBOOK_SIZE_FULL, length - RB_LOGBOOK_SIZE_FULL);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to receive the dive profile.");
|
||||
return status;
|
||||
}
|
||||
|
||||
// Update and emit a progress event.
|
||||
if (progress && osize > length) {
|
||||
progress->current += osize - length;
|
||||
device_event_emit ((dc_device_t *) device, DC_EVENT_PROGRESS, progress);
|
||||
}
|
||||
} else {
|
||||
// Read the output data packet.
|
||||
status = hw_ostc3_read (device, progress, output, osize);
|
||||
status = hw_ostc3_read (device, progress, output, length);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to receive the answer.");
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (delay && device->available == 0) {
|
||||
dc_iostream_poll (device->iostream, delay);
|
||||
@ -373,6 +427,9 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
|
||||
}
|
||||
}
|
||||
|
||||
if (actual)
|
||||
*actual = length;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -445,9 +502,9 @@ hw_ostc3_device_id (hw_ostc3_device_t *device, unsigned char data[], unsigned in
|
||||
|
||||
// Send the command.
|
||||
unsigned char hardware[SZ_HARDWARE2] = {0};
|
||||
status = hw_ostc3_transfer (device, NULL, HARDWARE2, NULL, 0, hardware, SZ_HARDWARE2, NODELAY);
|
||||
status = hw_ostc3_transfer (device, NULL, HARDWARE2, NULL, 0, hardware, SZ_HARDWARE2, NULL, NODELAY);
|
||||
if (status == DC_STATUS_UNSUPPORTED) {
|
||||
status = hw_ostc3_transfer (device, NULL, HARDWARE, NULL, 0, hardware + 1, SZ_HARDWARE, NODELAY);
|
||||
status = hw_ostc3_transfer (device, NULL, HARDWARE, NULL, 0, hardware + 1, SZ_HARDWARE, NULL, NODELAY);
|
||||
}
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
@ -469,7 +526,7 @@ hw_ostc3_device_init_download (hw_ostc3_device_t *device)
|
||||
dc_context_t *context = (abstract ? abstract->context : NULL);
|
||||
|
||||
// Send the init command.
|
||||
dc_status_t status = hw_ostc3_transfer (device, NULL, INIT, NULL, 0, NULL, 0, NODELAY);
|
||||
dc_status_t status = hw_ostc3_transfer (device, NULL, INIT, NULL, 0, NULL, 0, NULL, NODELAY);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (context, "Failed to send the command.");
|
||||
return status;
|
||||
@ -562,7 +619,7 @@ hw_ostc3_device_init (hw_ostc3_device_t *device, hw_ostc3_state_t state)
|
||||
|
||||
// Read the version information.
|
||||
unsigned char version[SZ_VERSION] = {0};
|
||||
rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, version, sizeof(version), NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, version, sizeof(version), NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the version information.");
|
||||
return rc;
|
||||
@ -592,7 +649,7 @@ hw_ostc3_device_close (dc_device_t *abstract)
|
||||
|
||||
// Send the exit command
|
||||
if (device->state == DOWNLOAD || device->state == SERVICE) {
|
||||
rc = hw_ostc3_transfer (device, NULL, EXIT, NULL, 0, NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, EXIT, NULL, 0, NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to send the command.");
|
||||
dc_status_set_error(&status, rc);
|
||||
@ -636,7 +693,7 @@ hw_ostc3_device_version (dc_device_t *abstract, unsigned char data[], unsigned i
|
||||
return rc;
|
||||
|
||||
// Send the command.
|
||||
rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, data, size, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, data, size, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -709,11 +766,11 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
|
||||
// This is slower, but also works for older firmware versions.
|
||||
unsigned int compact = 1;
|
||||
rc = hw_ostc3_transfer (device, &progress, COMPACT,
|
||||
NULL, 0, header, RB_LOGBOOK_SIZE_COMPACT * RB_LOGBOOK_COUNT, NODELAY);
|
||||
NULL, 0, header, RB_LOGBOOK_SIZE_COMPACT * RB_LOGBOOK_COUNT, NULL, NODELAY);
|
||||
if (rc == DC_STATUS_UNSUPPORTED) {
|
||||
compact = 0;
|
||||
rc = hw_ostc3_transfer (device, &progress, HEADER,
|
||||
NULL, 0, header, RB_LOGBOOK_SIZE_FULL * RB_LOGBOOK_COUNT, NODELAY);
|
||||
NULL, 0, header, RB_LOGBOOK_SIZE_FULL * RB_LOGBOOK_COUNT, NULL, NODELAY);
|
||||
}
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the header.");
|
||||
@ -769,8 +826,8 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
|
||||
unsigned int length = RB_LOGBOOK_SIZE_FULL + array_uint24_le (header + offset + logbook->profile) - 3;
|
||||
if (!compact) {
|
||||
// Workaround for a bug in older firmware versions.
|
||||
unsigned int firmware = array_uint16_be (header + offset + 0x30);
|
||||
if (firmware < 93)
|
||||
unsigned int firmware = array_uint16_be (header + offset + HDR_FULL_FIRMWARE);
|
||||
if (firmware < OSTC3FW(0,93))
|
||||
length -= 3;
|
||||
}
|
||||
if (length < RB_LOGBOOK_SIZE_FULL) {
|
||||
@ -817,15 +874,15 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
|
||||
unsigned int length = RB_LOGBOOK_SIZE_FULL + array_uint24_le (header + offset + logbook->profile) - 3;
|
||||
if (!compact) {
|
||||
// Workaround for a bug in older firmware versions.
|
||||
unsigned int firmware = array_uint16_be (header + offset + 0x30);
|
||||
if (firmware < 93)
|
||||
unsigned int firmware = array_uint16_be (header + offset + HDR_FULL_FIRMWARE);
|
||||
if (firmware < OSTC3FW(0,93))
|
||||
length -= 3;
|
||||
}
|
||||
|
||||
// Download the dive.
|
||||
unsigned char number[1] = {idx};
|
||||
rc = hw_ostc3_transfer (device, &progress, DIVE,
|
||||
number, sizeof (number), profile, length, NODELAY);
|
||||
number, sizeof (number), profile, length, &length, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the dive.");
|
||||
free (profile);
|
||||
@ -834,11 +891,15 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
|
||||
}
|
||||
|
||||
// Verify the header in the logbook and profile are identical.
|
||||
if (!compact && memcmp (profile, header + offset, logbook->size) != 0) {
|
||||
if (memcmp (profile + HDR_FULL_VERSION, header + offset + logbook->version, 1) != 0 ||
|
||||
compact ?
|
||||
memcmp (profile + HDR_FULL_SUMMARY, header + offset + HDR_COMPACT_SUMMARY, 10) != 0 ||
|
||||
memcmp (profile + HDR_FULL_NUMBER, header + offset + HDR_COMPACT_NUMBER, 2) != 0 :
|
||||
memcmp (profile + HDR_FULL_SUMMARY, header + offset + HDR_FULL_SUMMARY, RB_LOGBOOK_SIZE_FULL - HDR_FULL_SUMMARY) != 0) {
|
||||
ERROR (abstract->context, "Unexpected profile header.");
|
||||
free (profile);
|
||||
free (header);
|
||||
return rc;
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
// Detect invalid profile data.
|
||||
@ -852,8 +913,8 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
|
||||
} else if (length == RB_LOGBOOK_SIZE_FULL + 2) {
|
||||
// A profile containing only the 2 byte end-of-profile
|
||||
// marker is considered a valid empty profile.
|
||||
} else if (length < RB_LOGBOOK_SIZE_FULL + 5 + 2 ||
|
||||
array_uint24_le (profile + RB_LOGBOOK_SIZE_FULL) + delta != array_uint24_le (profile + 9)) {
|
||||
} else if (length < RB_LOGBOOK_SIZE_FULL + 5 ||
|
||||
array_uint24_le (profile + RB_LOGBOOK_SIZE_FULL) + delta != array_uint24_le (profile + HDR_FULL_LENGTH)) {
|
||||
// If there is more data available, then there should be a
|
||||
// valid profile header containing a length matching the
|
||||
// length in the dive header.
|
||||
@ -861,7 +922,7 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
|
||||
length = RB_LOGBOOK_SIZE_FULL;
|
||||
}
|
||||
|
||||
if (callback && !callback (profile, length, profile + 12, sizeof (device->fingerprint), userdata))
|
||||
if (callback && !callback (profile, length, profile + HDR_FULL_SUMMARY, sizeof (device->fingerprint), userdata))
|
||||
break;
|
||||
}
|
||||
|
||||
@ -877,11 +938,6 @@ hw_ostc3_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
{
|
||||
hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract;
|
||||
|
||||
if (datetime == NULL) {
|
||||
ERROR (abstract->context, "Invalid parameter specified.");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
dc_status_t rc = hw_ostc3_device_init (device, DOWNLOAD);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
@ -890,7 +946,7 @@ hw_ostc3_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
|
||||
unsigned char packet[6] = {
|
||||
datetime->hour, datetime->minute, datetime->second,
|
||||
datetime->month, datetime->day, datetime->year - 2000};
|
||||
rc = hw_ostc3_transfer (device, NULL, CLOCK, packet, sizeof (packet), NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, CLOCK, packet, sizeof (packet), NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -918,7 +974,7 @@ hw_ostc3_device_display (dc_device_t *abstract, const char *text)
|
||||
return rc;
|
||||
|
||||
// Send the command.
|
||||
rc = hw_ostc3_transfer (device, NULL, DISPLAY, packet, sizeof (packet), NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, DISPLAY, packet, sizeof (packet), NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -946,7 +1002,7 @@ hw_ostc3_device_customtext (dc_device_t *abstract, const char *text)
|
||||
return rc;
|
||||
|
||||
// Send the command.
|
||||
rc = hw_ostc3_transfer (device, NULL, CUSTOMTEXT, packet, sizeof (packet), NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, CUSTOMTEXT, packet, sizeof (packet), NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -972,7 +1028,7 @@ hw_ostc3_device_config_read (dc_device_t *abstract, unsigned int config, unsigne
|
||||
|
||||
// Send the command.
|
||||
unsigned char command[1] = {config};
|
||||
rc = hw_ostc3_transfer (device, NULL, READ, command, sizeof (command), data, size, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, READ, command, sizeof (command), data, size, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -999,7 +1055,7 @@ hw_ostc3_device_config_write (dc_device_t *abstract, unsigned int config, const
|
||||
// Send the command.
|
||||
unsigned char command[SZ_CONFIG + 1] = {config};
|
||||
memcpy(command + 1, data, size);
|
||||
rc = hw_ostc3_transfer (device, NULL, WRITE, command, size + 1, NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, WRITE, command, size + 1, NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -1019,7 +1075,7 @@ hw_ostc3_device_config_reset (dc_device_t *abstract)
|
||||
return rc;
|
||||
|
||||
// Send the command.
|
||||
rc = hw_ostc3_transfer (device, NULL, RESET, NULL, 0, NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, RESET, NULL, 0, NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
@ -1245,7 +1301,7 @@ hw_ostc3_firmware_erase (hw_ostc3_device_t *device, unsigned int addr, unsigned
|
||||
array_uint24_be_set (buffer, addr);
|
||||
buffer[3] = blocks;
|
||||
|
||||
return hw_ostc3_transfer (device, NULL, S_ERASE, buffer, sizeof (buffer), NULL, 0, delay);
|
||||
return hw_ostc3_transfer (device, NULL, S_ERASE, buffer, sizeof (buffer), NULL, 0, NULL, delay);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
@ -1255,7 +1311,7 @@ hw_ostc3_firmware_block_read (hw_ostc3_device_t *device, unsigned int addr, unsi
|
||||
array_uint24_be_set (buffer, addr);
|
||||
array_uint24_be_set (buffer + 3, block_size);
|
||||
|
||||
return hw_ostc3_transfer (device, NULL, S_BLOCK_READ, buffer, sizeof (buffer), block, block_size, NODELAY);
|
||||
return hw_ostc3_transfer (device, NULL, S_BLOCK_READ, buffer, sizeof (buffer), block, block_size, NULL, NODELAY);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
@ -1270,7 +1326,7 @@ hw_ostc3_firmware_block_write1 (hw_ostc3_device_t *device, unsigned int addr, co
|
||||
array_uint24_be_set (buffer, addr);
|
||||
memcpy (buffer + 3, block, block_size);
|
||||
|
||||
return hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE, buffer, 3 + block_size, NULL, 0, TIMEOUT);
|
||||
return hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE, buffer, 3 + block_size, NULL, 0, NULL, TIMEOUT);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
@ -1289,7 +1345,7 @@ hw_ostc3_firmware_block_write2 (hw_ostc3_device_t *device, unsigned int address,
|
||||
array_uint24_be_set (buffer, address);
|
||||
memcpy (buffer + 3, data + nbytes, SZ_FIRMWARE_BLOCK2);
|
||||
|
||||
status = hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE2, buffer, sizeof(buffer), NULL, 0, NODELAY);
|
||||
status = hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE2, buffer, sizeof(buffer), NULL, 0, NULL, NODELAY);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
@ -1330,7 +1386,7 @@ hw_ostc3_firmware_upgrade (dc_device_t *abstract, unsigned int checksum)
|
||||
buffer[4] = (buffer[4]<<1 | buffer[4]>>7);
|
||||
}
|
||||
|
||||
rc = hw_ostc3_transfer (device, NULL, S_UPGRADE, buffer, sizeof (buffer), NULL, 0, NODELAY);
|
||||
rc = hw_ostc3_transfer (device, NULL, S_UPGRADE, buffer, sizeof (buffer), NULL, 0, NULL, NODELAY);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (context, "Failed to send flash firmware command");
|
||||
return rc;
|
||||
@ -1391,7 +1447,7 @@ hw_ostc3_device_fwupdate3 (dc_device_t *abstract, const char *filename)
|
||||
|
||||
for (unsigned int len = 0; len < SZ_FIRMWARE; len += SZ_FIRMWARE_BLOCK) {
|
||||
char status[SZ_DISPLAY + 1]; // Status message on the display
|
||||
snprintf (status, sizeof(status), " Uploading %2d%%", (100 * len) / SZ_FIRMWARE);
|
||||
dc_platform_snprintf (status, sizeof(status), " Uploading %2d%%", (100 * len) / SZ_FIRMWARE);
|
||||
hw_ostc3_device_display (abstract, status);
|
||||
|
||||
rc = hw_ostc3_firmware_block_write (device, FIRMWARE_AREA + len, firmware->data + len, SZ_FIRMWARE_BLOCK);
|
||||
@ -1410,7 +1466,7 @@ hw_ostc3_device_fwupdate3 (dc_device_t *abstract, const char *filename)
|
||||
for (unsigned int len = 0; len < SZ_FIRMWARE; len += SZ_FIRMWARE_BLOCK) {
|
||||
unsigned char block[SZ_FIRMWARE_BLOCK];
|
||||
char status[SZ_DISPLAY + 1]; // Status message on the display
|
||||
snprintf (status, sizeof(status), " Verifying %2d%%", (100 * len) / SZ_FIRMWARE);
|
||||
dc_platform_snprintf (status, sizeof(status), " Verifying %2d%%", (100 * len) / SZ_FIRMWARE);
|
||||
hw_ostc3_device_display (abstract, status);
|
||||
|
||||
rc = hw_ostc3_firmware_block_read (device, FIRMWARE_AREA + len, block, sizeof (block));
|
||||
@ -1512,7 +1568,7 @@ hw_ostc3_device_fwupdate4 (dc_device_t *abstract, const char *filename)
|
||||
// Read the firmware version info.
|
||||
unsigned char fwinfo[SZ_FWINFO] = {0};
|
||||
status = hw_ostc3_transfer (device, NULL, S_FWINFO,
|
||||
data + offset + 4, 1, fwinfo, sizeof(fwinfo), NODELAY);
|
||||
data + offset + 4, 1, fwinfo, sizeof(fwinfo), NULL, NODELAY);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to read the firmware info.");
|
||||
goto error;
|
||||
@ -1525,7 +1581,7 @@ hw_ostc3_device_fwupdate4 (dc_device_t *abstract, const char *filename)
|
||||
!array_isequal(fwinfo, sizeof(fwinfo), 0xFF))
|
||||
{
|
||||
status = hw_ostc3_transfer (device, &progress, S_UPLOAD,
|
||||
data + offset, length, NULL, 0, usecs / 1000);
|
||||
data + offset, length, NULL, 0, NULL, usecs / 1000);
|
||||
if (status != DC_STATUS_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -81,8 +81,6 @@
|
||||
|
||||
#define OSTC4 0x3B
|
||||
|
||||
#define UNSUPPORTED 0xFFFFFFFF
|
||||
|
||||
#define OSTC3FW(major,minor) ( \
|
||||
(((major) & 0xFF) << 8) | \
|
||||
((minor) & 0xFF))
|
||||
@ -112,15 +110,17 @@ typedef struct hw_ostc_layout_t {
|
||||
unsigned int salinity;
|
||||
unsigned int avgdepth;
|
||||
unsigned int duration;
|
||||
unsigned int deco_info1;
|
||||
unsigned int deco_info2;
|
||||
unsigned int deco_model;
|
||||
unsigned int gf;
|
||||
unsigned int decomodel;
|
||||
unsigned int divemode;
|
||||
} hw_ostc_layout_t;
|
||||
|
||||
typedef struct hw_ostc_gasmix_t {
|
||||
unsigned int oxygen;
|
||||
unsigned int helium;
|
||||
unsigned int type;
|
||||
unsigned int enabled;
|
||||
unsigned int diluent;
|
||||
} hw_ostc_gasmix_t;
|
||||
|
||||
typedef struct hw_ostc_parser_t {
|
||||
@ -150,6 +150,9 @@ static const dc_parser_vtable_t hw_ostc_parser_vtable = {
|
||||
sizeof(hw_ostc_parser_t),
|
||||
DC_FAMILY_HW_OSTC,
|
||||
hw_ostc_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
hw_ostc_parser_get_datetime, /* datetime */
|
||||
hw_ostc_parser_get_field, /* fields */
|
||||
hw_ostc_parser_samples_foreach, /* samples_foreach */
|
||||
@ -169,9 +172,8 @@ static const hw_ostc_layout_t hw_ostc_layout_ostc = {
|
||||
43, /* salinity */
|
||||
45, /* avgdepth */
|
||||
47, /* duration */
|
||||
49, /* deco_info1 */
|
||||
50, /* deco_info1 */
|
||||
0, /* deco_model */
|
||||
49, /* gf */
|
||||
UNDEFINED, /* decomodel */
|
||||
51, /* divemode */
|
||||
};
|
||||
|
||||
@ -188,9 +190,8 @@ static const hw_ostc_layout_t hw_ostc_layout_frog = {
|
||||
43, /* salinity */
|
||||
45, /* avgdepth */
|
||||
47, /* duration */
|
||||
49, /* deco_info1 */
|
||||
50, /* deco_info2 */
|
||||
0, /* deco_model */
|
||||
49, /* gf */
|
||||
UNDEFINED, /* decomodel */
|
||||
51, /* divemode */
|
||||
};
|
||||
|
||||
@ -207,14 +208,13 @@ static const hw_ostc_layout_t hw_ostc_layout_ostc3 = {
|
||||
70, /* salinity */
|
||||
73, /* avgdepth */
|
||||
75, /* duration */
|
||||
77, /* deco_info1 */
|
||||
78, /* deco_info2 */
|
||||
79, /* deco_model */
|
||||
77, /* gf */
|
||||
79, /* decomodel */
|
||||
82, /* divemode */
|
||||
};
|
||||
|
||||
static unsigned int
|
||||
hw_ostc_find_gasmix (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he, unsigned int type)
|
||||
hw_ostc_find_gasmix (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he, unsigned int dil, unsigned int type)
|
||||
{
|
||||
unsigned int offset = 0;
|
||||
unsigned int count = parser->ngasmixes;
|
||||
@ -226,7 +226,7 @@ hw_ostc_find_gasmix (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he,
|
||||
|
||||
unsigned int i = offset;
|
||||
while (i < count) {
|
||||
if (o2 == parser->gasmix[i].oxygen && he == parser->gasmix[i].helium)
|
||||
if (o2 == parser->gasmix[i].oxygen && he == parser->gasmix[i].helium && dil == parser->gasmix[i].diluent)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
@ -234,6 +234,18 @@ hw_ostc_find_gasmix (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he,
|
||||
return i;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
hw_ostc_is_ccr (unsigned int divemode, unsigned int version)
|
||||
{
|
||||
if (version == 0x21) {
|
||||
return divemode == OSTC_ZHL16_CC || divemode == OSTC_ZHL16_CC_GF || divemode == OSTC_PSCR_GF;
|
||||
} else if (version == 0x23 || version == 0x24) {
|
||||
return divemode == OSTC3_CC || divemode == OSTC3_PSCR;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
hw_ostc_parser_cache (hw_ostc_parser_t *parser)
|
||||
{
|
||||
@ -282,6 +294,13 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
// Get the dive mode.
|
||||
unsigned int divemode = layout->divemode < header ?
|
||||
data[layout->divemode] : UNDEFINED;
|
||||
|
||||
// Get the CCR mode.
|
||||
unsigned int ccr = hw_ostc_is_ccr (divemode, version);
|
||||
|
||||
// Get all the gas mixes, the index of the inital mix,
|
||||
// the initial setpoint (used in the fixed setpoint CCR mode),
|
||||
// and the initial CNS from the header
|
||||
@ -298,19 +317,25 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
|
||||
for (unsigned int i = 0; i < ngasmixes; ++i) {
|
||||
gasmix[i].oxygen = data[25 + 2 * i];
|
||||
gasmix[i].helium = 0;
|
||||
gasmix[i].type = 0;
|
||||
gasmix[i].enabled = 1;
|
||||
gasmix[i].diluent = 0;
|
||||
}
|
||||
} else if (version == 0x23 || version == 0x24) {
|
||||
ngasmixes = 5;
|
||||
for (unsigned int i = 0; i < ngasmixes; ++i) {
|
||||
gasmix[i].oxygen = data[28 + 4 * i + 0];
|
||||
gasmix[i].helium = data[28 + 4 * i + 1];
|
||||
gasmix[i].type = data[28 + 4 * i + 3];
|
||||
gasmix[i].enabled = gasmix[i].type != 0;
|
||||
gasmix[i].diluent = ccr;
|
||||
// Find the first gas marked as the initial gas.
|
||||
if (initial == UNDEFINED && data[28 + 4 * i + 3] == 1) {
|
||||
initial = i + 1; /* One based index! */
|
||||
}
|
||||
}
|
||||
// The first fixed setpoint is the initial setpoint in CCR mode.
|
||||
if (data[layout->divemode] == OSTC3_CC || data[layout->divemode] == OSTC3_PSCR) {
|
||||
if (ccr) {
|
||||
initial_setpoint = data[60];
|
||||
}
|
||||
// Initial CNS
|
||||
@ -323,6 +348,13 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
|
||||
for (unsigned int i = 0; i < ngasmixes; ++i) {
|
||||
gasmix[i].oxygen = data[19 + 2 * i + 0];
|
||||
gasmix[i].helium = data[19 + 2 * i + 1];
|
||||
gasmix[i].type = 0;
|
||||
if (version == 0x21) {
|
||||
gasmix[i].enabled = data[53] & (1 << i);
|
||||
} else {
|
||||
gasmix[i].enabled = 1;
|
||||
}
|
||||
gasmix[i].diluent = ccr;
|
||||
}
|
||||
}
|
||||
if (initial != UNDEFINED) {
|
||||
@ -382,6 +414,9 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
|
||||
for (unsigned int i = 0; i < NGASMIXES; ++i) {
|
||||
parser->gasmix[i].oxygen = 0;
|
||||
parser->gasmix[i].helium = 0;
|
||||
parser->gasmix[i].type = 0;
|
||||
parser->gasmix[i].enabled = 0;
|
||||
parser->gasmix[i].diluent = 0;
|
||||
}
|
||||
parser->serial = serial;
|
||||
|
||||
@ -421,6 +456,9 @@ hw_ostc_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsig
|
||||
for (unsigned int i = 0; i < NGASMIXES; ++i) {
|
||||
parser->gasmix[i].oxygen = 0;
|
||||
parser->gasmix[i].helium = 0;
|
||||
parser->gasmix[i].type = 0;
|
||||
parser->gasmix[i].enabled = 0;
|
||||
parser->gasmix[i].diluent = 0;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
@ -512,6 +550,7 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
|
||||
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||
dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
|
||||
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||
|
||||
unsigned int salinity = data[layout->salinity];
|
||||
@ -615,6 +654,70 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_DECOMODEL:
|
||||
if (version == 0x21) {
|
||||
switch (data[layout->divemode]) {
|
||||
case OSTC_APNEA:
|
||||
case OSTC_GAUGE:
|
||||
decomodel->type = DC_DECOMODEL_NONE;
|
||||
break;
|
||||
case OSTC_ZHL16_OC:
|
||||
case OSTC_ZHL16_CC:
|
||||
decomodel->type = DC_DECOMODEL_BUHLMANN;
|
||||
decomodel->params.gf.low = 100;
|
||||
decomodel->params.gf.high = 100;
|
||||
break;
|
||||
case OSTC_ZHL16_OC_GF:
|
||||
case OSTC_ZHL16_CC_GF:
|
||||
case OSTC_PSCR_GF:
|
||||
decomodel->type = DC_DECOMODEL_BUHLMANN;
|
||||
decomodel->params.gf.low = data[layout->gf + 0];
|
||||
decomodel->params.gf.high = data[layout->gf + 1];
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
} else if (version == 0x22) {
|
||||
switch (data[layout->divemode]) {
|
||||
case FROG_ZHL16:
|
||||
decomodel->type = DC_DECOMODEL_BUHLMANN;
|
||||
decomodel->params.gf.low = 100;
|
||||
decomodel->params.gf.high = 100;
|
||||
break;
|
||||
case FROG_ZHL16_GF:
|
||||
decomodel->type = DC_DECOMODEL_BUHLMANN;
|
||||
decomodel->params.gf.low = data[layout->gf + 0];
|
||||
decomodel->params.gf.high = data[layout->gf + 1];
|
||||
break;
|
||||
case FROG_APNEA:
|
||||
decomodel->type = DC_DECOMODEL_NONE;
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
} else if (version == 0x23 || version == 0x24) {
|
||||
switch (data[layout->decomodel]) {
|
||||
case OSTC3_ZHL16:
|
||||
decomodel->type = DC_DECOMODEL_BUHLMANN;
|
||||
decomodel->params.gf.low = 100;
|
||||
decomodel->params.gf.high = 100;
|
||||
break;
|
||||
case OSTC3_ZHL16_GF:
|
||||
decomodel->type = DC_DECOMODEL_BUHLMANN;
|
||||
decomodel->params.gf.low = data[layout->gf + 0];
|
||||
decomodel->params.gf.high = data[layout->gf + 1];
|
||||
break;
|
||||
case OSTC4_VPM:
|
||||
decomodel->type = DC_DECOMODEL_VPM;
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
} else {
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
decomodel->conservatism = 0;
|
||||
break;
|
||||
case DC_FIELD_STRING:
|
||||
switch(flags) {
|
||||
case 0: /* serial */
|
||||
@ -657,11 +760,11 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
|
||||
|
||||
case 4: /* Deco model */
|
||||
string->desc = "Deco model";
|
||||
if (((version == 0x23 || version == 0x24) && data[layout->deco_model] == OSTC3_ZHL16) ||
|
||||
if (((version == 0x23 || version == 0x24) && data[layout->decomodel] == OSTC3_ZHL16) ||
|
||||
(version == 0x22 && data[layout->divemode] == FROG_ZHL16) ||
|
||||
(version == 0x21 && (data[layout->divemode] == OSTC_ZHL16_OC || data[layout->divemode] == OSTC_ZHL16_CC)))
|
||||
strncpy(buf, "ZH-L16", BUFLEN);
|
||||
else if (((version == 0x23 || version == 0x24) && data[layout->deco_model] == OSTC3_ZHL16_GF) ||
|
||||
else if (((version == 0x23 || version == 0x24) && data[layout->decomodel] == OSTC3_ZHL16_GF) ||
|
||||
(version == 0x22 && data[layout->divemode] == FROG_ZHL16_GF) ||
|
||||
(version == 0x21 && (data[layout->divemode] == OSTC_ZHL16_OC_GF || data[layout->divemode] == OSTC_ZHL16_CC_GF)))
|
||||
strncpy(buf, "ZH-L16-GF", BUFLEN);
|
||||
@ -672,14 +775,14 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
|
||||
break;
|
||||
case 5: /* Deco model info */
|
||||
string->desc = "Deco model info";
|
||||
if (((version == 0x23 || version == 0x24) && data[layout->deco_model] == OSTC3_ZHL16) ||
|
||||
if (((version == 0x23 || version == 0x24) && data[layout->decomodel] == OSTC3_ZHL16) ||
|
||||
(version == 0x22 && data[layout->divemode] == FROG_ZHL16) ||
|
||||
(version == 0x21 && (data[layout->divemode] == OSTC_ZHL16_OC || data[layout->divemode] == OSTC_ZHL16_CC)))
|
||||
snprintf(buf, BUFLEN, "Saturation %u, Desaturation %u", data[layout->deco_info1], data[layout->deco_info2]);
|
||||
else if (((version == 0x23 || version == 0x24) && data[layout->deco_model] == OSTC3_ZHL16_GF) ||
|
||||
snprintf(buf, BUFLEN, "Saturation %u, Desaturation %u", data[layout->gf], data[layout->gf+1]);
|
||||
else if (((version == 0x23 || version == 0x24) && data[layout->decomodel] == OSTC3_ZHL16_GF) ||
|
||||
(version == 0x22 && data[layout->divemode] == FROG_ZHL16_GF) ||
|
||||
(version == 0x21 && (data[layout->divemode] == OSTC_ZHL16_OC_GF || data[layout->divemode] == OSTC_ZHL16_CC_GF)))
|
||||
snprintf(buf, BUFLEN, "GF %u/%u", data[layout->deco_info1], data[layout->deco_info2]);
|
||||
snprintf(buf, BUFLEN, "GF %u/%u", data[layout->gf], data[layout->gf+1]);
|
||||
else
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
break;
|
||||
@ -714,8 +817,10 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
|
||||
const hw_ostc_layout_t *layout = parser->layout;
|
||||
|
||||
// Exit if no profile data available.
|
||||
if (size == header || (size == header + 2 &&
|
||||
data[header] == 0xFD && data[header + 1] == 0xFD)) {
|
||||
const unsigned char empty[] = {0x08, 0x00, 0x00, 0xFD, 0xFD};
|
||||
if (size == header ||
|
||||
(size == header + 2 && memcmp(data + header, empty + 3, 2) == 0) ||
|
||||
(size == header + 5 && memcmp(data + header, empty, 5) == 0)) {
|
||||
parser->cached = PROFILE;
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
@ -803,6 +908,13 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
|
||||
firmware = array_uint16_be (data + layout->firmware);
|
||||
}
|
||||
|
||||
// Get the dive mode.
|
||||
unsigned int divemode = layout->divemode < header ?
|
||||
data[layout->divemode] : UNDEFINED;
|
||||
|
||||
// Get the CCR mode.
|
||||
unsigned int ccr = hw_ostc_is_ccr (divemode, version);
|
||||
|
||||
unsigned int time = 0;
|
||||
unsigned int nsamples = 0;
|
||||
unsigned int tank = parser->initial != UNDEFINED ? parser->initial : 0;
|
||||
@ -910,7 +1022,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
|
||||
}
|
||||
unsigned int o2 = data[offset];
|
||||
unsigned int he = data[offset + 1];
|
||||
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, MANUAL);
|
||||
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, ccr, MANUAL);
|
||||
if (idx >= parser->ngasmixes) {
|
||||
if (idx >= NGASMIXES) {
|
||||
ERROR (abstract->context, "Maximum number of gas mixes reached.");
|
||||
@ -918,6 +1030,9 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
|
||||
}
|
||||
parser->gasmix[idx].oxygen = o2;
|
||||
parser->gasmix[idx].helium = he;
|
||||
parser->gasmix[idx].type = 0;
|
||||
parser->gasmix[idx].enabled = 1;
|
||||
parser->gasmix[idx].diluent = ccr;
|
||||
parser->ngasmixes = idx + 1;
|
||||
}
|
||||
|
||||
@ -934,8 +1049,8 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
unsigned int idx = data[offset];
|
||||
if (idx < 1 || idx > parser->ngasmixes) {
|
||||
ERROR(abstract->context, "Invalid gas mix.");
|
||||
if (idx < 1 || idx > parser->nfixed) {
|
||||
ERROR(abstract->context, "Invalid gas mix (%u).", idx);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
idx--; /* Convert to a zero based index. */
|
||||
@ -968,7 +1083,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
|
||||
|
||||
unsigned int o2 = data[offset];
|
||||
unsigned int he = data[offset + 1];
|
||||
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, MANUAL);
|
||||
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, 0, MANUAL);
|
||||
if (idx >= parser->ngasmixes) {
|
||||
if (idx >= NGASMIXES) {
|
||||
ERROR (abstract->context, "Maximum number of gas mixes reached.");
|
||||
@ -976,6 +1091,9 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
|
||||
}
|
||||
parser->gasmix[idx].oxygen = o2;
|
||||
parser->gasmix[idx].helium = he;
|
||||
parser->gasmix[idx].type = 0;
|
||||
parser->gasmix[idx].enabled = 1;
|
||||
parser->gasmix[idx].diluent = 0;
|
||||
parser->ngasmixes = idx + 1;
|
||||
}
|
||||
|
||||
@ -1098,7 +1216,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
|
||||
|
||||
unsigned int o2 = data[offset];
|
||||
unsigned int he = data[offset + 1];
|
||||
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, MANUAL);
|
||||
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, 0, MANUAL);
|
||||
if (idx >= parser->ngasmixes) {
|
||||
if (idx >= NGASMIXES) {
|
||||
ERROR (abstract->context, "Maximum number of gas mixes reached.");
|
||||
@ -1106,6 +1224,9 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
|
||||
}
|
||||
parser->gasmix[idx].oxygen = o2;
|
||||
parser->gasmix[idx].helium = he;
|
||||
parser->gasmix[idx].type = 0;
|
||||
parser->gasmix[idx].enabled = 1;
|
||||
parser->gasmix[idx].diluent = 0;
|
||||
parser->ngasmixes = idx + 1;
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,6 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h> // malloc, free
|
||||
#include <stdio.h> // snprintf
|
||||
#include <string.h>
|
||||
|
||||
#include "socket.h"
|
||||
@ -314,7 +313,7 @@ dc_irda_open (dc_iostream_t **out, dc_context_t *context, unsigned int address,
|
||||
peer.irdaDeviceID[1] = (address >> 8) & 0xFF;
|
||||
peer.irdaDeviceID[2] = (address >> 16) & 0xFF;
|
||||
peer.irdaDeviceID[3] = (address >> 24) & 0xFF;
|
||||
snprintf (peer.irdaServiceName, sizeof(peer.irdaServiceName), "LSAP-SEL%u", lsap);
|
||||
dc_platform_snprintf (peer.irdaServiceName, sizeof(peer.irdaServiceName), "LSAP-SEL%u", lsap);
|
||||
#else
|
||||
struct sockaddr_irda peer;
|
||||
peer.sir_family = AF_IRDA;
|
||||
|
||||
@ -89,6 +89,9 @@ dc_custom_open
|
||||
|
||||
dc_parser_new
|
||||
dc_parser_new2
|
||||
dc_parser_set_clock
|
||||
dc_parser_set_atmospheric
|
||||
dc_parser_set_density
|
||||
dc_parser_get_type
|
||||
dc_parser_set_data
|
||||
dc_parser_get_datetime
|
||||
|
||||
@ -66,6 +66,9 @@
|
||||
#define TEC 2
|
||||
#define REC 3
|
||||
|
||||
#define ZHL16GF 0
|
||||
#define RGBM 1
|
||||
|
||||
#define NORMAL 0
|
||||
#define BOOKMARK 1
|
||||
#define ALARM_DEPTH 2
|
||||
@ -123,6 +126,9 @@ static const dc_parser_vtable_t liquivision_lynx_parser_vtable = {
|
||||
sizeof(liquivision_lynx_parser_t),
|
||||
DC_FAMILY_LIQUIVISION_LYNX,
|
||||
liquivision_lynx_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
liquivision_lynx_parser_get_datetime, /* datetime */
|
||||
liquivision_lynx_parser_get_field, /* fields */
|
||||
liquivision_lynx_parser_samples_foreach, /* samples_foreach */
|
||||
@ -231,6 +237,7 @@ liquivision_lynx_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
dc_tank_t *tank = (dc_tank_t *) value;
|
||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||
dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
|
||||
|
||||
if (value) {
|
||||
switch (type) {
|
||||
@ -288,6 +295,22 @@ liquivision_lynx_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_DECOMODEL:
|
||||
switch (abstract->data[93]) {
|
||||
case ZHL16GF:
|
||||
decomodel->type = DC_DECOMODEL_BUHLMANN;
|
||||
decomodel->conservatism = 0;
|
||||
decomodel->params.gf.low = 0;
|
||||
decomodel->params.gf.high = 0;
|
||||
break;
|
||||
case RGBM:
|
||||
decomodel->type = DC_DECOMODEL_RGBM;
|
||||
decomodel->conservatism = 0;
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_GASMIX_COUNT:
|
||||
*((unsigned int *) value) = parser->ngasmixes;
|
||||
break;
|
||||
|
||||
@ -56,6 +56,9 @@ static const dc_parser_vtable_t mares_darwin_parser_vtable = {
|
||||
sizeof(mares_darwin_parser_t),
|
||||
DC_FAMILY_MARES_DARWIN,
|
||||
mares_darwin_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
mares_darwin_parser_get_datetime, /* datetime */
|
||||
mares_darwin_parser_get_field, /* fields */
|
||||
mares_darwin_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -276,6 +276,9 @@ static const dc_parser_vtable_t mares_iconhd_parser_vtable = {
|
||||
sizeof(mares_iconhd_parser_t),
|
||||
DC_FAMILY_MARES_ICONHD,
|
||||
mares_iconhd_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
mares_iconhd_parser_get_datetime, /* datetime */
|
||||
mares_iconhd_parser_get_field, /* fields */
|
||||
mares_iconhd_parser_samples_foreach, /* samples_foreach */
|
||||
@ -1089,6 +1092,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
unsigned int depth = 0, temperature = 0;
|
||||
unsigned int gasmix = 0, alarms = 0;
|
||||
unsigned int decostop = 0, decodepth = 0, decotime = 0, tts = 0;
|
||||
unsigned int bookmark = 0;
|
||||
if (parser->model == GENIUS || parser->model == HORIZON) {
|
||||
if (parser->logformat == 1) {
|
||||
if (!mares_genius_isvalid (data + offset, SDPT_SIZE, SDPT_TYPE)) {
|
||||
@ -1102,6 +1106,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
alarms = array_uint32_le (data + offset + marker + 0x14);
|
||||
misc = array_uint32_le (data + offset + marker + 0x18);
|
||||
deco = array_uint32_le (data + offset + marker + 0x1C);
|
||||
bookmark = (misc >> 2) & 0x0F;
|
||||
gasmix = (misc >> 6) & 0x0F;
|
||||
decostop = (misc >> 10) & 0x01;
|
||||
if (decostop) {
|
||||
@ -1123,6 +1128,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
decotime = array_uint16_le (data + offset + marker + 0x0A);
|
||||
alarms = array_uint32_le (data + offset + marker + 0x0C);
|
||||
misc = array_uint32_le (data + offset + marker + 0x14);
|
||||
bookmark = (misc >> 2) & 0x0F;
|
||||
gasmix = (misc >> 6) & 0x0F;
|
||||
decostop = (misc >> 18) & 0x01;
|
||||
decodepth = (misc >> 19) & 0x7F;
|
||||
@ -1159,6 +1165,15 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
}
|
||||
}
|
||||
|
||||
// Bookmark
|
||||
if (bookmark) {
|
||||
sample.event.type = SAMPLE_EVENT_BOOKMARK;
|
||||
sample.event.time = 0;
|
||||
sample.event.flags = 0;
|
||||
sample.event.value = bookmark;
|
||||
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
|
||||
}
|
||||
|
||||
if (parser->model == GENIUS || parser->model == HORIZON) {
|
||||
// Deco stop / NDL.
|
||||
if (decostop) {
|
||||
|
||||
@ -68,6 +68,9 @@ static const dc_parser_vtable_t mares_nemo_parser_vtable = {
|
||||
sizeof(mares_nemo_parser_t),
|
||||
DC_FAMILY_MARES_NEMO,
|
||||
mares_nemo_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
mares_nemo_parser_get_datetime, /* datetime */
|
||||
mares_nemo_parser_get_field, /* fields */
|
||||
mares_nemo_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -471,11 +471,6 @@ mclean_extreme_device_timesync(dc_device_t *abstract, const dc_datetime_t *datet
|
||||
{
|
||||
mclean_extreme_device_t *device = (mclean_extreme_device_t *)abstract;
|
||||
|
||||
if (datetime == NULL) {
|
||||
ERROR(abstract->context, "Invalid parameter specified.");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
// Get the UTC timestamp.
|
||||
dc_ticks_t ticks = dc_datetime_mktime(datetime);
|
||||
if (ticks == -1 || ticks < EPOCH || ticks - EPOCH > 0xFFFFFFFF) {
|
||||
|
||||
@ -67,6 +67,9 @@ static const dc_parser_vtable_t mclean_extreme_parser_vtable = {
|
||||
sizeof(mclean_extreme_parser_t),
|
||||
DC_FAMILY_MCLEAN_EXTREME,
|
||||
mclean_extreme_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
mclean_extreme_parser_get_datetime, /* datetime */
|
||||
mclean_extreme_parser_get_field, /* fields */
|
||||
mclean_extreme_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -35,14 +35,6 @@
|
||||
|
||||
#define ISINSTANCE(device) dc_device_isinstance((device), &oceanic_atom2_device_vtable.base)
|
||||
|
||||
#define PROPLUSX 0x4552
|
||||
#define VTX 0x4557
|
||||
#define I750TC 0x455A
|
||||
#define SAGE 0x4647
|
||||
#define I770R 0x4651
|
||||
#define GEO40 0x4653
|
||||
#define BEACON 0x4742
|
||||
|
||||
#define MAXPACKET 256
|
||||
#define MAXRETRIES 2
|
||||
#define MAXDELAY 16
|
||||
@ -62,9 +54,13 @@
|
||||
#define ACK 0x5A
|
||||
#define NAK 0xA5
|
||||
|
||||
#define REPEAT 50
|
||||
|
||||
typedef struct oceanic_atom2_device_t {
|
||||
oceanic_common_device_t base;
|
||||
dc_iostream_t *iostream;
|
||||
unsigned int handshake_repeat;
|
||||
unsigned int handshake_counter;
|
||||
unsigned int sequence;
|
||||
unsigned int delay;
|
||||
unsigned int extra;
|
||||
@ -365,7 +361,7 @@ static const oceanic_common_layout_t oceanic_proplusx_layout = {
|
||||
};
|
||||
|
||||
static const oceanic_common_layout_t aqualung_i770r_layout = {
|
||||
0x440000, /* memsize */
|
||||
0x640000, /* memsize */
|
||||
0x40000, /* highmem */
|
||||
0x0000, /* cf_devinfo */
|
||||
0x0040, /* cf_pointers */
|
||||
@ -373,7 +369,7 @@ static const oceanic_common_layout_t aqualung_i770r_layout = {
|
||||
0x10000, /* rb_logbook_end */
|
||||
16, /* rb_logbook_entry_size */
|
||||
0x40000, /* rb_profile_begin */
|
||||
0x440000, /* rb_profile_end */
|
||||
0x640000, /* rb_profile_end */
|
||||
0, /* pt_mode_global */
|
||||
1, /* pt_mode_logbook */
|
||||
0, /* pt_mode_serial */
|
||||
@ -410,100 +406,103 @@ static const oceanic_common_layout_t aqualung_i450t_layout = {
|
||||
};
|
||||
|
||||
static const oceanic_common_version_t versions[] = {
|
||||
{"OCEVEO10 \0\0 8K", 0, &oceanic_veo1_layout},
|
||||
{"AERIS XR1 NX R\0\0", 0, &oceanic_veo1_layout},
|
||||
{"OCEVEO10 \0\0 8K", 0, VEO10, &oceanic_veo1_layout},
|
||||
{"AERIS XR1 NX R\0\0", 0, XR1NX, &oceanic_veo1_layout},
|
||||
|
||||
{"ATOM rev\0\0 256K", 0, &oceanic_atom1_layout},
|
||||
{"ATOM rev\0\0 256K", 0, ATOM1, &oceanic_atom1_layout},
|
||||
|
||||
{"MANTA R\0\0 512K", 0x3242, &oceanic_atom2a_layout},
|
||||
{"MANTA R\0\0 512K", 0, &oceanic_atom2c_layout},
|
||||
{"2M ATOM r\0\0 512K", 0x3349, &oceanic_atom2a_layout},
|
||||
{"2M ATOM r\0\0 512K", 0, &oceanic_atom2c_layout},
|
||||
{"MANTA R\0\0 512K", 0x3242, MANTA, &oceanic_atom2a_layout},
|
||||
{"MANTA R\0\0 512K", 0, MANTA, &oceanic_atom2c_layout},
|
||||
{"2M ATOM r\0\0 512K", 0x3349, ATOM2, &oceanic_atom2a_layout},
|
||||
{"2M ATOM r\0\0 512K", 0, ATOM2, &oceanic_atom2c_layout},
|
||||
|
||||
{"INSIGHT2 \0\0 512K", 0, &oceanic_atom2a_layout},
|
||||
{"OCEVEO30 \0\0 512K", 0, &oceanic_atom2a_layout},
|
||||
{"ATMOSAI R\0\0 512K", 0, &oceanic_atom2a_layout},
|
||||
{"PROPLUS2 \0\0 512K", 0, &oceanic_atom2a_layout},
|
||||
{"OCEGEO20 \0\0 512K", 0, &oceanic_atom2a_layout},
|
||||
{"OCE GEO R\0\0 512K", 0, &oceanic_atom2a_layout},
|
||||
{"AQUAI200 \0\0 512K", 0, &oceanic_atom2a_layout},
|
||||
{"AQUA200C \0\0 512K", 0, &oceanic_atom2a_layout},
|
||||
{"INSIGHT2 \0\0 512K", 0, INSIGHT2, &oceanic_atom2a_layout},
|
||||
{"OCEVEO30 \0\0 512K", 0, VEO30, &oceanic_atom2a_layout},
|
||||
{"ATMOSAI R\0\0 512K", 0, ATMOSAI2, &oceanic_atom2a_layout},
|
||||
{"PROPLUS2 \0\0 512K", 0, PROPLUS21, &oceanic_atom2a_layout},
|
||||
{"OCEGEO20 \0\0 512K", 0, GEO20, &oceanic_atom2a_layout},
|
||||
{"OCE GEO R\0\0 512K", 0, GEO, &oceanic_atom2a_layout},
|
||||
{"AQUAI200 \0\0 512K", 0, I200, &oceanic_atom2a_layout},
|
||||
{"AQUA200C \0\0 512K", 0, I200C, &oceanic_atom2a_layout},
|
||||
|
||||
{"ELEMENT2 \0\0 512K", 0, &oceanic_atom2b_layout},
|
||||
{"OCEVEO20 \0\0 512K", 0, &oceanic_atom2b_layout},
|
||||
{"TUSAZEN \0\0 512K", 0, &oceanic_atom2b_layout},
|
||||
{"AQUAI300 \0\0 512K", 0, &oceanic_atom2b_layout},
|
||||
{"HOLLDG03 \0\0 512K", 0, &oceanic_atom2b_layout},
|
||||
{"AQUAI100 \0\0 512K", 0, &oceanic_atom2b_layout},
|
||||
{"AQUA300C \0\0 512K", 0, &oceanic_atom2b_layout},
|
||||
{"OCEGEO40 \0\0 512K", 0, &oceanic_atom2b_layout},
|
||||
{"VEOSMART \0\0 512K", 0, &oceanic_atom2b_layout},
|
||||
{"ELEMENT2 \0\0 512K", 0, ELEMENT2, &oceanic_atom2b_layout},
|
||||
{"OCEVEO20 \0\0 512K", 0, VEO20, &oceanic_atom2b_layout},
|
||||
{"TUSAZEN \0\0 512K", 0, ZEN, &oceanic_atom2b_layout},
|
||||
{"AQUAI300 \0\0 512K", 0, I300, &oceanic_atom2b_layout},
|
||||
{"HOLLDG03 \0\0 512K", 0, DG03, &oceanic_atom2b_layout},
|
||||
{"AQUAI100 \0\0 512K", 0, I100, &oceanic_atom2b_layout},
|
||||
{"AQUA300C \0\0 512K", 0, I300C, &oceanic_atom2b_layout},
|
||||
{"OCEGEO40 \0\0 512K", 0, GEO40, &oceanic_atom2b_layout},
|
||||
{"VEOSMART \0\0 512K", 0, VEO40, &oceanic_atom2b_layout},
|
||||
|
||||
{"2M EPIC r\0\0 512K", 0, &oceanic_atom2c_layout},
|
||||
{"EPIC1 R\0\0 512K", 0, &oceanic_atom2c_layout},
|
||||
{"AERIA300 \0\0 512K", 0, &oceanic_atom2c_layout},
|
||||
{"2M EPIC r\0\0 512K", 0, EPICA, &oceanic_atom2c_layout},
|
||||
{"EPIC1 R\0\0 512K", 0, EPICB, &oceanic_atom2c_layout},
|
||||
{"AERIA300 \0\0 512K", 0, A300, &oceanic_atom2c_layout},
|
||||
|
||||
{"OCE VT3 R\0\0 512K", 0, &oceanic_default_layout},
|
||||
{"ELITET3 R\0\0 512K", 0, &oceanic_default_layout},
|
||||
{"ELITET31 \0\0 512K", 0, &oceanic_default_layout},
|
||||
{"DATAMASK \0\0 512K", 0, &oceanic_default_layout},
|
||||
{"COMPMASK \0\0 512K", 0, &oceanic_default_layout},
|
||||
{"OCE VT3 R\0\0 512K", 0, VT3, &oceanic_default_layout},
|
||||
{"ELITET3 R\0\0 512K", 0, T3A, &oceanic_default_layout},
|
||||
{"ELITET31 \0\0 512K", 0, T3B, &oceanic_default_layout},
|
||||
{"DATAMASK \0\0 512K", 0, DATAMASK, &oceanic_default_layout},
|
||||
{"COMPMASK \0\0 512K", 0, COMPUMASK, &oceanic_default_layout},
|
||||
|
||||
{"WISDOM R\0\0 512K", 0, &sherwood_wisdom_layout},
|
||||
{"WISDOM R\0\0 512K", 0x3342, WISDOM3, &sherwood_wisdom_layout},
|
||||
{"WISDOM R\0\0 512K", 0, WISDOM2, &sherwood_wisdom_layout},
|
||||
|
||||
{"PROPLUS3 \0\0 512K", 0, &oceanic_proplus3_layout},
|
||||
{"PROPLUS4 \0\0 512K", 0, &oceanic_proplus3_layout},
|
||||
{"PROPLUS3 \0\0 512K", 0, PROPLUS3, &oceanic_proplus3_layout},
|
||||
{"PROPLUS4 \0\0 512K", 0, PROPLUS4, &oceanic_proplus3_layout},
|
||||
|
||||
{"TUZENAIR \0\0 512K", 0, &tusa_zenair_layout},
|
||||
{"AMPHOSSW \0\0 512K", 0, &tusa_zenair_layout},
|
||||
{"AMPHOAIR \0\0 512K", 0, &tusa_zenair_layout},
|
||||
{"VOYAGE2G \0\0 512K", 0, &tusa_zenair_layout},
|
||||
{"TUSTALIS \0\0 512K", 0, &tusa_zenair_layout},
|
||||
{"AMPHOS20 \0\0 512K", 0, &tusa_zenair_layout},
|
||||
{"AMPAIR20 \0\0 512K", 0, &tusa_zenair_layout},
|
||||
{"TUZENAIR \0\0 512K", 0, ZENAIR, &tusa_zenair_layout},
|
||||
{"AMPHOSSW \0\0 512K", 0, AMPHOS, &tusa_zenair_layout},
|
||||
{"AMPHOAIR \0\0 512K", 0, AMPHOSAIR, &tusa_zenair_layout},
|
||||
{"VOYAGE2G \0\0 512K", 0, VOYAGER2G, &tusa_zenair_layout},
|
||||
{"TUSTALIS \0\0 512K", 0, TALIS, &tusa_zenair_layout},
|
||||
{"AMPHOS20 \0\0 512K", 0, AMPHOS2, &tusa_zenair_layout},
|
||||
{"AMPAIR20 \0\0 512K", 0, AMPHOSAIR2, &tusa_zenair_layout},
|
||||
|
||||
{"REACPRO2 \0\0 512K", 0, &oceanic_reactpro_layout},
|
||||
{"REACPRO2 \0\0 512K", 0, REACTPROWHITE, &oceanic_reactpro_layout},
|
||||
|
||||
{"FREEWAER \0\0 512K", 0, &aeris_f10_layout},
|
||||
{"OCEANF10 \0\0 512K", 0, &aeris_f10_layout},
|
||||
{"MUNDIAL R\0\0 512K", 0, &aeris_f10_layout},
|
||||
{"FREEWAER \0\0 512K", 0, F10A, &aeris_f10_layout},
|
||||
{"OCEANF10 \0\0 512K", 0, F10B, &aeris_f10_layout},
|
||||
{"MUNDIAL R\0\0 512K", 0x3300, MUNDIAL3, &aeris_f10_layout},
|
||||
{"MUNDIAL R\0\0 512K", 0, MUNDIAL2, &aeris_f10_layout},
|
||||
|
||||
{"AERISF11 \0\0 1024", 0, &aeris_f11_layout},
|
||||
{"OCEANF11 \0\0 1024", 0, &aeris_f11_layout},
|
||||
{"AERISF11 \0\0 1024", 0, F11A, &aeris_f11_layout},
|
||||
{"OCEANF11 \0\0 1024", 0, F11B, &aeris_f11_layout},
|
||||
|
||||
{"OCWATCH R\0\0 1024", 0, &oceanic_oc1_layout},
|
||||
{"OC1WATCH \0\0 1024", 0, &oceanic_oc1_layout},
|
||||
{"OCSWATCH \0\0 1024", 0, &oceanic_oc1_layout},
|
||||
{"AQUAI550 \0\0 1024", 0, &oceanic_oc1_layout},
|
||||
{"AQUA550C \0\0 1024", 0, &oceanic_oc1_layout},
|
||||
{"WISDOM04 \0\0 1024", 0, &oceanic_oc1_layout},
|
||||
{"AQUA470C \0\0 1024", 0, &oceanic_oc1_layout},
|
||||
{"AQUA200C \0\0 1024", 0, &oceanic_oc1_layout},
|
||||
{"OCWATCH R\0\0 1024", 0, OC1A, &oceanic_oc1_layout},
|
||||
{"OC1WATCH \0\0 1024", 0, OC1B, &oceanic_oc1_layout},
|
||||
{"OCSWATCH \0\0 1024", 0, OCS, &oceanic_oc1_layout},
|
||||
{"AQUAI550 \0\0 1024", 0, I550, &oceanic_oc1_layout},
|
||||
{"AQUA550C \0\0 1024", 0, I550C, &oceanic_oc1_layout},
|
||||
{"WISDOM04 \0\0 1024", 0, WISDOM4, &oceanic_oc1_layout},
|
||||
{"AQUA470C \0\0 1024", 0, I470TC, &oceanic_oc1_layout},
|
||||
{"AQUA200C \0\0 1024", 0, I200CV2, &oceanic_oc1_layout},
|
||||
{"GEOAIR \0\0 1024", 0, GEOAIR, &oceanic_oc1_layout},
|
||||
|
||||
{"OCEANOCI \0\0 1024", 0, &oceanic_oci_layout},
|
||||
{"OCEANOCI \0\0 1024", 0, OCI, &oceanic_oci_layout},
|
||||
|
||||
{"OCEATOM3 \0\0 1024", 0, &oceanic_atom3_layout},
|
||||
{"ATOM31 \0\0 1024", 0, &oceanic_atom3_layout},
|
||||
{"OCEATOM3 \0\0 1024", 0, ATOM3, &oceanic_atom3_layout},
|
||||
{"ATOM31 \0\0 1024", 0, ATOM31, &oceanic_atom3_layout},
|
||||
|
||||
{"OCEANVT4 \0\0 1024", 0, &oceanic_vt4_layout},
|
||||
{"OCEAVT41 \0\0 1024", 0, &oceanic_vt4_layout},
|
||||
{"AERISAIR \0\0 1024", 0, &oceanic_vt4_layout},
|
||||
{"SWVISION \0\0 1024", 0, &oceanic_vt4_layout},
|
||||
{"XPSUBAIR \0\0 1024", 0, &oceanic_vt4_layout},
|
||||
{"OCEANVT4 \0\0 1024", 0, VT4, &oceanic_vt4_layout},
|
||||
{"OCEAVT41 \0\0 1024", 0, VT41, &oceanic_vt4_layout},
|
||||
{"AERISAIR \0\0 1024", 0, A300AI, &oceanic_vt4_layout},
|
||||
{"SWVISION \0\0 1024", 0, VISION, &oceanic_vt4_layout},
|
||||
{"XPSUBAIR \0\0 1024", 0, XPAIR, &oceanic_vt4_layout},
|
||||
|
||||
{"HOLLDG04 \0\0 2048", 0, &hollis_tx1_layout},
|
||||
{"HOLLDG04 \0\0 2048", 0, TX1, &hollis_tx1_layout},
|
||||
|
||||
{"AER300CS \0\0 2048", 0, &aeris_a300cs_layout},
|
||||
{"OCEANVTX \0\0 2048", 0, &aeris_a300cs_layout},
|
||||
{"AQUAI750 \0\0 2048", 0, &aeris_a300cs_layout},
|
||||
{"SWDRAGON \0\0 2048", 0, &aeris_a300cs_layout},
|
||||
{"SWBEACON \0\0 2048", 0, &aeris_a300cs_layout},
|
||||
{"AER300CS \0\0 2048", 0, A300CS, &aeris_a300cs_layout},
|
||||
{"OCEANVTX \0\0 2048", 0, VTX, &aeris_a300cs_layout},
|
||||
{"AQUAI750 \0\0 2048", 0, I750TC, &aeris_a300cs_layout},
|
||||
{"SWDRAGON \0\0 2048", 0, SAGE, &aeris_a300cs_layout},
|
||||
{"SWBEACON \0\0 2048", 0, BEACON, &aeris_a300cs_layout},
|
||||
|
||||
{"AQUAI450 \0\0 2048", 0, &aqualung_i450t_layout},
|
||||
{"AQUAI450 \0\0 2048", 0, I450T, &aqualung_i450t_layout},
|
||||
|
||||
{"OCEANOCX \0\0 \0\0\0\0", 0, &oceanic_proplusx_layout},
|
||||
{"OCEANOCX \0\0 \0\0\0\0", 0, PROPLUSX, &oceanic_proplusx_layout},
|
||||
|
||||
{"AQUA770R \0\0 \0\0\0\0", 0, &aqualung_i770r_layout},
|
||||
{"AQUA770R \0\0 \0\0\0\0", 0, I770R, &aqualung_i770r_layout},
|
||||
};
|
||||
|
||||
/*
|
||||
@ -932,8 +931,8 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream
|
||||
}
|
||||
|
||||
// Detect the memory layout.
|
||||
device->base.layout = OCEANIC_COMMON_MATCH(device->base.version, versions, &device->base.firmware);
|
||||
if (device->base.layout == NULL) {
|
||||
const oceanic_common_version_t *version = OCEANIC_COMMON_MATCH(device->base.version, versions, &device->base.firmware);
|
||||
if (version == NULL) {
|
||||
WARNING (context, "Unsupported device detected (%s)!", device->base.version);
|
||||
if (memcmp(device->base.version + 12, "256K", 4) == 0) {
|
||||
device->base.layout = &oceanic_atom1_layout;
|
||||
@ -946,10 +945,15 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream
|
||||
} else {
|
||||
device->base.layout = &oceanic_default_layout;
|
||||
}
|
||||
device->base.model = 0;
|
||||
} else {
|
||||
device->base.layout = version->layout;
|
||||
device->base.model = version->model;
|
||||
}
|
||||
|
||||
// Set the big page support.
|
||||
if (device->base.layout == &aeris_f11_layout) {
|
||||
if (device->base.layout == &aeris_f11_layout ||
|
||||
device->base.layout == &oceanic_proplus3_layout) {
|
||||
device->bigpage = 8;
|
||||
} else if (device->base.layout == &oceanic_proplusx_layout ||
|
||||
device->base.layout == &aqualung_i770r_layout ||
|
||||
@ -957,6 +961,11 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream
|
||||
device->bigpage = 16;
|
||||
}
|
||||
|
||||
// Repeat the handshaking every few packets.
|
||||
device->handshake_repeat = dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE &&
|
||||
device->base.model == PROPLUS4;
|
||||
device->handshake_counter = 0;
|
||||
|
||||
*out = (dc_device_t*) device;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
@ -1043,7 +1052,7 @@ oceanic_atom2_device_read (dc_device_t *abstract, unsigned int address, unsigned
|
||||
break;
|
||||
case 8:
|
||||
read_cmd = CMD_READ8;
|
||||
crc_size = 1;
|
||||
crc_size = device->base.model == PROPLUS4 ? 2 : 1;
|
||||
break;
|
||||
case 16:
|
||||
read_cmd = CMD_READ16;
|
||||
@ -1074,6 +1083,12 @@ oceanic_atom2_device_read (dc_device_t *abstract, unsigned int address, unsigned
|
||||
unsigned int page = (address - highmem) / pagesize;
|
||||
|
||||
if (page != device->cached_page || highmem != device->cached_highmem) {
|
||||
if (device->handshake_repeat && ++device->handshake_counter % REPEAT == 0) {
|
||||
unsigned char version[PAGESIZE] = {0};
|
||||
oceanic_atom2_device_version (abstract, version, sizeof (version));
|
||||
oceanic_atom2_ble_handshake (device);
|
||||
}
|
||||
|
||||
// Read the package.
|
||||
unsigned int number = highmem ? page : page * device->bigpage; // This is always PAGESIZE, even in big page mode.
|
||||
unsigned char command[] = {read_cmd,
|
||||
|
||||
@ -33,78 +33,6 @@
|
||||
|
||||
#define ISINSTANCE(parser) dc_parser_isinstance((parser), &oceanic_atom2_parser_vtable)
|
||||
|
||||
#define ATOM1 0x4250
|
||||
#define EPICA 0x4257
|
||||
#define VT3 0x4258
|
||||
#define T3A 0x4259
|
||||
#define ATOM2 0x4342
|
||||
#define GEO 0x4344
|
||||
#define MANTA 0x4345
|
||||
#define DATAMASK 0x4347
|
||||
#define COMPUMASK 0x4348
|
||||
#define OC1A 0x434E
|
||||
#define F10A 0x434D
|
||||
#define WISDOM2 0x4350
|
||||
#define INSIGHT2 0x4353
|
||||
#define ELEMENT2 0x4357
|
||||
#define VEO20 0x4359
|
||||
#define VEO30 0x435A
|
||||
#define ZEN 0x4441
|
||||
#define ZENAIR 0x4442
|
||||
#define ATMOSAI2 0x4443
|
||||
#define PROPLUS21 0x4444
|
||||
#define GEO20 0x4446
|
||||
#define VT4 0x4447
|
||||
#define OC1B 0x4449
|
||||
#define VOYAGER2G 0x444B
|
||||
#define ATOM3 0x444C
|
||||
#define DG03 0x444D
|
||||
#define OCS 0x4450
|
||||
#define OC1C 0x4451
|
||||
#define VT41 0x4452
|
||||
#define EPICB 0x4453
|
||||
#define T3B 0x4455
|
||||
#define ATOM31 0x4456
|
||||
#define A300AI 0x4457
|
||||
#define WISDOM3 0x4458
|
||||
#define A300 0x445A
|
||||
#define TX1 0x4542
|
||||
#define MUNDIAL2 0x4543
|
||||
#define AMPHOS 0x4545
|
||||
#define AMPHOSAIR 0x4546
|
||||
#define PROPLUS3 0x4548
|
||||
#define F11A 0x4549
|
||||
#define OCI 0x454B
|
||||
#define A300CS 0x454C
|
||||
#define TALIS 0x454E
|
||||
#define MUNDIAL3 0x4550
|
||||
#define PROPLUSX 0x4552
|
||||
#define F10B 0x4553
|
||||
#define F11B 0x4554
|
||||
#define XPAIR 0x4555
|
||||
#define VISION 0x4556
|
||||
#define VTX 0x4557
|
||||
#define I300 0x4559
|
||||
#define I750TC 0x455A
|
||||
#define I450T 0x4641
|
||||
#define I550 0x4642
|
||||
#define I200 0x4646
|
||||
#define SAGE 0x4647
|
||||
#define I300C 0x4648
|
||||
#define I200C 0x4649
|
||||
#define I100 0x464E
|
||||
#define I770R 0x4651
|
||||
#define I550C 0x4652
|
||||
#define GEO40 0x4653
|
||||
#define VEO40 0x4654
|
||||
#define WISDOM4 0x4655
|
||||
#define PROPLUS4 0x4656
|
||||
#define AMPHOS2 0x4657
|
||||
#define AMPHOSAIR2 0x4658
|
||||
#define BEACON 0x4742
|
||||
#define I470TC 0x4743
|
||||
#define I200CV2 0x4749
|
||||
|
||||
#define NORMAL 0
|
||||
#define GAUGE 1
|
||||
#define FREEDIVE 2
|
||||
@ -143,6 +71,9 @@ static const dc_parser_vtable_t oceanic_atom2_parser_vtable = {
|
||||
sizeof(oceanic_atom2_parser_t),
|
||||
DC_FAMILY_OCEANIC_ATOM2,
|
||||
oceanic_atom2_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
oceanic_atom2_parser_get_datetime, /* datetime */
|
||||
oceanic_atom2_parser_get_field, /* fields */
|
||||
oceanic_atom2_parser_samples_foreach, /* samples_foreach */
|
||||
@ -178,7 +109,8 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
|
||||
model == I300 || model == I550 ||
|
||||
model == I200 || model == I200C ||
|
||||
model == I300C || model == GEO40 ||
|
||||
model == VEO40 || model == I470TC) {
|
||||
model == VEO40 || model == I470TC ||
|
||||
model == GEOAIR) {
|
||||
parser->headersize -= PAGESIZE;
|
||||
} else if (model == VT4 || model == VT41) {
|
||||
parser->headersize += PAGESIZE;
|
||||
@ -285,6 +217,7 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim
|
||||
case WISDOM4:
|
||||
case I470TC:
|
||||
case I200CV2:
|
||||
case GEOAIR:
|
||||
datetime->year = ((p[5] & 0xE0) >> 5) + ((p[7] & 0xE0) >> 2) + 2000;
|
||||
datetime->month = (p[3] & 0x0F);
|
||||
datetime->day = ((p[0] & 0x80) >> 3) + ((p[3] & 0xF0) >> 4);
|
||||
@ -735,7 +668,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
|
||||
parser->model == VTX || parser->model == I450T ||
|
||||
parser->model == I750TC || parser->model == PROPLUSX ||
|
||||
parser->model == I770R || parser->model == I470TC ||
|
||||
parser->model == SAGE || parser->model == BEACON) {
|
||||
parser->model == SAGE || parser->model == BEACON ||
|
||||
parser->model == GEOAIR) {
|
||||
samplesize = PAGESIZE;
|
||||
}
|
||||
|
||||
@ -912,7 +846,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
|
||||
parser->model == I200 || parser->model == I100 ||
|
||||
parser->model == I300C || parser->model == I200C ||
|
||||
parser->model == GEO40 || parser->model == VEO40 ||
|
||||
parser->model == I470TC || parser->model == I200CV2) {
|
||||
parser->model == I470TC || parser->model == I200CV2 ||
|
||||
parser->model == GEOAIR) {
|
||||
temperature = data[offset + 3];
|
||||
} else if (parser->model == OCS || parser->model == TX1) {
|
||||
temperature = data[offset + 1];
|
||||
@ -956,7 +891,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
|
||||
if (have_pressure) {
|
||||
if (parser->model == OC1A || parser->model == OC1B ||
|
||||
parser->model == OC1C || parser->model == OCI ||
|
||||
parser->model == I450T || parser->model == I470TC)
|
||||
parser->model == I450T || parser->model == I470TC ||
|
||||
parser->model == GEOAIR)
|
||||
pressure = (data[offset + 10] + (data[offset + 11] << 8)) & 0x0FFF;
|
||||
else if (parser->model == VT4 || parser->model == VT41||
|
||||
parser->model == ATOM3 || parser->model == ATOM31 ||
|
||||
@ -991,7 +927,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
|
||||
parser->model == I200 || parser->model == I100 ||
|
||||
parser->model == I300C || parser->model == I200C ||
|
||||
parser->model == GEO40 || parser->model == VEO40 ||
|
||||
parser->model == I470TC || parser->model == I200CV2)
|
||||
parser->model == I470TC || parser->model == I200CV2 ||
|
||||
parser->model == GEOAIR)
|
||||
depth = (data[offset + 4] + (data[offset + 5] << 8)) & 0x0FFF;
|
||||
else if (parser->model == ATOM1)
|
||||
depth = data[offset + 3] * 16;
|
||||
@ -1047,7 +984,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
|
||||
parser->model == I100 || parser->model == I300C ||
|
||||
parser->model == I450T || parser->model == I200C ||
|
||||
parser->model == GEO40 || parser->model == VEO40 ||
|
||||
parser->model == I470TC || parser->model == I200CV2) {
|
||||
parser->model == I470TC || parser->model == I200CV2 ||
|
||||
parser->model == GEOAIR) {
|
||||
decostop = (data[offset + 7] & 0xF0) >> 4;
|
||||
decotime = array_uint16_le(data + offset + 6) & 0x0FFF;
|
||||
have_deco = 1;
|
||||
@ -1072,7 +1010,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
|
||||
} else if (parser->model == I450T || parser->model == OC1A ||
|
||||
parser->model == OC1B || parser->model == OC1C ||
|
||||
parser->model == OCI || parser->model == PROPLUSX ||
|
||||
parser->model == I770R || parser->model == I470TC) {
|
||||
parser->model == I770R || parser->model == I470TC ||
|
||||
parser->model == GEOAIR) {
|
||||
rbt = array_uint16_le(data + offset + 8) & 0x01FF;
|
||||
have_rbt = 1;
|
||||
} else if (parser->model == VISION || parser->model == XPAIR ||
|
||||
@ -1089,7 +1028,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
|
||||
// Bookmarks
|
||||
unsigned int have_bookmark = 0;
|
||||
if (parser->model == OC1A || parser->model == OC1B ||
|
||||
parser->model == OC1C || parser->model == OCI) {
|
||||
parser->model == OC1C || parser->model == OCI ||
|
||||
parser->model == GEOAIR) {
|
||||
have_bookmark = data[offset + 12] & 0x80;
|
||||
}
|
||||
if (have_bookmark) {
|
||||
|
||||
@ -56,8 +56,9 @@ get_profile_first (const unsigned char data[], const oceanic_common_layout_t *la
|
||||
}
|
||||
|
||||
unsigned int npages = (layout->memsize - layout->highmem) / pagesize;
|
||||
|
||||
if (npages > 0x2000) {
|
||||
if (npages > 0x4000) {
|
||||
value &= 0x7FFF;
|
||||
} else if (npages > 0x2000) {
|
||||
value &= 0x3FFF;
|
||||
} else if (npages > 0x1000) {
|
||||
value &= 0x1FFF;
|
||||
@ -86,7 +87,9 @@ get_profile_last (const unsigned char data[], const oceanic_common_layout_t *lay
|
||||
|
||||
unsigned int npages = (layout->memsize - layout->highmem) / pagesize;
|
||||
|
||||
if (npages > 0x2000) {
|
||||
if (npages > 0x4000) {
|
||||
value &= 0x7FFF;
|
||||
} else if (npages > 0x2000) {
|
||||
value &= 0x3FFF;
|
||||
} else if (npages > 0x1000) {
|
||||
value &= 0x1FFF;
|
||||
@ -129,7 +132,7 @@ oceanic_common_match_pattern (const unsigned char *string, const unsigned char *
|
||||
return 1;
|
||||
}
|
||||
|
||||
const oceanic_common_layout_t *
|
||||
const oceanic_common_version_t *
|
||||
oceanic_common_match (const unsigned char *version, const oceanic_common_version_t patterns[], size_t n, unsigned int *firmware)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
@ -140,7 +143,7 @@ oceanic_common_match (const unsigned char *version, const oceanic_common_version
|
||||
if (firmware) {
|
||||
*firmware = fw;
|
||||
}
|
||||
return patterns[i].layout;
|
||||
return patterns + i;
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,6 +160,7 @@ oceanic_common_device_init (oceanic_common_device_t *device)
|
||||
device->firmware = 0;
|
||||
memset (device->version, 0, sizeof (device->version));
|
||||
memset (device->fingerprint, 0, sizeof (device->fingerprint));
|
||||
device->model = 0;
|
||||
device->layout = NULL;
|
||||
device->multipage = 1;
|
||||
}
|
||||
|
||||
@ -28,6 +28,103 @@
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
// vtpro
|
||||
#define AERIS500AI 0x4151
|
||||
#define VERSAPRO 0x4155
|
||||
#define ATMOS2 0x4158
|
||||
#define PROPLUS2 0x4159
|
||||
#define ATMOSAI 0x4244
|
||||
#define VTPRO 0x4245
|
||||
#define WISDOM 0x4246
|
||||
#define ELITE 0x424F
|
||||
|
||||
// veo250
|
||||
#define REACTPRO 0x4247
|
||||
#define VEO200 0x424B
|
||||
#define VEO250 0x424C
|
||||
#define XP5 0x4251
|
||||
#define VEO180 0x4252
|
||||
#define XR2 0x4255
|
||||
#define INSIGHT 0x425A
|
||||
#define DG02 0x4352
|
||||
|
||||
// atom2
|
||||
#define ATOM1 0x4250
|
||||
#define EPICA 0x4257
|
||||
#define VT3 0x4258
|
||||
#define T3A 0x4259
|
||||
#define ATOM2 0x4342
|
||||
#define GEO 0x4344
|
||||
#define MANTA 0x4345
|
||||
#define XR1NX 0x4346
|
||||
#define DATAMASK 0x4347
|
||||
#define COMPUMASK 0x4348
|
||||
#define F10A 0x434D
|
||||
#define OC1A 0x434E
|
||||
#define WISDOM2 0x4350
|
||||
#define INSIGHT2 0x4353
|
||||
#define REACTPROWHITE 0x4354
|
||||
#define ELEMENT2 0x4357
|
||||
#define VEO10 0x4358
|
||||
#define VEO20 0x4359
|
||||
#define VEO30 0x435A
|
||||
#define ZEN 0x4441
|
||||
#define ZENAIR 0x4442
|
||||
#define ATMOSAI2 0x4443
|
||||
#define PROPLUS21 0x4444
|
||||
#define GEO20 0x4446
|
||||
#define VT4 0x4447
|
||||
#define OC1B 0x4449
|
||||
#define VOYAGER2G 0x444B
|
||||
#define ATOM3 0x444C
|
||||
#define DG03 0x444D
|
||||
#define OCS 0x4450
|
||||
#define OC1C 0x4451
|
||||
#define VT41 0x4452
|
||||
#define EPICB 0x4453
|
||||
#define T3B 0x4455
|
||||
#define ATOM31 0x4456
|
||||
#define A300AI 0x4457
|
||||
#define WISDOM3 0x4458
|
||||
#define A300 0x445A
|
||||
#define TX1 0x4542
|
||||
#define MUNDIAL2 0x4543
|
||||
#define AMPHOS 0x4545
|
||||
#define AMPHOSAIR 0x4546
|
||||
#define PROPLUS3 0x4548
|
||||
#define F11A 0x4549
|
||||
#define OCI 0x454B
|
||||
#define A300CS 0x454C
|
||||
#define TALIS 0x454E
|
||||
#define MUNDIAL3 0x4550
|
||||
#define PROPLUSX 0x4552
|
||||
#define F10B 0x4553
|
||||
#define F11B 0x4554
|
||||
#define XPAIR 0x4555
|
||||
#define VISION 0x4556
|
||||
#define VTX 0x4557
|
||||
#define I300 0x4559
|
||||
#define I750TC 0x455A
|
||||
#define I450T 0x4641
|
||||
#define I550 0x4642
|
||||
#define I200 0x4646
|
||||
#define SAGE 0x4647
|
||||
#define I300C 0x4648
|
||||
#define I200C 0x4649
|
||||
#define I100 0x464E
|
||||
#define I770R 0x4651
|
||||
#define I550C 0x4652
|
||||
#define GEO40 0x4653
|
||||
#define VEO40 0x4654
|
||||
#define WISDOM4 0x4655
|
||||
#define PROPLUS4 0x4656
|
||||
#define AMPHOS2 0x4657
|
||||
#define AMPHOSAIR2 0x4658
|
||||
#define BEACON 0x4742
|
||||
#define I470TC 0x4743
|
||||
#define I200CV2 0x4749
|
||||
#define GEOAIR 0x474B
|
||||
|
||||
#define PAGESIZE 0x10
|
||||
#define FPMAXSIZE 0x20
|
||||
|
||||
@ -64,6 +161,7 @@ typedef struct oceanic_common_device_t {
|
||||
unsigned int firmware;
|
||||
unsigned char version[PAGESIZE];
|
||||
unsigned char fingerprint[FPMAXSIZE];
|
||||
unsigned int model;
|
||||
const oceanic_common_layout_t *layout;
|
||||
unsigned int multipage;
|
||||
} oceanic_common_device_t;
|
||||
@ -77,10 +175,11 @@ typedef struct oceanic_common_device_vtable_t {
|
||||
typedef struct oceanic_common_version_t {
|
||||
unsigned char pattern[PAGESIZE + 1];
|
||||
unsigned int firmware;
|
||||
unsigned int model;
|
||||
const oceanic_common_layout_t *layout;
|
||||
} oceanic_common_version_t;
|
||||
|
||||
const oceanic_common_layout_t *
|
||||
const oceanic_common_version_t *
|
||||
oceanic_common_match (const unsigned char *version, const oceanic_common_version_t patterns[], size_t n, unsigned int *firmware);
|
||||
|
||||
void
|
||||
|
||||
@ -78,14 +78,14 @@ static const oceanic_common_layout_t oceanic_veo250_layout = {
|
||||
};
|
||||
|
||||
static const oceanic_common_version_t versions[] = {
|
||||
{"GENREACT \0\0 256K", 0, &oceanic_veo250_layout},
|
||||
{"VEO 200 R\0\0 256K", 0, &oceanic_veo250_layout},
|
||||
{"VEO 250 R\0\0 256K", 0, &oceanic_veo250_layout},
|
||||
{"SEEMANN R\0\0 256K", 0, &oceanic_veo250_layout},
|
||||
{"VEO 180 R\0\0 256K", 0, &oceanic_veo250_layout},
|
||||
{"AERISXR2 \0\0 256K", 0, &oceanic_veo250_layout},
|
||||
{"INSIGHT R\0\0 256K", 0, &oceanic_veo250_layout},
|
||||
{"HO DGO2 R\0\0 256K", 0, &oceanic_veo250_layout},
|
||||
{"GENREACT \0\0 256K", 0, REACTPRO, &oceanic_veo250_layout},
|
||||
{"VEO 200 R\0\0 256K", 0, VEO200, &oceanic_veo250_layout},
|
||||
{"VEO 250 R\0\0 256K", 0, VEO250, &oceanic_veo250_layout},
|
||||
{"SEEMANN R\0\0 256K", 0, XP5, &oceanic_veo250_layout},
|
||||
{"VEO 180 R\0\0 256K", 0, VEO180, &oceanic_veo250_layout},
|
||||
{"AERISXR2 \0\0 256K", 0, XR2, &oceanic_veo250_layout},
|
||||
{"INSIGHT R\0\0 256K", 0, INSIGHT, &oceanic_veo250_layout},
|
||||
{"HO DGO2 R\0\0 256K", 0, DG02, &oceanic_veo250_layout},
|
||||
};
|
||||
|
||||
static dc_status_t
|
||||
@ -316,10 +316,14 @@ oceanic_veo250_device_open (dc_device_t **out, dc_context_t *context, dc_iostrea
|
||||
}
|
||||
|
||||
// Detect the memory layout.
|
||||
device->base.layout = OCEANIC_COMMON_MATCH(device->base.version, versions, &device->base.firmware);
|
||||
if (device->base.layout == NULL) {
|
||||
const oceanic_common_version_t *version = OCEANIC_COMMON_MATCH(device->base.version, versions, &device->base.firmware);
|
||||
if (version == NULL) {
|
||||
WARNING (context, "Unsupported device detected!");
|
||||
device->base.layout = &oceanic_veo250_layout;
|
||||
device->base.model = 0;
|
||||
} else {
|
||||
device->base.layout = version->layout;
|
||||
device->base.model = version->model;
|
||||
}
|
||||
|
||||
*out = (dc_device_t*) device;
|
||||
|
||||
@ -31,12 +31,6 @@
|
||||
|
||||
#define ISINSTANCE(parser) dc_parser_isinstance((parser), &oceanic_veo250_parser_vtable)
|
||||
|
||||
#define REACTPRO 0x4247
|
||||
#define VEO200 0x424B
|
||||
#define VEO250 0x424C
|
||||
#define INSIGHT 0x425A
|
||||
#define REACTPROWHITE 0x4354
|
||||
|
||||
typedef struct oceanic_veo250_parser_t oceanic_veo250_parser_t;
|
||||
|
||||
struct oceanic_veo250_parser_t {
|
||||
@ -57,6 +51,9 @@ static const dc_parser_vtable_t oceanic_veo250_parser_vtable = {
|
||||
sizeof(oceanic_veo250_parser_t),
|
||||
DC_FAMILY_OCEANIC_VEO250,
|
||||
oceanic_veo250_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
oceanic_veo250_parser_get_datetime, /* datetime */
|
||||
oceanic_veo250_parser_get_field, /* fields */
|
||||
oceanic_veo250_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -40,8 +40,6 @@
|
||||
#define NAK 0xA5
|
||||
#define END 0x51
|
||||
|
||||
#define AERIS500AI 0x4151
|
||||
|
||||
typedef enum oceanic_vtpro_protocol_t {
|
||||
MOD,
|
||||
INTR,
|
||||
@ -50,7 +48,6 @@ typedef enum oceanic_vtpro_protocol_t {
|
||||
typedef struct oceanic_vtpro_device_t {
|
||||
oceanic_common_device_t base;
|
||||
dc_iostream_t *iostream;
|
||||
unsigned int model;
|
||||
oceanic_vtpro_protocol_t protocol;
|
||||
} oceanic_vtpro_device_t;
|
||||
|
||||
@ -120,14 +117,14 @@ static const oceanic_common_layout_t aeris_500ai_layout = {
|
||||
};
|
||||
|
||||
static const oceanic_common_version_t versions[] = {
|
||||
{"VERSAPRO \0\0 256K", 0, &oceanic_vtpro_layout},
|
||||
{"ATMOSTWO \0\0 256K", 0, &oceanic_vtpro_layout},
|
||||
{"PROPLUS2 \0\0 256K", 0, &oceanic_vtpro_layout},
|
||||
{"ATMOSAIR \0\0 256K", 0, &oceanic_vtpro_layout},
|
||||
{"VTPRO r\0\0 256K", 0, &oceanic_vtpro_layout},
|
||||
{"ELITE r\0\0 256K", 0, &oceanic_vtpro_layout},
|
||||
{"VERSAPRO \0\0 256K", 0, VERSAPRO, &oceanic_vtpro_layout},
|
||||
{"ATMOSTWO \0\0 256K", 0, ATMOS2, &oceanic_vtpro_layout},
|
||||
{"PROPLUS2 \0\0 256K", 0, PROPLUS2, &oceanic_vtpro_layout},
|
||||
{"ATMOSAIR \0\0 256K", 0, ATMOSAI, &oceanic_vtpro_layout},
|
||||
{"VTPRO r\0\0 256K", 0, VTPRO, &oceanic_vtpro_layout},
|
||||
{"ELITE r\0\0 256K", 0, ELITE, &oceanic_vtpro_layout},
|
||||
|
||||
{"WISDOM r\0\0 256K", 0, &oceanic_wisdom_layout},
|
||||
{"WISDOM r\0\0 256K", 0, WISDOM, &oceanic_wisdom_layout},
|
||||
};
|
||||
|
||||
static dc_status_t
|
||||
@ -381,7 +378,7 @@ oceanic_vtpro_device_logbook (dc_device_t *abstract, dc_event_progress_t *progre
|
||||
{
|
||||
oceanic_vtpro_device_t *device = (oceanic_vtpro_device_t *) abstract;
|
||||
|
||||
if (device->model == AERIS500AI) {
|
||||
if (device->base.model == AERIS500AI) {
|
||||
return oceanic_aeris500ai_device_logbook (abstract, progress, logbook);
|
||||
} else {
|
||||
return oceanic_common_device_logbook (abstract, progress, logbook);
|
||||
@ -412,7 +409,6 @@ oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, dc_iostream
|
||||
|
||||
// Set the default values.
|
||||
device->iostream = iostream;
|
||||
device->model = model;
|
||||
if (model == AERIS500AI) {
|
||||
device->protocol = INTR;
|
||||
} else {
|
||||
@ -492,11 +488,16 @@ oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, dc_iostream
|
||||
// Detect the memory layout.
|
||||
if (model == AERIS500AI) {
|
||||
device->base.layout = &aeris_500ai_layout;
|
||||
device->base.model = AERIS500AI;
|
||||
} else {
|
||||
device->base.layout = OCEANIC_COMMON_MATCH(device->base.version, versions, &device->base.firmware);
|
||||
if (device->base.layout == NULL) {
|
||||
const oceanic_common_version_t * version = OCEANIC_COMMON_MATCH(device->base.version, versions, &device->base.firmware);
|
||||
if (version == NULL) {
|
||||
WARNING (context, "Unsupported device detected!");
|
||||
device->base.layout = &oceanic_vtpro_layout;
|
||||
device->base.model = 0;
|
||||
} else {
|
||||
device->base.layout = version->layout;
|
||||
device->base.model = version->model;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -31,8 +31,6 @@
|
||||
|
||||
#define ISINSTANCE(parser) dc_parser_isinstance((parser), &oceanic_vtpro_parser_vtable)
|
||||
|
||||
#define AERIS500AI 0x4151
|
||||
|
||||
typedef struct oceanic_vtpro_parser_t oceanic_vtpro_parser_t;
|
||||
|
||||
struct oceanic_vtpro_parser_t {
|
||||
@ -53,6 +51,9 @@ static const dc_parser_vtable_t oceanic_vtpro_parser_vtable = {
|
||||
sizeof(oceanic_vtpro_parser_t),
|
||||
DC_FAMILY_OCEANIC_VTPRO,
|
||||
oceanic_vtpro_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
oceanic_vtpro_parser_get_datetime, /* datetime */
|
||||
oceanic_vtpro_parser_get_field, /* fields */
|
||||
oceanic_vtpro_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
1052
src/oceans_s1.c
1052
src/oceans_s1.c
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,24 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
// Copyright (C) 2020 Linus Torvalds
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2020 Linus Torvalds
|
||||
* Copyright (C) 2022 Jef Driesen
|
||||
*
|
||||
* 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 OCEANS_S1_H
|
||||
#define OCEANS_S1_H
|
||||
|
||||
68
src/oceans_s1_common.c
Normal file
68
src/oceans_s1_common.c
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2022 Jef Driesen
|
||||
*
|
||||
* 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 <string.h> // memcmp, memcpy
|
||||
#include <stdlib.h> // malloc, free
|
||||
#include <stddef.h>
|
||||
|
||||
#include "oceans_s1_common.h"
|
||||
|
||||
int
|
||||
oceans_s1_getline (char **line, size_t *linelen, const unsigned char **data, size_t *size)
|
||||
{
|
||||
if (line == NULL || linelen == NULL || data == NULL || size == NULL)
|
||||
return -1;
|
||||
|
||||
if (*size == 0)
|
||||
return -1;
|
||||
|
||||
// Find the end of the line.
|
||||
unsigned int strip = 0;
|
||||
const unsigned char *p = *data, *end = p + *size;
|
||||
while (p != end) {
|
||||
unsigned char c = *p++;
|
||||
if (c == '\r' || c == '\n') {
|
||||
strip = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the length of the line.
|
||||
size_t len = p - *data;
|
||||
|
||||
// Resize the buffer (if necessary).
|
||||
if (*line == NULL || len + 1 > *linelen) {
|
||||
char *buf = (char *) malloc (len + 1);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
free (*line);
|
||||
*line = buf;
|
||||
*linelen = len + 1;
|
||||
}
|
||||
|
||||
// Copy the data.
|
||||
memcpy (*line, *data, len - strip);
|
||||
(*line)[len - strip] = 0;
|
||||
*data += len;
|
||||
*size -= len;
|
||||
|
||||
return len - strip;
|
||||
}
|
||||
35
src/oceans_s1_common.h
Normal file
35
src/oceans_s1_common.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2022 Jef Driesen
|
||||
*
|
||||
* 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 OCEANS_S1_COMMON_H
|
||||
#define OCEANS_S1_COMMON_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int
|
||||
oceans_s1_getline (char **line, size_t *linelen, const unsigned char **data, size_t *size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* OCEANS_S1_COMMON_H */
|
||||
@ -1,26 +1,61 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
// Copyright (C) 2020 Linus Torvalds
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2020 Linus Torvalds
|
||||
* Copyright (C) 2022 Jef Driesen
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "oceans_s1.h"
|
||||
#include "oceans_s1_common.h"
|
||||
#include "context-private.h"
|
||||
#include "parser-private.h"
|
||||
#include "field-cache.h"
|
||||
#include "platform.h"
|
||||
#include "array.h"
|
||||
|
||||
#define SCUBA 0
|
||||
#define APNEA 1
|
||||
|
||||
#define EVENT_DIVE_STARTED 0x0001
|
||||
#define EVENT_DIVE_ENDED 0x0002
|
||||
#define EVENT_DIVE_RESUMED 0x0004
|
||||
#define EVENT_PING_SENT 0x0008
|
||||
#define EVENT_PING_RECEIVED 0x0010
|
||||
#define EVENT_DECO_STOP 0x0020
|
||||
#define EVENT_SAFETY_STOP 0x0040
|
||||
#define EVENT_BATTERY_LOW 0x0080
|
||||
#define EVENT_BACKLIGHT_ON 0x0100
|
||||
|
||||
typedef struct oceans_s1_parser_t oceans_s1_parser_t;
|
||||
|
||||
struct oceans_s1_parser_t {
|
||||
dc_parser_t base;
|
||||
int divenr;
|
||||
unsigned int maxdepth, duration;
|
||||
long long date;
|
||||
struct dc_field_cache cache;
|
||||
// Cached fields.
|
||||
dc_ticks_t timestamp;
|
||||
unsigned int cached;
|
||||
unsigned int number;
|
||||
unsigned int divemode;
|
||||
unsigned int oxygen;
|
||||
unsigned int maxdepth;
|
||||
unsigned int divetime;
|
||||
};
|
||||
|
||||
static dc_status_t oceans_s1_parser_set_data(dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
@ -32,6 +67,9 @@ static const dc_parser_vtable_t oceans_s1_parser_vtable = {
|
||||
sizeof(oceans_s1_parser_t),
|
||||
DC_FAMILY_OCEANS_S1,
|
||||
oceans_s1_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
oceans_s1_parser_get_datetime, /* datetime */
|
||||
oceans_s1_parser_get_field, /* fields */
|
||||
oceans_s1_parser_samples_foreach, /* samples_foreach */
|
||||
@ -39,7 +77,7 @@ static const dc_parser_vtable_t oceans_s1_parser_vtable = {
|
||||
};
|
||||
|
||||
dc_status_t
|
||||
oceans_s1_parser_create(dc_parser_t **out, dc_context_t *context)
|
||||
oceans_s1_parser_create (dc_parser_t **out, dc_context_t *context)
|
||||
{
|
||||
oceans_s1_parser_t *parser = NULL;
|
||||
|
||||
@ -47,160 +85,233 @@ oceans_s1_parser_create(dc_parser_t **out, dc_context_t *context)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
// Allocate memory.
|
||||
parser = (oceans_s1_parser_t*)dc_parser_allocate(context, &oceans_s1_parser_vtable);
|
||||
parser = (oceans_s1_parser_t *) dc_parser_allocate (context, &oceans_s1_parser_vtable);
|
||||
if (parser == NULL) {
|
||||
ERROR(context, "Failed to allocate memory.");
|
||||
ERROR (context, "Failed to allocate memory.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
*out = (dc_parser_t*)parser;
|
||||
// Set the default values.
|
||||
parser->cached = 0;
|
||||
parser->timestamp = 0;
|
||||
parser->number = 0;
|
||||
parser->divemode = 0;
|
||||
parser->oxygen = 0;
|
||||
parser->maxdepth = 0;
|
||||
parser->divetime = 0;
|
||||
|
||||
*out = (dc_parser_t *) parser;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static const unsigned char *get_string_line(const unsigned char *in, const unsigned char **next)
|
||||
static dc_status_t
|
||||
oceans_s1_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
|
||||
{
|
||||
const unsigned char *line;
|
||||
unsigned char c;
|
||||
oceans_s1_parser_t *parser = (oceans_s1_parser_t *) abstract;
|
||||
|
||||
if (!in) {
|
||||
*next = NULL;
|
||||
return NULL;
|
||||
}
|
||||
// Reset the cache.
|
||||
parser->cached = 0;
|
||||
parser->timestamp = 0;
|
||||
parser->number = 0;
|
||||
parser->divemode = 0;
|
||||
parser->oxygen = 0;
|
||||
parser->maxdepth = 0;
|
||||
parser->divetime = 0;
|
||||
|
||||
while (isspace(*in))
|
||||
in++;
|
||||
|
||||
if (!*in) {
|
||||
*next = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
line = in;
|
||||
while ((c = *in) != 0) {
|
||||
if (c == '\r' || c == '\n')
|
||||
break;
|
||||
in++;
|
||||
}
|
||||
*next = in;
|
||||
return line;
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
oceans_s1_parse_dive(struct oceans_s1_parser_t *s1, const unsigned char *data, dc_sample_callback_t callback, void *userdata)
|
||||
oceans_s1_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
{
|
||||
const unsigned char *line;
|
||||
unsigned int sample_interval = 10;
|
||||
unsigned int sample_time = 0;
|
||||
oceans_s1_parser_t *parser = (oceans_s1_parser_t *) abstract;
|
||||
|
||||
memset(&s1->cache, 0, sizeof(s1->cache));
|
||||
if (!parser->cached) {
|
||||
dc_status_t status = oceans_s1_parser_samples_foreach (abstract, NULL, NULL);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
while ((line = get_string_line(data, &data)) != NULL) {
|
||||
if (!dc_datetime_gmtime (datetime, parser->timestamp))
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
oceans_s1_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||
{
|
||||
oceans_s1_parser_t *parser = (oceans_s1_parser_t *) abstract;
|
||||
|
||||
if (!parser->cached) {
|
||||
dc_status_t status = oceans_s1_parser_samples_foreach (abstract, NULL, NULL);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
|
||||
if (value) {
|
||||
switch (type) {
|
||||
case DC_FIELD_DIVETIME:
|
||||
*((unsigned int *) value) = parser->divetime;
|
||||
break;
|
||||
case DC_FIELD_MAXDEPTH:
|
||||
*((double *) value) = parser->maxdepth / 100.0;
|
||||
break;
|
||||
case DC_FIELD_GASMIX_COUNT:
|
||||
*((unsigned int *) value) = parser->divemode == SCUBA;
|
||||
break;
|
||||
case DC_FIELD_GASMIX:
|
||||
gasmix->helium = 0.0;
|
||||
gasmix->oxygen = parser->oxygen / 100.0;
|
||||
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
|
||||
break;
|
||||
case DC_FIELD_DIVEMODE:
|
||||
switch (parser->divemode) {
|
||||
case SCUBA:
|
||||
*((dc_divemode_t *) value) = DC_DIVEMODE_OC;
|
||||
break;
|
||||
case APNEA:
|
||||
*((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE;
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
oceans_s1_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
oceans_s1_parser_t *parser = (oceans_s1_parser_t *) abstract;
|
||||
const unsigned char *data = abstract->data;
|
||||
size_t size = abstract->size;
|
||||
|
||||
dc_ticks_t timestamp = 0;
|
||||
unsigned int number = 0, divemode = 0, oxygen = 0;
|
||||
unsigned int maxdepth = 0, divetime = 0;
|
||||
unsigned int interval = 10;
|
||||
unsigned int time = 0;
|
||||
|
||||
char *ptr = NULL;
|
||||
size_t len = 0;
|
||||
int n = 0;
|
||||
while ((n = oceans_s1_getline (&ptr, &len, &data, &size)) != -1) {
|
||||
dc_sample_value_t sample = {0};
|
||||
int depth = 0, temp = 0, flags = 0;
|
||||
|
||||
if (!strncmp(line, "divelog ", 8)) {
|
||||
sscanf(line, "divelog v1,%us/sample", &sample_interval);
|
||||
// Ignore empty lines.
|
||||
if (n == 0)
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(line, "dive ", 5)) {
|
||||
int nr, unknown, o2;
|
||||
long long date;
|
||||
|
||||
sscanf(line, "dive %d,%d,%d,%lld", &nr, &unknown, &o2, &date);
|
||||
s1->divenr = nr;
|
||||
s1->date = date;
|
||||
// I think "unknown" is dive mode
|
||||
if (o2) {
|
||||
dc_gasmix_t mix = { 0 };
|
||||
mix.oxygen = o2 / 100.0;
|
||||
DC_ASSIGN_FIELD(s1->cache, GASMIX_COUNT, 1);
|
||||
DC_ASSIGN_IDX(s1->cache, GASMIX, 0, mix);
|
||||
// Ignore leading whitespace.
|
||||
const char *line = ptr;
|
||||
while (*line == ' ')
|
||||
line++;
|
||||
|
||||
if (strncmp (line, "divelog", 7) == 0) {
|
||||
if (sscanf (line, "divelog v1,%us/sample", &interval) != 1) {
|
||||
ERROR (parser->base.context, "Failed to parse the line '%s'.", line);
|
||||
status = DC_STATUS_DATAFORMAT;
|
||||
goto error_free;
|
||||
}
|
||||
continue;
|
||||
if (interval == 0) {
|
||||
ERROR (parser->base.context, "Invalid sample interval (%u).", interval);
|
||||
status = DC_STATUS_DATAFORMAT;
|
||||
goto error_free;
|
||||
}
|
||||
} else if (strncmp (line, "dive", 4) == 0) {
|
||||
if (sscanf (line, "dive %u,%u,%u," DC_FORMAT_INT64, &number, &divemode, &oxygen, ×tamp) != 4) {
|
||||
ERROR (parser->base.context, "Failed to parse the line '%s'.", line);
|
||||
status = DC_STATUS_DATAFORMAT;
|
||||
goto error_free;
|
||||
}
|
||||
} else if (strncmp (line, "continue", 8) == 0) {
|
||||
unsigned int depth = 0, seconds = 0;
|
||||
if (sscanf (line, "continue %u,%u", &depth, &seconds) != 2) {
|
||||
ERROR (parser->base.context, "Failed to parse the line '%s'.", line);
|
||||
status = DC_STATUS_DATAFORMAT;
|
||||
goto error_free;
|
||||
}
|
||||
if (!strncmp(line, "continue ", 9)) {
|
||||
int depth = 0, seconds = 0;
|
||||
sscanf(line, "continue %d,%d", &depth, &seconds);
|
||||
|
||||
// Create surface samples for the surface time,
|
||||
// and then a depth sample at the stated depth
|
||||
if (callback) {
|
||||
if (seconds >= sample_interval*2) {
|
||||
dc_sample_value_t sample = {0};
|
||||
sample.time = sample_time + sample_interval;
|
||||
callback(DC_SAMPLE_TIME, sample, userdata);
|
||||
// and then a depth sample at the stated depth.
|
||||
unsigned int nsamples = seconds / interval;
|
||||
for (unsigned int i = 0; i < nsamples; ++i) {
|
||||
time += interval;
|
||||
sample.time = time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
sample.depth = 0;
|
||||
callback(DC_SAMPLE_DEPTH, sample, userdata);
|
||||
|
||||
sample.time = sample_time + seconds - sample_interval;
|
||||
callback(DC_SAMPLE_TIME, sample, userdata);
|
||||
sample.depth = 0;
|
||||
callback(DC_SAMPLE_DEPTH, sample, userdata);
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
}
|
||||
sample.time = sample_time + seconds;
|
||||
callback(DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
time += interval;
|
||||
sample.time = time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
sample.depth = depth / 100.0;
|
||||
callback(DC_SAMPLE_DEPTH, sample, userdata);
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
} else if (strncmp(line, "enddive", 7) == 0) {
|
||||
if (sscanf(line, "enddive %u,%u", &maxdepth, &divetime) != 2) {
|
||||
ERROR (parser->base.context, "Failed to parse the line '%s'.", line);
|
||||
status = DC_STATUS_DATAFORMAT;
|
||||
goto error_free;
|
||||
}
|
||||
sample_time += seconds;
|
||||
continue;
|
||||
} else if (strncmp (line, "endlog", 6) == 0) {
|
||||
// Nothing to do.
|
||||
} else {
|
||||
unsigned int depth = 0, events = 0;
|
||||
int temperature = 0;
|
||||
if (sscanf (line, "%u,%d,%u", &depth, &temperature, &events) != 3) {
|
||||
ERROR (parser->base.context, "Failed to parse the line '%s'.", line);
|
||||
status = DC_STATUS_DATAFORMAT;
|
||||
goto error_free;
|
||||
}
|
||||
if (!strncmp(line, "enddive ", 8)) {
|
||||
int maxdepth = 0, duration = 0;
|
||||
sscanf(line, "enddive %d,%d", &maxdepth, &duration);
|
||||
DC_ASSIGN_FIELD(s1->cache, MAXDEPTH, maxdepth / 100.0);
|
||||
DC_ASSIGN_FIELD(s1->cache, DIVETIME, duration);
|
||||
s1->maxdepth = maxdepth;
|
||||
s1->duration = duration;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(line, "%d,%d,%d", &depth, &temp, &flags) != 3)
|
||||
continue;
|
||||
|
||||
sample_time += sample_interval;
|
||||
if (callback) {
|
||||
dc_sample_value_t sample = {0};
|
||||
sample.time = sample_time;
|
||||
callback(DC_SAMPLE_TIME, sample, userdata);
|
||||
time += interval;
|
||||
sample.time = time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
sample.depth = depth / 100.0;
|
||||
callback(DC_SAMPLE_DEPTH, sample, userdata);
|
||||
sample.temperature = temp;
|
||||
callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
|
||||
sample.temperature = temperature;
|
||||
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
|
||||
|
||||
if (events & EVENT_DECO_STOP) {
|
||||
sample.deco.type = DC_DECO_DECOSTOP;
|
||||
} else if (events & EVENT_SAFETY_STOP) {
|
||||
sample.deco.type = DC_DECO_SAFETYSTOP;
|
||||
} else {
|
||||
sample.deco.type = DC_DECO_NDL;
|
||||
}
|
||||
sample.deco.depth = 0.0;
|
||||
sample.deco.time = 0;
|
||||
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
|
||||
}
|
||||
}
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
oceans_s1_parser_set_data(dc_parser_t *abstract, const unsigned char *data, unsigned int size)
|
||||
{
|
||||
struct oceans_s1_parser_t *s1 = (struct oceans_s1_parser_t *)abstract;
|
||||
|
||||
return oceans_s1_parse_dive(s1, data, NULL, NULL);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
oceans_s1_parser_get_datetime(dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
{
|
||||
oceans_s1_parser_t *s1 = (oceans_s1_parser_t *)abstract;
|
||||
|
||||
dc_datetime_gmtime(datetime, s1->date);
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
oceans_s1_parser_get_field(dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
|
||||
{
|
||||
oceans_s1_parser_t *s1 = (oceans_s1_parser_t *)abstract;
|
||||
|
||||
return dc_field_get(&s1->cache, type, flags, value);
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
oceans_s1_parser_samples_foreach(dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
struct oceans_s1_parser_t *s1 = (struct oceans_s1_parser_t *)abstract;
|
||||
|
||||
return oceans_s1_parse_dive(s1, s1->base.data, callback, userdata);
|
||||
|
||||
// Cache the data for later use.
|
||||
parser->timestamp = timestamp;
|
||||
parser->number = number;
|
||||
parser->divemode = divemode;
|
||||
parser->oxygen = oxygen;
|
||||
parser->maxdepth = maxdepth;
|
||||
parser->divetime = divetime;
|
||||
parser->cached = 1;
|
||||
|
||||
error_free:
|
||||
free (ptr);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -25,6 +25,10 @@
|
||||
#include <libdivecomputer/context.h>
|
||||
#include <libdivecomputer/parser.h>
|
||||
|
||||
#define DEF_DENSITY_FRESH 1000.0
|
||||
#define DEF_DENSITY_SALT 1025.0
|
||||
#define DEF_ATMOSPHERIC ATM
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
@ -48,6 +52,12 @@ struct dc_parser_vtable_t {
|
||||
|
||||
dc_status_t (*set_data) (dc_parser_t *parser, const unsigned char *data, unsigned int size);
|
||||
|
||||
dc_status_t (*set_clock) (dc_parser_t *parser, unsigned int devtime, dc_ticks_t systime);
|
||||
|
||||
dc_status_t (*set_atmospheric) (dc_parser_t *parser, double atmospheric);
|
||||
|
||||
dc_status_t (*set_density) (dc_parser_t *parser, double density);
|
||||
|
||||
dc_status_t (*datetime) (dc_parser_t *parser, dc_datetime_t *datetime);
|
||||
|
||||
dc_status_t (*field) (dc_parser_t *parser, dc_field_type_t type, unsigned int flags, void *value);
|
||||
|
||||
56
src/parser.c
56
src/parser.c
@ -35,7 +35,6 @@
|
||||
#include "uwatec_memomouse.h"
|
||||
#include "uwatec_smart.h"
|
||||
#include "oceanic_atom2.h"
|
||||
#include "oceanic_atom2.h"
|
||||
#include "oceanic_veo250.h"
|
||||
#include "oceanic_vtpro.h"
|
||||
#include "mares_darwin.h"
|
||||
@ -62,11 +61,11 @@
|
||||
#include "sporasub_sp2.h"
|
||||
#include "deepsix_excursion.h"
|
||||
#include "seac_screen.h"
|
||||
#include "deepblu_cosmiq.h"
|
||||
#include "oceans_s1.h"
|
||||
|
||||
// Not merged upstream yet
|
||||
#include "garmin.h"
|
||||
#include "deepblu.h"
|
||||
#include "oceans_s1.h"
|
||||
|
||||
#include "context-private.h"
|
||||
#include "parser-private.h"
|
||||
@ -197,6 +196,12 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
||||
case DC_FAMILY_SEAC_SCREEN:
|
||||
rc = seac_screen_parser_create (&parser, context);
|
||||
break;
|
||||
case DC_FAMILY_DEEPBLU_COSMIQ:
|
||||
rc = deepblu_cosmiq_parser_create (&parser, context);
|
||||
break;
|
||||
case DC_FAMILY_OCEANS_S1:
|
||||
rc = oceans_s1_parser_create (&parser, context);
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
@ -204,12 +209,6 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
|
||||
case DC_FAMILY_GARMIN:
|
||||
rc = garmin_parser_create (&parser, context);
|
||||
break;
|
||||
case DC_FAMILY_DEEPBLU:
|
||||
rc = deepblu_parser_create (&parser, context);
|
||||
break;
|
||||
case DC_FAMILY_OCEANS_S1:
|
||||
rc = oceans_s1_parser_create(&parser, context);
|
||||
break;
|
||||
}
|
||||
|
||||
*out = parser;
|
||||
@ -290,6 +289,45 @@ dc_parser_get_type (dc_parser_t *parser)
|
||||
}
|
||||
|
||||
|
||||
dc_status_t
|
||||
dc_parser_set_clock (dc_parser_t *parser, unsigned int devtime, dc_ticks_t systime)
|
||||
{
|
||||
if (parser == NULL)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
|
||||
if (parser->vtable->set_clock == NULL)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
|
||||
return parser->vtable->set_clock (parser, devtime, systime);
|
||||
}
|
||||
|
||||
|
||||
dc_status_t
|
||||
dc_parser_set_atmospheric (dc_parser_t *parser, double atmospheric)
|
||||
{
|
||||
if (parser == NULL)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
|
||||
if (parser->vtable->set_atmospheric == NULL)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
|
||||
return parser->vtable->set_atmospheric (parser, atmospheric);
|
||||
}
|
||||
|
||||
|
||||
dc_status_t
|
||||
dc_parser_set_density (dc_parser_t *parser, double density)
|
||||
{
|
||||
if (parser == NULL)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
|
||||
if (parser->vtable->set_density == NULL)
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
|
||||
return parser->vtable->set_density (parser, density);
|
||||
}
|
||||
|
||||
|
||||
dc_status_t
|
||||
dc_parser_set_data (dc_parser_t *parser, const unsigned char *data, unsigned int size)
|
||||
{
|
||||
|
||||
@ -28,6 +28,8 @@
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
int
|
||||
@ -49,3 +51,48 @@ dc_platform_sleep (unsigned int milliseconds)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dc_platform_vsnprintf (char *str, size_t size, const char *format, va_list ap)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
if (size == 0)
|
||||
return -1;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/*
|
||||
* The non-standard vsnprintf implementation provided by MSVC doesn't null
|
||||
* terminate the string and returns a negative value if the destination
|
||||
* buffer is too small.
|
||||
*/
|
||||
n = _vsnprintf (str, size - 1, format, ap);
|
||||
if (n == size - 1 || n < 0)
|
||||
str[size - 1] = 0;
|
||||
#else
|
||||
/*
|
||||
* The C99 vsnprintf function will always null terminate the string. If the
|
||||
* destination buffer is too small, the return value is the number of
|
||||
* characters that would have been written if the buffer had been large
|
||||
* enough.
|
||||
*/
|
||||
n = vsnprintf (str, size, format, ap);
|
||||
if (n >= 0 && (size_t) n >= size)
|
||||
n = -1;
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
dc_platform_snprintf (char *str, size_t size, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int n = 0;
|
||||
|
||||
va_start (ap, format);
|
||||
n = dc_platform_vsnprintf (str, size, format, ap);
|
||||
va_end (ap);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
@ -22,18 +22,27 @@
|
||||
#ifndef DC_PLATFORM_H
|
||||
#define DC_PLATFORM_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define DC_ATTR_FORMAT_PRINTF(a,b) __attribute__((format(printf, a, b)))
|
||||
#else
|
||||
#define DC_ATTR_FORMAT_PRINTF(a,b)
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DC_PRINTF_SIZE "%Iu"
|
||||
#define DC_FORMAT_INT64 "%I64d"
|
||||
#else
|
||||
#define DC_PRINTF_SIZE "%zu"
|
||||
#define DC_FORMAT_INT64 "%lld"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#define strcasecmp _stricmp
|
||||
#define strncasecmp _strnicmp
|
||||
#if _MSC_VER < 1800
|
||||
@ -47,6 +56,13 @@ extern "C" {
|
||||
|
||||
int dc_platform_sleep(unsigned int milliseconds);
|
||||
|
||||
/*
|
||||
* A wrapper for the vsnprintf function, which will always null terminate the
|
||||
* string and returns a negative value if the destination buffer is too small.
|
||||
*/
|
||||
int dc_platform_snprintf (char *str, size_t size, const char *format, ...) DC_ATTR_FORMAT_PRINTF(3, 4);
|
||||
int dc_platform_vsnprintf (char *str, size_t size, const char *format, va_list ap) DC_ATTR_FORMAT_PRINTF(3, 0);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@ -49,6 +49,9 @@ struct reefnet_sensus_parser_t {
|
||||
};
|
||||
|
||||
static dc_status_t reefnet_sensus_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
static dc_status_t reefnet_sensus_parser_set_clock (dc_parser_t *abstract, unsigned int devtime, dc_ticks_t systime);
|
||||
static dc_status_t reefnet_sensus_parser_set_atmospheric (dc_parser_t *abstract, double atmospheric);
|
||||
static dc_status_t reefnet_sensus_parser_set_density (dc_parser_t *abstract, double density);
|
||||
static dc_status_t reefnet_sensus_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
|
||||
static dc_status_t reefnet_sensus_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
|
||||
static dc_status_t reefnet_sensus_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
|
||||
@ -57,6 +60,9 @@ static const dc_parser_vtable_t reefnet_sensus_parser_vtable = {
|
||||
sizeof(reefnet_sensus_parser_t),
|
||||
DC_FAMILY_REEFNET_SENSUS,
|
||||
reefnet_sensus_parser_set_data, /* set_data */
|
||||
reefnet_sensus_parser_set_clock, /* set_clock */
|
||||
reefnet_sensus_parser_set_atmospheric, /* set_atmospheric */
|
||||
reefnet_sensus_parser_set_density, /* set_density */
|
||||
reefnet_sensus_parser_get_datetime, /* datetime */
|
||||
reefnet_sensus_parser_get_field, /* fields */
|
||||
reefnet_sensus_parser_samples_foreach, /* samples_foreach */
|
||||
@ -80,8 +86,8 @@ reefnet_sensus_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
|
||||
}
|
||||
|
||||
// Set the default values.
|
||||
parser->atmospheric = ATM;
|
||||
parser->hydrostatic = 1025.0 * GRAVITY;
|
||||
parser->atmospheric = DEF_ATMOSPHERIC;
|
||||
parser->hydrostatic = DEF_DENSITY_SALT * GRAVITY;
|
||||
parser->devtime = devtime;
|
||||
parser->systime = systime;
|
||||
parser->cached = 0;
|
||||
@ -123,6 +129,40 @@ reefnet_sensus_parser_set_calibration (dc_parser_t *abstract, double atmospheric
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
reefnet_sensus_parser_set_clock (dc_parser_t *abstract, unsigned int devtime, dc_ticks_t systime)
|
||||
{
|
||||
reefnet_sensus_parser_t *parser = (reefnet_sensus_parser_t *) abstract;
|
||||
|
||||
parser->devtime = devtime;
|
||||
parser->systime = systime;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
reefnet_sensus_parser_set_atmospheric (dc_parser_t *abstract, double atmospheric)
|
||||
{
|
||||
reefnet_sensus_parser_t *parser = (reefnet_sensus_parser_t *) abstract;
|
||||
|
||||
parser->atmospheric = atmospheric;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
reefnet_sensus_parser_set_density (dc_parser_t *abstract, double density)
|
||||
{
|
||||
reefnet_sensus_parser_t *parser = (reefnet_sensus_parser_t *) abstract;
|
||||
|
||||
parser->hydrostatic = density * GRAVITY;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
reefnet_sensus_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
{
|
||||
|
||||
@ -48,6 +48,9 @@ struct reefnet_sensuspro_parser_t {
|
||||
};
|
||||
|
||||
static dc_status_t reefnet_sensuspro_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
static dc_status_t reefnet_sensuspro_parser_set_clock (dc_parser_t *abstract, unsigned int devtime, dc_ticks_t systime);
|
||||
static dc_status_t reefnet_sensuspro_parser_set_atmospheric (dc_parser_t *abstract, double atmospheric);
|
||||
static dc_status_t reefnet_sensuspro_parser_set_density (dc_parser_t *abstract, double density);
|
||||
static dc_status_t reefnet_sensuspro_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
|
||||
static dc_status_t reefnet_sensuspro_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
|
||||
static dc_status_t reefnet_sensuspro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
|
||||
@ -56,6 +59,9 @@ static const dc_parser_vtable_t reefnet_sensuspro_parser_vtable = {
|
||||
sizeof(reefnet_sensuspro_parser_t),
|
||||
DC_FAMILY_REEFNET_SENSUSPRO,
|
||||
reefnet_sensuspro_parser_set_data, /* set_data */
|
||||
reefnet_sensuspro_parser_set_clock, /* set_clock */
|
||||
reefnet_sensuspro_parser_set_atmospheric, /* set_atmospheric */
|
||||
reefnet_sensuspro_parser_set_density, /* set_density */
|
||||
reefnet_sensuspro_parser_get_datetime, /* datetime */
|
||||
reefnet_sensuspro_parser_get_field, /* fields */
|
||||
reefnet_sensuspro_parser_samples_foreach, /* samples_foreach */
|
||||
@ -79,8 +85,8 @@ reefnet_sensuspro_parser_create (dc_parser_t **out, dc_context_t *context, unsig
|
||||
}
|
||||
|
||||
// Set the default values.
|
||||
parser->atmospheric = ATM;
|
||||
parser->hydrostatic = 1025.0 * GRAVITY;
|
||||
parser->atmospheric = DEF_ATMOSPHERIC;
|
||||
parser->hydrostatic = DEF_DENSITY_SALT * GRAVITY;
|
||||
parser->devtime = devtime;
|
||||
parser->systime = systime;
|
||||
parser->cached = 0;
|
||||
@ -122,6 +128,40 @@ reefnet_sensuspro_parser_set_calibration (dc_parser_t *abstract, double atmosphe
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
reefnet_sensuspro_parser_set_clock (dc_parser_t *abstract, unsigned int devtime, dc_ticks_t systime)
|
||||
{
|
||||
reefnet_sensuspro_parser_t *parser = (reefnet_sensuspro_parser_t *) abstract;
|
||||
|
||||
parser->devtime = devtime;
|
||||
parser->systime = systime;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
reefnet_sensuspro_parser_set_atmospheric (dc_parser_t *abstract, double atmospheric)
|
||||
{
|
||||
reefnet_sensuspro_parser_t *parser = (reefnet_sensuspro_parser_t *) abstract;
|
||||
|
||||
parser->atmospheric = atmospheric;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
reefnet_sensuspro_parser_set_density (dc_parser_t *abstract, double density)
|
||||
{
|
||||
reefnet_sensuspro_parser_t *parser = (reefnet_sensuspro_parser_t *) abstract;
|
||||
|
||||
parser->hydrostatic = density * GRAVITY;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
reefnet_sensuspro_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
{
|
||||
|
||||
@ -48,6 +48,9 @@ struct reefnet_sensusultra_parser_t {
|
||||
};
|
||||
|
||||
static dc_status_t reefnet_sensusultra_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
static dc_status_t reefnet_sensusultra_parser_set_clock (dc_parser_t *abstract, unsigned int devtime, dc_ticks_t systime);
|
||||
static dc_status_t reefnet_sensusultra_parser_set_atmospheric (dc_parser_t *abstract, double atmospheric);
|
||||
static dc_status_t reefnet_sensusultra_parser_set_density (dc_parser_t *abstract, double density);
|
||||
static dc_status_t reefnet_sensusultra_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
|
||||
static dc_status_t reefnet_sensusultra_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
|
||||
static dc_status_t reefnet_sensusultra_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
|
||||
@ -56,6 +59,9 @@ static const dc_parser_vtable_t reefnet_sensusultra_parser_vtable = {
|
||||
sizeof(reefnet_sensusultra_parser_t),
|
||||
DC_FAMILY_REEFNET_SENSUSULTRA,
|
||||
reefnet_sensusultra_parser_set_data, /* set_data */
|
||||
reefnet_sensusultra_parser_set_clock, /* set_clock */
|
||||
reefnet_sensusultra_parser_set_atmospheric, /* set_atmospheric */
|
||||
reefnet_sensusultra_parser_set_density, /* set_density */
|
||||
reefnet_sensusultra_parser_get_datetime, /* datetime */
|
||||
reefnet_sensusultra_parser_get_field, /* fields */
|
||||
reefnet_sensusultra_parser_samples_foreach, /* samples_foreach */
|
||||
@ -79,8 +85,8 @@ reefnet_sensusultra_parser_create (dc_parser_t **out, dc_context_t *context, uns
|
||||
}
|
||||
|
||||
// Set the default values.
|
||||
parser->atmospheric = ATM;
|
||||
parser->hydrostatic = 1025.0 * GRAVITY;
|
||||
parser->atmospheric = DEF_ATMOSPHERIC;
|
||||
parser->hydrostatic = DEF_DENSITY_SALT * GRAVITY;
|
||||
parser->devtime = devtime;
|
||||
parser->systime = systime;
|
||||
parser->cached = 0;
|
||||
@ -122,6 +128,40 @@ reefnet_sensusultra_parser_set_calibration (dc_parser_t *abstract, double atmosp
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
reefnet_sensusultra_parser_set_clock (dc_parser_t *abstract, unsigned int devtime, dc_ticks_t systime)
|
||||
{
|
||||
reefnet_sensusultra_parser_t *parser = (reefnet_sensusultra_parser_t *) abstract;
|
||||
|
||||
parser->devtime = devtime;
|
||||
parser->systime = systime;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
reefnet_sensusultra_parser_set_atmospheric (dc_parser_t *abstract, double atmospheric)
|
||||
{
|
||||
reefnet_sensusultra_parser_t *parser = (reefnet_sensusultra_parser_t *) abstract;
|
||||
|
||||
parser->atmospheric = atmospheric;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
reefnet_sensusultra_parser_set_density (dc_parser_t *abstract, double density)
|
||||
{
|
||||
reefnet_sensusultra_parser_t *parser = (reefnet_sensusultra_parser_t *) abstract;
|
||||
|
||||
parser->hydrostatic = density * GRAVITY;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
reefnet_sensusultra_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
{
|
||||
|
||||
@ -44,6 +44,8 @@ struct seac_screen_parser_t {
|
||||
unsigned int cached;
|
||||
unsigned int ngasmixes;
|
||||
unsigned int oxygen[NGASMIXES];
|
||||
unsigned int gf_low;
|
||||
unsigned int gf_high;
|
||||
};
|
||||
|
||||
static dc_status_t seac_screen_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
@ -55,6 +57,9 @@ static const dc_parser_vtable_t seac_screen_parser_vtable = {
|
||||
sizeof(seac_screen_parser_t),
|
||||
DC_FAMILY_SEAC_SCREEN,
|
||||
seac_screen_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
seac_screen_parser_get_datetime, /* datetime */
|
||||
seac_screen_parser_get_field, /* fields */
|
||||
seac_screen_parser_samples_foreach, /* samples_foreach */
|
||||
@ -82,6 +87,8 @@ seac_screen_parser_create (dc_parser_t **out, dc_context_t *context)
|
||||
for (unsigned int i = 0; i < NGASMIXES; ++i) {
|
||||
parser->oxygen[i] = 0;
|
||||
}
|
||||
parser->gf_low = 0;
|
||||
parser->gf_high = 0;
|
||||
|
||||
*out = (dc_parser_t *) parser;
|
||||
|
||||
@ -99,6 +106,8 @@ seac_screen_parser_set_data (dc_parser_t *abstract, const unsigned char *data, u
|
||||
for (unsigned int i = 0; i < NGASMIXES; ++i) {
|
||||
parser->oxygen[i] = 0;
|
||||
}
|
||||
parser->gf_low = 0;
|
||||
parser->gf_high = 0;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
@ -207,6 +216,7 @@ seac_screen_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsig
|
||||
}
|
||||
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
|
||||
|
||||
if (value) {
|
||||
switch (type) {
|
||||
@ -249,6 +259,12 @@ seac_screen_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsig
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_DECOMODEL:
|
||||
decomodel->type = DC_DECOMODEL_BUHLMANN;
|
||||
decomodel->conservatism = 0;
|
||||
decomodel->params.gf.low = parser->gf_low;
|
||||
decomodel->params.gf.high = parser->gf_high;
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
}
|
||||
@ -279,6 +295,9 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
unsigned int oxygen[NGASMIXES] = {0};
|
||||
unsigned int o2_previous = INVALID;
|
||||
|
||||
unsigned int gf_low = 0;
|
||||
unsigned int gf_high = 0;
|
||||
|
||||
unsigned int time = 0;
|
||||
unsigned int offset = SZ_HEADER;
|
||||
while (offset + SZ_SAMPLE <= size) {
|
||||
@ -298,6 +317,8 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
unsigned int decotime = array_uint16_le (data + offset + 0x10);
|
||||
unsigned int ndl_tts = array_uint16_le (data + offset + 0x12);
|
||||
unsigned int cns = array_uint16_le (data + offset + 0x16);
|
||||
unsigned int gf_hi = data[offset + 0x3B];
|
||||
unsigned int gf_lo = data[offset + 0x3C];
|
||||
|
||||
if (id != dive_id) {
|
||||
ERROR (abstract->context, "Unexpected sample id (%u %u).", dive_id, id);
|
||||
@ -362,6 +383,12 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
sample.cns = cns / 100.0;
|
||||
if (callback) callback (DC_SAMPLE_CNS, sample, userdata);
|
||||
|
||||
// Deco model
|
||||
if (gf_low == 0 && gf_high == 0) {
|
||||
gf_low = gf_lo;
|
||||
gf_high = gf_hi;
|
||||
}
|
||||
|
||||
offset += SZ_SAMPLE;
|
||||
}
|
||||
|
||||
@ -370,6 +397,8 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
parser->oxygen[i] = oxygen[i];
|
||||
}
|
||||
parser->ngasmixes = ngasmixes;
|
||||
parser->gf_low = gf_low;
|
||||
parser->gf_high = gf_high;
|
||||
parser->cached = 1;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
|
||||
@ -30,13 +30,13 @@
|
||||
#include <fcntl.h> // fcntl
|
||||
#include <termios.h> // tcgetattr, tcsetattr, cfsetispeed, cfsetospeed, tcflush, tcsendbreak
|
||||
#include <sys/ioctl.h> // ioctl
|
||||
#include <sys/select.h> // select
|
||||
#ifdef HAVE_LINUX_SERIAL_H
|
||||
#include <linux/serial.h>
|
||||
#endif
|
||||
#ifdef HAVE_IOKIT_SERIAL_IOSS_H
|
||||
#include <IOKit/serial/ioss.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <fnmatch.h>
|
||||
@ -225,7 +225,7 @@ dc_serial_iterator_next (dc_iterator_t *abstract, void *out)
|
||||
continue;
|
||||
|
||||
char filename[sizeof(device->name)];
|
||||
int n = snprintf (filename, sizeof (filename), "%s/%s", DIRNAME, ep->d_name);
|
||||
int n = dc_platform_snprintf (filename, sizeof (filename), "%s/%s", DIRNAME, ep->d_name);
|
||||
if (n < 0 || (size_t) n >= sizeof (filename)) {
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
@ -248,6 +248,7 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
|
||||
case 0x0C0D: // current docs (June 2023) show this as Perdix AI
|
||||
case 0x0D0D: // current docs (June 2023) imply this is not a valid hardware ID
|
||||
case 0x7C2D: // Perdix AI
|
||||
case 0x8D6C:
|
||||
model = PERDIXAI;
|
||||
break;
|
||||
case 0xC407: // Perdix 2
|
||||
|
||||
@ -79,13 +79,23 @@
|
||||
#define M_OC_REC 6
|
||||
#define M_FREEDIVE 7
|
||||
|
||||
#define AI_OFF 0
|
||||
#define AI_HPCCR 4
|
||||
#define AI_ON 5
|
||||
|
||||
#define GF 0
|
||||
#define VPMB 1
|
||||
#define VPMB_GFS 2
|
||||
#define DCIEM 3
|
||||
|
||||
#define METRIC 0
|
||||
#define IMPERIAL 1
|
||||
|
||||
#define NGASMIXES 10
|
||||
#define MAXSTRINGS 32
|
||||
#define NTANKS 4
|
||||
#define NGASMIXES 20
|
||||
#define NFIXED 10
|
||||
#define NTANKS 6
|
||||
#define NRECORDS 8
|
||||
#define MAXSTRINGS 32
|
||||
|
||||
#define PREDATOR 2
|
||||
#define PETREL 3
|
||||
@ -98,6 +108,7 @@ typedef struct shearwater_predator_parser_t shearwater_predator_parser_t;
|
||||
typedef struct shearwater_predator_gasmix_t {
|
||||
unsigned int oxygen;
|
||||
unsigned int helium;
|
||||
unsigned int diluent;
|
||||
} shearwater_predator_gasmix_t;
|
||||
|
||||
typedef struct shearwater_predator_tank_t {
|
||||
@ -131,6 +142,7 @@ struct shearwater_predator_parser_t {
|
||||
shearwater_predator_gasmix_t gasmix[NGASMIXES];
|
||||
shearwater_predator_tank_t tank[NTANKS];
|
||||
unsigned int tankidx[NTANKS];
|
||||
unsigned int aimode;
|
||||
unsigned int calibrated;
|
||||
double calibration[3];
|
||||
unsigned int serial;
|
||||
@ -153,6 +165,9 @@ static const dc_parser_vtable_t shearwater_predator_parser_vtable = {
|
||||
sizeof(shearwater_predator_parser_t),
|
||||
DC_FAMILY_SHEARWATER_PREDATOR,
|
||||
shearwater_predator_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
shearwater_predator_parser_get_datetime, /* datetime */
|
||||
shearwater_predator_parser_get_field, /* fields */
|
||||
shearwater_predator_parser_samples_foreach, /* samples_foreach */
|
||||
@ -163,6 +178,9 @@ static const dc_parser_vtable_t shearwater_petrel_parser_vtable = {
|
||||
sizeof(shearwater_predator_parser_t),
|
||||
DC_FAMILY_SHEARWATER_PETREL,
|
||||
shearwater_predator_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
shearwater_predator_parser_get_datetime, /* datetime */
|
||||
shearwater_predator_parser_get_field, /* fields */
|
||||
shearwater_predator_parser_samples_foreach, /* samples_foreach */
|
||||
@ -171,11 +189,11 @@ static const dc_parser_vtable_t shearwater_petrel_parser_vtable = {
|
||||
|
||||
|
||||
static unsigned int
|
||||
shearwater_predator_find_gasmix (shearwater_predator_parser_t *parser, unsigned int o2, unsigned int he)
|
||||
shearwater_predator_find_gasmix (shearwater_predator_parser_t *parser, unsigned int o2, unsigned int he, unsigned int dil)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
while (i < parser->ngasmixes) {
|
||||
if (o2 == parser->gasmix[i].oxygen && he == parser->gasmix[i].helium)
|
||||
if (o2 == parser->gasmix[i].oxygen && he == parser->gasmix[i].helium && dil == parser->gasmix[i].diluent)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
@ -230,6 +248,7 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig
|
||||
for (unsigned int i = 0; i < NGASMIXES; ++i) {
|
||||
parser->gasmix[i].oxygen = 0;
|
||||
parser->gasmix[i].helium = 0;
|
||||
parser->gasmix[i].diluent = 0;
|
||||
}
|
||||
parser->ntanks = 0;
|
||||
for (unsigned int i = 0; i < NTANKS; ++i) {
|
||||
@ -244,13 +263,14 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig
|
||||
parser->tank[i].battery = 0;
|
||||
parser->tankidx[i] = i;
|
||||
}
|
||||
parser->aimode = AI_OFF;
|
||||
parser->calibrated = 0;
|
||||
for (unsigned int i = 0; i < 3; ++i) {
|
||||
parser->calibration[i] = 0.0;
|
||||
}
|
||||
parser->units = METRIC;
|
||||
parser->density = 1025;
|
||||
parser->atmospheric = ATM / (BAR / 1000);
|
||||
parser->density = DEF_DENSITY_SALT;
|
||||
parser->atmospheric = DEF_ATMOSPHERIC / (BAR / 1000);
|
||||
|
||||
DC_ASSIGN_FIELD(parser->cache, DIVEMODE, DC_DIVEMODE_OC);
|
||||
|
||||
@ -294,6 +314,7 @@ shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char
|
||||
for (unsigned int i = 0; i < NGASMIXES; ++i) {
|
||||
parser->gasmix[i].oxygen = 0;
|
||||
parser->gasmix[i].helium = 0;
|
||||
parser->gasmix[i].diluent = 0;
|
||||
}
|
||||
parser->ntanks = 0;
|
||||
for (unsigned int i = 0; i < NTANKS; ++i) {
|
||||
@ -307,13 +328,14 @@ shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char
|
||||
memset (parser->tank[i].name, 0, sizeof (parser->tank[i].name));
|
||||
parser->tankidx[i] = i;
|
||||
}
|
||||
parser->aimode = AI_OFF;
|
||||
parser->calibrated = 0;
|
||||
for (unsigned int i = 0; i < 3; ++i) {
|
||||
parser->calibration[i] = 0.0;
|
||||
}
|
||||
parser->units = METRIC;
|
||||
parser->density = 1025;
|
||||
parser->atmospheric = ATM / (BAR / 1000);
|
||||
parser->density = DEF_DENSITY_SALT;
|
||||
parser->atmospheric = DEF_ATMOSPHERIC / (BAR / 1000);
|
||||
|
||||
DC_ASSIGN_FIELD(parser->cache, DIVEMODE, DC_DIVEMODE_OC);
|
||||
|
||||
@ -495,10 +517,18 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
unsigned int divemode = M_OC_TEC;
|
||||
|
||||
// Get the gas mixes.
|
||||
unsigned int ngasmixes = 0;
|
||||
unsigned int ngasmixes = NFIXED;
|
||||
shearwater_predator_gasmix_t gasmix[NGASMIXES] = {0};
|
||||
shearwater_predator_tank_t tank[NTANKS] = {0};
|
||||
unsigned int o2_previous = 0, he_previous = 0;
|
||||
unsigned int o2_previous = UNDEFINED, he_previous = UNDEFINED, dil_previous = UNDEFINED;
|
||||
unsigned int aimode = AI_OFF;
|
||||
if (!pnf) {
|
||||
for (unsigned int i = 0; i < NFIXED; ++i) {
|
||||
gasmix[i].oxygen = data[20 + i];
|
||||
gasmix[i].helium = data[30 + i];
|
||||
gasmix[i].diluent = i >= 5;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int offset = headersize;
|
||||
unsigned int length = size - footersize;
|
||||
@ -515,18 +545,20 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
if (type == LOG_RECORD_DIVE_SAMPLE) {
|
||||
// Status flags.
|
||||
unsigned int status = data[offset + 11 + pnf];
|
||||
if ((status & OC) == 0) {
|
||||
unsigned int ccr = (status & OC) == 0;
|
||||
if (ccr) {
|
||||
divemode = status & SC ? M_SC : M_CC;
|
||||
}
|
||||
|
||||
// Gaschange.
|
||||
unsigned int o2 = data[offset + 7 + pnf];
|
||||
unsigned int he = data[offset + 8 + pnf];
|
||||
if (o2 != o2_previous || he != he_previous) {
|
||||
if ((o2 != o2_previous || he != he_previous || ccr != dil_previous) &&
|
||||
(o2 != 0 || he != 0)) {
|
||||
// Find the gasmix in the list.
|
||||
unsigned int idx = 0;
|
||||
while (idx < ngasmixes) {
|
||||
if (o2 == gasmix[idx].oxygen && he == gasmix[idx].helium)
|
||||
if (o2 == gasmix[idx].oxygen && he == gasmix[idx].helium && ccr == gasmix[idx].diluent)
|
||||
break;
|
||||
idx++;
|
||||
}
|
||||
@ -539,11 +571,13 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
}
|
||||
gasmix[idx].oxygen = o2;
|
||||
gasmix[idx].helium = he;
|
||||
gasmix[idx].diluent = ccr;
|
||||
ngasmixes = idx + 1;
|
||||
}
|
||||
|
||||
o2_previous = o2;
|
||||
he_previous = he;
|
||||
dil_previous = ccr;
|
||||
}
|
||||
|
||||
// Tank pressure
|
||||
@ -559,17 +593,18 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
// level (0=normal, 1=critical, 2=warning), and the lower 12
|
||||
// bits the tank pressure in units of 2 psi.
|
||||
unsigned int pressure = array_uint16_be (data + offset + pnf + idx[i]);
|
||||
unsigned int id = (aimode == AI_HPCCR ? 4 : 0) + i;
|
||||
if (pressure < 0xFFF0) {
|
||||
unsigned int battery = 1u << (pressure >> 12);
|
||||
pressure &= 0x0FFF;
|
||||
if (!tank[i].active) {
|
||||
tank[i].active = 1;
|
||||
tank[i].beginpressure = pressure;
|
||||
tank[i].endpressure = pressure;
|
||||
tank[i].battery = 0;
|
||||
if (!tank[id].active) {
|
||||
tank[id].active = 1;
|
||||
tank[id].beginpressure = pressure;
|
||||
tank[id].endpressure = pressure;
|
||||
tank[id].battery = 0;
|
||||
}
|
||||
tank[i].endpressure = pressure;
|
||||
tank[i].battery |= battery;
|
||||
tank[id].endpressure = pressure;
|
||||
tank[id].battery |= battery;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -578,14 +613,33 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
if (logversion >= 13) {
|
||||
for (unsigned int i = 0; i < 2; ++i) {
|
||||
unsigned int pressure = array_uint16_be (data + offset + pnf + i * 2);
|
||||
unsigned int id = 2 + i;
|
||||
if (pressure < 0xFFF0) {
|
||||
pressure &= 0x0FFF;
|
||||
if (!tank[i + 2].active) {
|
||||
tank[i + 2].active = 1;
|
||||
tank[i + 2].beginpressure = pressure;
|
||||
tank[i + 2].endpressure = pressure;
|
||||
if (!tank[id].active) {
|
||||
tank[id].active = 1;
|
||||
tank[id].beginpressure = pressure;
|
||||
tank[id].endpressure = pressure;
|
||||
}
|
||||
tank[i + 2].endpressure = pressure;
|
||||
tank[id].endpressure = pressure;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Tank pressure (HP CCR)
|
||||
if (logversion >= 14) {
|
||||
for (unsigned int i = 0; i < 2; ++i) {
|
||||
unsigned int pressure = array_uint16_be (data + offset + pnf + 4 + i * 2);
|
||||
unsigned int id = 4 + i;
|
||||
if (pressure) {
|
||||
if (!tank[id].active) {
|
||||
tank[id].active = 1;
|
||||
tank[id].enabled = 1;
|
||||
tank[id].beginpressure = pressure;
|
||||
tank[id].endpressure = pressure;
|
||||
tank[id].name[0] = i == 0 ? 'D': 'O';
|
||||
tank[id].name[1] = 0;
|
||||
}
|
||||
tank[id].endpressure = pressure;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -596,24 +650,41 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
// Opening record
|
||||
parser->opening[type - LOG_RECORD_OPENING_0] = offset;
|
||||
|
||||
if (type == LOG_RECORD_OPENING_4) {
|
||||
if (type == LOG_RECORD_OPENING_0) {
|
||||
for (unsigned int i = 0; i < NFIXED; ++i) {
|
||||
gasmix[i].oxygen = data[offset + 20 + i];
|
||||
gasmix[i].diluent = i >= 5;
|
||||
}
|
||||
for (unsigned int i = 0; i < 2; ++i) {
|
||||
gasmix[i].helium = data[offset + 30 + i];
|
||||
}
|
||||
} else if (type == LOG_RECORD_OPENING_1) {
|
||||
for (unsigned int i = 2; i < NFIXED; ++i) {
|
||||
gasmix[i].helium = data[offset + 1 + i - 2];
|
||||
}
|
||||
} else if (type == LOG_RECORD_OPENING_4) {
|
||||
// Log version
|
||||
logversion = data[offset + 16];
|
||||
|
||||
// Air integration mode
|
||||
if (logversion >= 7) {
|
||||
unsigned int airmode = data[offset + 28];
|
||||
aimode = data[offset + 28];
|
||||
if (logversion < 13) {
|
||||
if (airmode == 1 || airmode == 2) {
|
||||
tank[airmode - 1].enabled = 1;
|
||||
} else if (airmode == 3) {
|
||||
if (aimode == 1 || aimode == 2) {
|
||||
tank[aimode - 1].enabled = 1;
|
||||
} else if (aimode == 3) {
|
||||
tank[0].enabled = 1;
|
||||
tank[1].enabled = 1;
|
||||
}
|
||||
}
|
||||
if (airmode == 4) {
|
||||
tank[0].enabled = 1;
|
||||
tank[1].enabled = 1;
|
||||
if (logversion < 14) {
|
||||
if (aimode == AI_HPCCR) {
|
||||
for (unsigned int i = 0; i < 2; ++i) {
|
||||
tank[4 + i].enabled = 1;
|
||||
tank[4 + i].name[0] = i == 0 ? 'D': 'O';
|
||||
tank[4 + i].name[1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (type == LOG_RECORD_OPENING_5) {
|
||||
@ -733,9 +804,14 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
parser->logversion = logversion;
|
||||
parser->headersize = headersize;
|
||||
parser->footersize = footersize;
|
||||
parser->ngasmixes = ngasmixes;
|
||||
parser->ngasmixes = 0;
|
||||
if (divemode != M_FREEDIVE) {
|
||||
for (unsigned int i = 0; i < ngasmixes; ++i) {
|
||||
parser->gasmix[i] = gasmix[i];
|
||||
if (gasmix[i].oxygen == 0 && gasmix[i].helium == 0)
|
||||
continue;
|
||||
parser->gasmix[parser->ngasmixes] = gasmix[i];
|
||||
parser->ngasmixes++;
|
||||
}
|
||||
}
|
||||
parser->ntanks = 0;
|
||||
for (unsigned int i = 0; i < NTANKS; ++i) {
|
||||
@ -747,6 +823,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
|
||||
parser->tankidx[i] = UNDEFINED;
|
||||
}
|
||||
}
|
||||
parser->aimode = aimode;
|
||||
parser->units = data[parser->opening[0] + 8];
|
||||
parser->atmospheric = array_uint16_be (data + parser->opening[1] + (parser->pnf ? 16 : 47));
|
||||
parser->density = array_uint16_be (data + parser->opening[3] + (parser->pnf ? 3 : 83));
|
||||
@ -801,9 +878,13 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
unsigned int decomodel_idx = parser->pnf ? parser->opening[2] + 18 : 67;
|
||||
unsigned int gf_idx = parser->pnf ? parser->opening[0] + 4 : 4;
|
||||
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
dc_tank_t *tank = (dc_tank_t *) value;
|
||||
dc_salinity_t *water = (dc_salinity_t *) value;
|
||||
dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
|
||||
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||
|
||||
if (value) {
|
||||
@ -853,6 +934,27 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ
|
||||
break;
|
||||
case DC_FIELD_DIVEMODE:
|
||||
return DC_FIELD_VALUE(parser->cache, value, DIVEMODE);
|
||||
case DC_FIELD_DECOMODEL:
|
||||
switch (data[decomodel_idx]) {
|
||||
case GF:
|
||||
decomodel->type = DC_DECOMODEL_BUHLMANN;
|
||||
decomodel->conservatism = 0;
|
||||
decomodel->params.gf.low = data[gf_idx + 0];
|
||||
decomodel->params.gf.high = data[gf_idx + 1];
|
||||
break;
|
||||
case VPMB:
|
||||
case VPMB_GFS:
|
||||
decomodel->type = DC_DECOMODEL_VPM;
|
||||
decomodel->conservatism = data[decomodel_idx + 1];
|
||||
break;
|
||||
case DCIEM:
|
||||
decomodel->type = DC_DECOMODEL_DCIEM;
|
||||
decomodel->conservatism = 0;
|
||||
break;
|
||||
default:
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_STRING:
|
||||
return dc_field_get_string(&parser->cache, flags, string);
|
||||
default:
|
||||
@ -878,7 +980,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
||||
return rc;
|
||||
|
||||
// Previous gas mix.
|
||||
unsigned int o2_previous = 0, he_previous = 0;
|
||||
unsigned int o2_previous = UNDEFINED, he_previous = UNDEFINED, dil_previous = UNDEFINED;
|
||||
|
||||
// Sample interval.
|
||||
unsigned int time = 0;
|
||||
@ -942,8 +1044,9 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
||||
|
||||
// Status flags.
|
||||
unsigned int status = data[offset + pnf + 11];
|
||||
unsigned int ccr = (status & OC) == 0;
|
||||
|
||||
if ((status & OC) == 0) {
|
||||
if (ccr) {
|
||||
// PPO2
|
||||
if ((status & PPO2_EXTERNAL) == 0) {
|
||||
if (!parser->calibrated) {
|
||||
@ -984,8 +1087,9 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
||||
// Gaschange.
|
||||
unsigned int o2 = data[offset + pnf + 7];
|
||||
unsigned int he = data[offset + pnf + 8];
|
||||
if (o2 != o2_previous || he != he_previous) {
|
||||
unsigned int idx = shearwater_predator_find_gasmix (parser, o2, he);
|
||||
if ((o2 != o2_previous || he != he_previous || ccr != dil_previous) &&
|
||||
(o2 != 0 || he != 0)) {
|
||||
unsigned int idx = shearwater_predator_find_gasmix (parser, o2, he, ccr);
|
||||
if (idx >= parser->ngasmixes) {
|
||||
ERROR (abstract->context, "Invalid gas mix.");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
@ -995,6 +1099,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
||||
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
|
||||
o2_previous = o2;
|
||||
he_previous = he;
|
||||
dil_previous = ccr;
|
||||
}
|
||||
|
||||
// Deco stop / NDL.
|
||||
@ -1027,9 +1132,10 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
||||
// level (0=normal, 1=critical, 2=warning), and the lower 12
|
||||
// bits the tank pressure in units of 2 psi.
|
||||
unsigned int pressure = array_uint16_be (data + offset + pnf + idx[i]);
|
||||
unsigned int id = (parser->aimode == AI_HPCCR ? 4 : 0) + i;
|
||||
if (pressure < 0xFFF0) {
|
||||
pressure &= 0x0FFF;
|
||||
sample.pressure.tank = parser->tankidx[i];
|
||||
sample.pressure.tank = parser->tankidx[id];
|
||||
sample.pressure.value = pressure * 2 * PSI / BAR;
|
||||
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
|
||||
}
|
||||
@ -1052,9 +1158,22 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
|
||||
if (parser->logversion >= 13) {
|
||||
for (unsigned int i = 0; i < 2; ++i) {
|
||||
unsigned int pressure = array_uint16_be (data + offset + pnf + i * 2);
|
||||
unsigned int id = 2 + i;
|
||||
if (pressure < 0xFFF0) {
|
||||
pressure &= 0x0FFF;
|
||||
sample.pressure.tank = parser->tankidx[i + 2];
|
||||
sample.pressure.tank = parser->tankidx[id];
|
||||
sample.pressure.value = pressure * 2 * PSI / BAR;
|
||||
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Tank pressure (HP CCR)
|
||||
if (parser->logversion >= 14) {
|
||||
for (unsigned int i = 0; i < 2; ++i) {
|
||||
unsigned int pressure = array_uint16_be (data + offset + pnf + 4 + i * 2);
|
||||
unsigned int id = 4 + i;
|
||||
if (pressure) {
|
||||
sample.pressure.tank = parser->tankidx[id];
|
||||
sample.pressure.value = pressure * 2 * PSI / BAR;
|
||||
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
|
||||
}
|
||||
|
||||
@ -461,7 +461,7 @@ sporasub_sp2_device_timesync (dc_device_t *abstract, const dc_datetime_t *dateti
|
||||
dc_status_t status = DC_STATUS_SUCCESS;
|
||||
sporasub_sp2_device_t *device = (sporasub_sp2_device_t *) abstract;
|
||||
|
||||
if (datetime == NULL || datetime->year < 2000) {
|
||||
if (datetime->year < 2000) {
|
||||
ERROR (abstract->context, "Invalid parameter specified.");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
@ -46,6 +46,9 @@ static const dc_parser_vtable_t sporasub_sp2_parser_vtable = {
|
||||
sizeof(sporasub_sp2_parser_t),
|
||||
DC_FAMILY_SPORASUB_SP2,
|
||||
sporasub_sp2_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
sporasub_sp2_parser_get_datetime, /* datetime */
|
||||
sporasub_sp2_parser_get_field, /* fields */
|
||||
sporasub_sp2_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -103,6 +103,9 @@ static const dc_parser_vtable_t suunto_d9_parser_vtable = {
|
||||
sizeof(suunto_d9_parser_t),
|
||||
DC_FAMILY_SUUNTO_D9,
|
||||
suunto_d9_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
suunto_d9_parser_get_datetime, /* datetime */
|
||||
suunto_d9_parser_get_field, /* fields */
|
||||
suunto_d9_parser_samples_foreach, /* samples_foreach */
|
||||
@ -365,6 +368,7 @@ suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigne
|
||||
return rc;
|
||||
|
||||
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
|
||||
dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
|
||||
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||
|
||||
char buf[BUFLEN];
|
||||
@ -415,6 +419,20 @@ suunto_d9_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigne
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_DECOMODEL:
|
||||
decomodel->type = DC_DECOMODEL_RGBM;
|
||||
if (parser->model == D4i ||parser->model == D6i ||
|
||||
parser->model == D9tx || parser->model == ZOOPNOVO_A ||
|
||||
parser->model == ZOOPNOVO_B || parser->model == VYPERNOVO ||
|
||||
parser->model == D4F)
|
||||
decomodel->conservatism = data[0x21] - 2;
|
||||
else if (parser->model == HELO2)
|
||||
decomodel->conservatism = data[0x23] - 2;
|
||||
else if (parser->model == DX)
|
||||
decomodel->conservatism = data[0x25] - 2;
|
||||
else
|
||||
decomodel->conservatism = data[0x1E];
|
||||
break;
|
||||
case DC_FIELD_STRING:
|
||||
switch (flags) {
|
||||
case 0: /* serial */
|
||||
|
||||
@ -52,6 +52,9 @@ static const dc_parser_vtable_t suunto_eon_parser_vtable = {
|
||||
sizeof(suunto_eon_parser_t),
|
||||
DC_FAMILY_SUUNTO_EON,
|
||||
suunto_eon_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
suunto_eon_parser_get_datetime, /* datetime */
|
||||
suunto_eon_parser_get_field, /* fields */
|
||||
suunto_eon_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -897,7 +897,7 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac
|
||||
break;
|
||||
}
|
||||
|
||||
len = snprintf(pathname, sizeof(pathname), "%s/%s", dive_directory, de->name);
|
||||
len = dc_platform_snprintf(pathname, sizeof(pathname), "%s/%s", dive_directory, de->name);
|
||||
if (len < 0 || (unsigned int) len >= sizeof(pathname)) {
|
||||
dc_status_set_error(&status, DC_STATUS_PROTOCOL);
|
||||
break;
|
||||
|
||||
@ -816,11 +816,11 @@ static void sample_setpoint_type(const struct type_desc *desc, struct sample_dat
|
||||
}
|
||||
|
||||
if (!strcasecmp(type, "Low"))
|
||||
sample.ppo2 = info->eon->cache.lowsetpoint;
|
||||
sample.setpoint = info->eon->cache.lowsetpoint;
|
||||
else if (!strcasecmp(type, "High"))
|
||||
sample.ppo2 = info->eon->cache.highsetpoint;
|
||||
sample.setpoint = info->eon->cache.highsetpoint;
|
||||
else if (!strcasecmp(type, "Custom"))
|
||||
sample.ppo2 = info->eon->cache.customsetpoint;
|
||||
sample.setpoint = info->eon->cache.customsetpoint;
|
||||
else {
|
||||
DEBUG(info->eon->base.context, "sample_setpoint_type(%u) unknown type '%s'", value, type);
|
||||
free(type);
|
||||
@ -1514,6 +1514,9 @@ static const dc_parser_vtable_t suunto_eonsteel_parser_vtable = {
|
||||
sizeof(suunto_eonsteel_parser_t),
|
||||
DC_FAMILY_SUUNTO_EONSTEEL,
|
||||
suunto_eonsteel_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
suunto_eonsteel_parser_get_datetime, /* datetime */
|
||||
suunto_eonsteel_parser_get_field, /* fields */
|
||||
suunto_eonsteel_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -47,6 +47,9 @@ static const dc_parser_vtable_t suunto_solution_parser_vtable = {
|
||||
sizeof(suunto_solution_parser_t),
|
||||
DC_FAMILY_SUUNTO_SOLUTION,
|
||||
suunto_solution_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
NULL, /* datetime */
|
||||
suunto_solution_parser_get_field, /* fields */
|
||||
suunto_solution_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -56,6 +56,9 @@ static const dc_parser_vtable_t suunto_vyper_parser_vtable = {
|
||||
sizeof(suunto_vyper_parser_t),
|
||||
DC_FAMILY_SUUNTO_VYPER,
|
||||
suunto_vyper_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
suunto_vyper_parser_get_datetime, /* datetime */
|
||||
suunto_vyper_parser_get_field, /* fields */
|
||||
suunto_vyper_parser_samples_foreach, /* samples_foreach */
|
||||
@ -243,6 +246,7 @@ suunto_vyper_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
|
||||
|
||||
dc_gasmix_t *gas = (dc_gasmix_t *) value;
|
||||
dc_tank_t *tank = (dc_tank_t *) value;
|
||||
dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
|
||||
dc_field_string_t *string = (dc_field_string_t *) value;
|
||||
char buf[BUFLEN];
|
||||
|
||||
@ -304,6 +308,10 @@ suunto_vyper_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
|
||||
*((dc_divemode_t *) value) = DC_DIVEMODE_OC;
|
||||
}
|
||||
break;
|
||||
case DC_FIELD_DECOMODEL:
|
||||
decomodel->type = DC_DECOMODEL_RGBM;
|
||||
decomodel->conservatism = (data[4] & 0x0F) / 3;
|
||||
break;
|
||||
case DC_FIELD_STRING:
|
||||
switch(flags) {
|
||||
case 0: /* serial */
|
||||
|
||||
@ -45,6 +45,9 @@ static const dc_parser_vtable_t tecdiving_divecomputereu_parser_vtable = {
|
||||
sizeof(tecdiving_divecomputereu_parser_t),
|
||||
DC_FAMILY_TECDIVING_DIVECOMPUTEREU,
|
||||
tecdiving_divecomputereu_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
tecdiving_divecomputereu_parser_get_datetime, /* datetime */
|
||||
tecdiving_divecomputereu_parser_get_field, /* fields */
|
||||
tecdiving_divecomputereu_parser_samples_foreach, /* samples_foreach */
|
||||
|
||||
@ -86,6 +86,7 @@ struct dc_usbhid_device_t {
|
||||
int interface;
|
||||
unsigned char endpoint_in;
|
||||
unsigned char endpoint_out;
|
||||
unsigned short packetsize;
|
||||
#elif defined(USE_HIDAPI)
|
||||
char *path;
|
||||
#endif
|
||||
@ -125,6 +126,7 @@ typedef struct dc_usbhid_t {
|
||||
int interface;
|
||||
unsigned char endpoint_in;
|
||||
unsigned char endpoint_out;
|
||||
unsigned short packetsize;
|
||||
unsigned int timeout;
|
||||
#elif defined(USE_HIDAPI)
|
||||
hid_device *handle;
|
||||
@ -507,6 +509,7 @@ dc_usbhid_iterator_next (dc_iterator_t *abstract, void *out)
|
||||
device->interface = interface->bInterfaceNumber;
|
||||
device->endpoint_in = ep_in->bEndpointAddress;
|
||||
device->endpoint_out = ep_out->bEndpointAddress;
|
||||
device->packetsize = ep_in->wMaxPacketSize;
|
||||
|
||||
*(dc_usbhid_device_t **) out = device;
|
||||
|
||||
@ -614,6 +617,7 @@ dc_usbhid_open (dc_iostream_t **out, dc_context_t *context, dc_usbhid_device_t *
|
||||
usbhid->interface = device->interface;
|
||||
usbhid->endpoint_in = device->endpoint_in;
|
||||
usbhid->endpoint_out = device->endpoint_out;
|
||||
usbhid->packetsize = device->packetsize;
|
||||
usbhid->timeout = 0;
|
||||
|
||||
#elif defined(USE_HIDAPI)
|
||||
@ -704,6 +708,10 @@ dc_usbhid_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual
|
||||
int nbytes = 0;
|
||||
|
||||
#if defined(USE_LIBUSB)
|
||||
if (size > usbhid->packetsize) {
|
||||
size = usbhid->packetsize;
|
||||
}
|
||||
|
||||
int rc = libusb_interrupt_transfer (usbhid->handle, usbhid->endpoint_in, data, size, &nbytes, usbhid->timeout);
|
||||
if (rc != LIBUSB_SUCCESS || nbytes < 0) {
|
||||
ERROR (abstract->context, "Usb read interrupt transfer failed (%s).",
|
||||
|
||||
@ -39,6 +39,7 @@ struct uwatec_memomouse_parser_t {
|
||||
};
|
||||
|
||||
static dc_status_t uwatec_memomouse_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
static dc_status_t uwatec_memomouse_parser_set_clock (dc_parser_t *abstract, unsigned int devtime, dc_ticks_t systime);
|
||||
static dc_status_t uwatec_memomouse_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
|
||||
static dc_status_t uwatec_memomouse_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
|
||||
static dc_status_t uwatec_memomouse_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
|
||||
@ -47,6 +48,9 @@ static const dc_parser_vtable_t uwatec_memomouse_parser_vtable = {
|
||||
sizeof(uwatec_memomouse_parser_t),
|
||||
DC_FAMILY_UWATEC_MEMOMOUSE,
|
||||
uwatec_memomouse_parser_set_data, /* set_data */
|
||||
uwatec_memomouse_parser_set_clock, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
uwatec_memomouse_parser_get_datetime, /* datetime */
|
||||
uwatec_memomouse_parser_get_field, /* fields */
|
||||
uwatec_memomouse_parser_samples_foreach, /* samples_foreach */
|
||||
@ -86,6 +90,18 @@ uwatec_memomouse_parser_set_data (dc_parser_t *abstract, const unsigned char *da
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
uwatec_memomouse_parser_set_clock (dc_parser_t *abstract, unsigned int devtime, dc_ticks_t systime)
|
||||
{
|
||||
uwatec_memomouse_parser_t *parser = (uwatec_memomouse_parser_t *) abstract;
|
||||
|
||||
parser->devtime = devtime;
|
||||
parser->systime = systime;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static dc_status_t
|
||||
uwatec_memomouse_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
{
|
||||
|
||||
@ -37,6 +37,8 @@
|
||||
#define PACKETSIZE_USBHID_TX 32
|
||||
|
||||
#define CMD_MODEL 0x10
|
||||
#define CMD_HARDWARE 0x11
|
||||
#define CMD_SOFTWARE 0x13
|
||||
#define CMD_SERIAL 0x14
|
||||
#define CMD_DEVTIME 0x1A
|
||||
#define CMD_HANDSHAKE1 0x1B
|
||||
@ -274,9 +276,13 @@ uwatec_smart_usbhid_send (uwatec_smart_device_t *device, unsigned char cmd, cons
|
||||
{
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
dc_device_t *abstract = (dc_device_t *) device;
|
||||
unsigned char buf[PACKETSIZE_USBHID_TX + 1];
|
||||
dc_transport_t transport = dc_iostream_get_transport(device->iostream);
|
||||
unsigned char buf[DATASIZE + 3];
|
||||
|
||||
if (size + 3 > sizeof(buf)) {
|
||||
size_t packetsize = transport == DC_TRANSPORT_USBHID ?
|
||||
PACKETSIZE_USBHID_TX + 1 : sizeof(buf);
|
||||
|
||||
if (size > DATASIZE || size + 3 > packetsize) {
|
||||
ERROR (abstract->context, "Command too large (" DC_PRINTF_SIZE ").", size);
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
@ -294,7 +300,7 @@ uwatec_smart_usbhid_send (uwatec_smart_device_t *device, unsigned char cmd, cons
|
||||
if (dc_iostream_get_transport(device->iostream) == DC_TRANSPORT_BLE) {
|
||||
rc = dc_iostream_write(device->iostream, buf + 1, size + 2, NULL);
|
||||
} else {
|
||||
rc = dc_iostream_write(device->iostream, buf, sizeof(buf), NULL);
|
||||
rc = dc_iostream_write(device->iostream, buf, packetsize, NULL);
|
||||
}
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to send the command.");
|
||||
@ -309,26 +315,16 @@ uwatec_smart_usbhid_receive (uwatec_smart_device_t *device, dc_event_progress_t
|
||||
{
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
dc_device_t *abstract = (dc_device_t *) device;
|
||||
unsigned char buf[MAX_PACKETSIZE];
|
||||
dc_transport_t transport;
|
||||
unsigned int pktsize;
|
||||
dc_transport_t transport = dc_iostream_get_transport(device->iostream);
|
||||
unsigned char buf[DATASIZE + 1];
|
||||
|
||||
/*
|
||||
* USB HID uses 64-byte packets, BLE uses variable sized ones.
|
||||
*
|
||||
* BLE used to be limited to 64 bytes too, but firmware 2.0
|
||||
* seems to have increased that to at least 101 bytes (one odd
|
||||
* byte header and 100 bytes of data).
|
||||
*/
|
||||
transport = dc_iostream_get_transport(device->iostream);
|
||||
pktsize = sizeof(buf);
|
||||
if (transport == DC_TRANSPORT_USBHID)
|
||||
pktsize = PACKETSIZE_USBHID_RX;
|
||||
size_t packetsize = transport == DC_TRANSPORT_USBHID ?
|
||||
PACKETSIZE_USBHID_RX : sizeof(buf);
|
||||
|
||||
size_t nbytes = 0;
|
||||
while (nbytes < size) {
|
||||
size_t transferred = 0;
|
||||
rc = dc_iostream_read (device->iostream, buf, pktsize, &transferred);
|
||||
rc = dc_iostream_read (device->iostream, buf, packetsize, &transferred);
|
||||
if (rc != DC_STATUS_SUCCESS) {
|
||||
ERROR (abstract->context, "Failed to receive the packet.");
|
||||
return rc;
|
||||
@ -377,7 +373,7 @@ uwatec_smart_usbhid_receive (uwatec_smart_device_t *device, dc_event_progress_t
|
||||
*
|
||||
* It may be just an oddly implemented sequence number. Whatever.
|
||||
*/
|
||||
unsigned int len = transferred-1;
|
||||
unsigned int len = transferred - 1;
|
||||
if (transport == DC_TRANSPORT_USBHID) {
|
||||
if (len > buf[0])
|
||||
len = buf[0];
|
||||
@ -574,6 +570,18 @@ uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
// Read the hardware version.
|
||||
unsigned char hardware[1] = {0};
|
||||
rc = uwatec_smart_transfer (device, CMD_HARDWARE, NULL, 0, hardware, sizeof (hardware));
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
// Read the software version.
|
||||
unsigned char software[1] = {0};
|
||||
rc = uwatec_smart_transfer (device, CMD_SOFTWARE, NULL, 0, software, sizeof (software));
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
// Read the serial number.
|
||||
unsigned char serial[4] = {0};
|
||||
rc = uwatec_smart_transfer (device, CMD_SERIAL, NULL, 0, serial, sizeof (serial));
|
||||
@ -591,7 +599,7 @@ uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
device->devtime = array_uint32_le (devtime);
|
||||
|
||||
// Update and emit a progress event.
|
||||
progress.current += 9;
|
||||
progress.current += 11;
|
||||
device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
// Emit a clock event.
|
||||
@ -603,7 +611,7 @@ uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
// Emit a device info event.
|
||||
dc_event_devinfo_t devinfo;
|
||||
devinfo.model = model[0];
|
||||
devinfo.firmware = 0;
|
||||
devinfo.firmware = bcd2dec (software[0]);
|
||||
devinfo.serial = array_uint32_le (serial);
|
||||
device_event_emit (&device->base, DC_EVENT_DEVINFO, &devinfo);
|
||||
|
||||
@ -627,7 +635,7 @@ uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
|
||||
unsigned int length = array_uint32_le (answer);
|
||||
|
||||
// Update and emit a progress event.
|
||||
progress.maximum = 4 + 9 + (length ? length + 4 : 0);
|
||||
progress.maximum = 4 + 11 + (length ? length + 4 : 0);
|
||||
progress.current += 4;
|
||||
device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress);
|
||||
|
||||
|
||||
@ -49,6 +49,7 @@
|
||||
#define ALADINA1 0x25
|
||||
#define MANTIS2 0x26
|
||||
#define ALADINA2 0x28
|
||||
#define G2TEK 0x31
|
||||
#define G2 0x32
|
||||
#define G2HUD 0x42
|
||||
|
||||
@ -167,6 +168,9 @@ static const dc_parser_vtable_t uwatec_smart_parser_vtable = {
|
||||
sizeof(uwatec_smart_parser_t),
|
||||
DC_FAMILY_UWATEC_SMART,
|
||||
uwatec_smart_parser_set_data, /* set_data */
|
||||
NULL, /* set_clock */
|
||||
NULL, /* set_atmospheric */
|
||||
NULL, /* set_density */
|
||||
uwatec_smart_parser_get_datetime, /* datetime */
|
||||
uwatec_smart_parser_get_field, /* fields */
|
||||
uwatec_smart_parser_samples_foreach, /* samples_foreach */
|
||||
@ -529,7 +533,8 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser)
|
||||
parser->model == CHROMIS || parser->model == MANTIS2 ||
|
||||
parser->model == G2 || parser->model == ALADINSPORTMATRIX ||
|
||||
parser->model == ALADINSQUARE || parser->model == G2HUD ||
|
||||
parser->model == ALADINA1 || parser->model == ALADINA2 ) {
|
||||
parser->model == ALADINA1 || parser->model == ALADINA2 ||
|
||||
parser->model == G2TEK) {
|
||||
unsigned int offset = header->tankpressure + 2 * i;
|
||||
endpressure = array_uint16_le(data + offset);
|
||||
beginpressure = array_uint16_le(data + offset + 2 * header->ngases);
|
||||
@ -619,6 +624,7 @@ uwatec_smart_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i
|
||||
break;
|
||||
case G2:
|
||||
case G2HUD:
|
||||
case G2TEK:
|
||||
case ALADINSPORTMATRIX:
|
||||
case ALADINA1:
|
||||
case ALADINA2:
|
||||
@ -965,7 +971,8 @@ uwatec_smart_parse (uwatec_smart_parser_t *parser, dc_sample_callback_t callback
|
||||
parser->model == CHROMIS || parser->model == MANTIS2 ||
|
||||
parser->model == G2 || parser->model == ALADINSPORTMATRIX ||
|
||||
parser->model == ALADINSQUARE || parser->model == G2HUD ||
|
||||
parser->model == ALADINA1 || parser->model == ALADINA2 ) {
|
||||
parser->model == ALADINA1 || parser->model == ALADINA2 ||
|
||||
parser->model == G2TEK) {
|
||||
// Uwatec Galileo
|
||||
id = uwatec_galileo_identify (data[offset]);
|
||||
} else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user