From 598377ec446e02e25d4acb0cbbb9429bd6991072 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 4 Sep 2014 19:11:01 +0200 Subject: [PATCH] Add support for the Citizen Hyper Aqualand. --- examples/universal.c | 1 + include/libdivecomputer/Makefile.am | 4 +- include/libdivecomputer/citizen.h | 27 ++ include/libdivecomputer/citizen_aqualand.h | 42 ++++ include/libdivecomputer/common.h | 2 + msvc/libdivecomputer.vcproj | 16 ++ src/Makefile.am | 1 + src/citizen_aqualand.c | 236 ++++++++++++++++++ src/citizen_aqualand_parser.c | 275 +++++++++++++++++++++ src/descriptor.c | 2 + src/device.c | 4 + src/libdivecomputer.symbols | 2 + src/parser.c | 4 + 13 files changed, 615 insertions(+), 1 deletion(-) create mode 100644 include/libdivecomputer/citizen.h create mode 100644 include/libdivecomputer/citizen_aqualand.h create mode 100644 src/citizen_aqualand.c create mode 100644 src/citizen_aqualand_parser.c diff --git a/examples/universal.c b/examples/universal.c index 56b1946..136454d 100644 --- a/examples/universal.c +++ b/examples/universal.c @@ -103,6 +103,7 @@ static const backend_table_t g_backends[] = { {"predator", DC_FAMILY_SHEARWATER_PREDATOR}, {"petrel", DC_FAMILY_SHEARWATER_PETREL}, {"nitekq", DC_FAMILY_DIVERITE_NITEKQ}, + {"aqualand", DC_FAMILY_CITIZEN_AQUALAND}, }; static dc_family_t diff --git a/include/libdivecomputer/Makefile.am b/include/libdivecomputer/Makefile.am index 891f4e0..3221d22 100644 --- a/include/libdivecomputer/Makefile.am +++ b/include/libdivecomputer/Makefile.am @@ -49,4 +49,6 @@ libdivecomputer_HEADERS = \ shearwater_petrel.h \ shearwater_predator.h \ diverite.h \ - diverite_nitekq.h + diverite_nitekq.h \ + citizen.h \ + citizen_aqualand.h diff --git a/include/libdivecomputer/citizen.h b/include/libdivecomputer/citizen.h new file mode 100644 index 0000000..01b7027 --- /dev/null +++ b/include/libdivecomputer/citizen.h @@ -0,0 +1,27 @@ +/* + * libdivecomputer + * + * Copyright (C) 2014 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 CITIZEN_H +#define CITIZEN_H + +#include "citizen_aqualand.h" + +#endif /* CITIZEN_H */ diff --git a/include/libdivecomputer/citizen_aqualand.h b/include/libdivecomputer/citizen_aqualand.h new file mode 100644 index 0000000..2b703a4 --- /dev/null +++ b/include/libdivecomputer/citizen_aqualand.h @@ -0,0 +1,42 @@ +/* + * libdivecomputer + * + * Copyright (C) 2014 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 CITIZEN_AQUALAND_H +#define CITIZEN_AQUALAND_H + +#include "context.h" +#include "device.h" +#include "parser.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +dc_status_t +citizen_aqualand_device_open (dc_device_t **device, dc_context_t *context, const char *name); + +dc_status_t +citizen_aqualand_parser_create (dc_parser_t **parser, dc_context_t *context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CITIZEN_AQUALAND_H */ diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index 4bcc700..708fd00 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -84,6 +84,8 @@ typedef enum dc_family_t { DC_FAMILY_SHEARWATER_PETREL, /* Dive Rite */ DC_FAMILY_DIVERITE_NITEKQ = (11 << 16), + /* Citizen */ + DC_FAMILY_CITIZEN_AQUALAND = (12 << 16), } dc_family_t; #ifdef __cplusplus diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index ff22d90..2fea2b9 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -198,6 +198,14 @@ RelativePath="..\src\checksum.c" > + + + + @@ -488,6 +496,14 @@ RelativePath="..\src\checksum.h" > + + + + diff --git a/src/Makefile.am b/src/Makefile.am index 458f1dd..1f52841 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,6 +53,7 @@ libdivecomputer_la_SOURCES = \ shearwater_predator.c shearwater_predator_parser.c \ shearwater_petrel.c \ diverite_nitekq.c diverite_nitekq_parser.c \ + citizen_aqualand.c citizen_aqualand_parser.c \ ringbuffer.h ringbuffer.c \ checksum.h checksum.c \ array.h array.c \ diff --git a/src/citizen_aqualand.c b/src/citizen_aqualand.c new file mode 100644 index 0000000..dc291b4 --- /dev/null +++ b/src/citizen_aqualand.c @@ -0,0 +1,236 @@ +/* + * libdivecomputer + * + * Copyright (C) 2014 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 "context-private.h" +#include "device-private.h" +#include "serial.h" +#include "checksum.h" +#include "ringbuffer.h" +#include "array.h" + +#define ISINSTANCE(device) dc_device_isinstance((device), &citizen_aqualand_device_vtable) + +#define EXITCODE(rc) \ +( \ + rc == -1 ? DC_STATUS_IO : DC_STATUS_TIMEOUT \ +) + +typedef struct citizen_aqualand_device_t { + dc_device_t base; + serial_t *port; + unsigned char fingerprint[8]; +} citizen_aqualand_device_t; + +static dc_status_t citizen_aqualand_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); +static dc_status_t citizen_aqualand_device_dump (dc_device_t *abstract, dc_buffer_t *buffer); +static dc_status_t citizen_aqualand_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t citizen_aqualand_device_close (dc_device_t *abstract); + +static const dc_device_vtable_t citizen_aqualand_device_vtable = { + DC_FAMILY_CITIZEN_AQUALAND, + citizen_aqualand_device_set_fingerprint, /* set_fingerprint */ + NULL, /* read */ + NULL, /* write */ + citizen_aqualand_device_dump, /* dump */ + citizen_aqualand_device_foreach, /* foreach */ + citizen_aqualand_device_close /* close */ +}; + + +dc_status_t +citizen_aqualand_device_open (dc_device_t **out, dc_context_t *context, const char *name) +{ + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + citizen_aqualand_device_t *device = (citizen_aqualand_device_t *) malloc (sizeof (citizen_aqualand_device_t)); + if (device == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Initialize the base class. + device_init (&device->base, context, &citizen_aqualand_device_vtable); + + // Set the default values. + device->port = NULL; + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + // 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 (4800 8N1). + rc = serial_configure (device->port, 4800, 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 (1000ms). + 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; + } + + // Make sure everything is in a sane state. + serial_sleep (device->port, 300); + serial_flush (device->port, SERIAL_QUEUE_BOTH); + + *out = (dc_device_t *) device; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +citizen_aqualand_device_close (dc_device_t *abstract) +{ + citizen_aqualand_device_t *device = (citizen_aqualand_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 +citizen_aqualand_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) +{ + citizen_aqualand_device_t *device = (citizen_aqualand_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 +citizen_aqualand_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) +{ + citizen_aqualand_device_t *device = (citizen_aqualand_device_t *) abstract; + + // Erase the current contents of the buffer and + // pre-allocate the required amount of memory. + if (!dc_buffer_clear (buffer)) { + ERROR (abstract->context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + } + + serial_set_dtr (device->port, 1); + + // Send the init byte. + const unsigned char init[] = {0x7F}; + int n = serial_write (device->port, init, sizeof (init)); + if (n != sizeof (init)) { + ERROR (abstract->context, "Failed to send the command."); + return EXITCODE (n); + } + + serial_sleep(device->port, 1200); + + // Send the command. + const unsigned char command[] = {0xFF}; + n = serial_write (device->port, command, sizeof (command)); + if (n != sizeof (command)) { + ERROR (abstract->context, "Failed to send the command."); + return EXITCODE (n); + } + + while (1) { + // Receive the response packet. + unsigned char answer[32] = {0}; + n = serial_read (device->port, answer, sizeof (answer)); + if (n != sizeof (answer)) { + ERROR (abstract->context, "Failed to receive the answer."); + return EXITCODE (n); + } + + dc_buffer_append(buffer, answer, sizeof (answer)); + + // Send the command. + n = serial_write (device->port, command, sizeof (command)); + if (n != sizeof (command)) { + ERROR (abstract->context, "Failed to send the command."); + return EXITCODE (n); + } + + if (answer[sizeof(answer) - 1] == 0xFF) + break; + } + + serial_set_dtr (device->port, 0); + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +citizen_aqualand_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) +{ + citizen_aqualand_device_t *device = (citizen_aqualand_device_t *) abstract; + + dc_buffer_t *buffer = dc_buffer_new (0); + if (buffer == NULL) + return DC_STATUS_NOMEMORY; + + dc_status_t rc = citizen_aqualand_device_dump (abstract, buffer); + if (rc != DC_STATUS_SUCCESS) { + dc_buffer_free (buffer); + return rc; + } + + unsigned char *data = dc_buffer_get_data (buffer); + unsigned int size = dc_buffer_get_size (buffer); + + if (callback && memcmp (data + 0x05, device->fingerprint, sizeof (device->fingerprint)) != 0) { + callback (data, size, data + 0x05, sizeof (device->fingerprint), userdata); + } + + dc_buffer_free (buffer); + + return rc; +} diff --git a/src/citizen_aqualand_parser.c b/src/citizen_aqualand_parser.c new file mode 100644 index 0000000..e9c2659 --- /dev/null +++ b/src/citizen_aqualand_parser.c @@ -0,0 +1,275 @@ +/* + * libdivecomputer + * + * Copyright (C) 2014 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 + +#include "context-private.h" +#include "parser-private.h" +#include "array.h" + +#define ISINSTANCE(parser) dc_device_isinstance((parser), &citizen_aqualand_parser_vtable) + +#define SZ_HEADER 32 + +typedef struct citizen_aqualand_parser_t { + dc_parser_t base; +} citizen_aqualand_parser_t; + +static dc_status_t citizen_aqualand_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); +static dc_status_t citizen_aqualand_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); +static dc_status_t citizen_aqualand_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); +static dc_status_t citizen_aqualand_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); +static dc_status_t citizen_aqualand_parser_destroy (dc_parser_t *abstract); + +static const dc_parser_vtable_t citizen_aqualand_parser_vtable = { + DC_FAMILY_CITIZEN_AQUALAND, + citizen_aqualand_parser_set_data, /* set_data */ + citizen_aqualand_parser_get_datetime, /* datetime */ + citizen_aqualand_parser_get_field, /* fields */ + citizen_aqualand_parser_samples_foreach, /* samples_foreach */ + citizen_aqualand_parser_destroy /* destroy */ +}; + + +dc_status_t +citizen_aqualand_parser_create (dc_parser_t **out, dc_context_t *context) +{ + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + citizen_aqualand_parser_t *parser = (citizen_aqualand_parser_t *) malloc (sizeof (citizen_aqualand_parser_t)); + if (parser == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Initialize the base class. + parser_init (&parser->base, context, &citizen_aqualand_parser_vtable); + + *out = (dc_parser_t*) parser; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +citizen_aqualand_parser_destroy (dc_parser_t *abstract) +{ + // Free memory. + free (abstract); + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +citizen_aqualand_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size) +{ + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +citizen_aqualand_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 = bcd2dec(p[0x05]) * 100 + bcd2dec(p[0x06]); + datetime->month = bcd2dec(p[0x07]); + datetime->day = bcd2dec(p[0x08]); + datetime->hour = bcd2dec(p[0x0A]); + datetime->minute = bcd2dec(p[0x0B]); + datetime->second = bcd2dec(p[0x0C]); + } + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +citizen_aqualand_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; + + unsigned int metric = (data[0x04] == 0xA6 ? 0 : 1); + unsigned int maxdepth = bcd2dec(data[0x12]) * 10 + ((data[0x13] >> 4) & 0x0F); + unsigned int divetime = (data[0x16] & 0x0F) * 100 + bcd2dec(data[0x17]); + + if (value) { + switch (type) { + case DC_FIELD_DIVETIME: + *((unsigned int *) value) = divetime * 60; + break; + case DC_FIELD_MAXDEPTH: + if (metric) + *((double *) value) = maxdepth / 10.0; + else + *((double *) value) = maxdepth * FEET; + break; + case DC_FIELD_GASMIX_COUNT: + *((unsigned int *) value) = 0; + break; + default: + return DC_STATUS_UNSUPPORTED; + } + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +citizen_aqualand_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + if (size < SZ_HEADER) + return DC_STATUS_DATAFORMAT; + + // Estimate the maximum number of samples. We calculate the number of + // 12 bit values that fit in the available profile data, and round the + // result upwards. The actual number of samples should always be smaller + // due to the presence of at least two end markers. + unsigned int maxcount = (2 * (size - SZ_HEADER) + 2) / 3; + + // Allocate storage for the processed 16 bit samples. + unsigned short *samples = (unsigned short *) malloc(maxcount * sizeof(unsigned short)); + if (samples == NULL) { + return DC_STATUS_NOMEMORY; + } + + // Pre-process the depth and temperature tables. The 12 bit BCD encoded + // values are converted into an array of 16 bit values, which is much + // more convenient to process in the second stage. + unsigned int nsamples = 0; + unsigned int count[2] = {0, 0}; + unsigned int offset = SZ_HEADER * 2; + unsigned int length = size * 2; + for (unsigned int i = 0; i < 2; ++i) { + const unsigned int marker = (i == 0 ? 0xEF : 0xFF); + + while (offset + 3 <= length) { + unsigned int value = 0; + unsigned int octet = offset / 2; + unsigned int nibble = offset % 2; + unsigned int hi = data[octet]; + unsigned int lo = data[octet + 1]; + + // Check for the end marker. + if (hi == marker || lo == marker) { + offset += nibble; + break; + } + + // Convert 12 bit BCD to decimal. + if (nibble) { + value = ((hi ) & 0x0F) * 100 + + ((lo >> 4) & 0x0F) * 10 + + ((lo ) & 0x0F); + } else { + value = ((hi >> 4) & 0x0F) * 100 + + ((hi ) & 0x0F) * 10 + + ((lo >> 4) & 0x0F); + } + + // Store the value. + samples[nsamples] = value; + count[i]++; + nsamples++; + + offset += 3; + } + + // Verify the end marker. + if (offset + 2 > length || data[offset / 2] != marker) { + ERROR (abstract->context, "No end marker found."); + free(samples); + return DC_STATUS_DATAFORMAT; + } + + offset += 2; + } + + unsigned int time = 0; + unsigned int interval = 5; + unsigned int metric = (data[0x04] == 0xA6 ? 0 : 1); + for (unsigned int i = 0; i < count[0]; ++i) { + dc_sample_value_t sample = {0}; + + // Get the depth value. + unsigned int depth = samples[i]; + + // Every 12th sample there is a strange sample that always contains + // the value 999. This is clearly not a valid depth, but when trying + // to skip these samples, the depth and temperatures go out of sync. + // Therefore we replace the bogus sample with an interpolated value. + if (depth == 999) { + depth = 0; + if (i > 0) { + depth += samples[i - 1]; + } + if (i < count[0] - 1) { + depth += samples[i + 1]; + } + depth /= 2; + } + + // Time + time += interval; + sample.time = time; + if (callback) callback (DC_SAMPLE_TIME, sample, userdata); + + // Depth + if (metric) + sample.depth = depth / 10.0; + else + sample.depth = depth * FEET; + if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); + + // Temperature + if (time % 300 == 0) { + unsigned int idx = count[0] + time / 300; + if (idx < nsamples) { + unsigned int temperature = samples[idx]; + if (metric) + sample.temperature = temperature / 10.0; + else + sample.temperature = (temperature - 32.0) * (5.0 / 9.0); + if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); + } + } + } + + free(samples); + + return DC_STATUS_SUCCESS; +} diff --git a/src/descriptor.c b/src/descriptor.c index 22ebe7c..93525e9 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -230,6 +230,8 @@ static const dc_descriptor_t g_descriptors[] = { {"Shearwater", "Petrel", DC_FAMILY_SHEARWATER_PETREL, 3}, /* Dive Rite NiTek Q */ {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0}, + /* Citizen Hyper Aqualand */ + {"Citizen", "Hyper Aqualand", DC_FAMILY_CITIZEN_AQUALAND, 0}, }; typedef struct dc_descriptor_iterator_t { diff --git a/src/device.c b/src/device.c index edc1cf2..909bac3 100644 --- a/src/device.c +++ b/src/device.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "device-private.h" #include "context-private.h" @@ -157,6 +158,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_DIVERITE_NITEKQ: rc = diverite_nitekq_device_open (&device, context, name); break; + case DC_FAMILY_CITIZEN_AQUALAND: + rc = citizen_aqualand_device_open (&device, context, name); + break; default: return DC_STATUS_INVALIDARGS; } diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 55852ae..8dfd7be 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -68,6 +68,7 @@ atomics_cobalt_parser_set_calibration shearwater_predator_parser_create shearwater_petrel_parser_create diverite_nitekq_parser_create +citizen_aqualand_parser_create dc_device_open dc_device_close @@ -168,3 +169,4 @@ shearwater_predator_extract_dives shearwater_petrel_device_open diverite_nitekq_device_open diverite_nitekq_extract_dives +citizen_aqualand_device_open diff --git a/src/parser.c b/src/parser.c index a17a586..718682a 100644 --- a/src/parser.c +++ b/src/parser.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "parser-private.h" #include "device-private.h" @@ -134,6 +135,9 @@ dc_parser_new (dc_parser_t **out, dc_device_t *device) case DC_FAMILY_DIVERITE_NITEKQ: rc = diverite_nitekq_parser_create (&parser, context); break; + case DC_FAMILY_CITIZEN_AQUALAND: + rc = citizen_aqualand_parser_create (&parser, context); + break; default: return DC_STATUS_INVALIDARGS; }