diff --git a/examples/Makefile.am b/examples/Makefile.am index efcbed5..fc0664d 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -23,6 +23,7 @@ bin_PROGRAMS = \ ostc \ frog \ edy \ + leonardo \ n2ition3 \ predator @@ -75,6 +76,8 @@ frog_SOURCES = hw_frog_test.c $(COMMON) edy_SOURCES = cressi_edy_test.c $(COMMON) +leonardo_SOURCES = cressi_leonardo_test.c $(COMMON) + n2ition3_SOURCES = zeagle_n2ition3_test.c $(COMMON) predator_SOURCES = shearwater_predator_test.c $(COMMON) diff --git a/examples/cressi_leonardo_test.c b/examples/cressi_leonardo_test.c new file mode 100644 index 0000000..76ff939 --- /dev/null +++ b/examples/cressi_leonardo_test.c @@ -0,0 +1,107 @@ +/* + * 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 // fopen, fwrite, fclose + +#include + +#include "utils.h" +#include "common.h" + +dc_status_t +test_dump_memory (const char* name, const char* filename) +{ + dc_context_t *context = NULL; + dc_device_t *device = NULL; + + dc_context_new (&context); + dc_context_set_loglevel (context, DC_LOGLEVEL_ALL); + dc_context_set_logfunc (context, logfunc, NULL); + + message ("cressi_leonardo_device_open\n"); + dc_status_t rc = cressi_leonardo_device_open (&device, context, name); + if (rc != DC_STATUS_SUCCESS) { + WARNING ("Error opening serial port."); + dc_context_free (context); + return rc; + } + + dc_buffer_t *buffer = dc_buffer_new (0); + + message ("dc_device_dump\n"); + rc = dc_device_dump (device, buffer); + if (rc != DC_STATUS_SUCCESS) { + WARNING ("Cannot read memory."); + dc_buffer_free (buffer); + dc_device_close (device); + dc_context_free (context); + return rc; + } + + message ("Dumping data\n"); + FILE* fp = fopen (filename, "wb"); + if (fp != NULL) { + fwrite (dc_buffer_get_data (buffer), sizeof (unsigned char), dc_buffer_get_size (buffer), fp); + fclose (fp); + } + + dc_buffer_free (buffer); + + message ("dc_device_close\n"); + rc = dc_device_close (device); + if (rc != DC_STATUS_SUCCESS) { + WARNING ("Cannot close device."); + dc_context_free (context); + return rc; + } + + dc_context_free (context); + + return DC_STATUS_SUCCESS; +} + + +int main(int argc, char *argv[]) +{ + message_set_logfile ("LEONARDO.LOG"); + +#ifdef _WIN32 + const char* name = "COM1"; +#else + const char* name = "/dev/ttyS0"; +#endif + + if (argc > 1) { + name = argv[1]; + } + + message ("DEVICE=%s\n", name); + + dc_status_t a = test_dump_memory (name, "LEONARDO.DMP"); + + message ("\nSUMMARY\n"); + message ("-------\n"); + message ("test_dump_memory: %s\n", errmsg (a)); + + message_set_logfile (NULL); + + return 0; +} diff --git a/examples/universal.c b/examples/universal.c index 397ae41..b305e29 100644 --- a/examples/universal.c +++ b/examples/universal.c @@ -94,6 +94,7 @@ static const backend_table_t g_backends[] = { {"ostc", DC_FAMILY_HW_OSTC}, {"frog", DC_FAMILY_HW_FROG}, {"edy", DC_FAMILY_CRESSI_EDY}, + {"leonardo", DC_FAMILY_CRESSI_LEONARDO}, {"n2ition3", DC_FAMILY_ZEAGLE_N2ITION3}, {"cobalt", DC_FAMILY_ATOMICS_COBALT}, {"predator", DC_FAMILY_SHEARWATER_PREDATOR} diff --git a/include/libdivecomputer/Makefile.am b/include/libdivecomputer/Makefile.am index d4cc7bb..f398d36 100644 --- a/include/libdivecomputer/Makefile.am +++ b/include/libdivecomputer/Makefile.am @@ -38,6 +38,7 @@ libdivecomputer_HEADERS = \ hw_frog.h \ cressi.h \ cressi_edy.h \ + cressi_leonardo.h \ zeagle.h \ zeagle_n2ition3.h \ atomics.h \ diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index 7a79975..a0e2997 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -71,6 +71,7 @@ typedef enum dc_family_t { DC_FAMILY_HW_FROG, /* Cressi */ DC_FAMILY_CRESSI_EDY = (7 << 16), + DC_FAMILY_CRESSI_LEONARDO, /* Zeagle */ DC_FAMILY_ZEAGLE_N2ITION3 = (8 << 16), /* Atomic Aquatics */ diff --git a/include/libdivecomputer/cressi.h b/include/libdivecomputer/cressi.h index 015f56c..8bcb7ac 100644 --- a/include/libdivecomputer/cressi.h +++ b/include/libdivecomputer/cressi.h @@ -23,5 +23,6 @@ #define CRESSI_H #include "cressi_edy.h" +#include "cressi_leonardo.h" #endif /* CRESSI_H */ diff --git a/include/libdivecomputer/cressi_leonardo.h b/include/libdivecomputer/cressi_leonardo.h new file mode 100644 index 0000000..bddc9d1 --- /dev/null +++ b/include/libdivecomputer/cressi_leonardo.h @@ -0,0 +1,45 @@ +/* + * 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 CRESSI_LEONARDO_H +#define CRESSI_LEONARDO_H + +#include "context.h" +#include "device.h" +#include "parser.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +dc_status_t +cressi_leonardo_device_open (dc_device_t **device, dc_context_t *context, const char *name); + +dc_status_t +cressi_leonardo_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata); + +dc_status_t +cressi_leonardo_parser_create (dc_parser_t **parser, dc_context_t *context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CRESSI_LEONARDO_H */ diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index c7f9450..068f4cf 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -210,6 +210,14 @@ RelativePath="..\src\cressi_edy_parser.c" > + + + + @@ -460,6 +468,10 @@ RelativePath="..\include\libdivecomputer\cressi_edy.h" > + + diff --git a/src/Makefile.am b/src/Makefile.am index dad3087..39b5d5c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -42,6 +42,7 @@ libdivecomputer_la_SOURCES = \ hw_ostc.c hw_ostc_parser.c \ hw_frog.c \ cressi_edy.c cressi_edy_parser.c \ + cressi_leonardo.c cressi_leonardo_parser.c \ zeagle_n2ition3.c \ atomics_cobalt.c atomics_cobalt_parser.c \ shearwater_predator.c shearwater_predator_parser.c \ diff --git a/src/cressi_leonardo.c b/src/cressi_leonardo.c new file mode 100644 index 0000000..c22fb85 --- /dev/null +++ b/src/cressi_leonardo.c @@ -0,0 +1,381 @@ +/* + * 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 // memcpy, memcmp +#include // malloc, free +#include // assert + +#include + +#include "context-private.h" +#include "device-private.h" +#include "serial.h" +#include "checksum.h" +#include "array.h" +#include "ringbuffer.h" + +#define EXITCODE(rc) \ +( \ + rc == -1 ? DC_STATUS_IO : DC_STATUS_TIMEOUT \ +) + +#define SZ_MEMORY 32000 + +#define RB_LOGBOOK_BEGIN 0x0100 +#define RB_LOGBOOK_END 0x1438 +#define RB_LOGBOOK_SIZE 0x52 +#define RB_LOGBOOK_COUNT ((RB_LOGBOOK_END - RB_LOGBOOK_BEGIN) / RB_LOGBOOK_SIZE) + +#define RB_PROFILE_BEGIN 0x1338 +#define RB_PROFILE_END SZ_MEMORY +#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, 0, RB_PROFILE_BEGIN, RB_PROFILE_END) + +typedef struct cressi_leonardo_device_t { + dc_device_t base; + serial_t *port; + unsigned char fingerprint[5]; +} cressi_leonardo_device_t; + +static dc_status_t cressi_leonardo_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); +static dc_status_t cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer); +static dc_status_t cressi_leonardo_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t cressi_leonardo_device_close (dc_device_t *abstract); + +static const device_backend_t cressi_leonardo_device_backend = { + DC_FAMILY_CRESSI_LEONARDO, + cressi_leonardo_device_set_fingerprint, /* set_fingerprint */ + NULL, /* read */ + NULL, /* write */ + cressi_leonardo_device_dump, /* dump */ + cressi_leonardo_device_foreach, /* foreach */ + cressi_leonardo_device_close /* close */ +}; + +static int +device_is_cressi_leonardo (dc_device_t *abstract) +{ + if (abstract == NULL) + return 0; + + return abstract->backend == &cressi_leonardo_device_backend; +} + +dc_status_t +cressi_leonardo_device_open (dc_device_t **out, dc_context_t *context, const char *name) +{ + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + cressi_leonardo_device_t *device = (cressi_leonardo_device_t *) malloc (sizeof (cressi_leonardo_device_t)); + if (device == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Initialize the base class. + device_init (&device->base, context, &cressi_leonardo_device_backend); + + // Set the default values. + device->port = NULL; + + // Open the device. + int rc = serial_open (&device->port, context, name); + if (rc == -1) { + ERROR (context, "Failed to open the serial port."); + free (device); + return DC_STATUS_IO; + } + + // Set the serial communication protocol (115200 8N1). + rc = serial_configure (device->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE); + if (rc == -1) { + ERROR (context, "Failed to set the terminal attributes."); + serial_close (device->port); + free (device); + return DC_STATUS_IO; + } + + // Set the timeout for receiving data (1000 ms). + if (serial_set_timeout (device->port, 1000) == -1) { + ERROR (context, "Failed to set the timeout."); + serial_close (device->port); + free (device); + return DC_STATUS_IO; + } + + // Clear the DTR and set the RTS line. + if (serial_set_dtr (device->port, 0) == -1 || + serial_set_rts (device->port, 1) == -1) { + ERROR (context, "Failed to set the DTR/RTS line."); + serial_close (device->port); + free (device); + return DC_STATUS_IO; + } + + *out = (dc_device_t *) device; + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +cressi_leonardo_device_close (dc_device_t *abstract) +{ + cressi_leonardo_device_t *device = (cressi_leonardo_device_t *) abstract; + + // Close the device. + if (serial_close (device->port) == -1) { + free (device); + return DC_STATUS_IO; + } + + // Free memory. + free (device); + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +cressi_leonardo_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) +{ + cressi_leonardo_device_t *device = (cressi_leonardo_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_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) +{ + cressi_leonardo_device_t *device = (cressi_leonardo_device_t *) abstract; + + // Erase the current contents of the buffer and + // pre-allocate the required amount of memory. + if (!dc_buffer_clear (buffer) || !dc_buffer_resize (buffer, SZ_MEMORY)) { + ERROR (abstract->context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + } + + // Enable progress notifications. + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + progress.maximum = SZ_MEMORY; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Send the command header to the dive computer. + const unsigned char command[] = {0x7B, 0x31, 0x32, 0x33, 0x44, 0x42, 0x41, 0x7d}; + int n = serial_write (device->port, command, sizeof (command)); + if (n != sizeof (command)) { + ERROR (abstract->context, "Failed to send the command."); + return EXITCODE (n); + } + + // Receive the header packet. + unsigned char header[7] = {0}; + n = serial_read (device->port, header, sizeof (header)); + if (n != sizeof (header)) { + ERROR (abstract->context, "Failed to receive the answer."); + return EXITCODE (n); + } + + // Verify the header packet. + const unsigned char expected[] = {0x7B, 0x21, 0x44, 0x35, 0x42, 0x33, 0x7d}; + if (memcmp (header, expected, sizeof (expected)) != 0) { + ERROR (abstract->context, "Unexpected answer byte."); + return DC_STATUS_PROTOCOL; + } + + unsigned char *data = dc_buffer_get_data (buffer); + + unsigned int nbytes = 0; + while (nbytes < SZ_MEMORY) { + // Set the minimum packet size. + unsigned int len = 1024; + + // Increase the packet size if more data is immediately available. + int available = serial_get_received (device->port); + if (available > len) + len = available; + + // Limit the packet size to the total size. + if (nbytes + len > SZ_MEMORY) + len = SZ_MEMORY - nbytes; + + // Read the packet. + n = serial_read (device->port, data + nbytes, len); + if (n != len) { + ERROR (abstract->context, "Failed to receive the answer."); + return EXITCODE (n); + } + + // Update and emit a progress event. + progress.current += len; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + nbytes += len; + } + + // Receive the trailer packet. + unsigned char trailer[4] = {0}; + n = serial_read (device->port, trailer, sizeof (trailer)); + if (n != sizeof (trailer)) { + ERROR (abstract->context, "Failed to receive the answer."); + return EXITCODE (n); + } + + // Convert to a binary checksum. + unsigned char checksum[2] = {0}; + array_convert_hex2bin (trailer, sizeof (trailer), checksum, sizeof (checksum)); + + // Verify the checksum. + unsigned int csum1 = array_uint16_be (checksum); + unsigned int csum2 = checksum_crc_ccitt_uint16 (data, SZ_MEMORY); + if (csum1 != csum2) { + ERROR (abstract->context, "Unexpected answer bytes."); + return DC_STATUS_PROTOCOL; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +cressi_leonardo_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) +{ + dc_buffer_t *buffer = dc_buffer_new (SZ_MEMORY); + if (buffer == NULL) + return DC_STATUS_NOMEMORY; + + dc_status_t rc = cressi_leonardo_device_dump (abstract, buffer); + if (rc != DC_STATUS_SUCCESS) { + dc_buffer_free (buffer); + return rc; + } + + rc = cressi_leonardo_extract_dives (abstract, dc_buffer_get_data (buffer), + dc_buffer_get_size (buffer), callback, userdata); + + dc_buffer_free (buffer); + + return rc; +} + +dc_status_t +cressi_leonardo_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata) +{ + cressi_leonardo_device_t *device = (cressi_leonardo_device_t *) abstract; + dc_context_t *context = (abstract ? abstract->context : NULL); + + if (abstract && !device_is_cressi_leonardo (abstract)) + return DC_STATUS_INVALIDARGS; + + if (size < SZ_MEMORY) + return DC_STATUS_DATAFORMAT; + + // Locate the most recent dive. + // The device maintains an internal counter which is incremented for every + // dive, and the current value at the time of the dive is stored in the + // dive header. Thus the most recent dive will have the highest value. + unsigned int count = 0; + unsigned int latest = 0; + unsigned int maximum = 0; + for (unsigned int i = 0; i < RB_LOGBOOK_COUNT; ++i) { + unsigned int offset = RB_LOGBOOK_BEGIN + i * RB_LOGBOOK_SIZE; + + // Ignore uninitialized header entries. + if (array_isequal (data + offset, RB_LOGBOOK_SIZE, 0xFF)) + break; + + // Get the internal dive number. + unsigned int current = array_uint16_le (data + offset); + if (current > maximum) { + maximum = current; + latest = i; + } + + count++; + } + + unsigned char *buffer = (unsigned char *) malloc (RB_LOGBOOK_SIZE + RB_PROFILE_END - RB_PROFILE_BEGIN); + if (buffer == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + for (unsigned int i = 0; i < count; ++i) { + unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; + unsigned int offset = RB_LOGBOOK_BEGIN + idx * RB_LOGBOOK_SIZE; + + // Get the ringbuffer pointers. + unsigned int header = array_uint16_le (data + offset + 2); + unsigned int footer = array_uint16_le (data + offset + 4); + if (header < RB_PROFILE_BEGIN || header + 2 > RB_PROFILE_END || + footer < RB_PROFILE_BEGIN || footer + 2 > RB_PROFILE_END) + { + ERROR (abstract->context, "Invalid ringbuffer pointer detected."); + free (buffer); + return DC_STATUS_DATAFORMAT; + } + + // Get the same pointers from the profile. + unsigned int header2 = array_uint16_le (data + footer); + unsigned int footer2 = array_uint16_le (data + header); + if (header2 != header || footer2 != footer) { + ERROR (abstract->context, "Invalid ringbuffer pointer detected."); + free (buffer); + return DC_STATUS_DATAFORMAT; + } + + // Calculate the profile address and length. + unsigned int address = header + 2; + unsigned int length = RB_PROFILE_DISTANCE (header, footer) - 2; + + // Check the fingerprint data. + if (device && memcmp (data + offset + 8, device->fingerprint, sizeof (device->fingerprint)) == 0) + break; + + // Copy the logbook entry. + memcpy (buffer, data + offset, RB_LOGBOOK_SIZE); + + // Copy the profile data. + if (address + length > RB_PROFILE_END) { + unsigned int len_a = RB_PROFILE_END - address; + unsigned int len_b = length - len_a; + memcpy (buffer + RB_LOGBOOK_SIZE, data + address, len_a); + memcpy (buffer + RB_LOGBOOK_SIZE + len_a, data + RB_PROFILE_BEGIN, len_b); + } else { + memcpy (buffer + RB_LOGBOOK_SIZE, data + address, length); + } + + if (callback && !callback (buffer, RB_LOGBOOK_SIZE + length, buffer + 8, sizeof (device->fingerprint), userdata)) { + break; + } + } + + free (buffer); + + return DC_STATUS_SUCCESS; +} diff --git a/src/cressi_leonardo_parser.c b/src/cressi_leonardo_parser.c new file mode 100644 index 0000000..3ce18aa --- /dev/null +++ b/src/cressi_leonardo_parser.c @@ -0,0 +1,194 @@ +/* + * 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 + +#include + +#include "context-private.h" +#include "parser-private.h" +#include "array.h" + +#define SZ_HEADER 82 + +typedef struct cressi_leonardo_parser_t cressi_leonardo_parser_t; + +struct cressi_leonardo_parser_t { + dc_parser_t base; +}; + +static dc_status_t cressi_leonardo_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); +static dc_status_t cressi_leonardo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); +static dc_status_t cressi_leonardo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); +static dc_status_t cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); +static dc_status_t cressi_leonardo_parser_destroy (dc_parser_t *abstract); + +static const parser_backend_t cressi_leonardo_parser_backend = { + DC_FAMILY_CRESSI_EDY, + cressi_leonardo_parser_set_data, /* set_data */ + cressi_leonardo_parser_get_datetime, /* datetime */ + cressi_leonardo_parser_get_field, /* fields */ + cressi_leonardo_parser_samples_foreach, /* samples_foreach */ + cressi_leonardo_parser_destroy /* destroy */ +}; + + +static int +parser_is_cressi_leonardo (dc_parser_t *abstract) +{ + if (abstract == NULL) + return 0; + + return abstract->backend == &cressi_leonardo_parser_backend; +} + + +dc_status_t +cressi_leonardo_parser_create (dc_parser_t **out, dc_context_t *context) +{ + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + cressi_leonardo_parser_t *parser = (cressi_leonardo_parser_t *) malloc (sizeof (cressi_leonardo_parser_t)); + if (parser == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Initialize the base class. + parser_init (&parser->base, context, &cressi_leonardo_parser_backend); + + *out = (dc_parser_t*) parser; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +cressi_leonardo_parser_destroy (dc_parser_t *abstract) +{ + if (! parser_is_cressi_leonardo (abstract)) + return DC_STATUS_INVALIDARGS; + + // Free memory. + free (abstract); + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +cressi_leonardo_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size) +{ + if (! parser_is_cressi_leonardo (abstract)) + return DC_STATUS_INVALIDARGS; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +cressi_leonardo_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 = p[8] + 2000; + datetime->month = p[9]; + datetime->day = p[10]; + datetime->hour = p[11]; + datetime->minute = p[12]; + datetime->second = 0; + } + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +cressi_leonardo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) +{ + if (abstract->size < SZ_HEADER) + return DC_STATUS_DATAFORMAT; + + const unsigned char *data = abstract->data; + + dc_gasmix_t *gasmix = (dc_gasmix_t *) value; + + if (value) { + switch (type) { + case DC_FIELD_DIVETIME: + *((unsigned int *) value) = array_uint16_le (data + 0x06) * 20; + break; + case DC_FIELD_MAXDEPTH: + *((double *) value) = array_uint16_le (data + 0x20) / 10.0; + break; + case DC_FIELD_GASMIX_COUNT: + *((unsigned int *) value) = 1; + break; + case DC_FIELD_GASMIX: + gasmix->helium = 0.0; + gasmix->oxygen = data[0x19] / 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_leonardo_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 = 20; + + unsigned int offset = SZ_HEADER; + while (offset + 2 <= size) { + dc_sample_value_t sample = {0}; + + unsigned int value = array_uint16_le (data + offset); + unsigned int depth = value & 0x3FFF; + + // Time (seconds). + time += interval; + sample.time = time; + if (callback) callback (DC_SAMPLE_TIME, sample, userdata); + + // Depth (1/10 m). + sample.depth = depth / 10.0; + if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); + + offset += 2; + } + + return DC_STATUS_SUCCESS; +} diff --git a/src/descriptor.c b/src/descriptor.c index 0e5e946..b550624 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -164,6 +164,8 @@ static const dc_descriptor_t g_descriptors[] = { {"Heinrichs Weikamp", "Frog", DC_FAMILY_HW_FROG, 0}, /* Cressi Edy */ {"Cressi", "Edy", DC_FAMILY_CRESSI_EDY, 0}, + /* Cressi Leonardo */ + {"Cressi", "Leonardo", DC_FAMILY_CRESSI_LEONARDO, 0}, /* Zeagle N2iTiON3 */ {"Zeagle", "N2iTiON3", DC_FAMILY_ZEAGLE_N2ITION3, 0}, {"Apeks", "Quantum X", DC_FAMILY_ZEAGLE_N2ITION3, 0}, diff --git a/src/device.c b/src/device.c index bb3d765..2684534 100644 --- a/src/device.c +++ b/src/device.c @@ -129,6 +129,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_CRESSI_EDY: rc = cressi_edy_device_open (&device, context, name); break; + case DC_FAMILY_CRESSI_LEONARDO: + rc = cressi_leonardo_device_open (&device, context, name); + break; case DC_FAMILY_ZEAGLE_N2ITION3: rc = zeagle_n2ition3_device_open (&device, context, name); break; diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index c470901..07b0a08 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -60,6 +60,7 @@ oceanic_veo250_parser_create oceanic_atom2_parser_create hw_ostc_parser_create cressi_edy_parser_create +cressi_leonardo_parser_create atomics_cobalt_parser_create atomics_cobalt_parser_set_calibration shearwater_predator_parser_create @@ -76,6 +77,7 @@ dc_device_set_fingerprint dc_device_write cressi_edy_device_open +cressi_leonardo_device_open mares_nemo_device_open mares_nemo_extract_dives mares_puck_device_open diff --git a/src/parser.c b/src/parser.c index 0e3a9e0..310d13a 100644 --- a/src/parser.c +++ b/src/parser.c @@ -108,6 +108,9 @@ dc_parser_new (dc_parser_t **out, dc_device_t *device) case DC_FAMILY_ZEAGLE_N2ITION3: rc = cressi_edy_parser_create (&parser, context, device->devinfo.model); break; + case DC_FAMILY_CRESSI_LEONARDO: + rc = cressi_leonardo_parser_create (&parser, context); + break; case DC_FAMILY_ATOMICS_COBALT: rc = atomics_cobalt_parser_create (&parser, context); break;