diff --git a/src/Makefile.am b/src/Makefile.am index fc1ad37..33a4155 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -57,7 +57,7 @@ libdivecomputer_la_SOURCES = \ oceanic_veo250.h oceanic_veo250.c \ oceanic_vtpro.h oceanic_vtpro.c \ mares.h \ - mares_nemo.h mares_nemo.c \ + mares_nemo.h mares_nemo.c mares_nemo_parser.c \ ringbuffer.h ringbuffer.c \ checksum.h checksum.c \ array.h array.c \ diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 9ee68b5..166413b 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -12,6 +12,7 @@ uwatec_smart_parser_create suunto_vyper_parser_create suunto_spyder_parser_create suunto_d9_parser_create +mares_nemo_parser_create device_close device_dump diff --git a/src/mares_nemo.h b/src/mares_nemo.h index dfc9764..bbb97d7 100644 --- a/src/mares_nemo.h +++ b/src/mares_nemo.h @@ -23,6 +23,7 @@ #define MARES_NEMO_H #include "device.h" +#include "parser.h" #ifdef __cplusplus extern "C" { @@ -37,6 +38,9 @@ mares_nemo_device_open (device_t **device, const char* name); device_status_t mares_nemo_extract_dives (const unsigned char data[], unsigned int size, dive_callback_t callback, void *userdata); +parser_status_t +mares_nemo_parser_create (parser_t **parser); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/mares_nemo_parser.c b/src/mares_nemo_parser.c new file mode 100644 index 0000000..93e7846 --- /dev/null +++ b/src/mares_nemo_parser.c @@ -0,0 +1,258 @@ +/* + * libdivecomputer + * + * Copyright (C) 2008 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 "mares_nemo.h" +#include "parser-private.h" +#include "units.h" +#include "utils.h" + +#define WARNING(expr) \ +{ \ + message ("%s:%d: %s\n", __FILE__, __LINE__, expr); \ +} + +typedef struct mares_nemo_parser_t mares_nemo_parser_t; + +struct mares_nemo_parser_t { + parser_t base; +}; + +static parser_status_t mares_nemo_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size); +static parser_status_t mares_nemo_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata); +static parser_status_t mares_nemo_parser_destroy (parser_t *abstract); + +static const parser_backend_t mares_nemo_parser_backend = { + PARSER_TYPE_MARES_NEMO, + mares_nemo_parser_set_data, /* set_data */ + mares_nemo_parser_samples_foreach, /* samples_foreach */ + mares_nemo_parser_destroy /* destroy */ +}; + + +static int +parser_is_mares_nemo (parser_t *abstract) +{ + if (abstract == NULL) + return 0; + + return abstract->backend == &mares_nemo_parser_backend; +} + + +parser_status_t +mares_nemo_parser_create (parser_t **out) +{ + if (out == NULL) + return PARSER_STATUS_ERROR; + + // Allocate memory. + mares_nemo_parser_t *parser = (mares_nemo_parser_t *) malloc (sizeof (mares_nemo_parser_t)); + if (parser == NULL) { + WARNING ("Failed to allocate memory."); + return PARSER_STATUS_MEMORY; + } + + // Initialize the base class. + parser_init (&parser->base, &mares_nemo_parser_backend); + + *out = (parser_t*) parser; + + return PARSER_STATUS_SUCCESS; +} + + +static parser_status_t +mares_nemo_parser_destroy (parser_t *abstract) +{ + if (! parser_is_mares_nemo (abstract)) + return PARSER_STATUS_TYPE_MISMATCH; + + // Free memory. + free (abstract); + + return PARSER_STATUS_SUCCESS; +} + + +static parser_status_t +mares_nemo_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size) +{ + if (! parser_is_mares_nemo (abstract)) + return PARSER_STATUS_TYPE_MISMATCH; + + return PARSER_STATUS_SUCCESS; +} + + +static parser_status_t +mares_nemo_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata) +{ + if (! parser_is_mares_nemo (abstract)) + return PARSER_STATUS_TYPE_MISMATCH; + + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + if (size < 5) + return PARSER_STATUS_ERROR; + + unsigned int length = data[0] + (data[1] << 8); + assert (length <= size); + + unsigned int mode = data[length - 1]; + + unsigned int nsamples = data[length - 3] + (data[length - 2] << 8); + + if (mode != 2) { + unsigned int time = 0; + for (unsigned int i = 0; i < nsamples; ++i) { + parser_sample_value_t sample = {0}; + + unsigned int idx = 2 + 2 * i; + unsigned int value = data[idx] + (data[idx + 1] << 8); + unsigned int depth = value & 0x0FFF; + unsigned int ascent = (value & 0xC000) >> 14; + unsigned int violation = (value & 0x2000) >> 13; + unsigned int deco = (value & 0x1000) >> 12; + + // Time (seconds). + time += 20; + sample.time = time; + if (callback) callback (SAMPLE_TYPE_TIME, sample, userdata); + + // Depth (1/10 m). + sample.depth = depth / 10.0; + if (callback) callback (SAMPLE_TYPE_DEPTH, sample, userdata); + + // Ascent rate + if (ascent) { + sample.event.type = SAMPLE_EVENT_ASCENT; + sample.event.time = 0; + sample.event.flags = 0; + sample.event.value = ascent; + if (callback) callback (SAMPLE_TYPE_EVENT, sample, userdata); + } + + // Deco violation + if (violation) { + sample.event.type = SAMPLE_EVENT_CEILING; + sample.event.time = 0; + sample.event.flags = 0; + sample.event.value = 0; + if (callback) callback (SAMPLE_TYPE_EVENT, sample, userdata); + } + + // Deco stop + if (deco) { + sample.event.type = SAMPLE_EVENT_DECOSTOP; + sample.event.time = 0; + sample.event.flags = 0; + sample.event.value = 0; + if (callback) callback (SAMPLE_TYPE_EVENT, sample, userdata); + } + } + } else { + // A freedive session contains only summaries for each individual + // freedive. The detailed profile data (if present) is stored after + // the normal dive data. We assume a freedive has a detailed profile + // when the buffer contains more data than the size indicated in the + // header. + int profiles = (size > length); + + unsigned int time = 0; + unsigned int offset = length; + for (unsigned int i = 0; i < nsamples; ++i) { + parser_sample_value_t sample = {0}; + + unsigned int idx = 2 + 6 * i; + unsigned int maxdepth = data[idx + 0] + (data[idx + 1] << 8); + unsigned int divetime = data[idx + 2] + data[idx + 3] * 60; + unsigned int surftime = data[idx + 4] + data[idx + 5] * 60; + + // Surface Time (seconds). + time += surftime; + sample.time = time; + if (callback) callback (SAMPLE_TYPE_TIME, sample, userdata); + + // Surface Depth (0 m). + sample.depth = 0.0; + if (callback) callback (SAMPLE_TYPE_DEPTH, sample, userdata); + + if (profiles) { + // Calculate the number of samples that should be present + // in the profile data, based on the divetime in the summary. + unsigned int n = (divetime + 3) / 4; + + // The last sample interval can be smaller than the normal + // 4 seconds. We keep track of the maximum divetime, to be + // able to adjust that last sample interval. + unsigned int maxtime = time + divetime; + + // Process all depth samples. Once a zero depth sample is + // reached, the current freedive profile is complete. + unsigned int count = 0; + while (offset + 2 <= size) { + unsigned int depth = data[offset] + (data[offset + 1] << 8); + offset += 2; + + if (depth == 0) + break; + + count++; + assert (count <= n); + + // Time (seconds). + time += 4; + if (time > maxtime) + time = maxtime; // Adjust the last sample. + sample.time = time; + if (callback) callback (SAMPLE_TYPE_TIME, sample, userdata); + + // Depth (1/10 m). + sample.depth = depth / 10.0; + if (callback) callback (SAMPLE_TYPE_DEPTH, sample, userdata); + } + + // Verify that the number of samples in the profile data + // equals the predicted number of samples (from the divetime + // in the summary entry). If both values are different, the + // the profile data is probably incorrect. + assert (count == n); + + } else { + // Dive Time (seconds). + time += divetime; + sample.time = time; + if (callback) callback (SAMPLE_TYPE_TIME, sample, userdata); + + // Maximum Depth (1/10 m). + sample.depth = maxdepth / 10.0; + if (callback) callback (SAMPLE_TYPE_DEPTH, sample, userdata); + } + } + assert (offset == size); + } + + return PARSER_STATUS_SUCCESS; +} diff --git a/src/parser.h b/src/parser.h index 8cfc01d..92f8da6 100644 --- a/src/parser.h +++ b/src/parser.h @@ -34,7 +34,8 @@ typedef enum parser_type_t { PARSER_TYPE_REEFNET_SENSUSPRO, PARSER_TYPE_REEFNET_SENSUSULTRA, PARSER_TYPE_UWATEC_MEMOMOUSE, - PARSER_TYPE_UWATEC_SMART + PARSER_TYPE_UWATEC_SMART, + PARSER_TYPE_MARES_NEMO } parser_type_t; typedef enum parser_status_t {