void MainWindow::on_actionAddDive_triggered() { if (DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING || ui.InfoWidget->isEditing()) { QMessageBox::warning(this, tr("Warning"), tr("Please save or cancel the current dive edit before trying to add a dive.")); return; } dive_list()->rememberSelection(); dive_list()->unselectDives(); disableDcShortcuts(); DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD); // now cheat - create one dive that we use to store the info tab data in struct dive *dive = alloc_dive(); dive->when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset(); dive->dc.model = "manually added dive"; // don't translate! this is stored in the XML file dive->latitude.udeg = 0; dive->longitude.udeg = 0; record_dive(dive); // this isn't in the UI yet, so let's call the C helper function - we'll fix this up when // accepting the dive select_dive(get_divenr(dive)); ui.InfoWidget->setCurrentIndex(0); ui.InfoWidget->updateDiveInfo(selected_dive); ui.InfoWidget->addDiveStarted(); ui.stackedWidget->setCurrentIndex(PLANNERPROFILE); // Planner. ui.infoPane->setCurrentIndex(MAINTAB); DivePlannerPointsModel::instance()->clear(); DivePlannerPointsModel::instance()->createSimpleDive(); ui.ListWidget->reload(DiveTripModel::CURRENT); }
void UndoDeleteDive::redo() { QList<struct dive*> newList; for (int i = 0; i < diveList.count(); i++) { // make a copy of the dive before deleting it struct dive* d = alloc_dive(); copy_dive(diveList.at(i), d); newList.append(d); // check for trip - if this is the last dive in the trip // the trip will get deleted, so we need to remember it as well if (d->divetrip && d->divetrip->nrdives == 1) { struct dive_trip *undo_trip = (struct dive_trip *)calloc(1, sizeof(struct dive_trip)); *undo_trip = *d->divetrip; undo_trip->location = copy_string(d->divetrip->location); undo_trip->notes = copy_string(d->divetrip->notes); undo_trip->nrdives = 0; undo_trip->next = NULL; undo_trip->dives = NULL; // update all the dives who were in this trip to point to the copy of the // trip that we are about to delete implicitly when deleting its last dive below Q_FOREACH(struct dive *inner_dive, newList) if (inner_dive->divetrip == d->divetrip) inner_dive->divetrip = undo_trip; d->divetrip = undo_trip; tripList.append(undo_trip); } //delete the dive delete_single_dive(get_divenr(diveList.at(i))); } mark_divelist_changed(true); MainWindow::instance()->refreshDisplay(); diveList.clear(); diveList = newList; }
void QMLManager::deleteDive(int id) { struct dive *d = get_dive_by_uniq_id(id); if (!d) { qDebug() << "oops, trying to delete non-existing dive"; return; } // clean up (or create) the storage for the deleted dive and trip (if applicable) if (!deletedDive) deletedDive = alloc_dive(); else clear_dive(deletedDive); copy_dive(d, deletedDive); if (!deletedTrip) { deletedTrip = (struct dive_trip *)calloc(1, sizeof(struct dive_trip)); } else { free(deletedTrip->location); free(deletedTrip->notes); memset(deletedTrip, 0, sizeof(struct dive_trip)); } // if this is the last dive in that trip, remember the trip as well if (d->divetrip && d->divetrip->nrdives == 1) { *deletedTrip = *d->divetrip; deletedTrip->location = copy_string(d->divetrip->location); deletedTrip->notes = copy_string(d->divetrip->notes); deletedTrip->nrdives = 0; deletedDive->divetrip = deletedTrip; } DiveListModel::instance()->removeDiveById(id); delete_single_dive(get_idx_by_uniq_id(id)); changesNeedSaving(); }
static struct dive *uemis_start_dive(uint32_t deviceid) { struct dive *dive = alloc_dive(); dive->downloaded = true; dive->dc.model = strdup("Uemis Zurich"); dive->dc.deviceid = deviceid; return dive; }
void DivePlannerPointsModel::createPlan(bool replanCopy) { // Ok, so, here the diveplan creates a dive struct deco_state *cache = NULL; bool oldRecalc = setRecalc(false); removeDeco(); createTemporaryPlan(); setRecalc(oldRecalc); //TODO: C-based function here? struct decostop stoptable[60]; plan(&diveplan, &displayed_dive, DECOTIMESTEP, stoptable, &cache, isPlanner(), true); free(cache); if (!current_dive || displayed_dive.id != current_dive->id) { // we were planning a new dive, not re-planning an existing on record_dive(clone_dive(&displayed_dive)); } else if (current_dive && displayed_dive.id == current_dive->id) { // we are replanning a dive - make sure changes are reflected // correctly in the dive structure and copy it back into the dive table displayed_dive.maxdepth.mm = 0; displayed_dive.dc.maxdepth.mm = 0; fixup_dive(&displayed_dive); // Try to identify old planner output and remove only this part // Treat user provided text as plain text. QTextDocument notesDocument; notesDocument.setHtml(current_dive->notes); QString oldnotes(notesDocument.toPlainText()); int disclaimerPosition = oldnotes.indexOf(disclaimer); if (disclaimerPosition == 0) oldnotes.clear(); else if (disclaimerPosition >= 1) oldnotes.truncate(disclaimerPosition-1); // Deal with line breaks oldnotes.replace("\n", "<br>"); oldnotes.append(displayed_dive.notes); displayed_dive.notes = strdup(oldnotes.toUtf8().data()); // If we save as new create a copy of the dive here if (replanCopy) { struct dive *copy = alloc_dive(); copy_dive(current_dive, copy); copy->id = 0; copy->selected = false; copy->divetrip = NULL; if (current_dive->divetrip) add_dive_to_trip(copy, current_dive->divetrip); record_dive(copy); } copy_dive(&displayed_dive, current_dive); } mark_divelist_changed(true); // Remove and clean the diveplan, so we don't delete // the dive by mistake. free_dps(&diveplan); setPlanMode(NOTHING); planCreated(); }
void DivePlannerPointsModel::computeVariations() { bool oldRecalc = setRecalc(false); struct dive *dive = alloc_dive(); copy_dive(&displayed_dive, dive); struct decostop original[60], deeper[60], shallower[60], shorter[60], longer[60]; struct deco_state *cache = NULL, *save = NULL; struct diveplan plan_copy; struct divedatapoint *last_segment; if(in_planner() && prefs.display_variations) { cache_deco_state(&save); cloneDiveplan(&plan_copy); plan(&plan_copy, dive, 1, original, &cache, true, false); free_dps(&plan_copy); restore_deco_state(save, false); last_segment = cloneDiveplan(&plan_copy); last_segment->depth.mm += 1000; last_segment->next->depth.mm += 1000; plan(&plan_copy, dive, 1, deeper, &cache, true, false); free_dps(&plan_copy); restore_deco_state(save, false); last_segment = cloneDiveplan(&plan_copy); last_segment->depth.mm -= 1000; last_segment->next->depth.mm -= 1000; plan(&plan_copy, dive, 1, shallower, &cache, true, false); free_dps(&plan_copy); restore_deco_state(save, false); last_segment = cloneDiveplan(&plan_copy); last_segment->next->time += 60; plan(&plan_copy, dive, 1, longer, &cache, true, false); free_dps(&plan_copy); restore_deco_state(save, false); last_segment = cloneDiveplan(&plan_copy); last_segment->next->time -= 60; plan(&plan_copy, dive, 1, shorter, &cache, true, false); free_dps(&plan_copy); restore_deco_state(save, false); #ifdef SHOWSTOPVARIATIONS printf("\n\n"); #endif QString notes(displayed_dive.notes); free(displayed_dive.notes); char buf[200]; sprintf(buf, "+ %d:%02d /m + %d:%02d /min", FRACTION(analyzeVariations(shallower, original, deeper, "m"),60), FRACTION(analyzeVariations(shorter, original, longer, "min"), 60)); displayed_dive.notes = strdup(notes.replace("VARIATIONS", QString(buf)).toUtf8().data()); } setRecalc(oldRecalc); }
static struct dive *create_new_dive(timestamp_t when) { struct dive *dive = alloc_dive(); /* We'll fill in more data from the dive file */ dive->when = when; if (active_trip) add_dive_to_trip(dive, active_trip); return dive; }
static void dive_start(void) { if (cur_dive) return; cur_dive = alloc_dive(); reset_dc_info(&cur_dive->dc); memset(&cur_tm, 0, sizeof(cur_tm)); if (cur_trip) { add_dive_to_trip(cur_dive, cur_trip); cur_dive->tripflag = IN_TRIP; } }
void MainWindow::on_actionAddDive_triggered() { struct dive *dive; dive = alloc_dive(); record_dive(dive); process_dives(FALSE, FALSE); ui->InfoWidget->reload(); ui->globe->reload(); ui->ListWidget->reload(DiveTripModel::TREE); ui->ListWidget->setFocus(); }
int try_to_open_csv(struct memblock *mem, enum csv_format type) { char *p = mem->buffer; char *header[8]; int i, time; timestamp_t date; struct dive *dive; struct divecomputer *dc; for (i = 0; i < 8; i++) { header[i] = p; p = strchr(p, ','); if (!p) return 0; p++; } date = parse_date(header[2]); if (!date) return 0; dive = alloc_dive(); dive->when = date; dive->number = atoi(header[1]); dc = &dive->dc; time = 0; for (;;) { char *end; double val; struct sample *sample; errno = 0; val = strtod(p, &end); // FIXME == localization issue if (end == p) break; if (errno) break; sample = prepare_sample(dc); sample->time.seconds = time; add_sample_data(sample, type, val); finish_sample(dc); time++; dc->duration.seconds = time; if (*end != ',') break; p = end + 1; } record_dive(dive); return 1; }
// create a new dive. set the current time and add it to the end of the dive list QString DiveListModel::startAddDive() { struct dive *d; d = alloc_dive(); d->when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset(); struct dive *pd = get_dive(dive_table.nr - 1); int nr = 1; if (pd && pd->number > 0) nr = pd->number + 1; d->number = nr; d->dc.model = strdup("manually added dive"); add_single_dive(-1, d); addDive(d); return QString::number(d->id); }
void UndoDeleteDive::redo() { QList<struct dive*> newList; for (int i = 0; i < diveList.count(); i++) { //make a copy of the dive before deleting it struct dive* d = alloc_dive(); copy_dive(diveList.at(i), d); newList.append(d); //delete the dive delete_single_dive(get_divenr(diveList.at(i))); } mark_divelist_changed(true); MainWindow::instance()->refreshDisplay(); diveList.clear(); diveList = newList; }
void DivePlannerPointsModel::createPlan(bool replanCopy) { // Ok, so, here the diveplan creates a dive char *cache = NULL; bool oldRecalc = setRecalc(false); removeDeco(); createTemporaryPlan(); setRecalc(oldRecalc); //TODO: C-based function here? bool did_deco = plan(&diveplan, &cache, isPlanner(), true); free(cache); if (!current_dive || displayed_dive.id != current_dive->id) { // we were planning a new dive, not re-planning an existing on record_dive(clone_dive(&displayed_dive)); } else if (current_dive && displayed_dive.id == current_dive->id) { // we are replanning a dive - make sure changes are reflected // correctly in the dive structure and copy it back into the dive table displayed_dive.maxdepth.mm = 0; displayed_dive.dc.maxdepth.mm = 0; fixup_dive(&displayed_dive); if (replanCopy) { struct dive *copy = alloc_dive(); copy_dive(current_dive, copy); copy->id = 0; copy->selected = false; copy->divetrip = NULL; if (current_dive->divetrip) add_dive_to_trip(copy, current_dive->divetrip); record_dive(copy); QString oldnotes(current_dive->notes); if (oldnotes.indexOf(QString(disclaimer)) >= 0) oldnotes.truncate(oldnotes.indexOf(QString(disclaimer))); if (did_deco) oldnotes.append(displayed_dive.notes); displayed_dive.notes = strdup(oldnotes.toUtf8().data()); } copy_dive(&displayed_dive, current_dive); } mark_divelist_changed(true); // Remove and clean the diveplan, so we don't delete // the dive by mistake. free_dps(&diveplan); setPlanMode(NOTHING); planCreated(); }
void MainWindow::on_actionAddDive_triggered() { // clear the selection for (int i = 0; i < dive_table.nr; i++) { struct dive *d = get_dive(i); if (d && d->selected) deselect_dive(i); } disableDcShortcuts(); DivePlannerPointsModel::instance()->setPlanMode(false); // now cheat - create one dive that we use to store the info tab data in struct dive *dive = alloc_dive(); dive->when = QDateTime::currentMSecsSinceEpoch() / 1000L; const char* model = strdup(tr("manually added dive").toLocal8Bit().constData()); dive->dc.model = model; // do not use tr here since it expects a char*. record_dive(dive); select_dive(get_divenr(dive)); ui.InfoWidget->updateDiveInfo(selected_dive); ui.stackedWidget->setCurrentIndex(PLANNERPROFILE); // Planner. ui.infoPane->setCurrentIndex(MAINTAB); DivePlannerPointsModel::instance()->createSimpleDive(); refreshDisplay(); ui.InfoWidget->addDiveStarted(); }
int parse_txt_file(const char *filename, const char *csv) { struct memblock memtxt, memcsv; if (readfile(filename, &memtxt) < 0) { return report_error(translate("gettextFromC", "Failed to read '%s'"), filename); } /* * MkVI stores some information in .txt file but the whole profile and events are stored in .csv file. First * make sure the input .txt looks like proper MkVI file, then start parsing the .csv. */ if (MATCH(memtxt.buffer, "MkVI_Config") == 0) { int d, m, y, he; int hh = 0, mm = 0, ss = 0; int prev_depth = 0, cur_sampletime = 0, prev_setpoint = -1, prev_ndl = -1; bool has_depth = false, has_setpoint = false, has_ndl = false; char *lineptr, *key, *value; int cur_cylinder_index = 0; unsigned int prev_time = 0; struct dive *dive; struct divecomputer *dc; struct tm cur_tm; value = parse_mkvi_value(memtxt.buffer, "Dive started at"); if (sscanf(value, "%d-%d-%d %d:%d:%d", &y, &m, &d, &hh, &mm, &ss) != 6) { free(value); return -1; } free(value); cur_tm.tm_year = y; cur_tm.tm_mon = m - 1; cur_tm.tm_mday = d; cur_tm.tm_hour = hh; cur_tm.tm_min = mm; cur_tm.tm_sec = ss; dive = alloc_dive(); dive->when = utc_mktime(&cur_tm);; dive->dc.model = strdup("Poseidon MkVI Discovery"); value = parse_mkvi_value(memtxt.buffer, "Rig Serial number"); dive->dc.deviceid = atoi(value); free(value); dive->dc.divemode = CCR; dive->dc.no_o2sensors = 2; dive->cylinder[cur_cylinder_index].cylinder_use = OXYGEN; dive->cylinder[cur_cylinder_index].type.size.mliter = 3000; dive->cylinder[cur_cylinder_index].type.workingpressure.mbar = 200000; dive->cylinder[cur_cylinder_index].type.description = strdup("3l Mk6"); dive->cylinder[cur_cylinder_index].gasmix.o2.permille = 1000; cur_cylinder_index++; dive->cylinder[cur_cylinder_index].cylinder_use = DILUENT; dive->cylinder[cur_cylinder_index].type.size.mliter = 3000; dive->cylinder[cur_cylinder_index].type.workingpressure.mbar = 200000; dive->cylinder[cur_cylinder_index].type.description = strdup("3l Mk6"); value = parse_mkvi_value(memtxt.buffer, "Helium percentage"); he = atoi(value); free(value); value = parse_mkvi_value(memtxt.buffer, "Nitrogen percentage"); dive->cylinder[cur_cylinder_index].gasmix.o2.permille = (100 - atoi(value) - he) * 10; free(value); dive->cylinder[cur_cylinder_index].gasmix.he.permille = he * 10; cur_cylinder_index++; lineptr = strstr(memtxt.buffer, "Dive started at"); while (!empty_string(lineptr) && (lineptr = strchr(lineptr, '\n'))) { ++lineptr; // Skip over '\n' key = next_mkvi_key(lineptr); if (!key) break; value = parse_mkvi_value(lineptr, key); if (!value) { free(key); break; } add_extra_data(&dive->dc, key, value); free(key); free(value); } dc = &dive->dc; /* * Read samples from the CSV file. A sample contains all the lines with same timestamp. The CSV file has * the following format: * * timestamp, type, value * * And following fields are of interest to us: * * 6 sensor1 * 7 sensor2 * 8 depth * 13 o2 tank pressure * 14 diluent tank pressure * 20 o2 setpoint * 39 water temp */ if (readfile(csv, &memcsv) < 0) { free(dive); return report_error(translate("gettextFromC", "Poseidon import failed: unable to read '%s'"), csv); } lineptr = memcsv.buffer; for (;;) { struct sample *sample; int type; int value; int sampletime; int gaschange = 0; /* Collect all the information for one sample */ sscanf(lineptr, "%d,%d,%d", &cur_sampletime, &type, &value); has_depth = false; has_setpoint = false; has_ndl = false; sample = prepare_sample(dc); /* * There was a bug in MKVI download tool that resulted in erroneous sample * times. This fix should work similarly as the vendor's own. */ sample->time.seconds = cur_sampletime < 0xFFFF * 3 / 4 ? cur_sampletime : prev_time; prev_time = sample->time.seconds; do { int i = sscanf(lineptr, "%d,%d,%d", &sampletime, &type, &value); switch (i) { case 3: switch (type) { case 0: //Mouth piece position event: 0=OC, 1=CC, 2=UN, 3=NC switch (value) { case 0: add_event(dc, cur_sampletime, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position OC")); break; case 1: add_event(dc, cur_sampletime, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position CC")); break; case 2: add_event(dc, cur_sampletime, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position unknown")); break; case 3: add_event(dc, cur_sampletime, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position not connected")); break; } break; case 3: //Power Off event add_event(dc, cur_sampletime, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Power off")); break; case 4: //Battery State of Charge in % #ifdef SAMPLE_EVENT_BATTERY add_event(dc, cur_sampletime, SAMPLE_EVENT_BATTERY, 0, value, QT_TRANSLATE_NOOP("gettextFromC", "battery")); #endif break; case 6: //PO2 Cell 1 Average add_sample_data(sample, POSEIDON_SENSOR1, value); break; case 7: //PO2 Cell 2 Average add_sample_data(sample, POSEIDON_SENSOR2, value); break; case 8: //Depth * 2 has_depth = true; prev_depth = value; add_sample_data(sample, POSEIDON_DEPTH, value); break; //9 Max Depth * 2 //10 Ascent/Descent Rate * 2 case 11: //Ascent Rate Alert >10 m/s add_event(dc, cur_sampletime, SAMPLE_EVENT_ASCENT, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "ascent")); break; case 13: //O2 Tank Pressure add_sample_pressure(sample, 0, lrint(value * 1000)); break; case 14: //Diluent Tank Pressure add_sample_pressure(sample, 1, lrint(value * 1000)); break; //16 Remaining dive time #1? //17 related to O2 injection case 20: //PO2 Setpoint has_setpoint = true; prev_setpoint = value; add_sample_data(sample, POSEIDON_SETPOINT, value); break; case 22: //End of O2 calibration Event: 0 = OK, 2 = Failed, rest of dive setpoint 1.0 if (value == 2) add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_END, 0, QT_TRANSLATE_NOOP("gettextFromC", "O₂ calibration failed")); add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_END, 0, QT_TRANSLATE_NOOP("gettextFromC", "O₂ calibration")); break; case 25: //25 Max Ascent depth add_sample_data(sample, POSEIDON_CEILING, value); break; case 31: //Start of O2 calibration Event add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_BEGIN, 0, QT_TRANSLATE_NOOP("gettextFromC", "O₂ calibration")); break; case 37: //Remaining dive time #2? has_ndl = true; prev_ndl = value; add_sample_data(sample, POSEIDON_NDL, value); break; case 39: // Water Temperature in Celcius add_sample_data(sample, POSEIDON_TEMP, value); break; case 85: //He diluent part in % gaschange += value << 16; break; case 86: //O2 diluent part in % gaschange += value; break; //239 Unknown, maybe PO2 at sensor validation? //240 Unknown, maybe PO2 at sensor validation? //247 Unknown, maybe PO2 Cell 1 during pressure test //248 Unknown, maybe PO2 Cell 2 during pressure test //250 PO2 Cell 1 //251 PO2 Cell 2 default: break; } /* sample types */ break; case EOF: break; default: printf("Unable to parse input: %s\n", lineptr); break; } lineptr = strchr(lineptr, '\n'); if (!lineptr || !*lineptr) break; lineptr++; /* Grabbing next sample time */ sscanf(lineptr, "%d,%d,%d", &cur_sampletime, &type, &value); } while (sampletime == cur_sampletime); if (gaschange) add_event(dc, cur_sampletime, SAMPLE_EVENT_GASCHANGE2, 0, gaschange, QT_TRANSLATE_NOOP("gettextFromC", "gaschange")); if (!has_depth) add_sample_data(sample, POSEIDON_DEPTH, prev_depth); if (!has_setpoint && prev_setpoint >= 0) add_sample_data(sample, POSEIDON_SETPOINT, prev_setpoint); if (!has_ndl && prev_ndl >= 0) add_sample_data(sample, POSEIDON_NDL, prev_ndl); finish_sample(dc); if (!lineptr || !*lineptr) break; } record_dive(dive); return 1; } else { return 0; } return 0; }
static void cochran_parse_dive(const unsigned char *decode, unsigned mod, const unsigned char *in, unsigned size, struct dive_table *table) { unsigned char *buf = malloc(size); struct dive *dive; struct divecomputer *dc; struct tm tm = {0}; uint32_t csum[5]; double max_depth, avg_depth, min_temp; unsigned int duration = 0, corrupt_dive = 0; /* * The scrambling has odd boundaries. I think the boundaries * match some data structure size, but I don't know. They were * discovered the same way we dynamically discover the decode * size: automatically looking for least random output. * * The boundaries are also this confused "off-by-one" thing, * the same way the file size is off by one. It's as if the * cochran software forgot to write one byte at the beginning. */ partial_decode(0, 0x0fff, decode, 1, mod, in, size, buf); partial_decode(0x0fff, 0x1fff, decode, 0, mod, in, size, buf); partial_decode(0x1fff, 0x2fff, decode, 0, mod, in, size, buf); partial_decode(0x2fff, 0x48ff, decode, 0, mod, in, size, buf); /* * This is not all the descrambling you need - the above are just * what appears to be the fixed-size blocks. The rest is also * scrambled, but there seems to be size differences in the data, * so this just descrambles part of it: */ if (size < 0x4914 + config.logbook_size) { // Analyst calls this a "Corrupt Beginning Summary" free(buf); return; } // Decode log entry (512 bytes + random prefix) partial_decode(0x48ff, 0x4914 + config.logbook_size, decode, 0, mod, in, size, buf); unsigned int sample_size = size - 0x4914 - config.logbook_size; int g; unsigned int sample_pre_offset = 0, sample_end_offset = 0; // Decode sample data partial_decode(0x4914 + config.logbook_size, size, decode, 0, mod, in, size, buf); #ifdef COCHRAN_DEBUG // Display pre-logbook data puts("\nPre Logbook Data\n"); cochran_debug_write(buf, 0x4914); // Display log book puts("\nLogbook Data\n"); cochran_debug_write(buf + 0x4914, config.logbook_size + 0x400); // Display sample data puts("\nSample Data\n"); #endif dive = alloc_dive(); dc = &dive->dc; unsigned char *log = (buf + 0x4914); switch (config.type) { case TYPE_GEMINI: case TYPE_COMMANDER: if (config.type == TYPE_GEMINI) { dc->model = "Gemini"; dc->deviceid = buf[0x18c] * 256 + buf[0x18d]; // serial no fill_default_cylinder(&dive->cylinder[0]); dive->cylinder[0].gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256 + log[CMD_O2_PERCENT + 1]) * 10; dive->cylinder[0].gasmix.he.permille = 0; } else { dc->model = "Commander"; dc->deviceid = array_uint32_le(buf + 0x31e); // serial no for (g = 0; g < 2; g++) { fill_default_cylinder(&dive->cylinder[g]); dive->cylinder[g].gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256 + log[CMD_O2_PERCENT + g * 2 + 1]) * 10; dive->cylinder[g].gasmix.he.permille = 0; } } tm.tm_year = log[CMD_YEAR]; tm.tm_mon = log[CMD_MON] - 1; tm.tm_mday = log[CMD_DAY]; tm.tm_hour = log[CMD_HOUR]; tm.tm_min = log[CMD_MIN]; tm.tm_sec = log[CMD_SEC]; tm.tm_isdst = -1; dive->when = dc->when = utc_mktime(&tm); dive->number = log[CMD_NUMBER] + log[CMD_NUMBER + 1] * 256 + 1; dc->duration.seconds = (log[CMD_BT] + log[CMD_BT + 1] * 256) * 60; dc->surfacetime.seconds = (log[CMD_SIT] + log[CMD_SIT + 1] * 256) * 60; dc->maxdepth.mm = lrint((log[CMD_MAX_DEPTH] + log[CMD_MAX_DEPTH + 1] * 256) / 4 * FEET * 1000); dc->meandepth.mm = lrint((log[CMD_AVG_DEPTH] + log[CMD_AVG_DEPTH + 1] * 256) / 4 * FEET * 1000); dc->watertemp.mkelvin = C_to_mkelvin((log[CMD_MIN_TEMP] / 32) - 1.8); dc->surface_pressure.mbar = lrint(ATM / BAR * pow(1 - 0.0000225577 * (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588) * 1000); dc->salinity = 10000 + 150 * log[CMD_WATER_CONDUCTIVITY]; SHA1(log + CMD_NUMBER, 2, (unsigned char *)csum); dc->diveid = csum[0]; if (log[CMD_MAX_DEPTH] == 0xff && log[CMD_MAX_DEPTH + 1] == 0xff) corrupt_dive = 1; sample_pre_offset = array_uint32_le(log + CMD_PREDIVE_OFFSET); sample_end_offset = array_uint32_le(log + CMD_END_OFFSET); break; case TYPE_EMC: dc->model = "EMC"; dc->deviceid = array_uint32_le(buf + 0x31e); // serial no for (g = 0; g < 4; g++) { fill_default_cylinder(&dive->cylinder[g]); dive->cylinder[g].gasmix.o2.permille = (log[EMC_O2_PERCENT + g * 2] / 256 + log[EMC_O2_PERCENT + g * 2 + 1]) * 10; dive->cylinder[g].gasmix.he.permille = (log[EMC_HE_PERCENT + g * 2] / 256 + log[EMC_HE_PERCENT + g * 2 + 1]) * 10; } tm.tm_year = log[EMC_YEAR]; tm.tm_mon = log[EMC_MON] - 1; tm.tm_mday = log[EMC_DAY]; tm.tm_hour = log[EMC_HOUR]; tm.tm_min = log[EMC_MIN]; tm.tm_sec = log[EMC_SEC]; tm.tm_isdst = -1; dive->when = dc->when = utc_mktime(&tm); dive->number = log[EMC_NUMBER] + log[EMC_NUMBER + 1] * 256 + 1; dc->duration.seconds = (log[EMC_BT] + log[EMC_BT + 1] * 256) * 60; dc->surfacetime.seconds = (log[EMC_SIT] + log[EMC_SIT + 1] * 256) * 60; dc->maxdepth.mm = lrint((log[EMC_MAX_DEPTH] + log[EMC_MAX_DEPTH + 1] * 256) / 4 * FEET * 1000); dc->meandepth.mm = lrint((log[EMC_AVG_DEPTH] + log[EMC_AVG_DEPTH + 1] * 256) / 4 * FEET * 1000); dc->watertemp.mkelvin = C_to_mkelvin((log[EMC_MIN_TEMP] - 32) / 1.8); dc->surface_pressure.mbar = lrint(ATM / BAR * pow(1 - 0.0000225577 * (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588) * 1000); dc->salinity = 10000 + 150 * (log[EMC_WATER_CONDUCTIVITY] & 0x3); SHA1(log + EMC_NUMBER, 2, (unsigned char *)csum); dc->diveid = csum[0]; if (log[EMC_MAX_DEPTH] == 0xff && log[EMC_MAX_DEPTH + 1] == 0xff) corrupt_dive = 1; sample_pre_offset = array_uint32_le(log + EMC_PREDIVE_OFFSET); sample_end_offset = array_uint32_le(log + EMC_END_OFFSET); break; } // Use the log information to determine actual profile sample size // Otherwise we will get surface time at end of dive. if (sample_pre_offset < sample_end_offset && sample_end_offset != 0xffffffff) sample_size = sample_end_offset - sample_pre_offset; cochran_parse_samples(dive, buf + 0x4914, buf + 0x4914 + config.logbook_size, sample_size, &duration, &max_depth, &avg_depth, &min_temp); // Check for corrupt dive if (corrupt_dive) { dc->maxdepth.mm = lrint(max_depth * FEET * 1000); dc->meandepth.mm = lrint(avg_depth * FEET * 1000); dc->watertemp.mkelvin = C_to_mkelvin((min_temp - 32) / 1.8); dc->duration.seconds = duration; } record_dive_to_table(dive, table); mark_divelist_changed(true); free(buf); }
/* * OSTCTools stores the raw dive data in heavily padded files, one dive * each file. So it's not necesary 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 dive_table *divetable) { FILE *archive; device_data_t *devdata = calloc(1, sizeof(device_data_t)); dc_family_t dc_fam; unsigned char *buffer = calloc(65536, 1), *tmp; struct dive *ostcdive = alloc_dive(); dc_status_t rc = 0; int model = 0, i = 0; unsigned int serial; struct extra_data *ptr; // Open the archive if ((archive = subsurface_fopen(file, "rb")) == NULL) { report_error(translate("gettextFromC", "Error: couldn't open the file")); return; } // Read dive number from the log tmp = calloc(2,1); fseek(archive, 258, 0); fread(tmp, 1, 2, archive); ostcdive->number = tmp[0] + (tmp[1] << 8); free(tmp); // Read device's serial number tmp = calloc(2, 1); fseek(archive, 265, 0); fread(tmp, 1, 2, archive); serial = tmp[0] + (tmp[1] << 8); free(tmp); // Read dive's raw data, header + profile fseek(archive, 456, 0); while (!feof(archive)) { fread(buffer+i, 1, 1, archive); if (buffer[i] == 0xFD && buffer[i-1] == 0xFD) break; i++; } // Try to determine the dc family based on the header type switch (buffer[2]) { case 0x20: case 0x21: dc_fam = DC_FAMILY_HW_OSTC; break; case 0x22: dc_fam = DC_FAMILY_HW_FROG; break; case 0x23: dc_fam = DC_FAMILY_HW_OSTC3; break; } // Prepare data to pass to libdivecomputer. OSTC protocol doesn't include // a model number so will use 0. ostc_prepare_data(model, dc_fam, devdata); tmp = calloc(strlen(devdata->vendor)+strlen(devdata->model)+28,1); sprintf(tmp,"%s %s (Imported from OSTCTools)", devdata->vendor, devdata->model); ostcdive->dc.model = copy_string(tmp); free(tmp); // Parse the dive data rc = libdc_buffer_parser(ostcdive, devdata, buffer, i+1); if (rc != DC_STATUS_SUCCESS) report_error("Libdc returned error -%s- for 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, remove // it from the list and add again. tmp = calloc(12,1); sprintf(tmp, "%d", serial); ostcdive->dc.serial = copy_string(tmp); free(tmp); ptr = ostcdive->dc.extra_data; while (strcmp(ptr->key, "Serial")) ptr = ptr->next; if (!strcmp(ptr->value, "0")) { add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial); *ptr = *(ptr)->next; } free(devdata); free(buffer); record_dive_to_table(ostcdive, divetable); mark_divelist_changed(true); sort_table(divetable); fclose(archive); }
/* * 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 dive_table *divetable) { FILE *archive; device_data_t *devdata = calloc(1, sizeof(device_data_t)); dc_family_t dc_fam; unsigned char *buffer = calloc(65536, 1), *uc_tmp; char *tmp; struct dive *ostcdive = alloc_dive(); dc_status_t rc = 0; int model, ret, i = 0; unsigned int serial; struct extra_data *ptr; // Open the archive if ((archive = subsurface_fopen(file, "rb")) == NULL) { report_error(translate("gettextFromC", "Failed to read '%s'"), file); free(ostcdive); goto out; } // Read dive number from the log uc_tmp = calloc(2, 1); fseek(archive, 258, 0); fread(uc_tmp, 1, 2, archive); ostcdive->number = uc_tmp[0] + (uc_tmp[1] << 8); free(uc_tmp); // Read device's serial number uc_tmp = calloc(2, 1); fseek(archive, 265, 0); fread(uc_tmp, 1, 2, archive); serial = uc_tmp[0] + (uc_tmp[1] << 8); free(uc_tmp); // Read dive's raw data, header + profile fseek(archive, 456, 0); while (!feof(archive)) { fread(buffer + i, 1, 1, archive); if (buffer[i] == 0xFD && buffer[i - 1] == 0xFD) break; i++; } // Try to determine the dc family based on the header type if (buffer[2] == 0x20 || buffer[2] == 0x21) { dc_fam = DC_FAMILY_HW_OSTC; } else { switch (buffer[8]) { case 0x22: dc_fam = DC_FAMILY_HW_FROG; break; case 0x23: dc_fam = DC_FAMILY_HW_OSTC3; break; default: report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number); free(ostcdive); fclose(archive); goto out; } } // Try to determine the model based on serial number switch (dc_fam) { case DC_FAMILY_HW_OSTC: if (serial > 7000) model = 3; //2C else if (serial > 2048) model = 2; //2N else if (serial > 300) model = 1; //MK2 else model = 0; //OSTC break; case DC_FAMILY_HW_FROG: model = 0; break; default: if (serial > 10000) model = 0x12; //Sport else model = 0x0A; //OSTC3 } // 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); free(ostcdive); fclose(archive); goto out; } tmp = calloc(strlen(devdata->vendor) + strlen(devdata->model) + 28, 1); sprintf(tmp, "%s %s (Imported from OSTCTools)", devdata->vendor, devdata->model); ostcdive->dc.model = copy_string(tmp); free(tmp); // Parse the dive data rc = libdc_buffer_parser(ostcdive, devdata, buffer, i + 1); if (rc != DC_STATUS_SUCCESS) 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, remove // it from the list and add again. tmp = calloc(12, 1); sprintf(tmp, "%d", serial); ostcdive->dc.serial = copy_string(tmp); free(tmp); if (ostcdive->dc.extra_data) { ptr = ostcdive->dc.extra_data; while (strcmp(ptr->key, "Serial")) ptr = ptr->next; if (!strcmp(ptr->value, "0")) { add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial); *ptr = *(ptr)->next; } } else { add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial); } record_dive_to_table(ostcdive, divetable); mark_divelist_changed(true); sort_table(divetable); fclose(archive); out: free(devdata); free(buffer); }
static int dive_cb(const unsigned char *data, unsigned int size, const unsigned char *fingerprint, unsigned int fsize, void *userdata) { int rc; parser_t *parser = NULL; device_data_t *devdata = userdata; dc_datetime_t dt = {0}; struct tm tm; struct dive *dive; rc = create_parser(devdata, &parser); if (rc != PARSER_STATUS_SUCCESS) { fprintf(stderr, "Unable to create parser for %s", devdata->name); return rc; } rc = parser_set_data(parser, data, size); if (rc != PARSER_STATUS_SUCCESS) { fprintf(stderr, "Error registering the data."); parser_destroy(parser); return rc; } dive = alloc_dive(); rc = parser_get_datetime(parser, &dt); if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) { fprintf(stderr, "Error parsing the datetime."); parser_destroy (parser); return rc; } tm.tm_year = dt.year; tm.tm_mon = dt.month-1; tm.tm_mday = dt.day; tm.tm_hour = dt.hour; tm.tm_min = dt.minute; tm.tm_sec = dt.second; dive->when = utc_mktime(&tm); // Parse the divetime. printf("Parsing the divetime.\n"); unsigned int divetime = 0; rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime); if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) { fprintf(stderr, "Error parsing the divetime."); parser_destroy(parser); return rc; } dive->duration.seconds = divetime; // Parse the maxdepth. printf("Parsing the maxdepth.\n"); double maxdepth = 0.0; rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth); if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) { fprintf(stderr, "Error parsing the maxdepth."); parser_destroy(parser); return rc; } dive->maxdepth.mm = maxdepth * 1000 + 0.5; // Parse the gas mixes. printf("Parsing the gas mixes.\n"); unsigned int ngases = 0; rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases); if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) { fprintf(stderr, "Error parsing the gas mix count."); parser_destroy(parser); return rc; } rc = parse_gasmixes(dive, parser, ngases); if (rc != PARSER_STATUS_SUCCESS) { fprintf(stderr, "Error parsing the gas mix."); parser_destroy(parser); return rc; } // Initialize the sample data. rc = parse_samples(&dive, parser); if (rc != PARSER_STATUS_SUCCESS) { fprintf(stderr, "Error parsing the samples."); parser_destroy(parser); return rc; } parser_destroy(parser); /* If we already saw this dive, abort. */ if (find_dive(dive, devdata)) return 0; record_dive(dive); return 1; }
static int dive_cb(const unsigned char *data, unsigned int size, const unsigned char *fingerprint, unsigned int fsize, void *userdata) { int rc; dc_parser_t *parser = NULL; device_data_t *devdata = userdata; dc_datetime_t dt = {0}; struct tm tm; struct dive *dive; rc = create_parser(devdata, &parser); if (rc != DC_STATUS_SUCCESS) { dev_info(devdata, _("Unable to create parser for %s %s"), devdata->vendor, devdata->product); return rc; } rc = dc_parser_set_data(parser, data, size); if (rc != DC_STATUS_SUCCESS) { dev_info(devdata, _("Error registering the data")); dc_parser_destroy(parser); return rc; } import_dive_number++; dive = alloc_dive(); rc = dc_parser_get_datetime(parser, &dt); if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { dev_info(devdata, _("Error parsing the datetime")); dc_parser_destroy(parser); return rc; } tm.tm_year = dt.year; tm.tm_mon = dt.month-1; tm.tm_mday = dt.day; tm.tm_hour = dt.hour; tm.tm_min = dt.minute; tm.tm_sec = dt.second; dive->when = utc_mktime(&tm); // Parse the divetime. dev_info(devdata, _("Dive %d: %s %d %04d"), import_dive_number, monthname(tm.tm_mon), tm.tm_mday, year(tm.tm_year)); unsigned int divetime = 0; rc = dc_parser_get_field (parser, DC_FIELD_DIVETIME, 0, &divetime); if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { dev_info(devdata, _("Error parsing the divetime")); dc_parser_destroy(parser); return rc; } dive->duration.seconds = divetime; // Parse the maxdepth. double maxdepth = 0.0; rc = dc_parser_get_field(parser, DC_FIELD_MAXDEPTH, 0, &maxdepth); if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { dev_info(devdata, _("Error parsing the maxdepth")); dc_parser_destroy(parser); return rc; } dive->maxdepth.mm = maxdepth * 1000 + 0.5; // Parse the gas mixes. unsigned int ngases = 0; rc = dc_parser_get_field(parser, DC_FIELD_GASMIX_COUNT, 0, &ngases); if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { dev_info(devdata, _("Error parsing the gas mix count")); dc_parser_destroy(parser); return rc; } // Check if the libdivecomputer version already supports salinity double salinity = 1.03; #ifdef DC_FIELD_SALINITY rc = dc_parser_get_field(parser, DC_FIELD_SALINITY, 0, &salinity); if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) { dev_info(devdata, _("Error obtaining water salinity")); dc_parser_destroy(parser); return rc; } #endif dive->salinity = salinity * 10000.0 + 0.5; rc = parse_gasmixes(devdata, dive, parser, ngases); if (rc != DC_STATUS_SUCCESS) { dev_info(devdata, _("Error parsing the gas mix")); dc_parser_destroy(parser); return rc; } // Initialize the sample data. rc = parse_samples(devdata, &dive, parser); if (rc != DC_STATUS_SUCCESS) { dev_info(devdata, _("Error parsing the samples")); dc_parser_destroy(parser); return rc; } dc_parser_destroy(parser); /* If we already saw this dive, abort. */ if (!devdata->force_download && find_dive(dive, devdata)) return 0; dive->downloaded = TRUE; record_dive(dive); mark_divelist_changed(TRUE); return 1; }