Added a parser for the Suunto D9 and Vyper2.
This commit is contained in:
parent
774c8ea2c0
commit
1a36118671
@ -41,7 +41,7 @@ libdivecomputer_la_SOURCES = \
|
||||
suunto_eon.h suunto_eon.c \
|
||||
suunto_vyper.h suunto_vyper.c suunto_vyper_parser.c suunto_spyder_parser.c \
|
||||
suunto_vyper2.h suunto_vyper2.c \
|
||||
suunto_d9.h suunto_d9.c \
|
||||
suunto_d9.h suunto_d9.c suunto_d9_parser.c \
|
||||
reefnet.h \
|
||||
reefnet_sensuspro.h reefnet_sensuspro.c reefnet_sensuspro_parser.c \
|
||||
reefnet_sensusultra.h reefnet_sensusultra.c reefnet_sensusultra_parser.c \
|
||||
|
||||
@ -10,6 +10,7 @@ reefnet_sensusultra_parser_set_calibration
|
||||
uwatec_memomouse_parser_create
|
||||
suunto_vyper_parser_create
|
||||
suunto_spyder_parser_create
|
||||
suunto_d9_parser_create
|
||||
|
||||
device_close
|
||||
device_dump
|
||||
|
||||
19
src/parser.h
19
src/parser.h
@ -30,6 +30,7 @@ typedef enum parser_type_t {
|
||||
PARSER_TYPE_NULL = 0,
|
||||
PARSER_TYPE_SUUNTO_SPYDER,
|
||||
PARSER_TYPE_SUUNTO_VYPER,
|
||||
PARSER_TYPE_SUUNTO_D9,
|
||||
PARSER_TYPE_REEFNET_SENSUSPRO,
|
||||
PARSER_TYPE_REEFNET_SENSUSULTRA,
|
||||
PARSER_TYPE_UWATEC_MEMOMOUSE
|
||||
@ -66,11 +67,25 @@ typedef enum parser_sample_event_t {
|
||||
SAMPLE_EVENT_BOOKMARK,
|
||||
SAMPLE_EVENT_SURFACE,
|
||||
SAMPLE_EVENT_SAFETYSTOP,
|
||||
SAMPLE_EVENT_GASCHANGE
|
||||
SAMPLE_EVENT_GASCHANGE,
|
||||
SAMPLE_EVENT_SAFETYSTOP_VOLUNTARY,
|
||||
SAMPLE_EVENT_SAFETYSTOP_MANDATORY,
|
||||
SAMPLE_EVENT_DEEPSTOP,
|
||||
SAMPLE_EVENT_CEILING_SAFETYSTOP,
|
||||
SAMPLE_EVENT_UNKNOWN,
|
||||
SAMPLE_EVENT_DIVETIME,
|
||||
SAMPLE_EVENT_MAXDEPTH,
|
||||
SAMPLE_EVENT_OLF,
|
||||
SAMPLE_EVENT_P02,
|
||||
SAMPLE_EVENT_AIRTIME,
|
||||
SAMPLE_EVENT_RGBM,
|
||||
SAMPLE_EVENT_HEADING
|
||||
} parser_sample_event_t;
|
||||
|
||||
typedef enum parser_sample_flags_t {
|
||||
SAMPLE_FLAGS_NONE
|
||||
SAMPLE_FLAGS_NONE = 0,
|
||||
SAMPLE_FLAGS_BEGIN = (1 << 0),
|
||||
SAMPLE_FLAGS_END = (1 << 1)
|
||||
} parser_sample_flags_t;
|
||||
|
||||
typedef enum parser_sample_vendor_t {
|
||||
|
||||
@ -22,12 +22,13 @@
|
||||
#ifndef SUUNTO_D9_H
|
||||
#define SUUNTO_D9_H
|
||||
|
||||
#include "device.h"
|
||||
#include "parser.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include "device.h"
|
||||
|
||||
#define SUUNTO_D9_MEMORY_SIZE 0x8000
|
||||
#define SUUNTO_D9_PACKET_SIZE 0x78
|
||||
#define SUUNTO_D9_VERSION_SIZE 0x04
|
||||
@ -38,6 +39,9 @@ suunto_d9_device_open (device_t **device, const char* name);
|
||||
device_status_t
|
||||
suunto_d9_device_reset_maxdepth (device_t *device);
|
||||
|
||||
parser_status_t
|
||||
suunto_d9_parser_create (parser_t **parser, unsigned int model);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
324
src/suunto_d9_parser.c
Normal file
324
src/suunto_d9_parser.c
Normal file
@ -0,0 +1,324 @@
|
||||
/*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h> // memcmp
|
||||
#include <assert.h>
|
||||
|
||||
#include "suunto_d9.h"
|
||||
#include "parser-private.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define WARNING(expr) \
|
||||
{ \
|
||||
message ("%s:%d: %s\n", __FILE__, __LINE__, expr); \
|
||||
}
|
||||
|
||||
#define SKIP 4
|
||||
|
||||
typedef struct suunto_d9_parser_t suunto_d9_parser_t;
|
||||
|
||||
struct suunto_d9_parser_t {
|
||||
parser_t base;
|
||||
unsigned int model;
|
||||
};
|
||||
|
||||
static parser_status_t suunto_d9_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size);
|
||||
static parser_status_t suunto_d9_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata);
|
||||
static parser_status_t suunto_d9_parser_destroy (parser_t *abstract);
|
||||
|
||||
static const parser_backend_t suunto_d9_parser_backend = {
|
||||
PARSER_TYPE_SUUNTO_D9,
|
||||
suunto_d9_parser_set_data, /* set_data */
|
||||
suunto_d9_parser_samples_foreach, /* samples_foreach */
|
||||
suunto_d9_parser_destroy /* destroy */
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
parser_is_suunto_d9 (parser_t *abstract)
|
||||
{
|
||||
if (abstract == NULL)
|
||||
return 0;
|
||||
|
||||
return abstract->backend == &suunto_d9_parser_backend;
|
||||
}
|
||||
|
||||
|
||||
parser_status_t
|
||||
suunto_d9_parser_create (parser_t **out, unsigned int model)
|
||||
{
|
||||
if (out == NULL)
|
||||
return PARSER_STATUS_ERROR;
|
||||
|
||||
// Allocate memory.
|
||||
suunto_d9_parser_t *parser = malloc (sizeof (suunto_d9_parser_t));
|
||||
if (parser == NULL) {
|
||||
WARNING ("Failed to allocate memory.");
|
||||
return PARSER_STATUS_MEMORY;
|
||||
}
|
||||
|
||||
// Initialize the base class.
|
||||
parser_init (&parser->base, &suunto_d9_parser_backend);
|
||||
|
||||
// Set the default values.
|
||||
parser->model = model;
|
||||
|
||||
*out = (parser_t*) parser;
|
||||
|
||||
return PARSER_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static parser_status_t
|
||||
suunto_d9_parser_destroy (parser_t *abstract)
|
||||
{
|
||||
if (! parser_is_suunto_d9 (abstract))
|
||||
return PARSER_STATUS_TYPE_MISMATCH;
|
||||
|
||||
// Free memory.
|
||||
free (abstract);
|
||||
|
||||
return PARSER_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static parser_status_t
|
||||
suunto_d9_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size)
|
||||
{
|
||||
if (! parser_is_suunto_d9 (abstract))
|
||||
return PARSER_STATUS_TYPE_MISMATCH;
|
||||
|
||||
return PARSER_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static parser_status_t
|
||||
suunto_d9_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata)
|
||||
{
|
||||
suunto_d9_parser_t *parser = (suunto_d9_parser_t*) abstract;
|
||||
|
||||
if (! parser_is_suunto_d9 (abstract))
|
||||
return PARSER_STATUS_TYPE_MISMATCH;
|
||||
|
||||
const unsigned char *data = abstract->data;
|
||||
unsigned int size = abstract->size;
|
||||
|
||||
if (size < 0x4E - SKIP)
|
||||
return PARSER_STATUS_ERROR;
|
||||
|
||||
unsigned int pressure_code = data[0x11 - SKIP] + (data[0x12 - SKIP] << 8);
|
||||
unsigned int pressure_offset = 0;
|
||||
if (pressure_code == 0xFFFF)
|
||||
pressure_offset = 3;
|
||||
|
||||
switch (parser->model) {
|
||||
case 0x0E: // D9
|
||||
case 0x0F: // D6
|
||||
case 0x10: // Vyper 2
|
||||
case 0x11: // Cobra 2
|
||||
break;
|
||||
case 0x12: // D4
|
||||
pressure_code = 0xFFFF;
|
||||
pressure_offset = 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Sample recording interval.
|
||||
unsigned int interval_sample = data[0x1C - SKIP];
|
||||
|
||||
// Temperature recording interval.
|
||||
unsigned int interval_temperature = data[0x47 - SKIP - pressure_offset];
|
||||
|
||||
// Offset to the first marker position.
|
||||
unsigned int marker = data[0x4C - SKIP - pressure_offset] +
|
||||
(data[0x4D - SKIP - pressure_offset] << 8);
|
||||
|
||||
unsigned int time = 0;
|
||||
unsigned int nsamples = 0;
|
||||
unsigned int offset = 0x4E - SKIP - pressure_offset;
|
||||
while (offset + 2 <= size) {
|
||||
parser_sample_value_t sample = {0};
|
||||
|
||||
// Time (seconds).
|
||||
sample.time = time;
|
||||
if (callback) callback (SAMPLE_TYPE_TIME, sample, userdata);
|
||||
|
||||
// Depth (cm).
|
||||
unsigned int depth = data[offset + 0] + (data[offset + 1] << 8);
|
||||
sample.depth = depth / 100.0;
|
||||
if (callback) callback (SAMPLE_TYPE_DEPTH, sample, userdata);
|
||||
offset += 2;
|
||||
|
||||
// Tank pressure (bar * 100).
|
||||
if (pressure_code != 0xFFFF) {
|
||||
assert (offset + 2 <= size);
|
||||
unsigned int pressure = data[offset + 0] + (data[offset + 1] << 8);
|
||||
sample.pressure.tank = 0;
|
||||
sample.pressure.value = pressure / 100.0;
|
||||
if (callback) callback (SAMPLE_TYPE_PRESSURE, sample, userdata);
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
// Temperature (degrees celcius).
|
||||
if (nsamples % interval_temperature == 0) {
|
||||
assert (offset + 1 <= size);
|
||||
int temperature = data[offset];
|
||||
sample.temperature = temperature;
|
||||
if (callback) callback (SAMPLE_TYPE_TEMPERATURE, sample, userdata);
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
// Events
|
||||
if ((nsamples + 1) == marker) {
|
||||
while (offset < size) {
|
||||
unsigned int event = data[offset++];
|
||||
unsigned int seconds, type, unknown, heading, percentage;
|
||||
unsigned int current, next;
|
||||
|
||||
sample.event.time = 0;
|
||||
sample.event.flags = 0;
|
||||
sample.event.value = 0;
|
||||
switch (event) {
|
||||
case 0x01: // Next Event Marker
|
||||
assert (offset + 4 <= size);
|
||||
current = data[offset + 0] + (data[offset + 1] << 8);
|
||||
next = data[offset + 2] + (data[offset + 3] << 8);
|
||||
assert (marker == current);
|
||||
marker += next;
|
||||
offset += 4;
|
||||
break;
|
||||
case 0x02: // Surfaced
|
||||
assert (offset + 2 <= size);
|
||||
unknown = data[offset + 0];
|
||||
seconds = data[offset + 1];
|
||||
sample.event.type = SAMPLE_EVENT_SURFACE;
|
||||
sample.event.time = seconds;
|
||||
if (callback) callback (SAMPLE_TYPE_EVENT, sample, userdata);
|
||||
offset += 2;
|
||||
break;
|
||||
case 0x03: // Event
|
||||
assert (offset + 2 <= size);
|
||||
type = data[offset + 0];
|
||||
seconds = data[offset + 1];
|
||||
switch (type & 0x7F) {
|
||||
case 0x00: // Voluntary Safety Stop
|
||||
sample.event.type = SAMPLE_EVENT_SAFETYSTOP_VOLUNTARY;
|
||||
break;
|
||||
case 0x01: // Mandatory Safety Stop
|
||||
sample.event.type = SAMPLE_EVENT_SAFETYSTOP_MANDATORY;
|
||||
break;
|
||||
case 0x02: // Deep Safety Stop
|
||||
sample.event.type = SAMPLE_EVENT_DEEPSTOP;
|
||||
break;
|
||||
case 0x03: // Deco
|
||||
sample.event.type = SAMPLE_EVENT_DECOSTOP;
|
||||
break;
|
||||
case 0x04: // Ascent Rate Warning
|
||||
sample.event.type = SAMPLE_EVENT_ASCENT;
|
||||
break;
|
||||
case 0x05: // Ceiling Broken
|
||||
sample.event.type = SAMPLE_EVENT_CEILING;
|
||||
break;
|
||||
case 0x06: // Mandatory Safety Stop Ceiling Error
|
||||
sample.event.type = SAMPLE_EVENT_CEILING_SAFETYSTOP;
|
||||
break;
|
||||
case 0x07: // Unknown (Deco related)
|
||||
sample.event.type = SAMPLE_EVENT_UNKNOWN;
|
||||
break;
|
||||
case 0x08: // Dive Time
|
||||
sample.event.type = SAMPLE_EVENT_DIVETIME;
|
||||
break;
|
||||
case 0x09: // Depth Alarm
|
||||
sample.event.type = SAMPLE_EVENT_MAXDEPTH;
|
||||
break;
|
||||
case 0x0A: // OLF 80
|
||||
sample.event.type = SAMPLE_EVENT_OLF;
|
||||
sample.event.value = 80;
|
||||
break;
|
||||
case 0x0B: // OLF 100
|
||||
sample.event.type = SAMPLE_EVENT_OLF;
|
||||
sample.event.value = 100;
|
||||
break;
|
||||
case 0x0C: // PO2
|
||||
sample.event.type = SAMPLE_EVENT_P02;
|
||||
break;
|
||||
case 0x0D: //Air Time Warning
|
||||
sample.event.type = SAMPLE_EVENT_AIRTIME;
|
||||
break;
|
||||
case 0x0E: // RGBM Warning
|
||||
sample.event.type = SAMPLE_EVENT_RGBM;
|
||||
break;
|
||||
default: // Unknown
|
||||
WARNING ("Unknown event");
|
||||
break;
|
||||
}
|
||||
if (type & 0x80)
|
||||
sample.event.flags = SAMPLE_FLAGS_END;
|
||||
else
|
||||
sample.event.flags = SAMPLE_FLAGS_BEGIN;
|
||||
sample.event.time = seconds;
|
||||
if (callback) callback (SAMPLE_TYPE_EVENT, sample, userdata);
|
||||
offset += 2;
|
||||
break;
|
||||
case 0x04: // Bookmark/Heading
|
||||
assert (offset + 4 <= size);
|
||||
unknown = data[offset + 0];
|
||||
seconds = data[offset + 1];
|
||||
heading = data[offset + 2] + (data[offset + 3] << 8);
|
||||
if (heading == 0xFFFF) {
|
||||
sample.event.type = SAMPLE_EVENT_BOOKMARK;
|
||||
sample.event.value = 0;
|
||||
} else {
|
||||
sample.event.type = SAMPLE_EVENT_HEADING;
|
||||
sample.event.value = heading / 4;
|
||||
}
|
||||
sample.event.time = seconds;
|
||||
if (callback) callback (SAMPLE_TYPE_EVENT, sample, userdata);
|
||||
offset += 4;
|
||||
break;
|
||||
case 0x05: // Gas Change
|
||||
assert (offset + 2 <= size);
|
||||
percentage = data[offset + 0];
|
||||
seconds = data[offset + 1];
|
||||
sample.event.type = SAMPLE_EVENT_GASCHANGE;
|
||||
sample.event.time = seconds;
|
||||
sample.event.value = percentage;
|
||||
if (callback) callback (SAMPLE_TYPE_EVENT, sample, userdata);
|
||||
offset += 2;
|
||||
break;
|
||||
default:
|
||||
WARNING ("Unknown event");
|
||||
break;
|
||||
}
|
||||
|
||||
if (event == 0x01)
|
||||
break;
|
||||
}
|
||||
}
|
||||
time += interval_sample;
|
||||
nsamples++;
|
||||
}
|
||||
|
||||
return PARSER_STATUS_SUCCESS;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user