diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | file.c | 2 | ||||
-rw-r--r-- | liquivision.c | 352 | ||||
-rw-r--r-- | qt-ui/mainwindow.cpp | 8 | ||||
-rw-r--r-- | subsurface.pro | 1 |
5 files changed, 361 insertions, 3 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index c04e9688f..868ad4f10 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 @@ -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 000000000..bb71bfdee --- /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 8a72bc898..3b74c86ad 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -727,7 +727,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);;"; @@ -739,7 +740,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; } @@ -1249,7 +1251,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 7b38fd6f1..07e7b29ab 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 \ |