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
[email protected]
http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface

Reply via email to