diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3fe3f374..d7f23872 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -25,13 +25,13 @@ jobs:
sudo apt-get update
sudo apt-get install libbluetooth-dev libusb-1.0-0-dev
- run: autoreconf --install --force
- - run: ./configure --prefix=/usr
+ - run: ./configure
- run: make
- run: make distcheck
- name: Package artifacts
run: |
make install DESTDIR=$PWD/artifacts
- tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr
+ tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr/local
- uses: actions/upload-artifact@v4
with:
name: ${{ github.job }}-${{ matrix.compiler }}
@@ -56,13 +56,13 @@ jobs:
- name: Install dependencies
run: brew install autoconf automake libtool hidapi libusb
- run: autoreconf --install --force
- - run: ./configure --prefix=/usr
+ - run: ./configure
- run: make
- run: make distcheck
- name: Package artifacts
run: |
make install DESTDIR=$PWD/artifacts
- tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr
+ tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr/local
- uses: actions/upload-artifact@v4
with:
name: ${{ github.job }}-${{ matrix.compiler }}
@@ -82,7 +82,9 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Install dependencies
- run: sudo apt-get install gcc-mingw-w64 binutils-mingw-w64 mingw-w64-tools
+ run: |
+ sudo apt-get update
+ sudo apt-get install gcc-mingw-w64 binutils-mingw-w64 mingw-w64-tools
- name: Install libusb
env:
LIBUSB_VERSION: 1.0.26
@@ -91,7 +93,7 @@ jobs:
tar xzf v${LIBUSB_VERSION}.tar.gz
pushd libusb-${LIBUSB_VERSION}
autoreconf --install --force
- ./configure --host=${{ matrix.arch }}-w64-mingw32 --prefix=/usr
+ ./configure --host=${{ matrix.arch }}-w64-mingw32
make
make install DESTDIR=$PWD/../artifacts
popd
@@ -103,14 +105,14 @@ jobs:
tar xzf hidapi-${HIDAPI_VERSION}.tar.gz
pushd hidapi-hidapi-${HIDAPI_VERSION}
autoreconf --install --force
- ./configure --host=${{ matrix.arch }}-w64-mingw32 --prefix=/usr LDFLAGS='-static-libgcc'
+ ./configure --host=${{ matrix.arch }}-w64-mingw32 LDFLAGS='-static-libgcc'
make
make install DESTDIR=$PWD/../artifacts
popd
- run: autoreconf --install --force
- - run: ./configure --host=${{ matrix.arch }}-w64-mingw32 --prefix=/usr
+ - run: ./configure --host=${{ matrix.arch }}-w64-mingw32
env:
- PKG_CONFIG_LIBDIR: ${{ github.workspace }}/artifacts/usr/lib/pkgconfig
+ PKG_CONFIG_LIBDIR: ${{ github.workspace }}/artifacts/usr/local/lib/pkgconfig
PKG_CONFIG_SYSROOT_DIR: ${{ github.workspace }}/artifacts
PKG_CONFIG_ALLOW_SYSTEM_CFLAGS: 1
PKG_CONFIG_ALLOW_SYSTEM_LIBS: 1
@@ -119,7 +121,7 @@ jobs:
- name: Package artifacts
run: |
make install DESTDIR=$PWD/artifacts
- tar -czf ${{ github.job }}-${{ matrix.arch }}.tar.gz -C artifacts usr
+ tar -czf ${{ github.job }}-${{ matrix.arch }}.tar.gz -C artifacts usr/local
- uses: actions/upload-artifact@v4
with:
name: ${{ github.job }}-${{ matrix.arch }}
@@ -140,18 +142,18 @@ jobs:
# CONFIGURATION: Release
#
# steps:
-# - uses: actions/checkout@v3
+# - uses: actions/checkout@v4
# - uses: msys2/setup-msys2@v2
# with:
# install: autoconf automake libtool pkg-config make gcc
# - run: |
# autoreconf --install --force
-# ./configure --prefix=/usr
+# ./configure
# make -C src revision.h
# shell: msys2 {0}
-# - uses: microsoft/setup-msbuild@v1
+# - uses: microsoft/setup-msbuild@v2
# - run: msbuild -m -p:Platform=${{ matrix.platform }} -p:Configuration=${{ env.CONFIGURATION }} contrib/msvc/libdivecomputer.vcxproj
-# - uses: actions/upload-artifact@v3
+# - uses: actions/upload-artifact@v4
# with:
# name: ${{ github.job }}-${{ matrix.platform }}
# path: contrib/msvc/${{ matrix.platform }}/${{ env.CONFIGURATION }}/bin
@@ -166,7 +168,7 @@ jobs:
- uses: actions/checkout@v4
- run: |
autoreconf --install --force
- ./configure --prefix=/usr
+ ./configure
make -C src revision.h
- run: $ANDROID_NDK/ndk-build -C contrib/android NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk
- uses: actions/upload-artifact@v4
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bc4773b5..f8f2b342 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -20,6 +20,7 @@ jobs:
- name: Build distribution tarball
id: build
run: |
+ sudo apt-get update
sudo apt-get install libbluetooth-dev libusb-1.0-0-dev
autoreconf --install --force
./configure
diff --git a/configure.ac b/configure.ac
index d418a756..57e2d678 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
# Versioning.
m4_define([dc_version_major],[0])
-m4_define([dc_version_minor],[9])
+m4_define([dc_version_minor],[10])
m4_define([dc_version_micro],[0])
m4_define([dc_version_suffix],[devel-Subsurface-NG])
m4_define([dc_version],dc_version_major.dc_version_minor.dc_version_micro[]m4_ifset([dc_version_suffix],-[dc_version_suffix]))
diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk
index 08dd3e9e..2f74444b 100644
--- a/contrib/android/Android.mk
+++ b/contrib/android/Android.mk
@@ -85,6 +85,7 @@ LOCAL_SRC_FILES := \
src/reefnet_sensusultra_parser.c \
src/ringbuffer.c \
src/seac_screen.c \
+ src/seac_screen_common.c \
src/seac_screen_parser.c \
src/serial_posix.c \
src/shearwater_common.c \
diff --git a/contrib/msvc/libdivecomputer.vcxproj b/contrib/msvc/libdivecomputer.vcxproj
index 81d6df3a..db583a37 100644
--- a/contrib/msvc/libdivecomputer.vcxproj
+++ b/contrib/msvc/libdivecomputer.vcxproj
@@ -253,6 +253,7 @@
+
@@ -370,6 +371,7 @@
+
diff --git a/examples/dctool_download.c b/examples/dctool_download.c
index 29742e0c..10964080 100644
--- a/examples/dctool_download.c
+++ b/examples/dctool_download.c
@@ -53,6 +53,7 @@ typedef struct dive_data_t {
dc_buffer_t **fingerprint;
unsigned int number;
dctool_output_t *output;
+ unsigned int limit;
} dive_data_t;
static int
@@ -96,6 +97,11 @@ dive_cb (const unsigned char *data, unsigned int size, const unsigned char *fing
cleanup:
dc_parser_destroy (parser);
+
+ if (divedata->limit > 0 && divedata->number >= divedata->limit) {
+ return 0;
+ }
+
return 1;
}
@@ -146,7 +152,7 @@ event_cb (dc_device_t *device, dc_event_type_t event, const void *data, void *us
}
static dc_status_t
-download (dc_context_t *context, dc_descriptor_t *descriptor, dc_transport_t transport, const char *devname, const char *cachedir, dc_buffer_t *fingerprint, dctool_output_t *output)
+download (dc_context_t *context, dc_descriptor_t *descriptor, dc_transport_t transport, const char *devname, const char *cachedir, dc_buffer_t *fingerprint, dctool_output_t *output, unsigned int limit)
{
dc_status_t rc = DC_STATUS_SUCCESS;
dc_iostream_t *iostream = NULL;
@@ -214,6 +220,7 @@ download (dc_context_t *context, dc_descriptor_t *descriptor, dc_transport_t tra
divedata.fingerprint = &ofingerprint;
divedata.number = 0;
divedata.output = output;
+ divedata.limit = limit;
// Download the dives.
message ("Downloading the dives.\n");
@@ -260,10 +267,11 @@ dctool_download_run (int argc, char *argv[], dc_context_t *context, dc_descripto
const char *filename = NULL;
const char *cachedir = NULL;
const char *format = "xml";
+ unsigned int limit = 0;
// Parse the command-line options.
int opt = 0;
- const char *optstring = "ht:o:p:c:f:u:";
+ const char *optstring = "ht:o:p:c:f:u:l:";
#ifdef HAVE_GETOPT_LONG
struct option options[] = {
{"help", no_argument, 0, 'h'},
@@ -273,6 +281,7 @@ dctool_download_run (int argc, char *argv[], dc_context_t *context, dc_descripto
{"cache", required_argument, 0, 'c'},
{"format", required_argument, 0, 'f'},
{"units", required_argument, 0, 'u'},
+ {"limit", required_argument, 0, 'l'},
{0, 0, 0, 0 }
};
while ((opt = getopt_long (argc, argv, optstring, options, NULL)) != -1) {
@@ -304,6 +313,9 @@ dctool_download_run (int argc, char *argv[], dc_context_t *context, dc_descripto
if (strcmp (optarg, "imperial") == 0)
units = DCTOOL_UNITS_IMPERIAL;
break;
+ case 'l':
+ limit = strtoul (optarg, NULL, 0);
+ break;
default:
return EXIT_FAILURE;
}
@@ -345,7 +357,7 @@ dctool_download_run (int argc, char *argv[], dc_context_t *context, dc_descripto
}
// Download the dives.
- status = download (context, descriptor, transport, argv[0], cachedir, fingerprint, output);
+ status = download (context, descriptor, transport, argv[0], cachedir, fingerprint, output, limit);
if (status != DC_STATUS_SUCCESS) {
message ("ERROR: %s\n", dctool_errmsg (status));
exitcode = EXIT_FAILURE;
@@ -375,6 +387,7 @@ const dctool_command_t dctool_download = {
" -c, --cache Cache directory\n"
" -f, --format Output format\n"
" -u, --units Set units (metric or imperial)\n"
+ " -l, --limit Maximum number of dives to download\n"
#else
" -h Show help message\n"
" -t Transport type\n"
@@ -383,6 +396,7 @@ const dctool_command_t dctool_download = {
" -c Cache directory\n"
" -f Output format\n"
" -u Set units (metric or imperial)\n"
+ " -l Maximum number of dives to download\n"
#endif
"\n"
"Supported output formats:\n"
diff --git a/src/Makefile.am b/src/Makefile.am
index fb95fdc7..87cd8ab0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -77,6 +77,7 @@ libdivecomputer_la_SOURCES = \
liquivision_lynx.h liquivision_lynx.c liquivision_lynx_parser.c \
sporasub_sp2.h sporasub_sp2.c sporasub_sp2_parser.c \
deepsix_excursion.h deepsix_excursion.c deepsix_excursion_parser.c \
+ seac_screen_common.h seac_screen_common.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 \
diff --git a/src/array.c b/src/array.c
index 6d97557c..b447b1d0 100644
--- a/src/array.c
+++ b/src/array.c
@@ -302,6 +302,15 @@ array_uint16_le (const unsigned char data[])
((unsigned int) data[1] << 8);
}
+float
+array_float_le (const unsigned char data[])
+{
+ float result = 0;
+ unsigned int value = array_uint32_le (data);
+ memcpy (&result, &value, sizeof(value));
+ return result;
+}
+
void
array_uint64_be_set (unsigned char data[], const unsigned long long input)
{
diff --git a/src/array.h b/src/array.h
index 81268779..d97abfe5 100644
--- a/src/array.h
+++ b/src/array.h
@@ -97,6 +97,9 @@ array_uint16_be (const unsigned char data[]);
unsigned short
array_uint16_le (const unsigned char data[]);
+float
+array_float_le (const unsigned char data[]);
+
void
array_uint64_be_set (unsigned char data[], const unsigned long long input);
diff --git a/src/deepblu_cosmiq_parser.c b/src/deepblu_cosmiq_parser.c
index 95b5ae78..635a446a 100644
--- a/src/deepblu_cosmiq_parser.c
+++ b/src/deepblu_cosmiq_parser.c
@@ -191,6 +191,12 @@ deepblu_cosmiq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
unsigned int time = 0;
unsigned int offset = SZ_HEADER;
while (offset + SZ_SAMPLE <= size) {
+ // Ignore empty samples.
+ if (array_isequal (data + offset, SZ_SAMPLE, 0xFF)) {
+ offset += SZ_SAMPLE;
+ continue;
+ }
+
dc_sample_value_t sample = {0};
unsigned int temperature = array_uint16_le(data + offset + 0);
unsigned int depth = array_uint16_le(data + offset + 2);
diff --git a/src/descriptor.c b/src/descriptor.c
index e3a8bc8b..44c6c565 100644
--- a/src/descriptor.c
+++ b/src/descriptor.c
@@ -323,9 +323,11 @@ static const dc_descriptor_t g_descriptors[] = {
{"Mares", "Puck Air 2", DC_FAMILY_MARES_ICONHD , 0x2D, DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Sirius", DC_FAMILY_MARES_ICONHD , 0x2F, DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Quad Ci", DC_FAMILY_MARES_ICONHD , 0x31, DC_TRANSPORT_BLE, dc_filter_mares},
+ {"Mares", "Quad 2", DC_FAMILY_MARES_ICONHD , 0x32, DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Puck 4", DC_FAMILY_MARES_ICONHD , 0x35, DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Puck Lite", DC_FAMILY_MARES_ICONHD , 0x35, DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Puck Pro EZ", DC_FAMILY_MARES_ICONHD , 0x35, DC_TRANSPORT_BLE, dc_filter_mares},
+ {"Mares", "Puck Pro Ultra", DC_FAMILY_MARES_ICONHD , 0x35, DC_TRANSPORT_BLE, dc_filter_mares},
/* Heinrichs Weikamp */
{"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, DC_TRANSPORT_SERIAL, NULL},
{"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, DC_TRANSPORT_SERIAL, NULL},
@@ -792,9 +794,11 @@ dc_filter_mares (const dc_descriptor_t *descriptor, dc_transport_t transport, co
"Mares Genius",
"Sirius",
"Quad Ci",
+ "Quad2",
"Puck4",
"Puck Lite",
"Puck",
+ "Puck Pro U",
};
if (transport == DC_TRANSPORT_BLE) {
diff --git a/src/halcyon_symbios.c b/src/halcyon_symbios.c
index 1ecdb992..3eabf806 100644
--- a/src/halcyon_symbios.c
+++ b/src/halcyon_symbios.c
@@ -210,7 +210,7 @@ halcyon_symbios_recv (halcyon_symbios_device_t *device, unsigned char cmd, unsig
}
static dc_status_t
-halcyon_symbios_transfer (halcyon_symbios_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize, unsigned int *errorcode)
+halcyon_symbios_transfer (halcyon_symbios_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize, unsigned int *actual, unsigned int *errorcode)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
@@ -231,11 +231,16 @@ halcyon_symbios_transfer (halcyon_symbios_device_t *device, unsigned char cmd, c
goto error_exit;
}
- // Verify the length of the packet.
- if (length != asize) {
- ERROR (abstract->context, "Unexpected packet length (%u).", length);
- status = DC_STATUS_PROTOCOL;
- goto error_exit;
+ if (actual == NULL) {
+ // Verify the length of the packet.
+ if (length != asize) {
+ ERROR (abstract->context, "Unexpected packet length (%u).", length);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+ } else {
+ // Return the actual length.
+ *actual = length;
}
error_exit:
@@ -257,7 +262,7 @@ halcyon_symbios_download (halcyon_symbios_device_t *device, dc_event_progress_t
// Request the data.
unsigned char response[4] = {0};
- status = halcyon_symbios_transfer (device, request, data, size, response, sizeof(response), &errcode);
+ status = halcyon_symbios_transfer (device, request, data, size, response, sizeof(response), NULL, &errcode);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to request the data.");
goto error_exit;
@@ -438,19 +443,27 @@ halcyon_symbios_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Read the device status.
- unsigned char info[20] = {0};
- status = halcyon_symbios_transfer (device, CMD_GET_STATUS, NULL, 0, info, sizeof(info), NULL);
+ unsigned char info[36] = {0};
+ unsigned int info_size = 0;
+ status = halcyon_symbios_transfer (device, CMD_GET_STATUS, NULL, 0, info, sizeof(info), &info_size, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the device status.");
goto error_exit;
}
- HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Version", info, sizeof(info));
+ // Verify the length of the packet.
+ if (info_size < 20) {
+ ERROR (abstract->context, "Unexpected packet length (%u).", info_size);
+ status = DC_STATUS_PROTOCOL;
+ goto error_exit;
+ }
+
+ HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Version", info, info_size);
// Emit a vendor event.
dc_event_vendor_t vendor;
vendor.data = info;
- vendor.size = sizeof(info);
+ vendor.size = info_size;
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
// Emit a device info event.
@@ -568,7 +581,7 @@ halcyon_symbios_device_timesync (dc_device_t *abstract, const dc_datetime_t *dat
datetime->minute,
datetime->second,
};
- status = halcyon_symbios_transfer (device, CMD_SET_TIME, request, sizeof(request), NULL, 0, NULL);
+ status = halcyon_symbios_transfer (device, CMD_SET_TIME, request, sizeof(request), NULL, 0, NULL, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to set the time.");
goto error_exit;
diff --git a/src/halcyon_symbios_parser.c b/src/halcyon_symbios_parser.c
index ae5f4202..485e4701 100644
--- a/src/halcyon_symbios_parser.c
+++ b/src/halcyon_symbios_parser.c
@@ -55,6 +55,10 @@
(type) == ID_HEADER || \
(type) == ID_FOOTER)
+#define LOGVERSION(major,minor) ( \
+ (((major) & 0xFF) << 8) | \
+ ((minor) & 0xFF))
+
#define UNDEFINED 0xFFFFFFFF
#define EPOCH 1609459200 /* 2021-01-01 00:00:00 */
@@ -87,7 +91,9 @@ typedef struct halcyon_symbios_parser_t {
dc_parser_t base;
// Cached fields.
unsigned int cached;
+ unsigned int logversion;
unsigned int datetime;
+ int timezone;
unsigned int divetime;
unsigned int maxdepth;
unsigned int divemode;
@@ -135,7 +141,9 @@ halcyon_symbios_parser_create (dc_parser_t **out, dc_context_t *context, const u
// Set the default values.
parser->cached = 0;
+ parser->logversion = 0;
parser->datetime = UNDEFINED;
+ parser->timezone = 0;
parser->divetime = 0;
parser->maxdepth = 0;
parser->divemode = UNDEFINED;
@@ -182,8 +190,23 @@ halcyon_symbios_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datet
dc_ticks_t ticks = (dc_ticks_t) parser->datetime + EPOCH;
- if (!dc_datetime_localtime (datetime, ticks))
- return DC_STATUS_DATAFORMAT;
+ if (parser->logversion >= LOGVERSION(1,9)) {
+ // For firmware versions with timezone support, the UTC offset of the
+ // device is used.
+ int timezone = parser->timezone * 3600;
+
+ ticks += timezone;
+
+ if (!dc_datetime_gmtime (datetime, ticks))
+ return DC_STATUS_DATAFORMAT;
+
+ datetime->timezone = timezone;
+ } else {
+ // For firmware versions without timezone support, the current timezone
+ // of the host system is used.
+ if (!dc_datetime_localtime (datetime, ticks))
+ return DC_STATUS_DATAFORMAT;
+ }
return DC_STATUS_SUCCESS;
}
@@ -311,7 +334,9 @@ halcyon_symbios_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
6, /* ID_GF_INFO */
};
+ unsigned int logversion = 0;
unsigned int time_start = UNDEFINED, time_end = UNDEFINED;
+ int timezone = 0;
unsigned int maxdepth = 0;
unsigned int divemode = UNDEFINED;
unsigned int atmospheric = UNDEFINED;
@@ -367,6 +392,7 @@ halcyon_symbios_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
}
if (type == ID_LOG_VERSION) {
+ logversion = array_uint16_be (data + offset + 2);
unsigned int version_major = data[offset + 2];
unsigned int version_minor = data[offset + 3];
DEBUG (abstract->context, "Version: %u.%u",
@@ -384,6 +410,7 @@ halcyon_symbios_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
unsigned int DC_ATTR_UNUSED detection = data[offset + 11];
unsigned int DC_ATTR_UNUSED noflytime = data[offset + 12];
divemode = data[offset + 13];
+ timezone = (signed char) data[offset + 14];
atmospheric = array_uint16_le(data + offset + 16);
unsigned int DC_ATTR_UNUSED number = array_uint16_le(data + offset + 18);
unsigned int DC_ATTR_UNUSED battery = array_uint16_le(data + offset + 20);
@@ -687,7 +714,9 @@ halcyon_symbios_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
}
parser->cached = 1;
+ parser->logversion = logversion;
parser->datetime = time_start;
+ parser->timezone = timezone;
if (time_start != UNDEFINED && time_end != UNDEFINED) {
parser->divetime = time_end - time_start;
} else {
diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c
index 160e5d1b..86f1cd32 100644
--- a/src/hw_ostc_parser.c
+++ b/src/hw_ostc_parser.c
@@ -86,7 +86,7 @@
#define OSTC4FW(major,minor,micro,beta) ( \
(((major) & 0x1F) << 11) | \
- (((minor) & 0x1F) >> 6) | \
+ (((minor) & 0x1F) << 6) | \
(((micro) & 0x1F) << 1) | \
((beta) & 0x01))
@@ -944,8 +944,18 @@ hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t
unsigned int firmware = 0;
if (parser->model == OSTC4) {
firmware = array_uint16_le (data + layout->firmware);
+ DEBUG (abstract->context, "Device: firmware=%u (%u.%u.%u.%u)",
+ firmware,
+ (firmware >> 11) & 0x1F,
+ (firmware >> 6) & 0x1F,
+ (firmware >> 1) & 0x1F,
+ (firmware ) & 0x01);
} else {
firmware = array_uint16_be (data + layout->firmware);
+ DEBUG (abstract->context, "Device: firmware=%u (%u.%u)",
+ firmware,
+ (firmware >> 8) & 0xFF,
+ (firmware ) & 0xFF);
}
// Get the dive mode.
@@ -1174,6 +1184,7 @@ hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t
if (callback) {
unsigned int value = array_uint16_le(data + offset);
+ dc_sample_type_t eventType = DC_SAMPLE_EVENT;
dc_sample_value_t sample = {
.event.type = SAMPLE_EVENT_STRING,
.event.flags = SAMPLE_FLAGS_SEVERITY_INFO,
@@ -1183,12 +1194,13 @@ hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t
if (value & OSTC4_COMPASS_HEADING_CLEARED_FLAG) {
snprintf(buf, BUFLEN, "Cleared compass heading");
} else {
- sample.event.value = heading;
if (value & OSTC4_COMPASS_HEADING_SET_FLAG) {
- sample.event.type = SAMPLE_EVENT_HEADING;
+ eventType = DC_SAMPLE_BEARING;
+ sample.bearing = heading;
snprintf(buf, BUFLEN, "Set compass heading [degrees]%s", sample.event.value ? "" : ": 0");
} else {
+ sample.event.value = heading;
snprintf(buf, BUFLEN, "Logged compass heading [degrees]%s", sample.event.value ? "" : ": 0");
}
@@ -1196,13 +1208,24 @@ hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t
sample.event.name = buf;
- callback(DC_SAMPLE_EVENT, &sample, userdata);
+ callback(eventType, &sample, userdata);
}
offset += 2;
length -= 2;
}
+ // GNSS position
+ if (events & 0x0400) {
+ if (length < 8) {
+ ERROR (abstract->context, "Buffer overflow detected!");
+ return DC_STATUS_DATAFORMAT;
+ }
+
+ offset += 8;
+ length -= 8;
+ }
+
// Scrubber state update
if (events & 0x0800) {
if (length < 2) {
@@ -1251,6 +1274,7 @@ hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t
offset += 2;
length -= 2;
}
+
}
// Extended sample info.
diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c
index 11b8a0dc..ea3fa563 100644
--- a/src/mares_iconhd.c
+++ b/src/mares_iconhd.c
@@ -51,6 +51,7 @@
#define PUCKAIR2 0x2D
#define SIRIUS 0x2F
#define QUADCI 0x31
+#define QUAD2 0x32
#define PUCK4 0x35
#define ISSMART(model) ( \
@@ -64,12 +65,14 @@
(model) == PUCKAIR2 || \
(model) == SIRIUS || \
(model) == QUADCI || \
+ (model) == QUAD2 || \
(model) == PUCK4)
#define ISSIRIUS(model) ( \
(model) == PUCKAIR2 || \
(model) == SIRIUS || \
(model) == QUADCI || \
+ (model) == QUAD2 || \
(model) == PUCK4)
#define MAXRETRIES 4
@@ -195,9 +198,11 @@ mares_iconhd_get_model (mares_iconhd_device_t *device)
{"Puck Air 2", PUCKAIR2},
{"Sirius", SIRIUS},
{"Quad Ci", QUADCI},
+ {"Quad2", QUAD2},
{"Puck4", PUCK4},
{"Puck Lite", PUCK4},
{"Puck", PUCK4},
+ {"Puck Pro U", PUCK4},
};
// Check the product name in the version packet against the list
@@ -667,6 +672,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
case PUCKAIR2:
case SIRIUS:
case QUADCI:
+ case QUAD2:
case PUCK4:
device->layout = &mares_genius_layout;
device->packetsize = 4096;
diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c
index b7545601..5be9c969 100644
--- a/src/mares_iconhd_parser.c
+++ b/src/mares_iconhd_parser.c
@@ -51,6 +51,7 @@
#define PUCKAIR2 0x2D
#define SIRIUS 0x2F
#define QUADCI 0x31
+#define QUAD2 0x32
#define PUCK4 0x35
#define ISSMART(model) ( \
@@ -64,6 +65,7 @@
(model) == PUCKAIR2 || \
(model) == SIRIUS || \
(model) == QUADCI || \
+ (model) == QUAD2 || \
(model) == PUCK4)
#define NGASMIXES_ICONHD 3
diff --git a/src/pelagic_i330r.c b/src/pelagic_i330r.c
index 17aad3bf..8cbbed95 100644
--- a/src/pelagic_i330r.c
+++ b/src/pelagic_i330r.c
@@ -58,11 +58,14 @@
#define RSP_READY 1
#define RSP_DONE 2
+#define RSP_ACCESSCODE_INVALID 13
#define MAXPACKET 255
#define MAXPASSCODE 6
+#define MAXRETRIES 3
+
typedef struct pelagic_i330r_device_t {
oceanic_common_device_t base;
dc_iostream_t *iostream;
@@ -276,6 +279,8 @@ pelagic_i330r_transfer (pelagic_i330r_device_t *device, unsigned char cmd, unsig
if (errorcode != response) {
ERROR (abstract->context, "Unexpected response code (%u)", errorcode);
+ if (errorcode == RSP_ACCESSCODE_INVALID)
+ return DC_STATUS_NOACCESS;
return DC_STATUS_PROTOCOL;
}
@@ -388,14 +393,29 @@ pelagic_i330r_init (pelagic_i330r_device_t *device)
return status;
}
- if (array_isequal (device->accesscode, sizeof(device->accesscode), 0)) {
- // Request to display the PIN code.
+ unsigned int nretries = 0;
+ while (1) {
+ // Try to request access.
status = pelagic_i330r_init_accesscode (device);
- if (status != DC_STATUS_SUCCESS) {
- ERROR (abstract->context, "Failed to display the PIN code.");
+ if (status != DC_STATUS_SUCCESS && status != DC_STATUS_NOACCESS) {
+ ERROR (abstract->context, "Failed to request access.");
return status;
}
+ if (array_isequal (device->accesscode, sizeof(device->accesscode), 0)) {
+ WARNING (abstract->context, "Access denied, no access code.");
+ } else {
+ if (status == DC_STATUS_SUCCESS)
+ break;
+ WARNING (abstract->context, "Access denied, invalid access code.");
+ }
+
+ // Abort if the maximum number of retries is reached.
+ if (nretries++ >= MAXRETRIES) {
+ ERROR (abstract->context, "Maximum number of retries reached.");
+ return DC_STATUS_NOACCESS;
+ }
+
// Get the bluetooth PIN code.
char pincode[6 + 1] = {0};
status = dc_iostream_ioctl (device->iostream, DC_IOCTL_BLE_GET_PINCODE, pincode, sizeof(pincode));
@@ -422,13 +442,6 @@ pelagic_i330r_init (pelagic_i330r_device_t *device)
}
}
- // Request access.
- status = pelagic_i330r_init_accesscode (device);
- if (status != DC_STATUS_SUCCESS) {
- ERROR (abstract->context, "Failed to request access.");
- return status;
- }
-
// Send the wakeup command.
status = pelagic_i330r_init_handshake (device, 1);
if (status != DC_STATUS_SUCCESS) {
diff --git a/src/platform.h b/src/platform.h
index f98f0d7a..5f7ca962 100644
--- a/src/platform.h
+++ b/src/platform.h
@@ -22,6 +22,7 @@
#ifndef DC_PLATFORM_H
#define DC_PLATFORM_H
+#include
#include
#ifdef __cplusplus
diff --git a/src/seac_screen.c b/src/seac_screen.c
index e8f91c67..06ac942a 100644
--- a/src/seac_screen.c
+++ b/src/seac_screen.c
@@ -23,6 +23,7 @@
#include // malloc, free
#include "seac_screen.h"
+#include "seac_screen_common.h"
#include "context-private.h"
#include "device-private.h"
#include "ringbuffer.h"
@@ -66,11 +67,8 @@
#define SZ_ADDRESS 4
#define SZ_READ 2048
-#define SZ_HEADER 128
-#define SZ_SAMPLE 64
-
-#define FP_OFFSET 0x0A
-#define FP_SIZE 7
+#define FP_OFFSET 0
+#define FP_SIZE 4
#define RB_PROFILE_DISTANCE(a,b,l) ringbuffer_distance (a, b, DC_RINGBUFFER_FULL, l->rb_profile_begin, l->rb_profile_end)
#define RB_PROFILE_INCR(a,b,l) ringbuffer_increment (a, b, l->rb_profile_begin, l->rb_profile_end)
@@ -95,15 +93,10 @@ typedef struct seac_screen_device_t {
dc_iostream_t *iostream;
const seac_screen_commands_t *cmds;
const seac_screen_layout_t *layout;
+ unsigned int fingerprint;
unsigned char info[SZ_HWINFO + SZ_SWINFO];
- unsigned char fingerprint[FP_SIZE];
} seac_screen_device_t;
-typedef struct seac_screen_logbook_t {
- unsigned int address;
- unsigned char header[SZ_HEADER];
-} seac_screen_logbook_t;
-
static dc_status_t seac_screen_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t seac_screen_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
static dc_status_t seac_screen_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
@@ -329,8 +322,8 @@ seac_screen_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t
device->iostream = iostream;
device->cmds = NULL;
device->layout = NULL;
+ device->fingerprint = 0;
memset (device->info, 0, sizeof (device->info));
- memset (device->fingerprint, 0, sizeof (device->fingerprint));
// Set the serial communication protocol (115200 8N1).
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
@@ -395,13 +388,14 @@ seac_screen_device_set_fingerprint (dc_device_t *abstract, const unsigned char d
{
seac_screen_device_t *device = (seac_screen_device_t *) abstract;
- if (size && size != sizeof (device->fingerprint))
+ if (size && size != FP_SIZE)
return DC_STATUS_INVALIDARGS;
- if (size)
- memcpy (device->fingerprint, data, sizeof (device->fingerprint));
- else
- memset (device->fingerprint, 0, sizeof (device->fingerprint));
+ if (size) {
+ device->fingerprint = array_uint32_le (data);
+ } else {
+ device->fingerprint = 0;
+ }
return DC_STATUS_SUCCESS;
}
@@ -533,21 +527,35 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
// Calculate the number of dives.
unsigned int ndives = last - first + 1;
+ // Check the fingerprint.
+ if (device->fingerprint >= last) {
+ ndives = 0;
+ } else if (device->fingerprint >= first) {
+ first = device->fingerprint + 1;
+ ndives = last - first + 1;
+ }
+
// Update and emit a progress event.
progress.current += SZ_RANGE;
- progress.maximum += SZ_RANGE + ndives * (SZ_ADDRESS + SZ_HEADER);
+ progress.maximum += SZ_RANGE + ndives * SZ_ADDRESS;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
- // Allocate memory for the logbook data.
- seac_screen_logbook_t *logbook = (seac_screen_logbook_t *) malloc (ndives * sizeof (seac_screen_logbook_t));
- if (logbook == NULL) {
+ // Exit if no dives to download.
+ if (ndives == 0) {
+ goto error_exit;
+ }
+
+ // Allocate memory for the dive addresses.
+ unsigned int *address = (unsigned int *) malloc (ndives * sizeof (unsigned int));
+ if (address == NULL) {
status = DC_STATUS_NOMEMORY;
goto error_exit;
}
- // Read the header of each dive in reverse order (most recent first).
+ // Read the address of each dive in reverse order (most recent first).
unsigned int eop = 0;
unsigned int previous = 0;
+ unsigned int begin = 0;
unsigned int count = 0;
unsigned int skip = 0;
unsigned int rb_profile_size = 0;
@@ -569,55 +577,71 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
goto error_free_logbook;
}
- // Get the dive address.
- logbook[i].address = array_uint32_be (rsp_address);
- if (logbook[i].address < layout->rb_profile_begin || logbook[i].address >= layout->rb_profile_end) {
- ERROR (abstract->context, "Invalid ringbuffer pointer (0x%08x).", logbook[i].address);
- status = DC_STATUS_DATAFORMAT;
- goto error_free_logbook;
- }
-
- // Read the dive header.
- status = seac_screen_device_read (abstract, logbook[i].address, logbook[i].header, SZ_HEADER);
- if (status != DC_STATUS_SUCCESS) {
- ERROR (abstract->context, "Failed to read the dive header.");
- goto error_free_logbook;
- }
-
// Update and emit a progress event.
- progress.current += SZ_ADDRESS + SZ_HEADER;
+ progress.current += SZ_ADDRESS;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
- // Check the header checksums.
- if (checksum_crc16_ccitt (logbook[i].header, SZ_HEADER / 2, 0xFFFF, 0x0000) != 0 ||
- checksum_crc16_ccitt (logbook[i].header + SZ_HEADER / 2, SZ_HEADER / 2, 0xFFFF, 0x0000) != 0) {
- ERROR (abstract->context, "Unexpected header checksum.");
+ // Get the dive address.
+ address[i] = array_uint32_be (rsp_address);
+ if (address[i] < layout->rb_profile_begin || address[i] >= layout->rb_profile_end) {
+ ERROR (abstract->context, "Invalid ringbuffer pointer (0x%08x).", address[i]);
status = DC_STATUS_DATAFORMAT;
goto error_free_logbook;
}
- // Check the fingerprint.
- if (memcmp (logbook[i].header + FP_OFFSET, device->fingerprint, sizeof (device->fingerprint)) == 0) {
- skip = 1;
- break;
- }
-
- // Get the number of samples.
- unsigned int nsamples = array_uint32_le (logbook[i].header + 0x44);
- unsigned int nbytes = SZ_HEADER + nsamples * SZ_SAMPLE;
-
// Get the end-of-profile pointer.
if (eop == 0) {
- eop = previous = RB_PROFILE_INCR (logbook[i].address, nbytes, layout);
+ // Read the dive header.
+ unsigned char header[SZ_HEADER] = {0};
+ status = seac_screen_device_read (abstract, address[i], header, sizeof(header));
+ if (status != DC_STATUS_SUCCESS) {
+ ERROR (abstract->context, "Failed to read the dive header.");
+ goto error_free_logbook;
+ }
+
+ // Update and emit a progress event.
+ progress.current += sizeof(header);
+ progress.maximum += sizeof(header);
+ device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
+
+ // Check the header records.
+ unsigned int isvalid = 1;
+ for (unsigned int j = 0; j < 2; ++j) {
+ unsigned int type = j == 0 ? HEADER1 : HEADER2;
+ if (!seac_screen_record_isvalid (abstract->context,
+ header + j * SZ_HEADER / 2, SZ_HEADER / 2,
+ type, number)) {
+ WARNING (abstract->context, "Invalid header record %u.", j);
+ isvalid = 0;
+ }
+ }
+
+ // For dives with an invalid header, the number of samples in the
+ // header is not guaranteed to be valid. Discard the entire dive
+ // instead and take its start address as the end of the profile.
+ if (!isvalid) {
+ WARNING (abstract->context, "Unable to locate the end of the profile.");
+ eop = previous = address[i];
+ begin = 1;
+ skip++;
+ continue;
+ }
+
+ // Get the number of samples.
+ unsigned int nsamples = array_uint32_le (header + 0x44);
+ unsigned int nbytes = SZ_HEADER + nsamples * SZ_SAMPLE;
+
+ // Calculate the end of the profile.
+ eop = previous = RB_PROFILE_INCR (address[i], nbytes, layout);
}
// Calculate the length.
- unsigned int length = RB_PROFILE_DISTANCE (logbook[i].address, previous, layout);
+ unsigned int length = RB_PROFILE_DISTANCE (address[i], previous, layout);
// Check for the end of the ringbuffer.
if (length > remaining) {
WARNING (abstract->context, "Reached the end of the ringbuffer.");
- skip = 1;
+ skip++;
break;
}
@@ -626,12 +650,12 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
// Move to the start of the current dive.
remaining -= length;
- previous = logbook[i].address;
+ previous = address[i];
count++;
}
// Update and emit a progress event.
- progress.maximum -= (ndives - count - skip) * (SZ_ADDRESS + SZ_HEADER) +
+ progress.maximum -= (ndives - count - skip) * SZ_ADDRESS +
((layout->rb_profile_end - layout->rb_profile_begin) - rb_profile_size);
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
@@ -658,12 +682,15 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
previous = eop;
unsigned int offset = rb_profile_size;
for (unsigned int i = 0; i < count; ++i) {
+ unsigned int idx = begin + i;
+ unsigned int number = last - idx;
+
// Calculate the length.
- unsigned int length = RB_PROFILE_DISTANCE (logbook[i].address, previous, layout);
+ unsigned int length = RB_PROFILE_DISTANCE (address[idx], previous, layout);
// Move to the start of the current dive.
offset -= length;
- previous = logbook[i].address;
+ previous = address[idx];
// Read the dive.
status = dc_rbstream_read (rbstream, &progress, profile + offset, length);
@@ -672,26 +699,46 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
goto error_free_rbstream;
}
- // Check the dive header.
- if (memcmp (profile + offset, logbook[i].header, SZ_HEADER) != 0) {
- ERROR (abstract->context, "Unexpected dive header.");
+ // Check the minimum header length.
+ if (length < SZ_HEADER) {
+ ERROR (abstract->context, "Unexpected dive length (%u).", length);
status = DC_STATUS_DATAFORMAT;
goto error_free_rbstream;
}
+ // Check the header records.
+ unsigned int isvalid = 1;
+ for (unsigned int j = 0; j < 2; ++j) {
+ unsigned int type = j == 0 ? HEADER1 : HEADER2;
+ if (!seac_screen_record_isvalid (abstract->context,
+ profile + offset + j * SZ_HEADER / 2, SZ_HEADER / 2,
+ type, number)) {
+ WARNING (abstract->context, "Invalid header record %u.", j);
+ isvalid = 0;
+ }
+ }
+
// Get the number of samples.
// The actual size of the dive, based on the number of samples, can
// sometimes be smaller than the maximum length. In that case, the
// remainder of the data is padded with 0xFF bytes.
- unsigned int nsamples = array_uint32_le (logbook[i].header + 0x44);
- unsigned int nbytes = SZ_HEADER + nsamples * SZ_SAMPLE;
+ unsigned int nsamples = 0;
+ unsigned int nbytes = 0;
+ if (isvalid) {
+ nsamples = array_uint32_le (profile + offset + 0x44);
+ nbytes = SZ_HEADER + nsamples * SZ_SAMPLE;
+ } else {
+ WARNING (abstract->context, "Unable to locate the padding bytes.");
+ nbytes = length;
+ }
+
if (nbytes > length) {
ERROR (abstract->context, "Unexpected dive length (%u %u).", nbytes, length);
status = DC_STATUS_DATAFORMAT;
goto error_free_rbstream;
}
- if (callback && !callback (profile + offset, nbytes, profile + offset + FP_OFFSET, sizeof(device->fingerprint), userdata)) {
+ if (callback && !callback (profile + offset, nbytes, profile + offset + FP_OFFSET, FP_SIZE, userdata)) {
break;
}
}
@@ -701,7 +748,7 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
error_free_profile:
free (profile);
error_free_logbook:
- free (logbook);
+ free (address);
error_exit:
return status;
}
diff --git a/src/seac_screen_common.c b/src/seac_screen_common.c
new file mode 100644
index 00000000..f5890a26
--- /dev/null
+++ b/src/seac_screen_common.c
@@ -0,0 +1,58 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2025 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 "seac_screen_common.h"
+#include "context-private.h"
+#include "checksum.h"
+#include "array.h"
+
+int
+seac_screen_record_isvalid (dc_context_t *context, const unsigned char data[], unsigned int size, unsigned int type, unsigned int id)
+{
+ // Check the record size.
+ if (size != SZ_RECORD) {
+ ERROR (context, "Unexpected record size (%u).", size);
+ return 0;
+ }
+
+ // Check the record checksum.
+ unsigned short csum = checksum_crc16_ccitt (data, size, 0xFFFF, 0x0000);
+ if (csum != 0) {
+ ERROR (context, "Unexpected record checksum (%04x).", csum);
+ return 0;
+ }
+
+ // Check the record type.
+ unsigned int rtype = data[size - 3];
+ if (rtype != type) {
+ ERROR (context, "Unexpected record type (%02x %02x).", rtype, type);
+ return 0;
+ }
+
+ // Check the record id.
+ unsigned int rid = array_uint32_le (data);
+ if (rid != id) {
+ ERROR (context, "Unexpected record id (%u %u).", rid, id);
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/src/seac_screen_common.h b/src/seac_screen_common.h
new file mode 100644
index 00000000..054e56b3
--- /dev/null
+++ b/src/seac_screen_common.h
@@ -0,0 +1,45 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2025 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 SEAC_SCREEN_COMMON_H
+#define SEAC_SCREEN_COMMON_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define HEADER1 0xCF
+#define HEADER2 0xC0
+#define SAMPLE 0xAA
+
+#define SZ_RECORD 64
+#define SZ_HEADER (SZ_RECORD * 2)
+#define SZ_SAMPLE SZ_RECORD
+
+int
+seac_screen_record_isvalid (dc_context_t *context, const unsigned char data[], unsigned int size, unsigned int type, unsigned int id);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* SEAC_SCREEN_COMMON_H */
diff --git a/src/seac_screen_parser.c b/src/seac_screen_parser.c
index b19c8ea9..42cf0864 100644
--- a/src/seac_screen_parser.c
+++ b/src/seac_screen_parser.c
@@ -22,16 +22,13 @@
#include
#include "seac_screen.h"
+#include "seac_screen_common.h"
#include "context-private.h"
#include "parser-private.h"
-#include "checksum.h"
#include "array.h"
#define ISINSTANCE(parser) dc_device_isinstance((parser), &seac_screen_parser_vtable)
-#define SZ_HEADER 128
-#define SZ_SAMPLE 64
-
#define NGASMIXES 2
#define INVALID 0xFFFFFFFF
@@ -152,6 +149,20 @@ seac_screen_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
if (abstract->size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
+ // Get the dive ID.
+ unsigned int dive_id = array_uint32_le (data + 0x00);
+
+ // Check the header records.
+ for (unsigned int i = 0; i < 2; ++i) {
+ unsigned int type = i == 0 ? HEADER1 : HEADER2;
+ if (!seac_screen_record_isvalid (abstract->context,
+ data + i * SZ_HEADER / 2, SZ_HEADER / 2,
+ type, dive_id)) {
+ ERROR (abstract->context, "Invalid header record %u.", i);
+ return DC_STATUS_DATAFORMAT;
+ }
+ }
+
// The date/time is stored as UTC time with a timezone offset. To convert to
// local time, the UTC time is first converted to unix time (seconds since
// the epoch), then adjusted for the timezone offset, and finally converted
@@ -271,14 +282,20 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
if (abstract->size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
- if (checksum_crc16_ccitt (data, SZ_HEADER / 2, 0xFFFF, 0x0000) != 0 ||
- checksum_crc16_ccitt (data + SZ_HEADER / 2, SZ_HEADER / 2, 0xFFFF, 0x0000) != 0) {
- ERROR (abstract->context, "Unexpected header checksum.");
- return DC_STATUS_DATAFORMAT;
- }
-
+ // Get the dive ID.
unsigned int dive_id = array_uint32_le (data + 0x00);
+ // Check the header records.
+ for (unsigned int i = 0; i < 2; ++i) {
+ unsigned int type = i == 0 ? HEADER1 : HEADER2;
+ if (!seac_screen_record_isvalid (abstract->context,
+ data + i * SZ_HEADER / 2, SZ_HEADER / 2,
+ type, dive_id)) {
+ ERROR (abstract->context, "Invalid header record %u.", i);
+ return DC_STATUS_DATAFORMAT;
+ }
+ }
+
unsigned int ngasmixes = 0;
unsigned int oxygen[NGASMIXES] = {0};
unsigned int o2_previous = INVALID;
@@ -291,12 +308,12 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
while (offset + SZ_SAMPLE <= size) {
dc_sample_value_t sample = {0};
- if (checksum_crc16_ccitt (data + offset, SZ_SAMPLE, 0xFFFF, 0x0000) != 0) {
- ERROR (abstract->context, "Unexpected sample checksum.");
+ // Check the sample record.
+ if (!seac_screen_record_isvalid (abstract->context, data + offset, SZ_SAMPLE, SAMPLE, dive_id)) {
+ ERROR (abstract->context, "Invalid sample record.");
return DC_STATUS_DATAFORMAT;
}
- unsigned int id = array_uint32_le (data + offset + 0x00);
unsigned int timestamp = array_uint32_le (data + offset + 0x04);
unsigned int depth = array_uint16_le (data + offset + 0x08);
unsigned int temperature = array_uint16_le (data + offset + 0x0A);
@@ -309,11 +326,6 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
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);
- return DC_STATUS_DATAFORMAT;
- }
-
// Time (seconds).
if (timestamp < time) {
ERROR (abstract->context, "Timestamp moved backwards (%u %u).", timestamp, time);
diff --git a/src/shearwater_common.c b/src/shearwater_common.c
index 0bb2bf99..0a863f19 100644
--- a/src/shearwater_common.c
+++ b/src/shearwater_common.c
@@ -746,6 +746,7 @@ dc_status_t shearwater_common_get_model(shearwater_common_device_t *device, unsi
case 0x0C0D:
case 0x7C2D:
case 0x8D6C:
+ case 0x425B:
*model = PERDIXAI;
break;
case 0x704C:
@@ -763,6 +764,7 @@ dc_status_t shearwater_common_get_model(shearwater_common_device_t *device, unsi
*model = PEREGRINE;
break;
case 0x1712:
+ case 0x813A:
*model = PEREGRINE_TX;
break;
case 0xC0E0:
diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c
index c9ad0004..9b86b6c8 100644
--- a/src/shearwater_predator_parser.c
+++ b/src/shearwater_predator_parser.c
@@ -39,6 +39,7 @@
#define LOG_RECORD_DIVE_SAMPLE 0x01
#define LOG_RECORD_FREEDIVE_SAMPLE 0x02
+#define LOG_RECORD_AVELO_SAMPLE 0x03
#define LOG_RECORD_OPENING_0 0x10
#define LOG_RECORD_OPENING_1 0x11
#define LOG_RECORD_OPENING_2 0x12
@@ -80,6 +81,7 @@
#define M_CC2 5
#define M_OC_REC 6
#define M_FREEDIVE 7
+#define M_AVELO 12
#define SM_3_GAS_NX 0
#define SM_AIR 1
@@ -540,10 +542,15 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
// Get the record type.
unsigned int type = pnf ? data[offset] : LOG_RECORD_DIVE_SAMPLE;
- if (type == LOG_RECORD_DIVE_SAMPLE) {
+ if (type == LOG_RECORD_DIVE_SAMPLE ||
+ type == LOG_RECORD_AVELO_SAMPLE) {
// Status flags.
- unsigned int status = data[offset + 11 + pnf];
- unsigned int ccr = (status & OC) == 0;
+ unsigned int status = 0;
+ unsigned int ccr = 0;
+ if (type != LOG_RECORD_AVELO_SAMPLE) {
+ status = data[offset + 11 + pnf];
+ ccr = (status & OC) == 0;
+ }
if (ccr) {
divemode = status & SC ? M_SC : M_CC;
}
@@ -583,7 +590,8 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
// Tank pressure
if (logversion >= 7) {
const unsigned int idx[2] = {27, 19};
- for (unsigned int i = 0; i < 2; ++i) {
+ const unsigned int count = type == LOG_RECORD_AVELO_SAMPLE ? 1 : 2;
+ for (unsigned int i = 0; i < count; ++i) {
// Values above 0xFFF0 are special codes:
// 0xFFFF AI is off
// 0xFFFE No comms for 90 seconds+
@@ -1038,7 +1046,30 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ
*((double *) value) = parser->atmospheric / 1000.0;
break;
case DC_FIELD_DIVEMODE:
- return DC_FIELD_VALUE(parser->cache, value, DIVEMODE);
+ switch (parser->divemode) {
+ case M_CC:
+ case M_CC2:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_CCR;
+ break;
+ case M_SC:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_SCR;
+ break;
+ case M_OC_TEC:
+ case M_OC_REC:
+ case M_AVELO:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_OC;
+ break;
+ case M_GAUGE:
+ case M_PPO2:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE;
+ break;
+ case M_FREEDIVE:
+ *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE;
+ break;
+ default:
+ return DC_STATUS_DATAFORMAT;
+ }
+ break;
case DC_FIELD_DECOMODEL:
switch (data[decomodel_idx]) {
case GF:
@@ -1109,11 +1140,8 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
// Get the record type.
unsigned int type = pnf ? data[offset] : LOG_RECORD_DIVE_SAMPLE;
- // stop parsing if we see the end block
- if (type == LOG_RECORD_FINAL && data[offset + 1] == 0xFD)
- break;
-
- if (type == LOG_RECORD_DIVE_SAMPLE) {
+ if (type == LOG_RECORD_DIVE_SAMPLE ||
+ type == LOG_RECORD_AVELO_SAMPLE) {
// Time (seconds).
time += interval;
sample.time = time;
@@ -1143,8 +1171,12 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
// Status flags.
- unsigned int status = data[offset + pnf + 11];
- unsigned int ccr = (status & OC) == 0;
+ unsigned int status = 0;
+ unsigned int ccr = 0;
+ if (type != LOG_RECORD_AVELO_SAMPLE) {
+ status = data[offset + 11 + pnf];
+ ccr = (status & OC) == 0;
+ }
if (ccr) {
// PPO2
@@ -1261,7 +1293,8 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
// detect tank pressure
if (parser->logversion >= 7) {
const unsigned int idx[2] = {27, 19};
- for (unsigned int i = 0; i < 2; ++i) {
+ const unsigned int count = type == LOG_RECORD_AVELO_SAMPLE ? 1 : 2;
+ for (unsigned int i = 0; i < count; ++i) {
// Tank pressure
// Values above 0xFFF0 are special codes:
// 0xFFFF AI is off