[PATCH] Added import for Liquivision LVD log files
Henrik Brautaset Aronsen
subsurface at henrik.synth.no
Fri Nov 7 09:18:04 PST 2014
It works fine with the files I tried. Just a couple of things:
I think you need to add ...
extern int try_to_open_liquivision(const char *filename, struct memblock
*mem);
... in file.h, my compiler complains:
file.c:361:10: warning: implicit declaration of function
'try_to_open_liquivision' is invalid in C99.
Also, there seems to be a = vs == problem in:
liquivision.c:225:23: note: use '=' to turn this equality comparison into
an assignment
sample->depth.mm == array_uint16_le(ds + (d - 1) * 2) * 10; // cm->mm
Henrik
On Fri, Nov 7, 2014 at 6:10 PM, Henrik Brautaset Aronsen <
subsurface at henrik.synth.no> wrote:
>
> Great work!
>
> H
>
> On Fri, Nov 7, 2014 at 5:30 PM, John Van Ostrand <john at vanostrand.com>
wrote:
>>
>> Support includes cylinder pressures and works for v3.0 log files.
>> ---
>> CMakeLists.txt | 1 +
>> file.c | 2 +
>> liquivision.c | 352
+++++++++++++++++++++++++++++++++++++++++++++++++++
>> qt-ui/mainwindow.cpp | 8 +-
>> subsurface.pro | 1 +
>> 5 files changed, 361 insertions(+), 3 deletions(-)
>> create mode 100644 liquivision.c
>>
>> diff --git a/CMakeLists.txt b/CMakeLists.txt
>> index c04e968..868ad4f 100644
>> --- a/CMakeLists.txt
>> +++ b/CMakeLists.txt
>> @@ -85,6 +85,7 @@ SET(SUBSURFACE_CORE_LIB_SRCS
>> equipment.c
>> file.c
>> libdivecomputer.c
>> + liquivision.c
>> load-git.c
>> membuffer.c
>> parse-xml.c
>> diff --git a/file.c b/file.c
>> index bab7909..dadc11f 100644
>> --- a/file.c
>> +++ b/file.c
>> @@ -357,6 +357,8 @@ static int open_by_filename(const char *filename,
const char *fmt, struct memblo
>> /* Cochran export comma-separated-value files */
>> if (!strcasecmp(fmt, "DPT"))
>> return try_to_open_csv(filename, mem, CSV_DEPTH);
>> + if (!strcasecmp(fmt, "LVD"))
>> + return try_to_open_liquivision(filename, mem);
>> if (!strcasecmp(fmt, "TMP"))
>> return try_to_open_csv(filename, mem, CSV_TEMP);
>> if (!strcasecmp(fmt, "HP1"))
>> diff --git a/liquivision.c b/liquivision.c
>> new file mode 100644
>> index 0000000..bb71bfd
>> --- /dev/null
>> +++ b/liquivision.c
>> @@ -0,0 +1,352 @@
>> +#include <string.h>
>> +
>> +#include "dive.h"
>> +#include "divelist.h"
>> +#include "file.h"
>> +
>> +
>> +// Convert bytes into an INT
>> +#define array_uint16_le(p) ((unsigned int) (p)[0] \
>> + + ((p)[1]<<8) )
>> +#define array_uint32_le(p) ((unsigned int) (p)[0] \
>> + + ((p)[1]<<8) +
((p)[2]<<16) \
>> + + ((p)[3]<<24))
>> +
>> +
>> +static void
>> +parse_dives (int log_version, const unsigned char *buf, unsigned int
buf_size) {
>> + unsigned int ptr = 0;
>> + unsigned char model;
>> +
>> + struct dive *dive;
>> + struct divecomputer *dc;
>> + struct sample *sample;
>> +
>> + while (ptr < buf_size) {
>> + dive = alloc_dive();
>> + dc = &dive->dc;
>> +
>> + // Model 0=Xen, 1,2=Xeo, 4=Lynx, other=Liquivision
>> + model = *(buf + ptr);
>> + switch (model) {
>> + case 0:
>> + dc->model = "Xen";
>> + break;
>> + case 1:
>> + case 2:
>> + dc->model = "Xeo";
>> + break;
>> + case 4:
>> + dc->model = "Lynx";
>> + break;
>> + default:
>> + dc->model = "LiquiVision";
>> + break;
>> + }
>> + ptr++;
>> +
>> + // Dive location, assemble Location and Place
>> + unsigned int len, place_len;
>> + len = array_uint32_le(buf + ptr);
>> + ptr += 4;
>> + place_len = array_uint32_le(buf + ptr + len);
>> +
>> + if (len && place_len) {
>> + dive->location = malloc(len + place_len + 4);
>> + memset(dive->location, 0, len + place_len + 4);
>> + memcpy(dive->location, buf + ptr, len);
>> + memcpy(dive->location + len, ", ", 2);
>> + memcpy(dive->location + len + 2, buf + ptr + len
+ 4, place_len);
>> + } else if (len) {
>> + dive->location = strndup(buf + ptr, len);
>> + } else if (place_len) {
>> + dive->location = strndup(buf + ptr + len + 4,
place_len);
>> + }
>> +
>> + ptr += len + 4 + place_len;
>> +
>> + // Dive comment
>> + len = array_uint32_le(buf + ptr);
>> + ptr += 4;
>> +
>> + // Blank notes are better than the default text
>> + if (len && strncmp(buf + ptr, "Comment ...", 11)) {
>> + dive->notes = strndup(buf + ptr, len);
>> + }
>> + ptr += len;
>> +
>> + dive->id = array_uint32_le(buf + ptr);
>> + ptr += 4;
>> +
>> + dive->number = array_uint16_le(buf + ptr) + 1;
>> + ptr += 2;
>> +
>> + dive->duration.seconds = array_uint32_le(buf + ptr);
// seconds
>> + ptr += 4;
>> +
>> + dive->maxdepth.mm = array_uint16_le(buf + ptr) * 10;
// cm->mm
>> + ptr += 2;
>> +
>> + dive->meandepth.mm = array_uint16_le(buf + ptr) * 10;
// cm->mm
>> + ptr += 2;
>> +
>> + dive->when = array_uint32_le(buf + ptr);
>> + ptr += 4;
>> +
>> + //unsigned int end_time = array_uint32_le(buf + ptr);
>> + ptr += 4;
>> +
>> + //unsigned int sit = array_uint32_le(buf + ptr);
>> + ptr += 4;
>> + //if (sit == 0xffffffff) {
>> + //}
>> +
>> + dive->surface_pressure.mbar = array_uint16_le(buf +
ptr); // ???
>> + ptr += 2;
>> +
>> + //unsigned int rep_dive = array_uint16_le(buf + ptr);
>> + ptr += 2;
>> +
>> + dive->mintemp.mkelvin =
C_to_mkelvin((float)array_uint16_le(buf + ptr)/10);// C->mK
>> + ptr += 2;
>> +
>> + dive->maxtemp.mkelvin =
C_to_mkelvin((float)array_uint16_le(buf + ptr)/10);// C->mK
>> + ptr += 2;
>> +
>> + dive->salinity = *(buf + ptr); // ???
>> + ptr += 1;
>> +
>> + unsigned int sample_count = array_uint32_le(buf + ptr);
>> + ptr += 4;
>> +
>> + // Sample interval
>> + unsigned char sample_interval;
>> + sample_interval = 1;
>> +
>> + unsigned char intervals[6] = {1,2,5,10,30,60};
>> + if (*(buf + ptr) < 6)
>> + sample_interval = intervals[*(buf + ptr)];
>> + ptr += 1;
>> +
>> + float start_cns = 0;
>> + unsigned char dive_mode = 0, algorithm = 0;
>> + if (array_uint32_le(buf + ptr) != sample_count) {
>> + // Xeo, with CNS and OTU
>> + start_cns = *(float *) (buf + ptr);
>> + ptr += 4;
>> + dive->cns = *(float *) (buf + ptr); // end
cns
>> + ptr += 4;
>> + dive->otu = *(float *) (buf + ptr);
>> + ptr += 4;
>> + dive_mode = *(buf + ptr++); // 0=Deco,
1=Gauge, 2=None
>> + algorithm = *(buf + ptr++); // 0=ZH-L16C+GF
>> + sample_count = array_uint32_le(buf + ptr);
>> + }
>> + ptr += 4;
>> +
>> + // Parse dive samples
>> + const unsigned char *ds = buf + ptr;
>> + const unsigned char *ts = buf + ptr + sample_count * 2 +
4;
>> + const unsigned char *ps = buf + ptr + sample_count * 4 +
4;
>> + unsigned int ps_count = array_uint32_le(ps);
>> + ps += 4;
>> +
>> + // Bump ptr
>> + ptr += sample_count * 4 + 4;
>> +
>> + // Handle events
>> + unsigned int event;
>> + unsigned int ps_ptr;
>> + ps_ptr = 0;
>> +
>> + unsigned int d = 0, e;
>> + int event_time, mbar, sensor;
>> +
>> + // Loop through events
>> + for (e = 0; e < ps_count; e++) {
>> + // Get event
>> + event = array_uint16_le(ps + ps_ptr);
>> + ps_ptr += 2;
>> +
>> + switch (event) {
>> + case 0x0002: // Unknown
>> + case 0x0004: // Unknown
>> + ps_ptr += 4;
>> + continue;
>> + case 0x0005: // Unknown
>> + ps_ptr += 6;
>> + continue;
>> + case 0x0007: // Gas
>> + // 4 byte time
>> + // 1 byte O2, 1 bye He
>> + ps_ptr += 6;
>> + continue;
>> + case 0x0008:
>> + // 4 byte time
>> + // 2 byte gas set point 2
>> + ps_ptr += 6;
>> + continue;
>> + case 0x000f:
>> + // Tank pressure
>> + event_time = array_uint32_le(ps +
ps_ptr);
>> + sensor = 0; //array_uint16_le(ps +
ps_ptr + 4);
>> + mbar = array_uint16_le(ps + ps_ptr + 6)
* 10; // cb->mb
>> + // 1 byte PSR
>> + // 1 byte ST
>> + ps_ptr += 10;
>> + break;
>> + case 0x0010:
>> + ps_ptr += 26;
>> + continue;
>> + case 0x0015: // Unknown
>> + ps_ptr += 2;
>> + continue;
>> + default:
>> + ps_ptr += 4;
>> + continue;
>> + }
>> +
>> + int sample_time, next_time, last_time;
>> + int depth_mm, last_depth, temp_mk, last_temp;
>> +
>> + while (true) {
>> + sample = prepare_sample(dc);
>> +
>> + // Get sample times
>> + sample_time = d * sample_interval;
>> + depth_mm = array_uint16_le(ds + d * 2) *
10; // cm->mm
>> + temp_mk =
C_to_mkelvin(array_uint16_le(ts + d * 2) / 10); // dC->mK
>> + next_time = (d < sample_count - 1 ? (d +
1) * sample_interval : sample_time);
>> + last_time = (d ? (d - 1) *
sample_interval : 0);
>> +
>> + if (d == sample_count) {
>> + // We still have events to record
>> + sample->time.seconds =
event_time;
>> + sample->depth.mm ==
array_uint16_le(ds + (d - 1) * 2) * 10; // cm->mm
>> + sample->temperature.mkelvin =
C_to_mkelvin(array_uint16_le(ts + (d - 1) * 2) / 10); // dC->mK
>> + sample->sensor = sensor;
>> + sample->cylinderpressure.mbar =
mbar;
>> + finish_sample(dc);
>> +
>> + break;
>> + } else if (event_time > sample_time) {
>> + // Record sample and loop
>> + sample->time.seconds =
sample_time;
>> + sample->depth.mm = depth_mm;
>> + sample->temperature.mkelvin =
temp_mk;
>> + finish_sample(dc);
>> + d++;
>> +
>> + continue;
>> + } else if (event_time == sample_time) {
>> + sample->time.seconds =
sample_time;
>> + sample->depth.mm = depth_mm;
>> + sample->temperature.mkelvin =
temp_mk;
>> + sample->sensor = sensor;
>> + sample->cylinderpressure.mbar =
mbar;
>> + finish_sample(dc);
>> +
>> + break;
>> + } else { // Event is prior to
sample
>> + sample->time.seconds =
event_time;
>> + sample->sensor = sensor;
>> + sample->cylinderpressure.mbar =
mbar;
>> + if (last_time == sample_time) {
>> + sample->depth.mm =
depth_mm;
>> +
sample->temperature.mkelvin = temp_mk;
>> + } else {
>> + // Extrapolate
>> + last_depth =
array_uint16_le(ds + (d - 1) * 2) * 10; // cm->mm
>> + last_temp =
C_to_mkelvin(array_uint16_le(ts + (d - 1) * 2) / 10); // dC->mK
>> + sample->depth.mm =
last_depth + (depth_mm - last_depth)
>> + * (event_time -
last_time) / sample_interval;
>> +
sample->temperature.mkelvin = last_temp + (temp_mk - last_temp)
>> + * (event_time -
last_time) / sample_interval;
>> + }
>> + finish_sample(dc);
>> +
>> + break;
>> + }
>> + } // while (true);
>> + } // for each event sample
>> +
>> + // record trailing depth samples
>> + for ( ;d < sample_count; d++) {
>> + sample = prepare_sample(dc);
>> + sample->time.seconds = d * sample_interval;
>> +
>> + sample->depth.mm = array_uint16_le(ds + d * 2) *
10; // cm->mm
>> + sample->temperature.mkelvin =
>> + C_to_mkelvin((float)array_uint16_le(ts +
d * 2) / 10);
>> + finish_sample(dc);
>> + }
>> +
>> + if (log_version == 3 && model == 4) {
>> + // Advance to begin of next dive
>> + switch (array_uint16_le(ps + ps_ptr)) {
>> + case 0x0000:
>> + ps_ptr += 5;
>> + break;
>> + case 0x0100:
>> + ps_ptr += 7;
>> + break;
>> + case 0x0200:
>> + ps_ptr += 9;
>> + break;
>> + case 0x0300:
>> + ps_ptr += 11;
>> + break;
>> + case 0x0b0b:
>> + ps_ptr += 27;
>> + break;
>> + }
>> +
>> + while (*(ps + ps_ptr) != 0x04)
>> + ps_ptr++;
>> + }
>> +
>> + // End dive
>> + dive->downloaded = true;
>> + record_dive(dive);
>> + mark_divelist_changed(true);
>> +
>> + // Advance ptr for next dive
>> + ptr += ps_ptr + 4;
>> + } // while
>> +
>> + save_dives("/tmp/test.xml");
>> +}
>> +
>> +
>> +int
>> +try_to_open_liquivision(const char *filename, struct memblock *mem)
>> +{
>> + void *name;
>> + const unsigned char *buf = mem->buffer;
>> + unsigned int buf_size = mem->size;
>> + unsigned int ptr;
>> + int log_version;
>> +
>> + // Get name
>> + unsigned int len = array_uint32_le(buf);
>> + if (len) {
>> + name = malloc(len);
>> + strncpy(name, buf + 4, len);
>> + }
>> + ptr = 4 + len;
>> +
>> + unsigned int dive_count = array_uint32_le(buf + ptr);
>> + if (dive_count == 0xffffffff) {
>> + // File version 3.0
>> + log_version = 3;
>> + ptr += 6;
>> + dive_count = array_uint32_le(buf + ptr);
>> + } else {
>> + log_version = 2;
>> + }
>> + ptr += 4;
>> +
>> + parse_dives(log_version, buf + ptr, buf_size - ptr);
>> +
>> + return 1;
>> +}
>> diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp
>> index cc2d970..5811d4e 100644
>> --- a/qt-ui/mainwindow.cpp
>> +++ b/qt-ui/mainwindow.cpp
>> @@ -725,7 +725,8 @@ QString MainWindow::filter()
>> QString f;
>> f += "ALL ( *.ssrf *.xml *.XML *.uddf *.udcf *.UDFC *.jlb *.JLB
";
>> f += "*.sde *.SDE *.dld *.DLD ";
>> - f += "*.db *.can";
>> + f += "*.db *.can ";
>> + f += "*.lvd ";
>> f += ");;";
>>
>> f += "Subsurface (*.ssrf);;";
>> @@ -737,7 +738,8 @@ QString MainWindow::filter()
>> f += "SDE (*.sde *.SDE);;";
>> f += "DLD (*.dld *.DLD);;";
>> f += "DB (*.db);;";
>> - f += "CAN (*.can)";
>> + f += "CAN (*.can);;";
>> + f += "LVD (*.lvd)";
>>
>> return f;
>> }
>> @@ -1247,7 +1249,7 @@ void MainWindow::loadFiles(const QStringList
fileNames)
>> void MainWindow::on_actionImportDiveLog_triggered()
>> {
>> QStringList fileNames = QFileDialog::getOpenFileNames(this,
tr("Open dive log file"), lastUsedDir(),
>> - tr("Dive log files (*.xml *.uddf *.udcf *.csv *.jlb
*.dld *.sde *.db *.can);;"
>> + tr("Dive log files (*.xml *.uddf *.udcf *.csv *.jlb
*.dld *.sde *.db *.can *.lvd);;"
>> "XML files (*.xml);;UDDF/UDCF files(*.uddf
*.udcf);;JDiveLog files(*.jlb);;"
>> "Suunto files(*.sde *.db);;CSV
files(*.csv);;MkVI files(*.txt);;All files(*)"));
>>
>> diff --git a/subsurface.pro b/subsurface.pro
>> index 7b38fd6..07e7b29 100644
>> --- a/subsurface.pro
>> +++ b/subsurface.pro
>> @@ -120,6 +120,7 @@ SOURCES = \
>> file.c \
>> gettextfromc.cpp \
>> libdivecomputer.c \
>> + liquivision.c \
>> load-git.c \
>> main.cpp \
>> membuffer.c \
>> --
>> 1.8.3.1
>>
>> _______________________________________________
>> subsurface mailing list
>> subsurface at subsurface-divelog.org
>> http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.subsurface-divelog.org/pipermail/subsurface/attachments/20141107/ddaa57d2/attachment-0001.html>
More information about the subsurface
mailing list