Add support for the Tecdiving DiveComputer.eu

This commit is contained in:
Jef Driesen 2018-04-12 21:18:27 +02:00
parent 2985616532
commit 472e9e984c
10 changed files with 813 additions and 0 deletions

View File

@ -88,6 +88,7 @@ static const backend_table_t g_backends[] = {
{"aqualand", DC_FAMILY_CITIZEN_AQUALAND, 0},
{"idive", DC_FAMILY_DIVESYSTEM_IDIVE, 0x03},
{"cochran", DC_FAMILY_COCHRAN_COMMANDER, 0},
{"divecomputereu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0},
};
static const transport_table_t g_transports[] = {

View File

@ -101,6 +101,8 @@ typedef enum dc_family_t {
DC_FAMILY_DIVESYSTEM_IDIVE = (13 << 16),
/* Cochran */
DC_FAMILY_COCHRAN_COMMANDER = (14 << 16),
/* Tecdiving */
DC_FAMILY_TECDIVING_DIVECOMPUTEREU = (15 << 16),
} dc_family_t;
#ifdef __cplusplus

View File

@ -482,6 +482,14 @@
RelativePath="..\src\suunto_vyper_parser.c"
>
</File>
<File
RelativePath="..\src\tecdiving_divecomputereu.c"
>
</File>
<File
RelativePath="..\src\tecdiving_divecomputereu_parser.c"
>
</File>
<File
RelativePath="..\src\timer.c"
>
@ -816,6 +824,10 @@
RelativePath="..\src\suunto_vyper2.h"
>
</File>
<File
RelativePath="..\src\tecdiving_divecomputereu.h"
>
</File>
<File
RelativePath="..\src\timer.h"
>

View File

@ -70,6 +70,7 @@ libdivecomputer_la_SOURCES = \
array.h array.c \
buffer.c \
cochran_commander.h cochran_commander.c cochran_commander_parser.c \
tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \
socket.h socket.c \
irda.c \
usbhid.c \

View File

@ -33,6 +33,7 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata);
static int dc_filter_suunto (dc_transport_t transport, const void *userdata);
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata);
static int dc_filter_hw (dc_transport_t transport, const void *userdata);
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata);
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
@ -323,6 +324,8 @@ static const dc_descriptor_t g_descriptors[] = {
{"Cochran", "EMC-14", DC_FAMILY_COCHRAN_COMMANDER, 3, DC_TRANSPORT_SERIAL, NULL},
{"Cochran", "EMC-16", DC_FAMILY_COCHRAN_COMMANDER, 4, DC_TRANSPORT_SERIAL, NULL},
{"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5, DC_TRANSPORT_SERIAL, NULL},
/* Tecdiving DiveComputer.eu */
{"Tecdiving", "DiveComputer.eu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_tecdiving},
};
static int
@ -447,6 +450,21 @@ static int dc_filter_shearwater (dc_transport_t transport, const void *userdata)
return 1;
}
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata)
{
static const char *bluetooth[] = {
"DiveComputer",
};
if (transport == DC_TRANSPORT_BLUETOOTH) {
return dc_filter_internal_name ((const char *) userdata, bluetooth, C_ARRAY_SIZE(bluetooth));
} else if (transport == DC_TRANSPORT_SERIAL) {
return dc_filter_internal_rfcomm ((const char *) userdata);
}
return 1;
}
dc_status_t
dc_descriptor_iterator (dc_iterator_t **out)
{

View File

@ -55,6 +55,7 @@
#include "citizen_aqualand.h"
#include "divesystem_idive.h"
#include "cochran_commander.h"
#include "tecdiving_divecomputereu.h"
#include "device-private.h"
#include "context-private.h"
@ -203,6 +204,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
case DC_FAMILY_COCHRAN_COMMANDER:
rc = cochran_commander_device_open (&device, context, iostream);
break;
case DC_FAMILY_TECDIVING_DIVECOMPUTEREU:
rc = tecdiving_divecomputereu_device_open (&device, context, iostream);
break;
default:
return DC_STATUS_INVALIDARGS;
}

View File

@ -55,6 +55,7 @@
#include "citizen_aqualand.h"
#include "divesystem_idive.h"
#include "cochran_commander.h"
#include "tecdiving_divecomputereu.h"
#include "context-private.h"
#include "parser-private.h"
@ -164,6 +165,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
case DC_FAMILY_COCHRAN_COMMANDER:
rc = cochran_commander_parser_create (&parser, context, model);
break;
case DC_FAMILY_TECDIVING_DIVECOMPUTEREU:
rc = tecdiving_divecomputereu_parser_create (&parser, context);
break;
default:
return DC_STATUS_INVALIDARGS;
}

View File

@ -0,0 +1,542 @@
/*
* 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> // memcmp, memcpy
#include <stdlib.h> // malloc, free
#include "tecdiving_divecomputereu.h"
#include "context-private.h"
#include "device-private.h"
#include "array.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &tecdiving_divecomputereu_device_vtable)
#define MAXRETRIES 14
#define STX 0x7E
#define CMD_INIT 0x53
#define CMD_LIST 0x57
#define CMD_DIVE 0x58
#define CMD_EXIT 0x59
#define RSP_INIT 0x56
#define RSP_LIST CMD_LIST
#define RSP_HEADER 0x51
#define RSP_PROFILE 0x52
#define SZ_MAXCMD 2
#define SZ_SUMMARY 7
#define SZ_SAMPLE 8
#define SZ_INIT 56
#define SZ_LIST (2 + 0x10000 * SZ_SUMMARY)
#define SZ_HEADER 100
#define SZ_PROFILE (1000 * SZ_SAMPLE)
#define NSTEPS 1000
#define STEP(i,n) (NSTEPS * (i) / (n))
typedef struct tecdiving_divecomputereu_device_t {
dc_device_t base;
dc_iostream_t *iostream;
unsigned char fingerprint[SZ_SUMMARY];
unsigned char version[SZ_INIT];
} tecdiving_divecomputereu_device_t;
static dc_status_t tecdiving_divecomputereu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t tecdiving_divecomputereu_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t tecdiving_divecomputereu_device_close (dc_device_t *abstract);
static const dc_device_vtable_t tecdiving_divecomputereu_device_vtable = {
sizeof(tecdiving_divecomputereu_device_t),
DC_FAMILY_TECDIVING_DIVECOMPUTEREU,
tecdiving_divecomputereu_device_set_fingerprint, /* set_fingerprint */
NULL, /* read */
NULL, /* write */
NULL, /* dump */
tecdiving_divecomputereu_device_foreach, /* foreach */
NULL, /* timesync */
tecdiving_divecomputereu_device_close, /* close */
};
static unsigned short
checksum_crc (const unsigned char data[], unsigned int size, unsigned short init)
{
unsigned short crc = init;
for (unsigned int i = 0; i < size; ++i) {
crc ^= data[i] << 8;
if (crc & 0x8000) {
crc <<= 1;
crc ^= 0x1021;
} else {
crc <<= 1;
}
}
return crc;
}
static dc_status_t
tecdiving_divecomputereu_send (tecdiving_divecomputereu_device_t *device, unsigned char cmd, const unsigned char data[], size_t size)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
unsigned short crc = 0;
if (device_is_cancelled (abstract))
return DC_STATUS_CANCELLED;
if (size > SZ_MAXCMD)
return DC_STATUS_INVALIDARGS;
// Setup the data packet
unsigned char packet[SZ_MAXCMD + 11] = {
STX,
0x00,
(size >> 0) & 0xFF,
(size >> 8) & 0xFF,
(size >> 16) & 0xFF,
(size >> 24) & 0xFF,
cmd,
};
memcpy(packet + 7, data, size);
crc = checksum_crc (packet + 1, size + 6, 0);
packet[size + 7] = (crc >> 8) & 0xFF;
packet[size + 8] = (crc ) & 0xFF;
packet[size + 9] = 0x00;
packet[size + 10] = 0x00;
// Give the dive computer some extra time.
dc_iostream_sleep (device->iostream, 300);
// Send the data packet.
status = dc_iostream_write (device->iostream, packet, size + 11, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
return status;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
tecdiving_divecomputereu_receive (tecdiving_divecomputereu_device_t *device, unsigned char rsp, unsigned char data[], size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
unsigned char header[7];
unsigned int nretries = 0;
// Read the packet start byte.
// Unfortunately it takes a relative long time, about 6-8 seconds,
// before the STX byte arrives. Hence the standard timeout of one
// second is not sufficient, and we need to retry a few times on
// timeout. The advantage over using a single read operation with a
// large timeout is that we can give the user a chance to cancel the
// operation.
while (1) {
status = dc_iostream_read (device->iostream, header + 0, 1, NULL);
if (status != DC_STATUS_SUCCESS) {
if (status != DC_STATUS_TIMEOUT) {
ERROR (abstract->context, "Failed to receive the packet start byte.");
return status;
}
// Abort if the maximum number of retries is reached.
if (nretries++ >= MAXRETRIES)
return status;
// Cancel if requested by the user.
if (device_is_cancelled (abstract))
return DC_STATUS_CANCELLED;
// Try again.
continue;
}
if (header[0] == STX)
break;
// Reset the retry counter.
nretries = 0;
}
// Read the packet header.
status = dc_iostream_read (device->iostream, header + 1, sizeof(header) - 1, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the packet header.");
return status;
}
// Verify the type byte.
unsigned int type = header[1];
if (type != 0x00) {
ERROR (abstract->context, "Unexpected type byte (%02x).", type);
return DC_STATUS_PROTOCOL;
}
// Verify the length.
unsigned int length = array_uint32_le (header + 2);
if (length > size) {
ERROR (abstract->context, "Unexpected packet length (%u).", length);
return DC_STATUS_PROTOCOL;
}
// Get the command type.
unsigned int cmd = header[6];
if (cmd != rsp) {
ERROR (abstract->context, "Unexpected command byte (%02x).", cmd);
return DC_STATUS_PROTOCOL;
}
size_t nbytes = 0;
while (nbytes < length) {
// Set the maximum packet size.
size_t len = 1000;
// Limit the packet size to the total size.
if (nbytes + len > length)
len = length - nbytes;
// Read the packet payload.
status = dc_iostream_read (device->iostream, data + nbytes, len, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the packet payload.");
return status;
}
nbytes += len;
}
// Read the packet checksum.
unsigned char checksum[4];
status = dc_iostream_read (device->iostream, checksum, sizeof(checksum), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the packet checksum.");
return status;
}
// Verify the checksum.
unsigned short crc = array_uint16_be (checksum);
unsigned short ccrc = 0;
ccrc = checksum_crc (header + 1, sizeof(header) - 1, ccrc);
ccrc = checksum_crc (data, length, ccrc);
if (crc != ccrc || checksum[2] != 0x00 || checksum[3] != 0) {
ERROR (abstract->context, "Unexpected packet checksum.");
return DC_STATUS_PROTOCOL;
}
if (actual == NULL) {
// Verify the actual length.
if (length != size) {
ERROR (abstract->context, "Unexpected packet length (%u).", length);
return DC_STATUS_PROTOCOL;
}
} else {
// Return the actual length.
*actual = length;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
tecdiving_divecomputereu_readdive (dc_device_t *abstract, dc_event_progress_t *progress, unsigned int idx, dc_buffer_t *buffer)
{
dc_status_t status = DC_STATUS_SUCCESS;
tecdiving_divecomputereu_device_t *device = (tecdiving_divecomputereu_device_t *) abstract;
// Erase the buffer.
dc_buffer_clear (buffer);
// Encode the one based logbook ID.
unsigned int number = idx + 1;
unsigned char id[] = {
(number >> 8) & 0xFF,
(number ) & 0xFF,
};
// Request the dive.
status = tecdiving_divecomputereu_send (device, CMD_DIVE, id, sizeof(id));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the dive command.");
return status;
}
// Read the dive header.
unsigned char header[SZ_HEADER];
status = tecdiving_divecomputereu_receive (device, RSP_HEADER, header, sizeof(header), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the dive header.");
return status;
}
// Get the number of samples.
unsigned int nsamples = array_uint32_be (header + 36);
// Calculate the total size.
unsigned int size = sizeof(header) + nsamples * SZ_SAMPLE;
// Update and emit a progress event.
if (progress) {
progress->current = (idx + 1) * NSTEPS + STEP(sizeof(header), size);
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
}
// Allocate memory for the dive.
if (!dc_buffer_resize (buffer, size)) {
ERROR (abstract->context, "Insufficient buffer space available.");
return DC_STATUS_NOMEMORY;
}
// Cache the pointer.
unsigned char *data = dc_buffer_get_data(buffer);
// Append the header.
memcpy (data, header, sizeof(header));
unsigned int nbytes = sizeof(header);
while (nbytes < size) {
// Get the packet size. The maximum size for a single data
// packet is 1000 samples.
unsigned int len = size - nbytes;
if (len > SZ_PROFILE)
len = SZ_PROFILE;
// Read the dive samples.
status = tecdiving_divecomputereu_receive (device, RSP_PROFILE, data + nbytes, len, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the dive samples.");
return status;
}
nbytes += len;
// Update and emit a progress event.
if (progress) {
progress->current = (idx + 1) * NSTEPS + STEP(nbytes, size);
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
}
}
return DC_STATUS_SUCCESS;
}
dc_status_t
tecdiving_divecomputereu_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
{
dc_status_t status = DC_STATUS_SUCCESS;
tecdiving_divecomputereu_device_t *device = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
device = (tecdiving_divecomputereu_device_t *) dc_device_allocate (context, &tecdiving_divecomputereu_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 (1000ms).
status = dc_iostream_set_timeout (device->iostream, 1000);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout.");
goto error_free;
}
// Send the init command.
status = tecdiving_divecomputereu_send (device, CMD_INIT, NULL, 0);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to send the init command.");
goto error_free;
}
// Read the device info.
status = tecdiving_divecomputereu_receive (device, RSP_INIT, device->version, sizeof(device->version), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to receive the device info.");
goto error_free;
}
*out = (dc_device_t *) device;
return DC_STATUS_SUCCESS;
error_free:
dc_device_deallocate ((dc_device_t *) device);
return status;
}
static dc_status_t
tecdiving_divecomputereu_device_close (dc_device_t *abstract)
{
dc_status_t status = DC_STATUS_SUCCESS;
tecdiving_divecomputereu_device_t *device = (tecdiving_divecomputereu_device_t *) abstract;
status = tecdiving_divecomputereu_send (device, CMD_EXIT, NULL, 0);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the exit command.");
return status;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
tecdiving_divecomputereu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
{
tecdiving_divecomputereu_device_t *device = (tecdiving_divecomputereu_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
tecdiving_divecomputereu_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
{
dc_status_t status = DC_STATUS_SUCCESS;
tecdiving_divecomputereu_device_t *device = (tecdiving_divecomputereu_device_t *) abstract;
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = 0;
devinfo.firmware = 0;
devinfo.serial = array_uint16_be (device->version + 0x22) << 16 | array_uint16_be (device->version + 0x26);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
// Emit a vendor event.
dc_event_vendor_t vendor;
vendor.data = device->version;
vendor.size = sizeof(device->version);
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
// Allocate memory for the dive list.
size_t length = SZ_LIST;
unsigned char *logbook = (unsigned char *) malloc (length);
if (logbook == NULL) {
status = DC_STATUS_NOMEMORY;
goto error_exit;
}
// Request the dive list.
status = tecdiving_divecomputereu_send (device, CMD_LIST, NULL, 0);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the list command.");
goto error_free;
}
// Read the dive list.
status = tecdiving_divecomputereu_receive (device, RSP_LIST, logbook, length, &length);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the logbook.");
goto error_free;
}
// Verify the minimum length.
if (length < 2) {
status = DC_STATUS_DATAFORMAT;
goto error_free;
}
// Get the number of logbook entries.
unsigned int nlogbooks = array_uint16_be (logbook);
if (length != 2 + nlogbooks * SZ_SUMMARY) {
status = DC_STATUS_DATAFORMAT;
goto error_free;
}
// Count the number of dives to download.
unsigned int ndives = 0;
for (unsigned int i = 0; i < nlogbooks; ++i) {
unsigned int offset = 2 + i * SZ_SUMMARY;
if (memcmp(logbook + offset, device->fingerprint, sizeof(device->fingerprint)) == 0)
break;
ndives++;
}
// Update and emit a progress event.
progress.current = 1 * NSTEPS;
progress.maximum = (ndives + 1) * NSTEPS;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Allocate a memory buffer for a single dive.
dc_buffer_t *buffer = dc_buffer_new(0);
if (buffer == NULL) {
status = DC_STATUS_NOMEMORY;
goto error_free;
}
for (unsigned int i = 0; i < ndives; ++i) {
unsigned int offset = 2 + i * SZ_SUMMARY;
// Read the dive.
status = tecdiving_divecomputereu_readdive (abstract, &progress, i, buffer);
if (status != DC_STATUS_SUCCESS) {
goto error_free;
}
// Cache the pointer.
unsigned char *data = dc_buffer_get_data(buffer);
unsigned int size = dc_buffer_get_size(buffer);
// Verify the logbook entry.
if (memcmp (data, logbook + offset, SZ_SUMMARY) != 0) {
ERROR (abstract->context, "Dive header doesn't match logbook entry.");
status = DC_STATUS_DATAFORMAT;
goto error_free;
}
if (callback && !callback (data, size, data, sizeof(device->fingerprint), userdata)) {
break;
}
}
error_free:
dc_buffer_free (buffer);
free (logbook);
error_exit:
return status;
}

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 TECDIVING_DIVECOMPUTEREU_H
#define TECDIVING_DIVECOMPUTEREU_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
tecdiving_divecomputereu_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t
tecdiving_divecomputereu_parser_create (dc_parser_t **parser, dc_context_t *context);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* TECDIVING_DIVECOMPUTEREU_H */

View File

@ -0,0 +1,186 @@
/*
* 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 "tecdiving_divecomputereu.h"
#include "context-private.h"
#include "parser-private.h"
#include "array.h"
#define ISINSTANCE(parser) dc_device_isinstance((parser), &tecdiving_divecomputereu_parser_vtable)
#define SZ_HEADER 100
typedef struct tecdiving_divecomputereu_parser_t tecdiving_divecomputereu_parser_t;
struct tecdiving_divecomputereu_parser_t {
dc_parser_t base;
};
static dc_status_t tecdiving_divecomputereu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t tecdiving_divecomputereu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t tecdiving_divecomputereu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t tecdiving_divecomputereu_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
static const dc_parser_vtable_t tecdiving_divecomputereu_parser_vtable = {
sizeof(tecdiving_divecomputereu_parser_t),
DC_FAMILY_TECDIVING_DIVECOMPUTEREU,
tecdiving_divecomputereu_parser_set_data, /* set_data */
tecdiving_divecomputereu_parser_get_datetime, /* datetime */
tecdiving_divecomputereu_parser_get_field, /* fields */
tecdiving_divecomputereu_parser_samples_foreach, /* samples_foreach */
NULL /* destroy */
};
dc_status_t
tecdiving_divecomputereu_parser_create (dc_parser_t **out, dc_context_t *context)
{
tecdiving_divecomputereu_parser_t *parser = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (tecdiving_divecomputereu_parser_t *) dc_parser_allocate (context, &tecdiving_divecomputereu_parser_vtable);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
*out = (dc_parser_t *) parser;
return DC_STATUS_SUCCESS;
}
static dc_status_t
tecdiving_divecomputereu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
return DC_STATUS_SUCCESS;
}
static dc_status_t
tecdiving_divecomputereu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
const unsigned char *data = abstract->data;
if (abstract->size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
if (datetime) {
datetime->year = data[2] + 2000;
datetime->month = data[3];
datetime->day = data[4];
datetime->hour = data[5];
datetime->minute = data[6];
datetime->second = data[7];
datetime->timezone = DC_TIMEZONE_NONE;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
tecdiving_divecomputereu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
{
const unsigned char *data = abstract->data;
if (abstract->size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
if (value) {
switch (type) {
case DC_FIELD_DIVETIME:
*((unsigned int *) value) = array_uint16_be (data + 23) * 60;
break;
case DC_FIELD_AVGDEPTH:
*((double *) value) = array_uint16_be (data + 27) / 100;
break;
case DC_FIELD_MAXDEPTH:
*((double *) value) = array_uint16_be (data + 29) / 10;
break;
case DC_FIELD_ATMOSPHERIC:
*((double *) value) = array_uint16_be (data + 14) / 1000.0;
break;
case DC_FIELD_TEMPERATURE_SURFACE:
*((double *) value) = (signed char) data[17];
break;
case DC_FIELD_TEMPERATURE_MINIMUM:
*((double *) value) = (signed char) data[41];
break;
case DC_FIELD_TEMPERATURE_MAXIMUM:
*((double *) value) = (signed char) data[42];
break;
default:
return DC_STATUS_UNSUPPORTED;
}
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
tecdiving_divecomputereu_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 = data[47];
unsigned int offset = SZ_HEADER;
while (offset + 8 <= size) {
dc_sample_value_t sample = {0};
// Time (seconds).
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
// Depth (1/10 m).
unsigned int depth = array_uint16_be (data + offset + 2);
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
// Temperature (Celsius).
signed int temperature = (signed char) data[offset];
sample.temperature = temperature;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
// ppO2
unsigned int ppo2 = data[offset + 1];
sample.ppo2 = ppo2 / 10.0;
if (callback) callback (DC_SAMPLE_PPO2, sample, userdata);
// Setpoint
unsigned int setpoint = data[offset + 4];
sample.setpoint = setpoint / 10.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata);
offset += 8;
}
return DC_STATUS_SUCCESS;
}