Great work! H
On Fri, Nov 7, 2014 at 5:30 PM, John Van Ostrand <[email protected]> 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 > [email protected] > http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface >
_______________________________________________ subsurface mailing list [email protected] http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface
