Add a common base class for the Mares backends.

The memory layout of the Mares Puck and Nemo devices is very similar, 
which allows to share the parsing code between the backends.
    
The Mares Puck protocol allows for a more efficient implementation, by 
reading only the data that we really need. But as an intermediate 
solution, reusing the Nemo code is good enough.
This commit is contained in:
Jef Driesen 2009-10-11 13:51:26 +00:00
parent a4247b7505
commit cc1a99b9c7
7 changed files with 317 additions and 124 deletions

View File

@ -64,6 +64,7 @@ libdivecomputer_la_SOURCES = \
oceanic_veo250.h oceanic_veo250.c \
oceanic_vtpro.h oceanic_vtpro.c \
mares.h \
mares_common.h mares_common.c \
mares_nemo.h mares_nemo.c mares_nemo_parser.c \
mares_puck.h mares_puck.c \
ringbuffer.h ringbuffer.c \

View File

@ -36,6 +36,7 @@ message_set_logfile
mares_nemo_device_open
mares_nemo_extract_dives
mares_puck_device_open
mares_puck_extract_dives
oceanic_atom2_device_open
oceanic_atom2_device_keepalive
oceanic_veo250_device_open

184
src/mares_common.c Normal file
View File

@ -0,0 +1,184 @@
/*
* libdivecomputer
*
* Copyright (C) 2009 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> // malloc
#include <string.h> // memcpy, memcmp
#include <assert.h> // assert
#include "mares_common.h"
#include "utils.h"
#include "array.h"
#define FP_OFFSET 8
#define FP_SIZE 5
void
mares_common_device_init (mares_common_device_t *device, const device_backend_t *backend)
{
assert (device != NULL);
// Initialize the base class.
device_init (&device->base, backend);
// Set the default values.
memset (device->fingerprint, 0, sizeof (device->fingerprint));
}
device_status_t
mares_common_device_set_fingerprint (mares_common_device_t *device, const unsigned char data[], unsigned int size)
{
assert (device != NULL);
if (size && size != sizeof (device->fingerprint))
return DEVICE_STATUS_ERROR;
if (size)
memcpy (device->fingerprint, data, sizeof (device->fingerprint));
else
memset (device->fingerprint, 0, sizeof (device->fingerprint));
return DEVICE_STATUS_SUCCESS;
}
device_status_t
mares_common_extract_dives (mares_common_device_t *device, const mares_common_layout_t *layout, const unsigned char data[], dive_callback_t callback, void *userdata)
{
assert (layout != NULL);
// Get the end of the profile ring buffer.
unsigned int eop = array_uint16_le (data + 0x6B);
// Make the ringbuffer linear, to avoid having to deal
// with the wrap point. The buffer has extra space to
// store the profile data for the freedives.
unsigned char *buffer = (unsigned char *) malloc (
layout->rb_profile_end - layout->rb_profile_begin +
layout->rb_freedives_end - layout->rb_freedives_begin);
if (buffer == NULL) {
WARNING ("Out of memory.");
return DEVICE_STATUS_MEMORY;
}
memcpy (buffer + 0, data + eop, layout->rb_profile_end - eop);
memcpy (buffer + layout->rb_profile_end - eop, data + layout->rb_profile_begin, eop - layout->rb_profile_begin);
// For a freedive session, the Mares Nemo stores all the freedives of
// that session in a single logbook entry, and each sample is actually
// a summary for each individual freedive in the session. The profile
// data is stored in a separate memory area. Since only the most recent
// recent freediving session can have profile data, we keep track of the
// number of freedives.
unsigned int nfreedives = 0;
unsigned int offset = layout->rb_profile_end - layout->rb_profile_begin;
while (offset >= 3) {
// Check the dive mode of the logbook entry. Valid modes are
// 0 (air), 1 (EANx), 2 (freedive) or 3 (bottom timer).
// If the ringbuffer has never reached the wrap point before,
// there will be "empty" memory (filled with 0xFF) and
// processing should stop at this point.
unsigned int mode = buffer[offset - 1];
if (mode == 0xFF)
break;
// The header and sample size are dependant on the dive mode. Only
// in freedive mode, the sizes are different from the other modes.
unsigned int header_size = 53;
unsigned int sample_size = 2;
if (mode == 2) {
header_size = 28;
sample_size = 6;
nfreedives++;
}
// Get the number of samples in the profile data.
unsigned int nsamples = array_uint16_le (buffer + offset - 3);
// Calculate the total number of bytes for this dive.
// If the buffer does not contain that much bytes, we reached the
// end of the ringbuffer. The current dive is incomplete (partially
// overwritten with newer data), and processing should stop.
unsigned int nbytes = 2 + nsamples * sample_size + header_size;
if (offset < nbytes)
break;
// Move to the start of the dive.
offset -= nbytes;
// Verify that the length that is stored in the profile data
// equals the calculated length. If both values are different,
// something is wrong and an error is returned.
unsigned int length = array_uint16_le (buffer + offset);
if (length != nbytes) {
WARNING ("Calculated and stored size are not equal.");
free (buffer);
return DEVICE_STATUS_ERROR;
}
// Process the profile data for the most recent freedive entry.
// Since we are processing the entries backwards (newest to oldest),
// this entry will always be the first one.
if (mode == 2 && nfreedives == 1) {
// Count the number of freedives in the profile data.
unsigned int count = 0;
unsigned int idx = layout->rb_freedives_begin;
while (idx + 2 <= layout->rb_freedives_end &&
count != nsamples)
{
// Each freedive in the session ends with a zero sample.
unsigned int sample = array_uint16_le (data + idx);
if (sample == 0)
count++;
// Move to the next sample.
idx += 2;
}
// Verify that the number of freedive entries in the session
// equals the number of freedives in the profile data. If
// both values are different, the profile data is incomplete.
assert (count == nsamples);
// Append the profile data to the main logbook entry. The
// buffer is guaranteed to have enough space, and the dives
// that will be overwritten have already been processed.
memcpy (buffer + offset + nbytes, data + layout->rb_freedives_begin, idx - layout->rb_freedives_begin);
nbytes += idx - layout->rb_freedives_begin;
}
unsigned int fp_offset = offset + length - FP_OFFSET;
if (device && memcmp (buffer + fp_offset, device->fingerprint, sizeof (device->fingerprint)) == 0) {
free (buffer);
return DEVICE_STATUS_SUCCESS;
}
if (callback && !callback (buffer + offset, nbytes, userdata)) {
free (buffer);
return DEVICE_STATUS_SUCCESS;
}
}
free (buffer);
return DEVICE_STATUS_SUCCESS;
}

55
src/mares_common.h Normal file
View File

@ -0,0 +1,55 @@
/*
* libdivecomputer
*
* Copyright (C) 2009 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 MARES_COMMON_H
#define MARES_COMMON_H
#include "device-private.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct mares_common_device_t {
device_t base;
unsigned char fingerprint[5];
} mares_common_device_t;
typedef struct mares_common_layout_t {
unsigned int rb_profile_begin;
unsigned int rb_profile_end;
unsigned int rb_freedives_begin;
unsigned int rb_freedives_end;
} mares_common_layout_t;
void
mares_common_device_init (mares_common_device_t *device, const device_backend_t *backend);
device_status_t
mares_common_device_set_fingerprint (mares_common_device_t *device, const unsigned char data[], unsigned int size);
device_status_t
mares_common_extract_dives (mares_common_device_t *device, const mares_common_layout_t *layout, const unsigned char data[], dive_callback_t callback, void *userdata);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* MARES_COMMON_H */

View File

@ -24,11 +24,11 @@
#include <assert.h> // assert
#include "device-private.h"
#include "mares_common.h"
#include "mares_nemo.h"
#include "serial.h"
#include "utils.h"
#include "checksum.h"
#include "array.h"
#define EXITCODE(rc) \
( \
@ -44,9 +44,8 @@
#define RB_FREEDIVES_END 0x4000
typedef struct mares_nemo_device_t {
device_t base;
mares_common_device_t base;
struct serial *port;
unsigned char fingerprint[FP_SIZE];
} mares_nemo_device_t;
static device_status_t mares_nemo_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size);
@ -65,6 +64,13 @@ static const device_backend_t mares_nemo_device_backend = {
mares_nemo_device_close /* close */
};
static const mares_common_layout_t mares_nemo_layout = {
0x0070, /* rb_profile_begin */
0x3400, /* rb_profile_end */
0x3400, /* rb_freedives_begin */
0x4000 /* rb_freedives_end */
};
static int
device_is_mares_nemo (device_t *abstract)
{
@ -89,11 +95,10 @@ mares_nemo_device_open (device_t **out, const char* name)
}
// Initialize the base class.
device_init (&device->base, &mares_nemo_device_backend);
mares_common_device_init (&device->base, &mares_nemo_device_backend);
// Set the default values.
device->port = NULL;
memset (device->fingerprint, 0, FP_SIZE);
// Open the device.
int rc = serial_open (&device->port, name);
@ -159,20 +164,12 @@ mares_nemo_device_close (device_t *abstract)
static device_status_t
mares_nemo_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size)
{
mares_nemo_device_t *device = (mares_nemo_device_t*) abstract;
mares_common_device_t *device = (mares_common_device_t*) abstract;
if (! device_is_mares_nemo (abstract))
return DEVICE_STATUS_TYPE_MISMATCH;
if (size && size != FP_SIZE)
return DEVICE_STATUS_ERROR;
if (size)
memcpy (device->fingerprint, data, FP_SIZE);
else
memset (device->fingerprint, 0, FP_SIZE);
return DEVICE_STATUS_SUCCESS;
return mares_common_device_set_fingerprint (device, data, size);
}
@ -281,113 +278,13 @@ mares_nemo_device_foreach (device_t *abstract, dive_callback_t callback, void *u
device_status_t
mares_nemo_extract_dives (device_t *abstract, const unsigned char data[], unsigned int size, dive_callback_t callback, void *userdata)
{
mares_nemo_device_t *device = (mares_nemo_device_t *) abstract;
mares_common_device_t *device = (mares_common_device_t*) abstract;
if (abstract && !device_is_mares_nemo (abstract))
return DEVICE_STATUS_TYPE_MISMATCH;
assert (size >= MARES_NEMO_MEMORY_SIZE);
if (size < MARES_NEMO_MEMORY_SIZE)
return DEVICE_STATUS_ERROR;
// Get the end of the profile ring buffer.
unsigned int eop = array_uint16_le (data + 0x6B);
// Make the ringbuffer linear, to avoid having to deal
// with the wrap point. The buffer has extra space to
// store the profile data for the freedives.
unsigned char buffer[RB_PROFILE_END - RB_PROFILE_BEGIN + RB_FREEDIVES_END - RB_FREEDIVES_BEGIN] = {0};
memcpy (buffer + 0, data + eop, RB_PROFILE_END - eop);
memcpy (buffer + RB_PROFILE_END - eop, data + RB_PROFILE_BEGIN, eop - RB_PROFILE_BEGIN);
// For a freedive session, the Mares Nemo stores all the freedives of
// that session in a single logbook entry, and each sample is actually
// a summary for each individual freedive in the session. The profile
// data is stored in a separate memory area. Since only the most recent
// recent freediving session can have profile data, we keep track of the
// number of freedives.
unsigned int nfreedives = 0;
unsigned int offset = RB_PROFILE_END - RB_PROFILE_BEGIN;
while (offset >= 3) {
// Check the dive mode of the logbook entry. Valid modes are
// 0 (air), 1 (EANx), 2 (freedive) or 3 (bottom timer).
// If the ringbuffer has never reached the wrap point before,
// there will be "empty" memory (filled with 0xFF) and
// processing should stop at this point.
unsigned int mode = buffer[offset - 1];
if (mode == 0xFF)
break;
// The header and sample size are dependant on the dive mode. Only
// in freedive mode, the sizes are different from the other modes.
unsigned int header_size = 53;
unsigned int sample_size = 2;
if (mode == 2) {
header_size = 28;
sample_size = 6;
nfreedives++;
}
// Get the number of samples in the profile data.
unsigned int nsamples = array_uint16_le (buffer + offset - 3);
// Calculate the total number of bytes for this dive.
// If the buffer does not contain that much bytes, we reached the
// end of the ringbuffer. The current dive is incomplete (partially
// overwritten with newer data), and processing should stop.
unsigned int nbytes = 2 + nsamples * sample_size + header_size;
if (offset < nbytes)
break;
// Move to the start of the dive.
offset -= nbytes;
// Verify that the length that is stored in the profile data
// equals the calculated length. If both values are different,
// something is wrong and an error is returned.
unsigned int length = array_uint16_le (buffer + offset);
if (length != nbytes) {
WARNING ("Calculated and stored size are not equal.");
return DEVICE_STATUS_ERROR;
}
// Process the profile data for the most recent freedive entry.
// Since we are processing the entries backwards (newest to oldest),
// this entry will always be the first one.
if (mode == 2 && nfreedives == 1) {
// Count the number of freedives in the profile data.
unsigned int count = 0;
unsigned int idx = RB_FREEDIVES_BEGIN;
while (idx + 2 <= RB_FREEDIVES_END &&
count != nsamples)
{
// Each freedive in the session ends with a zero sample.
unsigned int sample = array_uint16_le (data + idx);
if (sample == 0)
count++;
// Move to the next sample.
idx += 2;
}
// Verify that the number of freedive entries in the session
// equals the number of freedives in the profile data. If
// both values are different, the profile data is incomplete.
assert (count == nsamples);
// Append the profile data to the main logbook entry. The
// buffer is guaranteed to have enough space, and the dives
// that will be overwritten have already been processed.
memcpy (buffer + offset + nbytes, data + RB_FREEDIVES_BEGIN, idx - RB_FREEDIVES_BEGIN);
nbytes += idx - RB_FREEDIVES_BEGIN;
}
unsigned int fp_offset = offset + length - FP_OFFSET;
if (device && memcmp (buffer + fp_offset, device->fingerprint, FP_SIZE) == 0)
return DEVICE_STATUS_SUCCESS;
if (callback && !callback (buffer + offset, nbytes, userdata))
return DEVICE_STATUS_SUCCESS;
}
return DEVICE_STATUS_SUCCESS;
return mares_common_extract_dives (device, &mares_nemo_layout, data, callback, userdata);
}

View File

@ -24,11 +24,11 @@
#include <assert.h> // assert
#include "device-private.h"
#include "mares_common.h"
#include "mares_puck.h"
#include "serial.h"
#include "utils.h"
#include "checksum.h"
#include "array.h"
#define EXITCODE(rc) \
( \
@ -38,25 +38,34 @@
#define MAXRETRIES 4
typedef struct mares_puck_device_t {
device_t base;
mares_common_device_t base;
struct serial *port;
} mares_puck_device_t;
static device_status_t mares_puck_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size);
static device_status_t mares_puck_device_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
static device_status_t mares_puck_device_dump (device_t *abstract, unsigned char data[], unsigned int size, unsigned int *result);
static device_status_t mares_puck_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata);
static device_status_t mares_puck_device_close (device_t *abstract);
static const device_backend_t mares_puck_device_backend = {
DEVICE_TYPE_MARES_PUCK,
NULL, /* set_fingerprint */
mares_puck_device_set_fingerprint, /* set_fingerprint */
NULL, /* version */
mares_puck_device_read, /* read */
NULL, /* write */
mares_puck_device_dump, /* dump */
NULL, /* foreach */
mares_puck_device_foreach, /* foreach */
mares_puck_device_close /* close */
};
static const mares_common_layout_t mares_puck_layout = {
0x0070, /* rb_profile_begin */
0x3400, /* rb_profile_end */
0x3400, /* rb_freedives_begin */
0x4000 /* rb_freedives_end */
};
static int
device_is_mares_puck (device_t *abstract)
{
@ -81,7 +90,7 @@ mares_puck_device_open (device_t **out, const char* name)
}
// Initialize the base class.
device_init (&device->base, &mares_puck_device_backend);
mares_common_device_init (&device->base, &mares_puck_device_backend);
// Set the default values.
device->port = NULL;
@ -271,6 +280,18 @@ mares_puck_transfer (mares_puck_device_t *device, const unsigned char command[],
}
static device_status_t
mares_puck_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size)
{
mares_common_device_t *device = (mares_common_device_t*) abstract;
if (! device_is_mares_puck (abstract))
return DEVICE_STATUS_TYPE_MISMATCH;
return mares_common_device_set_fingerprint (device, data, size);
}
static device_status_t
mares_puck_device_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size)
{
@ -337,3 +358,34 @@ mares_puck_device_dump (device_t *abstract, unsigned char data[], unsigned int s
return DEVICE_STATUS_SUCCESS;
}
static device_status_t
mares_puck_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata)
{
if (! device_is_mares_puck (abstract))
return DEVICE_STATUS_TYPE_MISMATCH;
unsigned char data[MARES_PUCK_MEMORY_SIZE] = {0};
device_status_t rc = mares_puck_device_dump (abstract, data, sizeof (data), NULL);
if (rc != DEVICE_STATUS_SUCCESS)
return rc;
return mares_puck_extract_dives (abstract, data, sizeof (data), callback, userdata);
}
device_status_t
mares_puck_extract_dives (device_t *abstract, const unsigned char data[], unsigned int size, dive_callback_t callback, void *userdata)
{
mares_common_device_t *device = (mares_common_device_t*) abstract;
if (abstract && !device_is_mares_puck (abstract))
return DEVICE_STATUS_TYPE_MISMATCH;
if (size < MARES_PUCK_MEMORY_SIZE)
return DEVICE_STATUS_ERROR;
return mares_common_extract_dives (device, &mares_puck_layout, data, callback, userdata);
}

View File

@ -35,6 +35,9 @@ extern "C" {
device_status_t
mares_puck_device_open (device_t **device, const char* name);
device_status_t
mares_puck_extract_dives (device_t *device, const unsigned char data[], unsigned int size, dive_callback_t callback, void *userdata);
#ifdef __cplusplus
}
#endif /* __cplusplus */