From 7f42acfdfb1400317b5198e5d7ca7da085252a55 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Mon, 11 Nov 2024 15:41:31 +1300 Subject: [PATCH] Import: Add Parsing for Divesoft Freedom+ Format Files. Switch the Divesoft log file importer to use the parser in libdivecomputer. This adds support for the newer Divesoft Freedom+ log file format. Also refactor the OSTCTools log file importer to share common functionality. Signed-off-by: Michael Keller --- Subsurface-mobile.pro | 1 + core/CMakeLists.txt | 1 + core/divesoft.cpp | 39 ++++ core/file.cpp | 54 ++++- core/file.h | 4 +- core/libdivecomputer.cpp | 23 +- core/libdivecomputer.h | 3 +- core/ostctools.cpp | 122 +++------- core/parse-xml.cpp | 493 --------------------------------------- core/parse.h | 1 - 10 files changed, 153 insertions(+), 588 deletions(-) create mode 100644 core/divesoft.cpp diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index e09a7a61d..ff82078e1 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -82,6 +82,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/save-git.cpp \ core/datatrak.cpp \ core/ostctools.cpp \ + core/divesoft.cpp \ core/planner.cpp \ core/save-xml.cpp \ core/cochran.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index f9c39f60d..51ce07238 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -77,6 +77,7 @@ set(SUBSURFACE_CORE_LIB_SRCS divesitetable.h divesitehelpers.cpp divesitehelpers.h + divesoft.cpp downloadfromdcthread.cpp downloadfromdcthread.h event.cpp diff --git a/core/divesoft.cpp b/core/divesoft.cpp new file mode 100644 index 000000000..cd76787ec --- /dev/null +++ b/core/divesoft.cpp @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +#include "errorhelper.h" +#include "subsurface-string.h" +#include "gettext.h" +#include "dive.h" +#include "divelist.h" +#include "divelog.h" +#include "extradata.h" +#include "format.h" +#include "libdivecomputer.h" + +int divesoft_import(const std::unique_ptr> &buffer, struct divelog *log) +{ + int model = 0; + if (strncmp((char *)(buffer->data() + 52), "7026", 4) == 0) + model = 10; + else if (strncmp((char *)(buffer->data() + 52), "7044", 4) == 0 || strncmp((char *)(buffer->data() + 52), "7273", 4) == 0) + model = 19; + device_data_t devdata; + int ret = prepare_device_descriptor(model, DC_FAMILY_DIVESOFT_FREEDOM, devdata); + if (ret == 0) + return report_error("%s", translate("gettextFromC", "Unknown DC")); + + auto d = std::make_unique(); + d->dcs[0].model = devdata.vendor + " " + devdata.model + " (Imported from file)"; + + // Parse the dive data + dc_status_t rc = libdc_buffer_parser(d.get(), &devdata, buffer->data(), buffer->size()); + if (rc != DC_STATUS_SUCCESS) + return report_error(translate("gettextFromC", "Error - %s - parsing dive %d"), errmsg(rc), d->number); + + log->dives.record_dive(std::move(d)); + + return 1; +} diff --git a/core/file.cpp b/core/file.cpp index 36d47b1c3..f2dfe533b 100644 --- a/core/file.cpp +++ b/core/file.cpp @@ -267,6 +267,44 @@ bool remote_repo_uptodate(const char *filename, struct git_info *info) return false; } +static std::unique_ptr> read_into_buffer(const char *file) +{ + const char *failed_to_read_msg = translate("gettextFromC", "Failed to read '%s'"); + + struct stat file_status; + if (stat(file, &file_status) < 0) { + report_error(failed_to_read_msg, file); + + return NULL; + } + + // Open the archive + FILE *archive; + if ((archive = subsurface_fopen(file, "rb")) == NULL) { + report_error(failed_to_read_msg, file); + + return NULL; + } + + // Read dive's raw data + auto buffer = std::make_unique>(file_status.st_size, 0); + int i = 0, c; + while ((c = getc(archive)) != EOF) { + (*buffer)[i] = c; + + i++; + } + if (ferror(archive)) { + report_error(failed_to_read_msg, file); + fclose(archive); + + return NULL; + } + fclose(archive); + + return buffer; +} + int parse_file(const char *filename, struct divelog *log) { struct git_info info; @@ -304,8 +342,13 @@ int parse_file(const char *filename, struct divelog *log) } /* Divesoft Freedom */ - if (fmt && (!strcasecmp(fmt + 1, "DLF"))) - return parse_dlf_buffer((unsigned char *)mem.data(), mem.size(), log); + if (fmt && (!strcasecmp(fmt + 1, "DLF"))) { + auto buffer = read_into_buffer(filename); + if (buffer == NULL) + return -1; + + return divesoft_import(buffer, log); + } /* DataTrak/Wlog */ if (fmt && !strcasecmp(fmt + 1, "LOG")) { @@ -321,8 +364,11 @@ int parse_file(const char *filename, struct divelog *log) /* OSTCtools */ if (fmt && (!strcasecmp(fmt + 1, "DIVE"))) { - ostctools_import(filename, log); - return 0; + auto buffer = read_into_buffer(filename); + if (buffer == NULL) + return -1; + + return ostctools_import(buffer, log); } return parse_file_buffer(filename, mem, log); diff --git a/core/file.h b/core/file.h index 054fd384b..93692f5c3 100644 --- a/core/file.h +++ b/core/file.h @@ -8,11 +8,13 @@ #include #include #include +#include struct divelog; struct zip; -extern void ostctools_import(const char *file, struct divelog *log); +extern int ostctools_import(const std::unique_ptr> &buffer, struct divelog *log); +extern int divesoft_import(const std::unique_ptr> &buffer, struct divelog *log); extern int parse_file(const char *filename, struct divelog *log); extern int try_to_open_zip(const char *filename, struct divelog *log); diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index f6dd7cac1..ef2fc21d7 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -1534,6 +1534,26 @@ std::string do_libdivecomputer_import(device_data_t *data) return err; } +/* + * Fills a device_data_t structure with known dc data and a descriptor. + */ +int prepare_device_descriptor(int data_model, dc_family_t dc_fam, device_data_t &dev_data) +{ + dev_data.device = NULL; + dev_data.context = NULL; + + dc_descriptor_t *data_descriptor = get_descriptor(dc_fam, data_model); + if (data_descriptor) { + dev_data.descriptor = data_descriptor; + dev_data.vendor = dc_descriptor_get_vendor(data_descriptor); + dev_data.model = dc_descriptor_get_product(data_descriptor); + } else { + return 0; + } + + return 1; +} + /* * Parse data buffers instead of dc devices downloaded data. * Intended to be used to parse profile data from binary files during import tasks. @@ -1543,7 +1563,7 @@ std::string do_libdivecomputer_import(device_data_t *data) * Note that dc_descriptor_t in data *must* have been filled using dc_descriptor_iterator() * calls. */ -dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, unsigned char *buffer, int size) +dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, const unsigned char *buffer, int size) { dc_status_t rc; dc_parser_t *parser = NULL; @@ -1556,6 +1576,7 @@ dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, unsigned case DC_FAMILY_HW_OSTC: case DC_FAMILY_HW_FROG: case DC_FAMILY_HW_OSTC3: + case DC_FAMILY_DIVESOFT_FREEDOM: rc = dc_parser_new2(&parser, data->context, data->descriptor, buffer, size); break; default: diff --git a/core/libdivecomputer.h b/core/libdivecomputer.h index 8a9dccf82..d5fa50a38 100644 --- a/core/libdivecomputer.h +++ b/core/libdivecomputer.h @@ -54,7 +54,8 @@ struct device_data_t { const char *errmsg (dc_status_t rc); std::string do_libdivecomputer_import(device_data_t *data); -dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, unsigned char *buffer, int size); +int prepare_device_descriptor(int data_model, dc_family_t dc_fam, device_data_t &dev_data); +dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, const unsigned char *buffer, int size); void logfunc(dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata); dc_descriptor_t *get_descriptor(dc_family_t type, unsigned int model); diff --git a/core/ostctools.cpp b/core/ostctools.cpp index 6af6bb121..7469261a2 100644 --- a/core/ostctools.cpp +++ b/core/ostctools.cpp @@ -14,101 +14,48 @@ #include "format.h" #include "libdivecomputer.h" -/* - * Fills a device_data_t structure with known dc data and a descriptor. - */ -static int ostc_prepare_data(int data_model, dc_family_t dc_fam, device_data_t &dev_data) -{ - dc_descriptor_t *data_descriptor; - - dev_data.device = NULL; - dev_data.context = NULL; - - data_descriptor = get_descriptor(dc_fam, data_model); - if (data_descriptor) { - dev_data.descriptor = data_descriptor; - dev_data.vendor = dc_descriptor_get_vendor(data_descriptor); - dev_data.model = dc_descriptor_get_product(data_descriptor); - } else { - return 0; - } - return 1; -} - /* * OSTCTools stores the raw dive data in heavily padded files, one dive * each file. So it's not necessary to iterate once and again on a parsing * function. Actually there's only one kind of archive for every DC model. */ -void ostctools_import(const char *file, struct divelog *log) +int ostctools_import(const std::unique_ptr> &buffer, struct divelog *log) { - FILE *archive; - device_data_t devdata; - dc_family_t dc_fam; - std::vector buffer(65536, 0); - unsigned char uc_tmp[2]; - auto ostcdive = std::make_unique(); - dc_status_t rc = DC_STATUS_SUCCESS; - int model, ret, i = 0, c; - unsigned int serial; - const char *failed_to_read_msg = translate("gettextFromC", "Failed to read '%s'"); - - // Open the archive - if ((archive = subsurface_fopen(file, "rb")) == NULL) { - report_error(failed_to_read_msg, file); - return; - } + if (buffer->size() < 456) + return report_error("%s", translate("gettextFromC", "Invalid OSTCTools file")); // Read dive number from the log - if (fseek(archive, 258, 0) == -1) { - report_error(failed_to_read_msg, file); - fclose(archive); - return; - } - if (fread(uc_tmp, 1, 2, archive) != 2) { - report_error(failed_to_read_msg, file); - fclose(archive); - return; - } - ostcdive->number = uc_tmp[0] + (uc_tmp[1] << 8); + auto ostcdive = std::make_unique(); + ostcdive->number = (*buffer)[258] + ((*buffer)[259] << 8); + // Read device's serial number - if (fseek(archive, 265, 0) == -1) { - report_error(failed_to_read_msg, file); - fclose(archive); - return; - } - if (fread(uc_tmp, 1, 2, archive) != 2) { - report_error(failed_to_read_msg, file); - fclose(archive); - return; - } - serial = uc_tmp[0] + (uc_tmp[1] << 8); + unsigned int serial = (*buffer)[265] + ((*buffer)[266] << 8); - // Read dive's raw data, header + profile - if (fseek(archive, 456, 0) == -1) { - report_error(failed_to_read_msg, file); - fclose(archive); - return; - } - while ((c = getc(archive)) != EOF) { - buffer[i] = c; - if (buffer[i] == 0xFD && buffer[i - 1] == 0xFD) - break; + // Trim the buffer to the actual dive data + buffer->erase(buffer->begin(), buffer->begin() + 456); + unsigned int i = 0; + bool end_marker = false; + for (auto c: *buffer) { i++; + if (c == 0xFD) { + if (end_marker) + break; + else + end_marker = true; + } else { + end_marker = false; + } } - if (ferror(archive)) { - report_error(failed_to_read_msg, file); - fclose(archive); - return; - } - fclose(archive); + if (end_marker) + buffer->erase(buffer->begin() + i, buffer->end()); // Try to determine the dc family based on the header type - if (buffer[2] == 0x20 || buffer[2] == 0x21) { + dc_family_t dc_fam; + if ((*buffer)[2] == 0x20 || (*buffer)[2] == 0x21) { dc_fam = DC_FAMILY_HW_OSTC; } else { - switch (buffer[8]) { + switch ((*buffer)[8]) { case 0x22: dc_fam = DC_FAMILY_HW_FROG; break; @@ -117,12 +64,12 @@ void ostctools_import(const char *file, struct divelog *log) dc_fam = DC_FAMILY_HW_OSTC3; break; default: - report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number); - return; + return report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number); } } // Try to determine the model based on serial number + int model; switch (dc_fam) { case DC_FAMILY_HW_OSTC: if (serial > 7000) @@ -145,17 +92,16 @@ void ostctools_import(const char *file, struct divelog *log) } // Prepare data to pass to libdivecomputer. - ret = ostc_prepare_data(model, dc_fam, devdata); - if (ret == 0) { - report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number); - return; - } + device_data_t devdata; + int ret = prepare_device_descriptor(model, dc_fam, devdata); + if (ret == 0) + return report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number); ostcdive->dcs[0].model = devdata.vendor + " " + devdata.model + " (Imported from OSTCTools)"; // Parse the dive data - rc = libdc_buffer_parser(ostcdive.get(), &devdata, buffer.data(), i + 1); + dc_status_t rc = libdc_buffer_parser(ostcdive.get(), &devdata, buffer->data(), buffer->size()); if (rc != DC_STATUS_SUCCESS) - report_error(translate("gettextFromC", "Error - %s - parsing dive %d"), errmsg(rc), ostcdive->number); + return report_error(translate("gettextFromC", "Error - %s - parsing dive %d"), errmsg(rc), ostcdive->number); // Serial number is not part of the header nor the profile, so libdc won't // catch it. If Serial is part of the extra_data, and set to zero, replace it. @@ -169,4 +115,6 @@ void ostctools_import(const char *file, struct divelog *log) add_extra_data(&ostcdive->dcs[0], "Serial", ostcdive->dcs[0].serial); log->dives.record_dive(std::move(ostcdive)); + + return 1; } diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index cd20599cf..7905336fa 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1770,499 +1770,6 @@ int parse_xml_buffer(const char *url, const char *buffer, int, struct divelog *l return ret; } -/* - * Parse a unsigned 32-bit integer in little-endian mode, - * that is seconds since Jan 1, 2000. - */ -static timestamp_t parse_dlf_timestamp(unsigned char *buffer) -{ - timestamp_t offset; - - offset = buffer[3]; - offset = (offset << 8) + buffer[2]; - offset = (offset << 8) + buffer[1]; - offset = (offset << 8) + buffer[0]; - - // Jan 1, 2000 is 946684800 seconds after Jan 1, 1970, which is - // the Unix epoch date that "timestamp_t" uses. - return offset + 946684800; -} - -int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) -{ - using namespace std::string_literals; - unsigned char *ptr = buffer; - unsigned char event; - bool found; - unsigned int time = 0; - char serial[6]; - struct battery_status { - uint16_t volt1; - uint8_t percent1; - uint16_t volt2; - uint8_t percent2; - }; - struct battery_status battery_start = {0, 0, 0, 0}; - struct battery_status battery_end = {0, 0, 0, 0}; - uint16_t o2_sensor_calibration_values[4] = {0}; - cylinder_t *cyl; - struct parser_state state; - - state.log = log; - - // Check for the correct file magic - if (ptr[0] != 'D' || ptr[1] != 'i' || ptr[2] != 'v' || ptr[3] != 'E') - return -1; - - dive_start(&state); - divecomputer_start(&state); - - state.cur_dc->model = "DLF import"; - // (ptr[7] << 8) + ptr[6] Is "Serial" - snprintf(serial, sizeof(serial), "%d", (ptr[7] << 8) + ptr[6]); - state.cur_dc->serial = serial; - state.cur_dc->when = parse_dlf_timestamp(ptr + 8); - state.cur_dive->when = state.cur_dc->when; - - state.cur_dc->duration.seconds = ((ptr[14] & 0xFE) << 16) + (ptr[13] << 8) + ptr[12]; - - // ptr[14] >> 1 is scrubber used in % - - // 3 bit dive type - switch((ptr[15] & 0x38) >> 3) { - case 0: // unknown - case 1: - state.cur_dc->divemode = OC; - break; - case 2: - state.cur_dc->divemode = CCR; - break; - case 3: - state.cur_dc->divemode = CCR; // mCCR - break; - case 4: - state.cur_dc->divemode = FREEDIVE; - break; - case 5: - state.cur_dc->divemode = OC; // Gauge - break; - case 6: - state.cur_dc->divemode = PSCR; // ASCR - break; - case 7: - state.cur_dc->divemode = PSCR; - break; - } - - state.cur_dc->maxdepth.mm = ((ptr[21] << 8) + ptr[20]) * 10; - state.cur_dc->surface_pressure.mbar = ((ptr[25] << 8) + ptr[24]) / 10; - - // Declare initial mix as first cylinder - cyl = state.cur_dive->get_or_create_cylinder(0); - cyl->gasmix.o2.permille = ptr[26] * 10; - cyl->gasmix.he.permille = ptr[27] * 10; - - /* Done with parsing what we know about the dive header */ - ptr += 32; - - // We're going to interpret ppO2 saved as a sensor value in these modes. - if (state.cur_dc->divemode == CCR || state.cur_dc->divemode == PSCR) - state.cur_dc->no_o2sensors = 1; - - for (; ptr < buffer + size; ptr += 16) { - time = ((ptr[0] >> 4) & 0x0f) + - ((ptr[1] << 4) & 0xff0) + - ((ptr[2] << 12) & 0x1f000); - event = ptr[0] & 0x0f; - switch (event) { - case 0: - /* Regular sample */ - sample_start(&state); - state.cur_sample->time.seconds = time; - state.cur_sample->depth.mm = ((ptr[5] << 8) + ptr[4]) * 10; - // Crazy precision on these stored values... - // Only store value if we're in CCR/PSCR mode, - // because we rather calculate ppo2 our selfs. - if (state.cur_dc->divemode == CCR || state.cur_dc->divemode == PSCR) - state.cur_sample->o2sensor[0].mbar = ((ptr[7] << 8) + ptr[6]) / 10; - - // In some test files, ndl / tts / temp is bogus if this bits are 1 - // flag bits in ptr[11] & 0xF0 is probably involved to, - if ((ptr[2] >> 5) != 1) { - // NDL in minutes, 10 bit - state.cur_sample->ndl.seconds = (((ptr[9] & 0x03) << 8) + ptr[8]) * 60; - // TTS in minutes, 10 bit - state.cur_sample->tts.seconds = (((ptr[10] & 0x0F) << 6) + (ptr[9] >> 2)) * 60; - // Temperature in 1/10 C, 10 bit signed - state.cur_sample->temperature.mkelvin = ((ptr[11] & 0x20) ? -1 : 1) * (((ptr[11] & 0x1F) << 4) + (ptr[10] >> 4)) * 100 + ZERO_C_IN_MKELVIN; - } - state.cur_sample->stopdepth.mm = ((ptr[13] << 8) + ptr[12]) * 10; - if (state.cur_sample->stopdepth.mm) - state.cur_sample->in_deco = true; - //ptr[14] is helium content, always zero? - //ptr[15] is setpoint, what the computer thinks you should aim for? - sample_end(&state); - break; - case 1: /* dive event */ - case 2: /* automatic parameter change */ - case 3: /* diver error */ - case 4: /* internal error */ - case 5: /* device activity log */ - //Event 18 is a button press. Lets ingore that event. - if (ptr[4] == 18) - continue; - - event_start(&state); - state.cur_event.time.seconds = time; - switch (ptr[4]) { - case 1: - state.cur_event.name = "Setpoint Manual"s; - state.cur_event.value = ptr[6]; - sample_start(&state); - state.cur_sample->setpoint.mbar = ptr[6] * 10; - sample_end(&state); - break; - case 2: - state.cur_event.name = "Setpoint Auto"s; - state.cur_event.value = ptr[6]; - sample_start(&state); - state.cur_sample->setpoint.mbar = ptr[6] * 10; - sample_end(&state); - switch (ptr[7]) { - case 0: - state.cur_event.name += " Manual"s; - break; - case 1: - state.cur_event.name += " Auto Start"s; - break; - case 2: - state.cur_event.name += " Auto Hypox"s; - break; - case 3: - state.cur_event.name += " Auto Timeout"s; - break; - case 4: - state.cur_event.name += " Auto Ascent"s; - break; - case 5: - state.cur_event.name += " Auto Stall"s; - break; - case 6: - state.cur_event.name += " Auto SP Low"s; - break; - default: - break; - } - break; - case 3: - // obsolete - state.cur_event.name = "OC"s; - break; - case 4: - // obsolete - state.cur_event.name = "CCR"s; - break; - case 5: - state.cur_event.name = "gaschange"s; - state.cur_event.type = SAMPLE_EVENT_GASCHANGE2; - state.cur_event.value = ptr[7] << 8 ^ ptr[6]; - - for (const auto [i, cyl]: enumerated_range(state.cur_dive->cylinders)) { - if (cyl.gasmix.o2.permille == ptr[6] * 10 && cyl.gasmix.he.permille == ptr[7] * 10) { - found = true; - state.cur_event.gas.index = i; - break; - } - } - if (!found) { - cyl = cylinder_start(&state); - cyl->gasmix.o2.permille = ptr[6] * 10; - cyl->gasmix.he.permille = ptr[7] * 10; - cylinder_end(&state); - state.cur_event.gas.index = static_cast(state.cur_dive->cylinders.size()) - 1; - } - break; - case 6: - state.cur_event.name = "Start"s; - break; - case 7: - state.cur_event.name = "Too Fast"s; - break; - case 8: - state.cur_event.name = "Above Ceiling"s; - break; - case 9: - state.cur_event.name = "Toxic"s; - break; - case 10: - state.cur_event.name = "Hypox"s; - break; - case 11: - state.cur_event.name = "Critical"s; - break; - case 12: - state.cur_event.name = "Sensor Disabled"s; - break; - case 13: - state.cur_event.name = "Sensor Enabled"s; - break; - case 14: - state.cur_event.name = "O2 Backup"s; - break; - case 15: - state.cur_event.name = "Peer Down"s; - break; - case 16: - state.cur_event.name = "HS Down"s; - break; - case 17: - state.cur_event.name = "Inconsistent"s; - break; - case 18: - // key pressed - It should never get in here - // as we ingored it at the parent 'case 5'. - break; - case 19: - // obsolete - state.cur_event.name = "SCR"s; - break; - case 20: - state.cur_event.name = "Above Stop"s; - break; - case 21: - state.cur_event.name = "Safety Miss"s; - break; - case 22: - state.cur_event.name = "Fatal"s; - break; - case 23: - state.cur_event.name = "gaschange"s; - state.cur_event.type = SAMPLE_EVENT_GASCHANGE2; - state.cur_event.value = ptr[7] << 8 ^ ptr[6]; - event_end(&state); - break; - case 24: - state.cur_event.name = "gaschange"s; - state.cur_event.type = SAMPLE_EVENT_GASCHANGE2; - state.cur_event.value = ptr[7] << 8 ^ ptr[6]; - event_end(&state); - // This is both a mode change and a gas change event - // so we encode it as two separate events. - event_start(&state); - state.cur_event.name = "Change Mode"s; - switch (ptr[8]) { - case 1: - state.cur_event.name += ": OC"s; - break; - case 2: - state.cur_event.name += ": CCR"s; - break; - case 3: - state.cur_event.name += ": mCCR"s; - break; - case 4: - state.cur_event.name += ": Free"s; - break; - case 5: - state.cur_event.name += ": Gauge"s; - break; - case 6: - state.cur_event.name += ": ASCR"s; - break; - case 7: - state.cur_event.name += ": PSCR"s; - break; - default: - break; - } - event_end(&state); - break; - case 25: - // uint16_t solenoid_bitmap = (ptr[7] << 8) + (ptr[6] << 0); - // uint32_t time = (ptr[11] << 24) + (ptr[10] << 16) + (ptr[9] << 8) + (ptr[8] << 0); - state.cur_event.name = format_string_std("CCR O2 solenoid %s", ptr[12] ? "opened": "closed"); - break; - case 26: - state.cur_event.name = "User mark"s; - break; - case 27: - state.cur_event.name = format_string_std("%sGF Switch (%d/%d)", ptr[6] ? "Bailout, ": "", ptr[7], ptr[8]); - break; - case 28: - state.cur_event.name = "Peer Up"s; - break; - case 29: - state.cur_event.name = "HS Up"s; - break; - case 30: - state.cur_event.name = format_string_std("CNS %d%%", ptr[6]); - break; - default: - // No values above 30 had any description - break; - } - event_end(&state); - break; - case 6: - /* device configuration */ - switch (((ptr[3] & 0x7f) << 3) + ((ptr[2] & 0xe0) >> 5)) { - // Buffer to print extra string into - // Local variables to temporary decode into - struct tm tm; - const char *device; - const char *deep_stops; - case 0: // TEST_CCR_FULL_1 - utc_mkdate(parse_dlf_timestamp(ptr + 12), &tm); - add_extra_data(state.cur_dc, "TEST_CCR_FULL_1", - format_string_std("START=%04u-%02u-%02u %02u:%02u:%02u,TEST=%02X%02X%02X%02X,RESULT=%02X%02X%02X%02X", - tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ptr[7], ptr[6], ptr[5], ptr[4], ptr[11], ptr[10], ptr[9], ptr[8])); - break; - case 1: // TEST_CCR_PARTIAL_1 - utc_mkdate(parse_dlf_timestamp(ptr + 12), &tm); - add_extra_data(state.cur_dc, "TEST_CCR_PARTIAL_1", - format_string_std("START=%04u-%02u-%02u %02u:%02u:%02u,TEST=%02X%02X%02X%02X,RESULT=%02X%02X%02X%02X", - tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ptr[7], ptr[6], ptr[5], ptr[4], ptr[11], ptr[10], ptr[9], ptr[8])); - break; - case 2: // CFG_OXYGEN_CALIBRATION - utc_mkdate(parse_dlf_timestamp(ptr + 12), &tm); - o2_sensor_calibration_values[0] = (ptr[5] << 8) + ptr[4]; - o2_sensor_calibration_values[1] = (ptr[7] << 8) + ptr[6]; - o2_sensor_calibration_values[2] = (ptr[9] << 8) + ptr[8]; - o2_sensor_calibration_values[3] = (ptr[11] << 8) + ptr[10]; - add_extra_data(state.cur_dc, "CFG_OXYGEN_CALIBRATION", - format_string_std("%04u,%04u,%04u,%04u,TIME=%04u-%02u-%02u %02u:%02u:%02u", - o2_sensor_calibration_values[0], o2_sensor_calibration_values[1], o2_sensor_calibration_values[2], o2_sensor_calibration_values[3], tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec)); - break; - case 3: // CFG_SERIAL - add_extra_data(state.cur_dc, "CFG_SERIAL", - format_string_std("PRODUCT=%c%c%c%c,SERIAL=%c%c%c%c%c%c%c%c", - ptr[4], ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15])); - break; - case 4: // CFG_CONFIG_DECO - switch ((ptr[5] & 0xC0) >> 6) { - case 0: - deep_stops = "none"; - break; - case 1: - deep_stops = "Pyle"; - break; - case 2: - deep_stops = "Sladek"; - break; - default: - deep_stops = "unknown"; - break; - } - - add_extra_data(state.cur_dc, "CFG_CONFIG_DECO part 1", - format_string_std("%s,%s,%s,safety stop required=%s,last_stop=%s,deco_algorithm=%s,stop_rounding=%u,deep_stops=%s", - (ptr[4] & 0x80) ? "imperial" : "metric", (ptr[4] & 0x40) ? "sea" : "fresh", (ptr[4] & 0x30) ? "stops" : "ceiling", (ptr[4] & 0x10) ? "yes" : "no", (ptr[4] & 0x08) ? "6m" : "3m", (ptr[4] & 0x04) ? "VPM" : "Buhlmann+GF", (ptr[4] & 0x03) ? (ptr[4] & 0x03) * 30 : 1, deep_stops)); - add_extra_data(state.cur_dc, "CFG_CONFIG_DECO part 2", - format_string_std("deep_stop_len=%u min,gas_switch_len=%u min,gf_low=%u,gf_high=%u,gf_low_bailout=%u,gf_high_bailout=%u,ppO2_low=%4.2f,ppO2_high=%4.2f", - (ptr[5] & 0x38) >> 3, ptr[5] & 0x07, ptr[6], ptr[7], ptr[8], ptr[9], ptr[10] / 100.0f, ptr[11] / 100.0f)); - add_extra_data(state.cur_dc, "CFG_CONFIG_DECO part 3", - format_string_std("alarm_global=%u,alarm_cns=%u,alarm_ppO2=%u,alarm_ceiling=%u,alarm_stop_miss=%u,alarm_decentrate=%u,alarm_ascentrate=%u", - (ptr[12] & 0x80) >> 7, (ptr[12] & 0x40) >> 6, (ptr[12] & 0x20) >> 5, (ptr[12] & 0x10) >> 4, (ptr[12] & 0x08) >> 3, (ptr[12] & 0x04) >> 2, (ptr[12] & 0x02) >> 1)); - break; - case 5: // CFG_VERSION - switch (ptr[4]) { - case 0: - device = "FREEDOM"; - break; - case 1: - device = "LIBERTY_CU"; - break; - case 2: - device = "LIBERTY_HS"; - break; - default: - device = "UNKNOWN"; - break; - } - add_extra_data(state.cur_dc, "CFG_VERSION", - format_string_std("DEVICE=%s,HW=%d.%d,FW=%d.%d.%d.%d,FLAGS=%04X", - device, ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], (ptr[15] << 24) + (ptr[14] << 16) + (ptr[13] << 8) + (ptr[12]), (ptr[11] << 8) + ptr[10])); - break; - } - break; - case 7: - /* measure record */ - switch (ptr[2] >> 5) { - case 1: - /* Record starting battery level */ - if (!battery_start.volt1 && !battery_start.volt2) { - battery_start.volt1 = (ptr[5] << 8) + ptr[4]; - battery_start.percent1 = ptr[6]; - battery_start.volt2 = (ptr[9] << 8) + ptr[8]; - battery_start.percent2 = ptr[10]; - } - - /* Measure Battery, recording the last reading only */ - battery_end.volt1 = (ptr[5] << 8) + ptr[4]; - battery_end.percent1 = ptr[6]; - battery_end.volt2 = (ptr[9] << 8) + ptr[8]; - battery_end.percent2 = ptr[10]; - break; - case 2: - /* Measure He */ - //report_info("%ds he2 cells(0.01 mV): %d %d", time, (ptr[5] << 8) + ptr[4], (ptr[9] << 8) + ptr[8]); - break; - case 3: - /* Measure Oxygen */ - //report_info("%d s: o2 cells(0.01 mV): %d %d %d %d", time, (ptr[5] << 8) + ptr[4], (ptr[7] << 8) + ptr[6], (ptr[9] << 8) + ptr[8], (ptr[11] << 8) + ptr[10]); - // [Pa/mV] coeficient O2 - // 100 Pa == 1 mbar - sample_start(&state); - state.cur_sample->time.seconds = time; - state.cur_sample->o2sensor[0].mbar = ( ((ptr[5] << 8) + ptr[4]) * o2_sensor_calibration_values[0]) / 10000; - state.cur_sample->o2sensor[1].mbar = ( ((ptr[7] << 8) + ptr[6]) * o2_sensor_calibration_values[1]) / 10000; - state.cur_sample->o2sensor[2].mbar = ( ((ptr[9] << 8) + ptr[8]) * o2_sensor_calibration_values[2]) / 10000; - state.cur_sample->o2sensor[3].mbar = ( ((ptr[11] << 8) + ptr[10]) * o2_sensor_calibration_values[3]) / 10000; - sample_end(&state); - break; - case 4: - /* Measure GPS */ - state.cur_location.lat.udeg = (int)((ptr[7] << 24) + (ptr[6] << 16) + (ptr[5] << 8) + (ptr[4] << 0)); - state.cur_location.lon.udeg = (int)((ptr[11] << 24) + (ptr[10] << 16) + (ptr[9] << 8) + (ptr[8] << 0)); - state.log->sites.create("DLF imported"s, state.cur_location)->add_dive(state.cur_dive.get()); - break; - default: - break; - } - break; - case 8: - /* Deco event */ - break; - default: - /* Unknown... */ - break; - } - } - - /* Recording the starting battery status to extra data */ - if (battery_start.volt1) { - std::string str = format_string_std("%dmV (%d%%)", battery_start.volt1, battery_start.percent1); - add_extra_data(state.cur_dc, "Battery 1 (start)", str.c_str()); - - str = format_string_std("%dmV (%d%%)", battery_start.volt2, battery_start.percent2); - add_extra_data(state.cur_dc, "Battery 2 (start)", str.c_str()); - } - - /* Recording the ending battery status to extra data */ - if (battery_end.volt1) { - std::string str = format_string_std("%dmV (%d%%)", battery_end.volt1, battery_end.percent1); - add_extra_data(state.cur_dc, "Battery 1 (end)", str.c_str()); - - str = format_string_std("%dmV (%d%%)", battery_end.volt2, battery_end.percent2); - add_extra_data(state.cur_dc, "Battery 2 (end)", str.c_str()); - } - - divecomputer_end(&state); - dive_end(&state); - return 0; -} - - void parse_xml_init() { LIBXML_TEST_VERSION diff --git a/core/parse.h b/core/parse.h index 4420088b1..44a954dd6 100644 --- a/core/parse.h +++ b/core/parse.h @@ -150,7 +150,6 @@ int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *buf, i int parse_shearwater_cloud_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct divelog *log); int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct divelog *log); int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct divelog *log); -int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log); std::string trimspace(const char *buffer); #endif