Merge git://github.com/libdivecomputer/libdivecomputer into Subsurface-NG

Update with Jef's upstream:

 - add support for Cressi Goa and Cartesio

 - update the Shearwater PNF parser to Jef's version

 - misc minor fixes

* git://github.com/libdivecomputer/libdivecomputer:
  Use the timezone setting of the dive computer
  Add support for the Cressi Goa and Cartesio
  Add an extra parameter for the initial CRC value
  Add support for the Ratio iDive Color series
  Shearwater Petrel Native Format parsing
  Shearwater: detect which logbook format is support
  Shearwater: add Teric to list of supported dive computers
  Shearwater: skip deleted dives
  Fix a potential buffer overflow
This commit is contained in:
Linus Torvalds 2019-01-22 11:54:02 +13:00
commit 1d09635a58
19 changed files with 1205 additions and 296 deletions

View File

@ -80,6 +80,7 @@ static const backend_table_t g_backends[] = {
{"ostc3", DC_FAMILY_HW_OSTC3, 0x0A},
{"edy", DC_FAMILY_CRESSI_EDY, 0x08},
{"leonardo", DC_FAMILY_CRESSI_LEONARDO, 1},
{"goa", DC_FAMILY_CRESSI_GOA, 2},
{"n2ition3", DC_FAMILY_ZEAGLE_N2ITION3, 0},
{"cobalt", DC_FAMILY_ATOMICS_COBALT, 0},
{"predator", DC_FAMILY_SHEARWATER_PREDATOR, 2},

View File

@ -90,6 +90,7 @@ typedef enum dc_family_t {
/* Cressi */
DC_FAMILY_CRESSI_EDY = (7 << 16),
DC_FAMILY_CRESSI_LEONARDO,
DC_FAMILY_CRESSI_GOA,
/* Zeagle */
DC_FAMILY_ZEAGLE_N2ITION3 = (8 << 16),
/* Atomic Aquatics */

View File

@ -238,6 +238,14 @@
RelativePath="..\src\cressi_edy_parser.c"
>
</File>
<File
RelativePath="..\src\cressi_goa.c"
>
</File>
<File
RelativePath="..\src\cressi_goa_parser.c"
>
</File>
<File
RelativePath="..\src\cressi_leonardo.c"
>
@ -592,6 +600,10 @@
RelativePath="..\src\cressi_edy.h"
>
</File>
<File
RelativePath="..\src\cressi_goa.h"
>
</File>
<File
RelativePath="..\src\cressi_leonardo.h"
>

View File

@ -55,6 +55,7 @@ libdivecomputer_la_SOURCES = \
aes.h aes.c \
cressi_edy.h cressi_edy.c cressi_edy_parser.c \
cressi_leonardo.h cressi_leonardo.c cressi_leonardo_parser.c \
cressi_goa.h cressi_goa.c cressi_goa_parser.c \
zeagle_n2ition3.h zeagle_n2ition3.c \
atomics_cobalt.h atomics_cobalt.c atomics_cobalt_parser.c \
shearwater_common.h shearwater_common.c \

View File

@ -69,7 +69,7 @@ checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char
unsigned short
checksum_crc_ccitt_uint16 (const unsigned char data[], unsigned int size)
checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init)
{
static const unsigned short crc_ccitt_table[] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
@ -106,7 +106,7 @@ checksum_crc_ccitt_uint16 (const unsigned char data[], unsigned int size)
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
unsigned short crc = 0xffff;
unsigned short crc = init;
for (unsigned int i = 0; i < size; ++i)
crc = (crc << 8) ^ crc_ccitt_table[(crc >> 8) ^ data[i]];

View File

@ -39,7 +39,7 @@ unsigned char
checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char init);
unsigned short
checksum_crc_ccitt_uint16 (const unsigned char data[], unsigned int size);
checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init);
unsigned int
checksum_crc32 (const unsigned char data[], unsigned int size);

498
src/cressi_goa.c Normal file
View File

@ -0,0 +1,498 @@
/*
* libdivecomputer
*
* Copyright (C) 2018 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> // memcpy, memcmp
#include <stdlib.h> // malloc, free
#include <assert.h> // assert
#include "cressi_goa.h"
#include "context-private.h"
#include "device-private.h"
#include "checksum.h"
#include "array.h"
#include "platform.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &cressi_goa_device_vtable)
#define CMD_VERSION 0x00
#define CMD_LOGBOOK 0x21
#define CMD_DIVE 0x22
#define HEADER 0xAA
#define TRAILER 0x55
#define END 0x04
#define ACK 0x06
#define SZ_DATA 512
#define SZ_PACKET 10
#define SZ_HEADER 23
#define FP_OFFSET 0x11
#define FP_SIZE 6
#define NSTEPS 1000
#define STEP(i,n) (NSTEPS * (i) / (n))
typedef struct cressi_goa_device_t {
dc_device_t base;
dc_iostream_t *iostream;
unsigned char fingerprint[FP_SIZE];
} cressi_goa_device_t;
static dc_status_t cressi_goa_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static const dc_device_vtable_t cressi_goa_device_vtable = {
sizeof(cressi_goa_device_t),
DC_FAMILY_CRESSI_GOA,
cressi_goa_device_set_fingerprint, /* set_fingerprint */
NULL, /* read */
NULL, /* write */
NULL, /* dump */
cressi_goa_device_foreach, /* foreach */
NULL, /* timesync */
NULL /* close */
};
static dc_status_t
cressi_goa_device_send (cressi_goa_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
if (size > SZ_PACKET) {
ERROR (abstract->context, "Unexpected payload size (%u).", size);
return DC_STATUS_INVALIDARGS;
}
// Setup the data packet.
unsigned short crc = 0;
unsigned char packet[SZ_PACKET + 8] = {
HEADER, HEADER, HEADER,
size,
cmd
};
if (size) {
memcpy (packet + 5, data, size);
}
crc = checksum_crc16_ccitt (packet + 3, size + 2, 0x000);
packet[5 + size + 0] = (crc ) & 0xFF; // Low
packet[5 + size + 1] = (crc >> 8) & 0xFF; // High
packet[5 + size + 2] = TRAILER;
// Wait a small amount of time before sending the command. Without
// this delay, the transfer will fail most of the time.
dc_iostream_sleep (device->iostream, 100);
// Send the command to the device.
status = dc_iostream_write (device->iostream, packet, size + 8, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
return status;
}
return status;
}
static dc_status_t
cressi_goa_device_receive (cressi_goa_device_t *device, unsigned char data[], unsigned int size)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
unsigned char packet[SZ_PACKET + 8];
// Read the header of the data packet.
status = dc_iostream_read (device->iostream, packet, 4, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
return status;
}
// Verify the header of the packet.
if (packet[0] != HEADER || packet[1] != HEADER || packet[2] != HEADER) {
ERROR (abstract->context, "Unexpected answer header byte.");
return DC_STATUS_PROTOCOL;
}
// Get the payload length.
unsigned int length = packet[3];
if (length > SZ_PACKET) {
ERROR (abstract->context, "Unexpected payload size (%u).", length);
return DC_STATUS_PROTOCOL;
}
// Read the remainder of the data packet.
status = dc_iostream_read (device->iostream, packet + 4, length + 4, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
return status;
}
// Verify the trailer of the packet.
if (packet[length + 7] != TRAILER) {
ERROR (abstract->context, "Unexpected answer trailer byte.");
return DC_STATUS_PROTOCOL;
}
// Verify the checksum of the packet.
unsigned short crc = array_uint16_le (packet + length + 5);
unsigned short ccrc = checksum_crc16_ccitt (packet + 3, length + 2, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL;
}
// Verify the payload length.
if (length != size) {
ERROR (abstract->context, "Unexpected payload size (%u).", length);
return DC_STATUS_PROTOCOL;
}
if (length) {
memcpy (data, packet + 5, length);
}
return status;
}
static dc_status_t
cressi_goa_device_download (cressi_goa_device_t *device, dc_buffer_t *buffer, dc_event_progress_t *progress)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
const unsigned char ack[] = {ACK};
const unsigned int initial = progress ? progress->current : 0;
// Erase the contents of the buffer.
if (!dc_buffer_clear (buffer)) {
ERROR (abstract->context, "Insufficient buffer space available.");
return DC_STATUS_NOMEMORY;
}
unsigned int skip = 2;
unsigned int size = 2;
unsigned int nbytes = 0;
while (nbytes < size) {
// Read the data packet.
unsigned char packet[3 + SZ_DATA + 2];
status = dc_iostream_read (device->iostream, packet, sizeof(packet), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
return status;
}
// Verify the checksum of the packet.
unsigned short crc = array_uint16_le (packet + sizeof(packet) - 2);
unsigned short ccrc = checksum_crc16_ccitt (packet + 3, sizeof(packet) - 5, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL;
}
// Send the ack byte to the device.
status = dc_iostream_write (device->iostream, ack, sizeof(ack), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the ack byte.");
return status;
}
// Get the total size from the first data packet.
if (nbytes == 0) {
size += array_uint16_le (packet + 3);
}
// Calculate the payload size of the packet.
unsigned int length = size - nbytes;
if (length > SZ_DATA) {
length = SZ_DATA;
}
// Append the payload to the output buffer.
if (!dc_buffer_append (buffer, packet + 3 + skip, length - skip)) {
ERROR (abstract->context, "Insufficient buffer space available.");
return DC_STATUS_NOMEMORY;
}
nbytes += length;
skip = 0;
// Update and emit a progress event.
if (progress) {
progress->current = initial + STEP(nbytes, size);
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
}
}
// Read the end byte.
unsigned char end = 0;
status = dc_iostream_read (device->iostream, &end, 1, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the end byte.");
return status;
}
// Verify the end byte.
if (end != END) {
ERROR (abstract->context, "Unexpected end byte (%02x).", end);
return DC_STATUS_PROTOCOL;
}
// Send the ack byte to the device.
status = dc_iostream_write (device->iostream, ack, sizeof(ack), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the ack byte.");
return status;
}
return status;
}
static dc_status_t
cressi_goa_device_transfer (cressi_goa_device_t *device,
unsigned char cmd,
const unsigned char input[], unsigned int isize,
unsigned char output[], unsigned int osize,
dc_buffer_t *buffer,
dc_event_progress_t *progress)
{
dc_status_t status = DC_STATUS_SUCCESS;
// Send the command to the dive computer.
status = cressi_goa_device_send (device, cmd, input, isize);
if (status != DC_STATUS_SUCCESS) {
return status;
}
// Receive the answer from the dive computer.
status = cressi_goa_device_receive (device, output, osize);
if (status != DC_STATUS_SUCCESS) {
return status;
}
// Download the optional and variable sized payload.
if (buffer) {
status = cressi_goa_device_download (device, buffer, progress);
if (status != DC_STATUS_SUCCESS) {
return status;
}
}
return status;
}
dc_status_t
cressi_goa_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
{
dc_status_t status = DC_STATUS_SUCCESS;
cressi_goa_device_t *device = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
device = (cressi_goa_device_t *) dc_device_allocate (context, &cressi_goa_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 serial communication protocol (115200 8N1).
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the terminal attributes.");
goto error_free;
}
// Set the timeout for receiving data (3000 ms).
status = dc_iostream_set_timeout (device->iostream, 3000);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout.");
goto error_free;
}
// Clear the RTS line.
status = dc_iostream_set_rts (device->iostream, 0);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to clear the RTS line.");
goto error_free;
}
// Clear the DTR line.
status = dc_iostream_set_dtr (device->iostream, 0);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to clear the DTR line.");
goto error_free;
}
dc_iostream_sleep (device->iostream, 100);
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
cressi_goa_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
{
cressi_goa_device_t *device = (cressi_goa_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
cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
{
dc_status_t status = DC_STATUS_SUCCESS;
cressi_goa_device_t *device = (cressi_goa_device_t *) abstract;
dc_buffer_t *logbook = NULL;
dc_buffer_t *dive = NULL;
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Read the version information.
unsigned char id[9] = {0};
status = cressi_goa_device_transfer (device, CMD_VERSION, NULL, 0, id, sizeof(id), NULL, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the version information.");
goto error_exit;
}
// Emit a vendor event.
dc_event_vendor_t vendor;
vendor.data = id;
vendor.size = sizeof (id);
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = id[4];
devinfo.firmware = array_uint16_le (id + 5);
devinfo.serial = array_uint32_le (id + 0);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
// Allocate memory for the logbook data.
logbook = dc_buffer_new(4096);
if (logbook == NULL) {
ERROR (abstract->context, "Failed to allocate memory.");
goto error_exit;
}
// Read the logbook data.
status = cressi_goa_device_transfer (device, CMD_LOGBOOK, NULL, 0, NULL, 0, logbook, &progress);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the logbook data.");
goto error_free_logbook;
}
const unsigned char *logbook_data = dc_buffer_get_data(logbook);
size_t logbook_size = dc_buffer_get_size(logbook);
// Count the number of dives.
unsigned int count = 0;
unsigned int offset = logbook_size;
while (offset > SZ_HEADER) {
// Move to the start of the logbook entry.
offset -= SZ_HEADER;
// Get the dive number.
unsigned int number= array_uint16_le (logbook_data + offset);
if (number == 0)
break;
// Compare the fingerprint to identify previously downloaded entries.
if (memcmp (logbook_data + offset + FP_OFFSET, device->fingerprint, sizeof(device->fingerprint)) == 0) {
break;
}
count++;
}
// Update and emit a progress event.
progress.maximum = (count + 1) * NSTEPS;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Allocate memory for the dive data.
dive = dc_buffer_new(4096);
if (dive == NULL) {
ERROR (abstract->context, "Failed to allocate memory.");
goto error_free_logbook;
}
// Download the dives.
offset = logbook_size;
for (unsigned int i = 0; i < count; ++i) {
// Move to the start of the logbook entry.
offset -= SZ_HEADER;
// Read the dive data.
status = cressi_goa_device_transfer (device, CMD_DIVE, logbook_data + offset, 2, NULL, 0, dive, &progress);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive data.");
goto error_free_dive;
}
const unsigned char *dive_data = dc_buffer_get_data (dive);
size_t dive_size = dc_buffer_get_size (dive);
// Verify the header in the logbook and dive data are identical.
// After the 2 byte dive number, the logbook header has 5 bytes
// extra, which are not present in the dive header.
if (dive_size < SZ_HEADER - 5 ||
memcmp (dive_data + 0, logbook_data + offset + 0, 2) != 0 ||
memcmp (dive_data + 2, logbook_data + offset + 7, SZ_HEADER - 7) != 0) {
ERROR (abstract->context, "Unexpected dive header.");
status = DC_STATUS_DATAFORMAT;
goto error_free_dive;
}
if (callback && !callback(dive_data, dive_size, dive_data + FP_OFFSET - 5, sizeof(device->fingerprint), userdata))
break;
}
error_free_dive:
dc_buffer_free(dive);
error_free_logbook:
dc_buffer_free(logbook);
error_exit:
return status;
}

43
src/cressi_goa.h Normal file
View File

@ -0,0 +1,43 @@
/*
* libdivecomputer
*
* Copyright (C) 2018 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 CRESSI_GOA_H
#define CRESSI_GOA_H
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/device.h>
#include <libdivecomputer/parser.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
dc_status_t
cressi_goa_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t
cressi_goa_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CRESSI_GOA_H */

209
src/cressi_goa_parser.c Normal file
View File

@ -0,0 +1,209 @@
/*
* libdivecomputer
*
* Copyright (C) 2018 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 "cressi_goa.h"
#include "context-private.h"
#include "parser-private.h"
#include "array.h"
#define ISINSTANCE(parser) dc_device_isinstance((parser), &cressi_goa_parser_vtable)
#define SZ_HEADER 0x5C
#define DEPTH 0
#define TEMPERATURE 3
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;
};
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);
static dc_status_t cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
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 */
cressi_goa_parser_get_datetime, /* datetime */
cressi_goa_parser_get_field, /* fields */
cressi_goa_parser_samples_foreach, /* samples_foreach */
NULL /* destroy */
};
dc_status_t
cressi_goa_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
{
cressi_goa_parser_t *parser = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (cressi_goa_parser_t *) dc_parser_allocate (context, &cressi_goa_parser_vtable);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
parser->model = model;
parser->cached = 0;
parser->maxdepth = 0.0;
*out = (dc_parser_t*) parser;
return DC_STATUS_SUCCESS;
}
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;
}
static dc_status_t
cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
if (abstract->size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
const unsigned char *p = abstract->data;
if (datetime) {
datetime->year = array_uint16_le(p + 0x0C);
datetime->month = p[0x0E];
datetime->day = p[0x0F];
datetime->hour = p[0x10];
datetime->minute = p[0x11];
datetime->second = 0;
datetime->timezone = DC_TIMEZONE_NONE;
}
return DC_STATUS_SUCCESS;
}
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;
if (abstract->size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
const unsigned char *data = abstract->data;
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;
}
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
if (value) {
switch (type) {
case DC_FIELD_DIVETIME:
*((unsigned int *) value) = array_uint16_le (data + 0x14);
break;
case DC_FIELD_MAXDEPTH:
*((double *) value) = parser->maxdepth;
break;
case DC_FIELD_GASMIX_COUNT:
*((unsigned int *) value) = 2;
break;
case DC_FIELD_GASMIX:
gasmix->helium = 0.0;
gasmix->oxygen = data[0x1B + 2 * flags] / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
default:
return DC_STATUS_UNSUPPORTED;
}
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
cressi_goa_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;
unsigned int time = 0;
unsigned int interval = 5;
unsigned int complete = 1;
unsigned int offset = SZ_HEADER;
while (offset + 2 <= size) {
dc_sample_value_t sample = {0};
// Get the sample type and value.
unsigned int raw = array_uint16_le (data + offset);
unsigned int type = (raw & 0x0003);
unsigned int value = (raw & 0xFFFC) >> 2;
if (complete) {
// Time (seconds).
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
complete = 0;
}
if (type == DEPTH) {
// Depth (1/10 m).
unsigned int depth = value & 0x0FFF;
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
complete = 1;
} else if (type == TEMPERATURE) {
// Temperature (1/10 °C).
sample.temperature = value / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
} else {
WARNING(abstract->context, "Unknown sample type %u.", type);
}
offset += 2;
}
return DC_STATUS_SUCCESS;
}

View File

@ -84,7 +84,7 @@ cressi_leonardo_make_ascii (const unsigned char raw[], unsigned int rsize, unsig
array_convert_bin2hex (raw, rsize, ascii + 1, 2 * rsize);
// Checksum
unsigned short crc = checksum_crc_ccitt_uint16 (ascii + 1, 2 * rsize);
unsigned short crc = checksum_crc16_ccitt (ascii + 1, 2 * rsize, 0xffff);
unsigned char checksum[] = {
(crc >> 8) & 0xFF, // High
(crc ) & 0xFF}; // Low
@ -129,7 +129,7 @@ cressi_leonardo_packet (cressi_leonardo_device_t *device, const unsigned char co
// Verify the checksum of the packet.
unsigned short crc = array_uint16_be (checksum);
unsigned short ccrc = checksum_crc_ccitt_uint16 (answer + 1, asize - 6);
unsigned short ccrc = checksum_crc16_ccitt (answer + 1, asize - 6, 0xffff);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL;
@ -372,7 +372,7 @@ cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
// Verify the checksum.
unsigned int csum1 = array_uint16_be (checksum);
unsigned int csum2 = checksum_crc_ccitt_uint16 (data, SZ_MEMORY);
unsigned int csum2 = checksum_crc16_ccitt (data, SZ_MEMORY, 0xffff);
if (csum1 != csum2) {
ERROR (abstract->context, "Unexpected answer bytes.");
return DC_STATUS_PROTOCOL;

View File

@ -278,6 +278,9 @@ static const dc_descriptor_t g_descriptors[] = {
{"Cressi", "Giotto", DC_FAMILY_CRESSI_LEONARDO, 4, DC_TRANSPORT_SERIAL, NULL},
{"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, NULL},
{"Cressi", "Goa", DC_FAMILY_CRESSI_GOA, 2, DC_TRANSPORT_SERIAL, NULL},
/* Zeagle N2iTiON3 */
{"Zeagle", "N2iTiON3", DC_FAMILY_ZEAGLE_N2ITION3, 0, DC_TRANSPORT_SERIAL, NULL},
{"Apeks", "Quantum X", DC_FAMILY_ZEAGLE_N2ITION3, 0, DC_TRANSPORT_SERIAL, NULL},
@ -322,6 +325,9 @@ static const dc_descriptor_t g_descriptors[] = {
{"Ratio", "iDive Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x42, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x44, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x45, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x52, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x54, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Tech", DC_FAMILY_DIVESYSTEM_IDIVE, 0x55, DC_TRANSPORT_SERIAL, NULL},
{"Seac", "Jack", DC_FAMILY_DIVESYSTEM_IDIVE, 0x1000, DC_TRANSPORT_SERIAL, NULL},
{"Seac", "Guru", DC_FAMILY_DIVESYSTEM_IDIVE, 0x1002, DC_TRANSPORT_SERIAL, NULL},
/* Cochran Commander */
@ -448,6 +454,7 @@ static int dc_filter_shearwater (dc_transport_t transport, const void *userdata)
"Petrel",
"Nerd",
"Perdix",
"Teric",
};
if (transport == DC_TRANSPORT_BLUETOOTH) {

View File

@ -47,6 +47,7 @@
#include "hw_ostc3.h"
#include "cressi_edy.h"
#include "cressi_leonardo.h"
#include "cressi_goa.h"
#include "zeagle_n2ition3.h"
#include "atomics_cobalt.h"
#include "shearwater_petrel.h"
@ -181,6 +182,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
case DC_FAMILY_CRESSI_LEONARDO:
rc = cressi_leonardo_device_open (&device, context, iostream);
break;
case DC_FAMILY_CRESSI_GOA:
rc = cressi_goa_device_open (&device, context, iostream);
break;
case DC_FAMILY_ZEAGLE_N2ITION3:
rc = zeagle_n2ition3_device_open (&device, context, iostream);
break;

View File

@ -196,7 +196,7 @@ divesystem_idive_send (divesystem_idive_device_t *device, const unsigned char co
packet[0] = START;
packet[1] = csize;
memcpy(packet + 2, command, csize);
crc = checksum_crc_ccitt_uint16 (packet, csize + 2);
crc = checksum_crc16_ccitt (packet, csize + 2, 0xffff);
packet[csize + 2] = (crc >> 8) & 0xFF;
packet[csize + 3] = (crc ) & 0xFF;
@ -257,7 +257,7 @@ divesystem_idive_receive (divesystem_idive_device_t *device, unsigned char answe
// Verify the checksum.
unsigned short crc = array_uint16_be (packet + len + 2);
unsigned short ccrc = checksum_crc_ccitt_uint16 (packet, len + 2);
unsigned short ccrc = checksum_crc16_ccitt (packet, len + 2, 0xffff);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected packet checksum.");
return DC_STATUS_PROTOCOL;

View File

@ -26,6 +26,8 @@
#include "parser-private.h"
#include "array.h"
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
#define ISINSTANCE(parser) dc_device_isinstance((parser), &divesystem_idive_parser_vtable)
#define IX3M_EASY 0x22
@ -151,13 +153,96 @@ divesystem_idive_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *date
{
divesystem_idive_parser_t *parser = (divesystem_idive_parser_t *) abstract;
static const signed char tz_array[] = {
-12, 0, /* UTC-12 */
-11, 0, /* UTC-11 */
-10, 0, /* UTC-10 */
-9, 30, /* UTC-9:30 */
-9, 0, /* UTC-9 */
-8, 0, /* UTC-8 */
-7, 0, /* UTC-7 */
-6, 0, /* UTC-6 */
-5, 0, /* UTC-5 */
-4, 30, /* UTC-4:30 */
-4, 0, /* UTC-4 */
-3, 30, /* UTC-3:30 */
-3, 0, /* UTC-3 */
-2, 0, /* UTC-2 */
-1, 0, /* UTC-1 */
0, 0, /* UTC */
1, 0, /* UTC+1 */
2, 0, /* UTC+2 */
3, 0, /* UTC+3 */
3, 30, /* UTC+3:30 */
4, 0, /* UTC+4 */
4, 30, /* UTC+4:30 */
5, 0, /* UTC+5 */
5, 30, /* UTC+5:30 */
5, 45, /* UTC+5:45 */
6, 0, /* UTC+6 */
6, 30, /* UTC+6:30 */
7, 0, /* UTC+7 */
8, 0, /* UTC+8 */
8, 45, /* UTC+8:45 */
9, 0, /* UTC+9 */
9, 30, /* UTC+9:30 */
9, 45, /* UTC+9:45 */
10, 0, /* UTC+10 */
10, 30, /* UTC+10:30 */
11, 0, /* UTC+11 */
11, 30, /* UTC+11:30 */
12, 0, /* UTC+12 */
12, 45, /* UTC+12:45 */
13, 0, /* UTC+13 */
13, 45, /* UTC+13:45 */
14, 0 /* UTC+14 */
};
if (abstract->size < parser->headersize)
return DC_STATUS_DATAFORMAT;
dc_ticks_t ticks = array_uint32_le(abstract->data + 7) + EPOCH;
// Detect the APOS4 firmware.
unsigned int firmware = 0;
unsigned int apos4 = 0;
if (parser->model >= IX3M_EASY) {
firmware = array_uint32_le(abstract->data + 0x2A);
apos4 = (firmware / 10000000) >= 4;
} else {
firmware = array_uint32_le(abstract->data + 0x2E);
apos4 = 0;
}
if (apos4) {
// For devices with timezone support, the UTC offset of the
// device is used. The UTC offset is stored as an index in the
// timezone table.
unsigned int tz_idx = abstract->data[48];
if ((tz_idx % 2) != 0 || tz_idx >= C_ARRAY_SIZE(tz_array)) {
ERROR (abstract->context, "Invalid timezone index (%u).", tz_idx);
return DC_STATUS_DATAFORMAT;
}
int timezone = tz_array[tz_idx] * 3600;
if (timezone < 0) {
timezone -= tz_array[tz_idx + 1] * 60;
} else {
timezone += tz_array[tz_idx + 1] * 60;
}
ticks += timezone;
if (!dc_datetime_gmtime (datetime, ticks))
return DC_STATUS_DATAFORMAT;
datetime->timezone = timezone;
} else {
// For devices 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;
}

View File

@ -47,6 +47,7 @@
#include "hw_ostc3.h"
#include "cressi_edy.h"
#include "cressi_leonardo.h"
#include "cressi_goa.h"
#include "zeagle_n2ition3.h"
#include "atomics_cobalt.h"
#include "shearwater_petrel.h"
@ -145,6 +146,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
case DC_FAMILY_CRESSI_LEONARDO:
rc = cressi_leonardo_parser_create (&parser, context, model);
break;
case DC_FAMILY_CRESSI_GOA:
rc = cressi_goa_parser_create (&parser, context, model);
break;
case DC_FAMILY_ATOMICS_COBALT:
rc = atomics_cobalt_parser_create (&parser, context);
break;

View File

@ -177,7 +177,7 @@ reefnet_sensuspro_handshake (reefnet_sensuspro_device_t *device)
// Verify the checksum of the handshake packet.
unsigned short crc = array_uint16_le (handshake + SZ_HANDSHAKE);
unsigned short ccrc = checksum_crc_ccitt_uint16 (handshake, SZ_HANDSHAKE);
unsigned short ccrc = checksum_crc16_ccitt (handshake, SZ_HANDSHAKE, 0xffff);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL;
@ -280,7 +280,7 @@ reefnet_sensuspro_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
}
unsigned short crc = array_uint16_le (answer + SZ_MEMORY);
unsigned short ccrc = checksum_crc_ccitt_uint16 (answer, SZ_MEMORY);
unsigned short ccrc = checksum_crc16_ccitt (answer, SZ_MEMORY, 0xffff);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL;

View File

@ -224,7 +224,7 @@ reefnet_sensusultra_packet (reefnet_sensusultra_device_t *device, unsigned char
// Verify the checksum of the packet.
unsigned short crc = array_uint16_le (data + size - 2);
unsigned short ccrc = checksum_crc_ccitt_uint16 (data + header, size - header - 2);
unsigned short ccrc = checksum_crc16_ccitt (data + header, size - header - 2, 0xffff);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL;
@ -477,7 +477,7 @@ reefnet_sensusultra_device_write_user (dc_device_t *abstract, const unsigned cha
}
// Send the checksum to the device.
unsigned short crc = checksum_crc_ccitt_uint16 (data, SZ_USER);
unsigned short crc = checksum_crc16_ccitt (data, SZ_USER, 0xffff);
rc = reefnet_sensusultra_send_ushort (device, crc);
if (rc != DC_STATUS_SUCCESS)
return rc;

View File

@ -26,6 +26,7 @@
#include "shearwater_common.h"
#include "context-private.h"
#include "device-private.h"
#include "platform.h"
#include "array.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &shearwater_petrel_device_vtable)
@ -262,20 +263,27 @@ shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
dc_buffer_free (manifests);
return rc;
}
if (dc_buffer_get_size (buffer) != 9) {
ERROR (abstract->context, "Unexpected packet size (" DC_PRINTF_SIZE " bytes).", dc_buffer_get_size(buffer));
dc_buffer_free (buffer);
dc_buffer_free (manifests);
return DC_STATUS_DATAFORMAT;
}
unsigned int base_addr = array_uint32_be (dc_buffer_get_data (buffer) + 1);
INFO(abstract->context, "RDBI command completed with %d bytes, evaluated as %08x", (int) dc_buffer_get_size (buffer), base_addr);
switch (base_addr) {
case 0xDD000000: // Predator - we shouldn't get here, we could give up or we can try 0xC0000000
case 0xC0000000: // Predator-Like Format (what we used to call the Petrel format)
case 0x90000000: // some firmware versions supported an earlier version of PNF without final record
// use the Predator-Like Format instead
base_addr = 0xC0000000u;
base_addr = 0xC0000000;
break;
case 0x80000000: // new Petrel Native Format with final record
// that's the correct address
break;
default: // unknown format
ERROR (abstract->context, "Unknown logbook download format.");
ERROR (abstract->context, "Unknown logbook format %08x", base_addr);
dc_buffer_free (buffer);
dc_buffer_free (manifests);
return DC_STATUS_DATAFORMAT;

View File

@ -40,8 +40,6 @@
dc_parser_isinstance((parser), &shearwater_predator_parser_vtable) || \
dc_parser_isinstance((parser), &shearwater_petrel_parser_vtable))
// Petrel Native Format constants
#define PNF_BLOCKSIZE 0x20
#define LOG_RECORD_DIVE_SAMPLE 0x01
#define LOG_RECORD_FREEDIVE_SAMPLE 0x02
#define LOG_RECORD_OPENING_0 0x10
@ -61,12 +59,11 @@
#define LOG_RECORD_CLOSING_6 0x26
#define LOG_RECORD_CLOSING_7 0x27
#define LOG_RECORD_FINAL 0xFF
#define NUM_BLOCK_IDS 0x28
// constant for the older Predator and Predator-like formats
#define SZ_BLOCK 0x80
#define SZ_SAMPLE_PREDATOR 0x10
#define SZ_SAMPLE_PETREL 0x20
#define SZ_SAMPLE_FREEDIVE 0x08
#define GASSWITCH 0x01
#define PPO2_EXTERNAL 0x02
@ -79,23 +76,29 @@
#define NGASMIXES 10
#define MAXSTRINGS 32
#define NRECORDS 7
#define PREDATOR 2
#define PETREL 3
#define UNDEFINED 0xFFFFFFFF
typedef struct shearwater_predator_parser_t shearwater_predator_parser_t;
struct shearwater_predator_parser_t {
dc_parser_t base;
unsigned int model;
unsigned int petrel;
unsigned int pnf;
unsigned int samplesize;
// Cached fields.
unsigned int cached;
unsigned int pnf;
unsigned int logversion;
unsigned int headersize;
unsigned int footersize;
unsigned int opening[NRECORDS];
unsigned int closing[NRECORDS];
unsigned int final;
unsigned int ngasmixes;
unsigned int oxygen[NGASMIXES];
unsigned int helium[NGASMIXES];
@ -103,9 +106,9 @@ struct shearwater_predator_parser_t {
double calibration[3];
unsigned int serial;
dc_divemode_t mode;
/* Block addresses for PNF */
unsigned int block_offset[NUM_BLOCK_IDS];
unsigned int units;
unsigned int atmospheric;
unsigned int density;
/* String fields */
dc_field_string_t strings[MAXSTRINGS];
@ -116,6 +119,8 @@ static dc_status_t shearwater_predator_parser_get_datetime (dc_parser_t *abstrac
static dc_status_t shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
static dc_status_t shearwater_predator_parser_cache (shearwater_predator_parser_t *parser);
static const dc_parser_vtable_t shearwater_predator_parser_vtable = {
sizeof(shearwater_predator_parser_t),
DC_FAMILY_SHEARWATER_PREDATOR,
@ -184,9 +189,15 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig
// Set the default values.
parser->cached = 0;
parser->pnf = 0;
parser->logversion = 0;
parser->headersize = 0;
parser->footersize = 0;
for (unsigned int i = 0; i < NRECORDS; ++i) {
parser->opening[i] = UNDEFINED;
parser->closing[i] = UNDEFINED;
}
parser->final = UNDEFINED;
parser->ngasmixes = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->oxygen[i] = 0;
@ -197,6 +208,9 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsig
parser->calibration[i] = 0.0;
}
parser->mode = DC_DIVEMODE_OC;
parser->units = METRIC;
parser->density = 1025;
parser->atmospheric = ATM / (BAR / 1000);
*out = (dc_parser_t *) parser;
@ -225,9 +239,15 @@ shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char
// Reset the cache.
parser->cached = 0;
parser->pnf = 0;
parser->logversion = 0;
parser->headersize = 0;
parser->footersize = 0;
for (unsigned int i = 0; i < NRECORDS; ++i) {
parser->opening[i] = UNDEFINED;
parser->closing[i] = UNDEFINED;
}
parser->final = UNDEFINED;
parser->ngasmixes = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->oxygen[i] = 0;
@ -238,6 +258,9 @@ shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char
parser->calibration[i] = 0.0;
}
parser->mode = DC_DIVEMODE_OC;
parser->units = METRIC;
parser->density = 1025;
parser->atmospheric = ATM / (BAR / 1000);
return DC_STATUS_SUCCESS;
}
@ -246,14 +269,15 @@ shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char
static dc_status_t
shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract;
const unsigned char *data = abstract->data;
if (size < 2 * SZ_BLOCK)
return DC_STATUS_DATAFORMAT;
// Cache the parser data.
dc_status_t rc = shearwater_predator_parser_cache (parser);
if (rc != DC_STATUS_SUCCESS)
return rc;
unsigned int ticks = array_uint32_be (data + 12);
unsigned int ticks = array_uint32_be (data + parser->opening[0] + 12);
if (!dc_datetime_gmtime (datetime, ticks))
return DC_STATUS_DATAFORMAT;
@ -361,8 +385,8 @@ add_battery_info(shearwater_predator_parser_t *parser, const char *desc, unsigne
static void
add_deco_model(shearwater_predator_parser_t *parser, const unsigned char *data)
{
unsigned int idx_deco_model = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_2] + 18 : 67;
unsigned int idx_gfs = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_3] + 5 : 85;
unsigned int idx_deco_model = parser->pnf ? parser->opening[2] + 18 : 67;
unsigned int idx_gfs = parser->pnf ? parser->opening[3] + 5 : 85;
switch (data[idx_deco_model]) {
case 0:
@ -385,7 +409,7 @@ add_battery_type(shearwater_predator_parser_t *parser, const unsigned char *data
if (parser->logversion < 7)
return;
unsigned int idx_battery_type = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_4] + 9 : 120;
unsigned int idx_battery_type = parser->pnf ? parser->opening[4] + 9 : 120;
switch (data[idx_battery_type]) {
case 1:
add_string(parser, "Battery type", "1.5V Alkaline");
@ -419,78 +443,47 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
return DC_STATUS_SUCCESS;
}
// the log formats are very similar - but the Petrel Native Format (PNF)
// is organized differently. There everything is in 32 byte (PNF_BLOCKSIZE) blocks
// and the offsets of various fields are different. It still seems to make sense
// to just all parse it in one place
// header and footer are concepts of the Predator and Predator-like formats
unsigned int headersize = SZ_BLOCK;
unsigned int footersize = SZ_BLOCK;
// Verify the minimum length.
if (size < 2) {
ERROR (abstract->context, "Invalid data length.");
return DC_STATUS_DATAFORMAT;
}
// The Petrel Native Format (PNF) is very similar to the legacy
// Predator and Predator-like format. The samples are simply offset
// by one (so we can use pnf as the offset). For the header and
// footer data, it's more complicated because of the new 32 byte
// block structure.
unsigned int pnf = parser->petrel ? array_uint16_be (data) != 0xFFFF : 0;
unsigned int headersize = 0;
unsigned int footersize = 0;
if (!pnf) {
// Opening and closing blocks.
headersize = SZ_BLOCK;
footersize = SZ_BLOCK;
if (size < headersize + footersize) {
ERROR (abstract->context, "Invalid data length.");
return DC_STATUS_DATAFORMAT;
}
// remember if this is a Petrel Native Format download
// if yes, we need different ways to access the various data fields
// for samples it's simple, they are just offset by one (so we can use pnf as offset)
// for header and footer data it's more complicated because of the new block structure
unsigned int pnf = parser->pnf = data[0] == 0x10 ? 1 : 0;
// sanity check on the log format
// is this a Predator-like or Petrel-native (or Teric style) log?
if (parser->petrel == 0 && pnf) {
ERROR (abstract->context, "This is a Petrel-native log, but we claim this is a Predator");
return DC_STATUS_DATAFORMAT;
}
memset (parser->block_offset, 0, NUM_BLOCK_IDS * sizeof(unsigned int));
if (pnf) {
// find the offsets of the various header and footer blocks
int i = 0, j = 0;
while (i < size) {
for (j = LOG_RECORD_OPENING_0; j < NUM_BLOCK_IDS; j++) {
if (data[i] == j)
parser->block_offset[j] = i;
if (j == LOG_RECORD_OPENING_7)
j = LOG_RECORD_CLOSING_0 - 1;
}
i += PNF_BLOCKSIZE;
}
}
// there is a small risk we are taking here... if the log were damaged and one or
// more of the blocks were missing, we'll default to looking into block 0 and
// report bogus data. This may be worth testing for?
// Log versions before 6 weren't reliably stored in the data, but
// 6 is also the oldest version that we assume in our code
unsigned int logversion = 6;
if (!pnf && data[127] > 6)
logversion = data[127];
if (pnf)
logversion = data[parser->block_offset[LOG_RECORD_OPENING_4] + 16];
INFO(abstract->context, "Shearwater log version %u\n", logversion);
memset(parser->strings, 0, sizeof(parser->strings));
add_string_fmt(parser, "Logversion", "%d%s", logversion, pnf ? "(PNF)" : "");
// Adjust the footersize for the final block.
if (parser->petrel == 1 || array_uint16_be (data + size - footersize) == 0xFFFD) {
if (parser->petrel || array_uint16_be (data + size - footersize) == 0xFFFD) {
footersize += SZ_BLOCK;
if (size < headersize + footersize) {
ERROR (abstract->context, "Invalid data length.");
return DC_STATUS_DATAFORMAT;
}
parser->final = size - SZ_BLOCK;
}
// if this is logversion 9 or higher, make sure this isn't a freedive, as we can't parse that
if (logversion > 9 && pnf) {
if (data[parser->block_offset[LOG_RECORD_OPENING_5] + 25] == LOG_RECORD_FREEDIVE_SAMPLE) {
ERROR (abstract->context, "Cannot parse freedive samples");
return DC_STATUS_DATAFORMAT;
// The Predator and Predator-like format have just one large 128
// byte opening and closing block. To minimize the differences
// with the PNF format, all record offsets are assigned the same
// value here.
for (unsigned int i = 0; i < NRECORDS; ++i) {
parser->opening[i] = 0;
parser->closing[i] = size - footersize;
}
}
@ -506,22 +499,19 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
// Transmitter battery levels
unsigned int t1_battery = 0, t2_battery = 0;
// the indices in the sample block are offset by 1 in PNF
unsigned int offset = pnf ? 0 : headersize;
unsigned int length = pnf ? size : size - footersize;
while (offset < length) {
// Ignore blocks that aren't dive samples
if (pnf && data[offset] != LOG_RECORD_DIVE_SAMPLE) {
offset += parser->samplesize;
continue;
}
unsigned int offset = headersize;
unsigned int length = size - footersize;
while (offset + parser->samplesize <= length) {
// Ignore empty samples.
if (array_isequal (data + offset, parser->samplesize, 0x00)) {
offset += parser->samplesize;
continue;
}
// Get the record type.
unsigned int type = pnf ? data[offset] : LOG_RECORD_DIVE_SAMPLE;
if (type == LOG_RECORD_DIVE_SAMPLE) {
// Status flags.
unsigned int status = data[offset + 11 + pnf];
if ((status & OC) == 0) {
@ -556,26 +546,49 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
}
// Transmitter battery levels
if (logversion >= 7) {
// T1 at offset 27, T2 at offset 19
t1_battery |= battery_state(data + offset + 27 + pnf);
t2_battery |= battery_state(data + offset + 19 + pnf);
} else if (type == LOG_RECORD_FREEDIVE_SAMPLE) {
// Freedive record
mode = DC_DIVEMODE_FREEDIVE;
} else if (type >= LOG_RECORD_OPENING_0 && type <= LOG_RECORD_OPENING_7) {
// Opening record
parser->opening[type - LOG_RECORD_OPENING_0] = offset;
} else if (type >= LOG_RECORD_CLOSING_0 && type <= LOG_RECORD_CLOSING_7) {
// Closing record
parser->closing[type - LOG_RECORD_CLOSING_0] = offset;
} else if (type == LOG_RECORD_FINAL) {
// Final record
parser->final = offset;
}
offset += parser->samplesize;
}
// for header and footer indices we use a variable base that is set to the
// correct value based on the log type
unsigned int base = 0;
// Verify the required opening/closing records.
for (unsigned int i = 0; i < NRECORDS - 2; ++i) {
if (parser->opening[i] == UNDEFINED || parser->closing[i] == UNDEFINED) {
ERROR (abstract->context, "Opening or closing record %u not found.", i);
return DC_STATUS_DATAFORMAT;
}
}
// Log versions before 6 weren't reliably stored in the data, but
// 6 is also the oldest version that we assume in our code
unsigned int logversion = data[parser->opening[4] + (pnf ? 16 : 127)];
// The transmitter battery levels are only valid for logversion 7+
if (logversion < 7) {
t1_battery = 0;
t2_battery = 0;
}
// Cache sensor calibration for later use
unsigned int nsensors = 0, ndefaults = 0;
// calibration value for sensors
base = pnf ? parser->block_offset[LOG_RECORD_OPENING_3] + 7 : 87;
unsigned int base = parser->opening[3] + (pnf ? 6 : 86);
for (size_t i = 0; i < 3; ++i) {
unsigned int calibration = array_uint16_be(data + base + i * 2);
unsigned int calibration = array_uint16_be(data + base + 1 + i * 2);
parser->calibration[i] = calibration / 100000.0;
if (parser->model == PREDATOR) {
// The Predator expects the mV output of the cells to be
@ -584,7 +597,7 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
// sensors lines up and matches the average.
parser->calibration[i] *= 2.2;
}
if (data[base - 1] & (1 << i)) {
if (data[base] & (1 << i)) {
if (calibration == 2100) {
ndefaults++;
}
@ -602,12 +615,13 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
if (mode != DC_DIVEMODE_OC)
add_string(parser, "PPO2 source", "voted/averaged");
} else {
parser->calibrated = data[base - 1];
parser->calibrated = data[base];
if (mode != DC_DIVEMODE_OC)
add_string(parser, "PPO2 source", "cells");
}
// Cache the data for later use.
parser->pnf = pnf;
parser->logversion = logversion;
parser->headersize = headersize;
parser->footersize = footersize;
@ -617,6 +631,11 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
parser->helium[i] = helium[i];
}
parser->mode = mode;
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));
parser->cached = 1;
add_string_fmt(parser, "Serial", "%08x", parser->serial);
// bytes 1-31 are identical in all formats
add_string_fmt(parser, "FW Version", "%2x", data[19]);
@ -625,7 +644,6 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
add_string_fmt(parser, "Battery at end", "%.1f V", data[9] / 10.0);
add_battery_info(parser, "T1 battery", t1_battery);
add_battery_info(parser, "T2 battery", t2_battery);
parser->cached = 1;
return DC_STATUS_SUCCESS;
}
@ -636,42 +654,29 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ
shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract;
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
// Cache the parser data.
dc_status_t rc = shearwater_predator_parser_cache (parser);
if (rc != DC_STATUS_SUCCESS)
return rc;
// Get the offset to the footer record.
unsigned int footer = size - parser->footersize;
// Get the unit system.
unsigned int units = data[8];
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
dc_salinity_t *water = (dc_salinity_t *) value;
dc_field_string_t *string = (dc_field_string_t *) value;
unsigned int density = 0;
// the first 32 bytes of the footer and closing block 0 are identical
unsigned int block_start = parser->pnf ? parser->block_offset[LOG_RECORD_CLOSING_0] : footer;
if (value) {
unsigned int idx;
switch (type) {
case DC_FIELD_DIVETIME:
// FIXME: this is wrong based on the documentation I received
// it should be a 3 byte value in offsets 6-8 that is dive length in seconds
*((unsigned int *) value) = array_uint16_be (data + block_start + 6) * 60;
if (parser->pnf)
*((unsigned int *) value) = array_uint24_be (data + parser->closing[0] + 6);
else
*((unsigned int *) value) = array_uint16_be (data + parser->closing[0] + 6) * 60;
break;
case DC_FIELD_MAXDEPTH:
if (units == IMPERIAL)
*((double *) value) = array_uint16_be (data + block_start + 4) * FEET;
if (parser->units == IMPERIAL)
*((double *) value) = array_uint16_be (data + parser->closing[0] + 4) * FEET;
else
*((double *) value) = array_uint16_be (data + block_start + 4);
// according to the documentation this should have been in tenth of a meter
// before, but the existing code for the Predator-like format didn't have
// that adjustment, so let's just do that for PNF (where we definitely need it).
*((double *) value) = array_uint16_be (data + parser->closing[0] + 4);
if (parser->pnf)
*((double *)value) /= 10.0;
break;
@ -684,17 +689,14 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
case DC_FIELD_SALINITY:
idx = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_3] + 3 : 83;
density = array_uint16_be (data + idx);
if (density == 1000)
if (parser->density == 1000)
water->type = DC_WATER_FRESH;
else
water->type = DC_WATER_SALT;
water->density = density;
water->density = parser->density;
break;
case DC_FIELD_ATMOSPHERIC:
idx = parser->pnf ? parser->block_offset[LOG_RECORD_OPENING_1] + 16 : 47;
*((double *) value) = array_uint16_be (data + idx) / 1000.0;
*((double *) value) = parser->atmospheric / 1000.0;
break;
case DC_FIELD_DIVEMODE:
*((dc_divemode_t *) value) = parser->mode;
@ -730,23 +732,25 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
if (rc != DC_STATUS_SUCCESS)
return rc;
// Get the unit system.
unsigned int units = data[8];
// Previous gas mix.
unsigned int o2_previous = 0, he_previous = 0;
// Sample interval.
unsigned int time = 0;
unsigned int interval = 10;
if (parser->pnf && parser->logversion >= 9) {
interval = array_uint16_be (data + parser->opening[5] + 23);
if (interval % 1000 != 0) {
ERROR (abstract->context, "Unsupported sample interval (%u ms).", interval);
return DC_STATUS_DATAFORMAT;
}
interval /= 1000;
}
unsigned int pnf = parser->pnf;
unsigned int offset = pnf ? 0 : parser->headersize;
unsigned int length = pnf ? size : size - parser->footersize;
unsigned int time_increment = 10;
// the time increment is now given in ms. not sure how we'll deal with that since all we do is full seconds
if (pnf && parser->logversion >= 9)
time_increment = array_uint16_be (data + parser->block_offset[LOG_RECORD_OPENING_5] + 23) / 1000;
while (offset < length) {
unsigned int offset = parser->headersize;
unsigned int length = size - parser->footersize;
while (offset + parser->samplesize <= length) {
dc_sample_value_t sample = {0};
// stop parsing if we see the end block
@ -764,14 +768,18 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
continue;
}
// Get the record type.
unsigned int type = pnf ? data[offset] : LOG_RECORD_DIVE_SAMPLE;
if (type == LOG_RECORD_DIVE_SAMPLE) {
// Time (seconds).
time += time_increment;
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
// Depth (1/10 m or ft).
unsigned int depth = array_uint16_be (data + pnf + offset);
if (units == IMPERIAL)
if (parser->units == IMPERIAL)
sample.depth = depth * FEET / 10.0;
else
sample.depth = depth / 10.0;
@ -786,7 +794,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
temperature = 0;
}
}
if (units == IMPERIAL)
if (parser->units == IMPERIAL)
sample.temperature = (temperature - 32.0) * (5.0 / 9.0);
else
sample.temperature = temperature;
@ -798,10 +806,10 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
if ((status & OC) == 0) {
// PPO2
if ((status & PPO2_EXTERNAL) == 0) {
if (!parser->calibrated) {
#ifdef SENSOR_AVERAGE
sample.ppo2 = data[offset + pnf + 6] / 100.0;
if (callback) callback (DC_SAMPLE_PPO2, sample, userdata);
} else {
#else
sample.ppo2 = data[offset + pnf + 12] * parser->calibration[0];
if (callback && (parser->calibrated & 0x01)) callback (DC_SAMPLE_PPO2, sample, userdata);
@ -810,7 +818,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
sample.ppo2 = data[offset + pnf + 15] * parser->calibration[2];
if (callback && (parser->calibrated & 0x04)) callback (DC_SAMPLE_PPO2, sample, userdata);
}
#endif
}
// Setpoint
@ -853,7 +861,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
unsigned int decostop = array_uint16_be (data + offset + pnf + 2);
if (decostop) {
sample.deco.type = DC_DECO_DECOSTOP;
if (units == IMPERIAL)
if (parser->units == IMPERIAL)
sample.deco.depth = decostop * FEET;
else
sample.deco.depth = decostop;
@ -903,6 +911,34 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
if (callback) callback (DC_SAMPLE_RBT, sample, userdata);
}
}
} else if (type == LOG_RECORD_FREEDIVE_SAMPLE) {
// A freedive record is actually 4 samples, each 8-bytes,
// packed into a standard 32-byte sized record. At the end
// of a dive, unused partial records will be 0 padded.
for (unsigned int i = 0; i < 4; ++i) {
unsigned int idx = offset + i * SZ_SAMPLE_FREEDIVE;
// Ignore empty samples.
if (array_isequal (data + idx, SZ_SAMPLE_FREEDIVE, 0x00)) {
break;
}
// Time (seconds).
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
// Depth (absolute pressure in millibar)
unsigned int depth = array_uint16_be (data + idx + 1);
sample.depth = (depth - parser->atmospheric) * (BAR / 1000.0) / (parser->density * GRAVITY);
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
// Temperature (1/10 °C).
int temperature = (signed short) array_uint16_be (data + idx + 3);
sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
}
}
offset += parser->samplesize;
}