diff --git a/examples/universal.c b/examples/universal.c index b305e29..0d90801 100644 --- a/examples/universal.c +++ b/examples/universal.c @@ -97,7 +97,8 @@ static const backend_table_t g_backends[] = { {"leonardo", DC_FAMILY_CRESSI_LEONARDO}, {"n2ition3", DC_FAMILY_ZEAGLE_N2ITION3}, {"cobalt", DC_FAMILY_ATOMICS_COBALT}, - {"predator", DC_FAMILY_SHEARWATER_PREDATOR} + {"predator", DC_FAMILY_SHEARWATER_PREDATOR}, + {"petrel", DC_FAMILY_SHEARWATER_PETREL}, }; static dc_family_t diff --git a/include/libdivecomputer/Makefile.am b/include/libdivecomputer/Makefile.am index f398d36..a3862b7 100644 --- a/include/libdivecomputer/Makefile.am +++ b/include/libdivecomputer/Makefile.am @@ -44,4 +44,5 @@ libdivecomputer_HEADERS = \ atomics.h \ atomics_cobalt.h \ shearwater.h \ + shearwater_petrel.h \ shearwater_predator.h diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index a0e2997..6434d67 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -77,7 +77,8 @@ typedef enum dc_family_t { /* Atomic Aquatics */ DC_FAMILY_ATOMICS_COBALT = (9 << 16), /* Shearwater */ - DC_FAMILY_SHEARWATER_PREDATOR = (10 << 16) + DC_FAMILY_SHEARWATER_PREDATOR = (10 << 16), + DC_FAMILY_SHEARWATER_PETREL } dc_family_t; #ifdef __cplusplus diff --git a/include/libdivecomputer/shearwater.h b/include/libdivecomputer/shearwater.h index fe8b5a1..df693e9 100644 --- a/include/libdivecomputer/shearwater.h +++ b/include/libdivecomputer/shearwater.h @@ -23,5 +23,6 @@ #define SHEARWATER_H #include "shearwater_predator.h" +#include "shearwater_petrel.h" #endif /* SHEARWATER_H */ diff --git a/include/libdivecomputer/shearwater_petrel.h b/include/libdivecomputer/shearwater_petrel.h new file mode 100644 index 0000000..5092127 --- /dev/null +++ b/include/libdivecomputer/shearwater_petrel.h @@ -0,0 +1,42 @@ +/* + * libdivecomputer + * + * Copyright (C) 2013 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 SHEARWATER_PETREL_H +#define SHEARWATER_PETREL_H + +#include "context.h" +#include "device.h" +#include "parser.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +dc_status_t +shearwater_petrel_device_open (dc_device_t **device, dc_context_t *context, const char *name); + +dc_status_t +shearwater_petrel_parser_create (dc_parser_t **parser, dc_context_t *context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* SHEARWATER_PETREL_H */ diff --git a/src/Makefile.am b/src/Makefile.am index 51cf70e..22d7f42 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -47,6 +47,7 @@ libdivecomputer_la_SOURCES = \ atomics_cobalt.c atomics_cobalt_parser.c \ shearwater_common.h shearwater_common.c \ shearwater_predator.c shearwater_predator_parser.c \ + shearwater_petrel.c \ ringbuffer.h ringbuffer.c \ checksum.h checksum.c \ array.h array.c \ diff --git a/src/descriptor.c b/src/descriptor.c index 0154dd9..132dce3 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -174,7 +174,8 @@ static const dc_descriptor_t g_descriptors[] = { {"Atomic Aquatics", "Cobalt", DC_FAMILY_ATOMICS_COBALT, 0}, /* Shearwater Predator */ {"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2}, - {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PREDATOR, 3}, + /* Shearwater Petrel */ + {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3}, }; typedef struct dc_descriptor_iterator_t { diff --git a/src/device.c b/src/device.c index 4b76a3c..286dbc6 100644 --- a/src/device.c +++ b/src/device.c @@ -141,6 +141,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_SHEARWATER_PREDATOR: rc = shearwater_predator_device_open (&device, context, name); break; + case DC_FAMILY_SHEARWATER_PETREL: + rc = shearwater_petrel_device_open (&device, context, name); + break; default: return DC_STATUS_INVALIDARGS; } diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 9748695..663ffd1 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -65,6 +65,7 @@ cressi_leonardo_parser_create atomics_cobalt_parser_create atomics_cobalt_parser_set_calibration shearwater_predator_parser_create +shearwater_petrel_parser_create dc_device_open dc_device_close @@ -150,3 +151,4 @@ atomics_cobalt_device_version atomics_cobalt_device_set_simulation shearwater_predator_device_open shearwater_predator_extract_dives +shearwater_petrel_device_open diff --git a/src/parser.c b/src/parser.c index c6276d8..c6a54f2 100644 --- a/src/parser.c +++ b/src/parser.c @@ -117,6 +117,9 @@ dc_parser_new (dc_parser_t **out, dc_device_t *device) case DC_FAMILY_SHEARWATER_PREDATOR: rc = shearwater_predator_parser_create (&parser, context); break; + case DC_FAMILY_SHEARWATER_PETREL: + rc = shearwater_petrel_parser_create (&parser, context); + break; default: return DC_STATUS_INVALIDARGS; } diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c new file mode 100644 index 0000000..5bc761d --- /dev/null +++ b/src/shearwater_petrel.c @@ -0,0 +1,221 @@ +/* + * libdivecomputer + * + * Copyright (C) 2013 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 // memcmp, memcpy +#include // malloc, free + +#include + +#include "shearwater_common.h" + +#include "context-private.h" +#include "device-private.h" +#include "array.h" + +#define ISINSTANCE(device) dc_device_isinstance((device), &shearwater_petrel_device_vtable) + +#define MANIFEST_ADDR 0xE0000000 +#define MANIFEST_SIZE 0x600 + +#define DIVE_ADDR 0xC0000000 +#define DIVE_SIZE 0xFFFFFF + +#define RECORD_SIZE 0x20 +#define RECORD_COUNT (MANIFEST_SIZE / RECORD_SIZE) + +typedef struct shearwater_petrel_device_t { + shearwater_common_device_t base; + unsigned char fingerprint[4]; +} shearwater_petrel_device_t; + +static dc_status_t shearwater_petrel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); +static dc_status_t shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t shearwater_petrel_device_close (dc_device_t *abstract); + +static const dc_device_vtable_t shearwater_petrel_device_vtable = { + DC_FAMILY_SHEARWATER_PETREL, + shearwater_petrel_device_set_fingerprint, /* set_fingerprint */ + NULL, /* read */ + NULL, /* write */ + NULL, /* dump */ + shearwater_petrel_device_foreach, /* foreach */ + shearwater_petrel_device_close /* close */ +}; + + +dc_status_t +shearwater_petrel_device_open (dc_device_t **out, dc_context_t *context, const char *name) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + shearwater_petrel_device_t *device = (shearwater_petrel_device_t *) malloc (sizeof (shearwater_petrel_device_t)); + if (device == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Initialize the base class. + device_init (&device->base.base, context, &shearwater_petrel_device_vtable); + + // Set the default values. + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + // Open the device. + rc = shearwater_common_open (&device->base, context, name); + if (rc != DC_STATUS_SUCCESS) { + free (device); + return rc; + } + + *out = (dc_device_t *) device; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +shearwater_petrel_device_close (dc_device_t *abstract) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + shearwater_common_device_t *device = (shearwater_common_device_t *) abstract; + + // Close the device. + rc = shearwater_common_close (device); + + // Free memory. + free (device); + + return rc; +} + + +static dc_status_t +shearwater_petrel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) +{ + shearwater_petrel_device_t *device = (shearwater_petrel_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 +shearwater_petrel_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) +{ + shearwater_petrel_device_t *device = (shearwater_petrel_device_t *) abstract; + dc_status_t rc = DC_STATUS_SUCCESS; + + // Allocate memory buffers for the manifests. + dc_buffer_t *buffer = dc_buffer_new (MANIFEST_SIZE); + dc_buffer_t *manifests = dc_buffer_new (MANIFEST_SIZE); + if (buffer == NULL || manifests == NULL) { + ERROR (abstract->context, "Insufficient buffer space available."); + dc_buffer_free (buffer); + dc_buffer_free (manifests); + return DC_STATUS_NOMEMORY; + } + + while (1) { + // Download a manifest. + rc = shearwater_common_download (&device->base, buffer, MANIFEST_ADDR, MANIFEST_SIZE, 0); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to download the manifest."); + dc_buffer_free (buffer); + dc_buffer_free (manifests); + return rc; + } + + // Cache the buffer pointer and size. + unsigned char *data = dc_buffer_get_data (buffer); + unsigned int size = dc_buffer_get_size (buffer); + + // Process the records in the manifest. + unsigned int count = 0; + unsigned int offset = 0; + while (offset < size) { + // Check for a valid dive header. + unsigned int header = array_uint16_be (data + offset); + if (header != 0xA5C4) + break; + + // Check the fingerprint data. + if (memcmp (data + offset + 4, device->fingerprint, sizeof (device->fingerprint)) == 0) + break; + + offset += RECORD_SIZE; + count++; + } + + // Append the manifest records to the main buffer. + if (!dc_buffer_append (manifests, data, count * RECORD_SIZE)) { + ERROR (abstract->context, "Insufficient buffer space available."); + dc_buffer_free (buffer); + dc_buffer_free (manifests); + return DC_STATUS_NOMEMORY; + } + + // Stop downloading manifest if there are no more records. + if (count != RECORD_COUNT) + break; + } + + // Cache the buffer pointer and size. + unsigned char *data = dc_buffer_get_data (manifests); + unsigned int size = dc_buffer_get_size (manifests); + + unsigned int offset = 0; + while (offset < size) { + // Get the address of the dive. + unsigned int address = array_uint32_be (data + offset + 20); + + // Download the dive. + rc = shearwater_common_download (&device->base, buffer, DIVE_ADDR + address, DIVE_SIZE, 1); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to download the dive."); + dc_buffer_free (buffer); + dc_buffer_free (manifests); + return rc; + } + + unsigned char *buf = dc_buffer_get_data (buffer); + unsigned int len = dc_buffer_get_size (buffer); + if (callback && !callback (buf, len, buf + 12, sizeof (device->fingerprint), userdata)) + break; + + offset += RECORD_SIZE; + } + + dc_buffer_free (manifests); + dc_buffer_free (buffer); + + return rc; +} diff --git a/src/shearwater_predator_parser.c b/src/shearwater_predator_parser.c index 9816569..88a4ec8 100644 --- a/src/shearwater_predator_parser.c +++ b/src/shearwater_predator_parser.c @@ -28,10 +28,13 @@ #include "parser-private.h" #include "array.h" -#define ISINSTANCE(parser) dc_parser_isinstance((parser), &shearwater_predator_parser_vtable) +#define ISINSTANCE(parser) ( \ + dc_parser_isinstance((parser), &shearwater_predator_parser_vtable) || \ + dc_parser_isinstance((parser), &shearwater_petrel_parser_vtable)) #define SZ_BLOCK 0x80 -#define SZ_SAMPLE 0x10 +#define SZ_SAMPLE_PREDATOR 0x10 +#define SZ_SAMPLE_PETREL 0x20 #define METRIC 0 #define IMPERIAL 1 @@ -40,6 +43,7 @@ typedef struct shearwater_predator_parser_t shearwater_predator_parser_t; struct shearwater_predator_parser_t { dc_parser_t base; + unsigned int petrel; }; static dc_status_t shearwater_predator_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); @@ -57,9 +61,18 @@ static const dc_parser_vtable_t shearwater_predator_parser_vtable = { shearwater_predator_parser_destroy /* destroy */ }; +static const dc_parser_vtable_t shearwater_petrel_parser_vtable = { + DC_FAMILY_SHEARWATER_PETREL, + shearwater_predator_parser_set_data, /* set_data */ + shearwater_predator_parser_get_datetime, /* datetime */ + shearwater_predator_parser_get_field, /* fields */ + shearwater_predator_parser_samples_foreach, /* samples_foreach */ + shearwater_predator_parser_destroy /* destroy */ +}; + dc_status_t -shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context) +shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int petrel) { if (out == NULL) return DC_STATUS_INVALIDARGS; @@ -72,7 +85,12 @@ shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context) } // Initialize the base class. - parser_init (&parser->base, context, &shearwater_predator_parser_vtable); + parser->petrel = petrel; + if (petrel) { + parser_init (&parser->base, context, &shearwater_predator_parser_vtable); + } else { + parser_init (&parser->base, context, &shearwater_predator_parser_vtable); + } *out = (dc_parser_t *) parser; @@ -80,6 +98,20 @@ shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context) } +dc_status_t +shearwater_predator_parser_create (dc_parser_t **out, dc_context_t *context) +{ + return shearwater_common_parser_create (out, context, 0); +} + + +dc_status_t +shearwater_petrel_parser_create (dc_parser_t **out, dc_context_t *context) +{ + return shearwater_common_parser_create (out, context, 1); +} + + static dc_status_t shearwater_predator_parser_destroy (dc_parser_t *abstract) { @@ -118,12 +150,23 @@ shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *d static dc_status_t shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) { + shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract; + const unsigned char *data = abstract->data; unsigned int size = abstract->size; if (size < 2 * SZ_BLOCK) return DC_STATUS_DATAFORMAT; + // Get the offset to the footer record. + unsigned int footer = size - SZ_BLOCK; + if (parser->petrel) { + if (size < 3 * SZ_BLOCK) + return DC_STATUS_DATAFORMAT; + + footer -= SZ_BLOCK; + } + // Get the unit system. unsigned int units = data[8]; @@ -134,13 +177,13 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ if (value) { switch (type) { case DC_FIELD_DIVETIME: - *((unsigned int *) value) = array_uint16_be (data + size - SZ_BLOCK + 6) * 60; + *((unsigned int *) value) = array_uint16_be (data + footer + 6) * 60; break; case DC_FIELD_MAXDEPTH: if (units == IMPERIAL) - *((double *) value) = array_uint16_be (data + size - SZ_BLOCK + 4) * FEET; + *((double *) value) = array_uint16_be (data + footer + 4) * FEET; else - *((double *) value) = array_uint16_be (data + size - SZ_BLOCK + 4); + *((double *) value) = array_uint16_be (data + footer + 4); break; case DC_FIELD_GASMIX_COUNT: *((unsigned int *) value) = 10; @@ -173,12 +216,29 @@ shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t typ static dc_status_t shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) { + shearwater_predator_parser_t *parser = (shearwater_predator_parser_t *) abstract; + const unsigned char *data = abstract->data; unsigned int size = abstract->size; if (size < 2 * SZ_BLOCK) return DC_STATUS_DATAFORMAT; + // Get the offset to the footer record. + unsigned int footer = size - SZ_BLOCK; + if (parser->petrel) { + if (size < 3 * SZ_BLOCK) + return DC_STATUS_DATAFORMAT; + + footer -= SZ_BLOCK; + } + + // Get the sample size. + unsigned int samplesize = SZ_SAMPLE_PREDATOR; + if (parser->petrel) { + samplesize = SZ_SAMPLE_PETREL; + } + // Get the unit system. unsigned int units = data[8]; @@ -187,12 +247,12 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal unsigned int time = 0; unsigned int offset = SZ_BLOCK; - while (offset + SZ_BLOCK < size) { + while (offset < footer) { dc_sample_value_t sample = {0}; // Ignore empty samples. - if (array_isequal (data + offset, SZ_SAMPLE, 0x00)) { - offset += SZ_SAMPLE; + if (array_isequal (data + offset, samplesize, 0x00)) { + offset += samplesize; continue; } @@ -249,7 +309,7 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal sample.deco.time = data[offset + 9] * 60; if (callback) callback (DC_SAMPLE_DECO, sample, userdata); - offset += SZ_SAMPLE; + offset += samplesize; } return DC_STATUS_SUCCESS;