void TestPlan::testVpmbMetricMultiLevelAir() { char *cache = NULL; setupPrefsVpmb(); prefs.unit_system = METRIC; prefs.units.length = units::METERS; struct diveplan testPlan = { 0 }; setupPlanVpmbMultiLevelAir(&testPlan); setCurrentAppState("PlanDive"); plan(&testPlan, &cache, 1, 0); #if DEBUG free(displayed_dive.notes); displayed_dive.notes = NULL; save_dive(stdout, &displayed_dive); #endif // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(first_ceiling_pressure.mbar, &displayed_dive) * 0.001)); // check benchmark run time of 167 minutes, and known Subsurface runtime of 169 minutes QVERIFY(compareDecoTime(displayed_dive.dc.duration.seconds, 167u * 60u + 20u, 169u * 60u + 20u)); }
void TestPlan::testImperial() { char *cache = NULL; setupPrefs(); prefs.unit_system = IMPERIAL; prefs.units.length = units::FEET; prefs.deco_mode = BUEHLMANN; struct diveplan testPlan = { 0 }; setupPlan(&testPlan); plan(&testPlan, &cache, 1, 0); #if DEBUG free(displayed_dive.notes); displayed_dive.notes = NULL; save_dive(stdout, &displayed_dive); #endif // check first gas change to EAN36 at 33m struct event *ev = displayed_dive.dc.events; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 36); QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 33528); // check second gas change to Oxygen at 6m ev = ev->next; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 100); QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 6096); // check expected run time of 110 minutes QVERIFY(compareDecoTime(displayed_dive.dc.duration.seconds, 110u * 60u - 2u, 110u * 60u - 2u)); }
void TestPlan::testVpmbMetric60m30minTx() { char *cache = NULL; setupPrefsVpmb(); prefs.unit_system = METRIC; prefs.units.length = units::METERS; struct diveplan testPlan = { 0 }; setupPlanVpmb60m30minTx(&testPlan); setCurrentAppState("PlanDive"); plan(&testPlan, &cache, 1, 0); #if DEBUG free(displayed_dive.notes); displayed_dive.notes = NULL; save_dive(stdout, &displayed_dive); #endif // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(first_ceiling_pressure.mbar, &displayed_dive) * 0.001)); // check first gas change to EAN50 at 21m struct event *ev = displayed_dive.dc.events; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 50); QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 21000); // check benchmark run time of 89 minutes, and known Subsurface runtime of 89 minutes QVERIFY(compareDecoTime(displayed_dive.dc.duration.seconds, 89u * 60u + 20u, 89u * 60u + 20u)); }
void TestPlan::testVpmbMetricMultiLevelAir() { struct deco_state *cache = NULL; setupPrefsVpmb(); prefs.unit_system = METRIC; prefs.units.length = units::METERS; struct diveplan testPlan = {}; setupPlanVpmbMultiLevelAir(&testPlan); setAppState(ApplicationState::PlanDive); plan(&test_deco_state, &testPlan, &displayed_dive, 60, stoptable, &cache, 1, 0); #if DEBUG free(displayed_dive.notes); displayed_dive.notes = NULL; save_dive(stdout, &displayed_dive, false); #endif // check minimum gas result struct divedatapoint *dp = testPlan.dp; while (!dp->minimum_gas.mbar && dp->next) dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 101l); // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &displayed_dive) * 0.001)); // check benchmark run time of 167 minutes, and known Subsurface runtime of 169 minutes QVERIFY(compareDecoTime(displayed_dive.dc.duration.seconds, 167u * 60u + 20u, 169u * 60u + 20u)); }
void save_dives(const char *filename) { int i; GList *trip = NULL; FILE *f = fopen(filename, "w"); if (!f) return; /* Flush any edits of current dives back to the dives! */ update_dive(current_dive); fprintf(f, "<dives>\n<program name='subsurface' version='%d'></program>\n", VERSION); /* save the trips */ while ((trip = NEXT_TRIP(trip)) != NULL) save_trip(f, trip->data); /* save the dives */ for (i = 0; i < dive_table.nr; i++) save_dive(f, get_dive(i)); fprintf(f, "</dives>\n"); fclose(f); }
void TestPlan::testMultipleGases() { struct deco_state *cache = NULL; setupPrefsVpmb(); prefs.unit_system = METRIC; prefs.units.length = units::METERS; struct diveplan testPlan = {}; setupPlanSeveralGases(&testPlan); setAppState(ApplicationState::PlanDive); plan(&test_deco_state, &testPlan, &displayed_dive, 60, stoptable, &cache, 1, 0); #if DEBUG free(displayed_dive.notes); displayed_dive.notes = NULL; save_dive(stdout, &displayed_dive, false); #endif gasmix gas; gas = get_gasmix_at_time(&displayed_dive, &displayed_dive.dc, {20 * 60 + 1}); QCOMPARE(get_o2(gas), 110); QVERIFY(compareDecoTime(displayed_dive.dc.duration.seconds, 2480u, 2480u)); }
void save_dives(const char *filename) { int i; struct dive *dive; dive_trip_t *trip = NULL; FILE *f = g_fopen(filename, "w"); if (!f) return; /* Flush any edits of current dives back to the dives! */ update_dive(current_dive); fprintf(f, "<dives>\n<program name='subsurface' version='%d'></program>\n", VERSION); /* save the dives */ for_each_dive(i, dive) { dive_trip_t *thistrip = dive->divetrip; if (trip != thistrip) { /* Close the old trip? */ if (trip) fprintf(f, "</trip>\n"); /* Open the new one */ if (thistrip) save_trip(f, thistrip); trip = thistrip; } save_dive(f, get_dive(i)); }
void DivePlannerPointsModel::createTemporaryPlan() { // Get the user-input and calculate the dive info free_dps(&diveplan); int lastIndex = -1; for (int i = 0; i < rowCount(); i++) { divedatapoint p = at(i); int deltaT = lastIndex != -1 ? p.time - at(lastIndex).time : p.time; lastIndex = i; if (i == 0 && prefs.drop_stone_mode) { /* Okay, we add a fist segment where we go down to depth */ plan_add_segment(&diveplan, p.depth / prefs.descrate, p.depth, p.gasmix, p.setpoint, true); deltaT -= p.depth / prefs.descrate; } if (p.entered) plan_add_segment(&diveplan, deltaT, p.depth, p.gasmix, p.setpoint, true); } // what does the cache do??? char *cache = NULL; struct divedatapoint *dp = NULL; for (int i = 0; i < MAX_CYLINDERS; i++) { cylinder_t *cyl = &displayed_dive.cylinder[i]; if (cyl->depth.mm) { dp = create_dp(0, cyl->depth.mm, cyl->gasmix, 0); if (diveplan.dp) { dp->next = diveplan.dp; diveplan.dp = dp; } else { dp->next = NULL; diveplan.dp = dp; } } } #if DEBUG_PLAN dump_plan(&diveplan); #endif if (recalcQ() && !diveplan_empty(&diveplan)) { plan(&diveplan, &cache, isPlanner(), false); /* TODO: * Hook this signal to the mainwindow(s), the call to MainWindow * can't be here as we are now dealing with QML too. */ //MainWindow::instance()->setPlanNotes(displayed_dive.notes); emit calculatedPlanNotes(displayed_dive.notes); } // throw away the cache free(cache); #if DEBUG_PLAN save_dive(stderr, &displayed_dive); dump_plan(&diveplan); #endif }
void DivePlannerPointsModel::createTemporaryPlan() { // Get the user-input and calculate the dive info free_dps(&diveplan); int lastIndex = -1; for (int i = 0; i < rowCount(); i++) { divedatapoint p = at(i); int deltaT = lastIndex != -1 ? p.time - at(lastIndex).time : p.time; lastIndex = i; if (i == 0 && mode == PLAN && prefs.drop_stone_mode) { /* Okay, we add a first segment where we go down to depth */ plan_add_segment(&diveplan, p.depth.mm / prefs.descrate, p.depth.mm, p.cylinderid, p.setpoint, true); deltaT -= p.depth.mm / prefs.descrate; } if (p.entered) plan_add_segment(&diveplan, deltaT, p.depth.mm, p.cylinderid, p.setpoint, true); } // what does the cache do??? struct deco_state *cache = NULL; struct divedatapoint *dp = NULL; for (int i = 0; i < MAX_CYLINDERS; i++) { cylinder_t *cyl = &displayed_dive.cylinder[i]; if (cyl->depth.mm && cyl->cylinder_use != NOT_USED) { dp = create_dp(0, cyl->depth.mm, i, 0); if (diveplan.dp) { dp->next = diveplan.dp; diveplan.dp = dp; } else { dp->next = NULL; diveplan.dp = dp; } } } #if DEBUG_PLAN dump_plan(&diveplan); #endif if (recalcQ() && !diveplan_empty(&diveplan)) { struct decostop stoptable[60]; plan(&diveplan, &displayed_dive, DECOTIMESTEP, stoptable, &cache, isPlanner(), false); computeVariations(); emit calculatedPlanNotes(); } // throw away the cache free(cache); #if DEBUG_PLAN save_dive(stderr, &displayed_dive); dump_plan(&diveplan); #endif }
void save_dives(const char *filename) { int i; FILE *f = fopen(filename, "w"); if (!f) return; /* Flush any edits of current dives back to the dives! */ update_dive(current_dive); fprintf(f, "<dives>\n<program name='subsurface' version='%d'></program>\n", VERSION); for (i = 0; i < dive_table.nr; i++) save_dive(f, get_dive(i)); fprintf(f, "</dives>\n"); fclose(f); }
void TestPlan::testVpmbMetric100m10min() { struct deco_state *cache = NULL; setupPrefsVpmb(); prefs.unit_system = METRIC; prefs.units.length = units::METERS; struct diveplan testPlan = {}; setupPlanVpmb100m10min(&testPlan); setAppState(ApplicationState::PlanDive); plan(&test_deco_state, &testPlan, &displayed_dive, 60, stoptable, &cache, 1, 0); #if DEBUG free(displayed_dive.notes); displayed_dive.notes = NULL; save_dive(stdout, &displayed_dive, false); #endif // check minimum gas result struct divedatapoint *dp = testPlan.dp; while (!dp->minimum_gas.mbar && dp->next) dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 175l); // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &displayed_dive) * 0.001)); // check first gas change to EAN50 at 21m struct event *ev = displayed_dive.dc.events; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 50); QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 21000); // check second gas change to Oxygen at 6m ev = ev->next; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 100); QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 6000); // check benchmark run time of 58 minutes, and known Subsurface runtime of 57 minutes QVERIFY(compareDecoTime(displayed_dive.dc.duration.seconds, 58u * 60u + 20u, 57u * 60u + 20u)); }
void TestPlan::testImperial() { struct deco_state *cache = NULL; setupPrefs(); prefs.unit_system = IMPERIAL; prefs.units.length = units::FEET; prefs.planner_deco_mode = BUEHLMANN; struct diveplan testPlan = {}; setupPlan(&testPlan); plan(&test_deco_state, &testPlan, &displayed_dive, 60, stoptable, &cache, 1, 0); #if DEBUG free(displayed_dive.notes); displayed_dive.notes = NULL; save_dive(stdout, &displayed_dive, false); #endif // check minimum gas result struct divedatapoint *dp = testPlan.dp; while (!dp->minimum_gas.mbar && dp->next) dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 154l); // check first gas change to EAN36 at 33m struct event *ev = displayed_dive.dc.events; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 36); QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 33528); // check second gas change to Oxygen at 6m ev = ev->next; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 100); QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 6096); // check expected run time of 111 minutes QVERIFY(compareDecoTime(displayed_dive.dc.duration.seconds, 111u * 60u - 2u, 111u * 60u - 2u)); }
/* simply overwrite the data in the displayed_dive * return false if something goes wrong */ static void create_dive_from_plan(struct diveplan *diveplan, bool track_gas) { struct divedatapoint *dp; struct divecomputer *dc; struct sample *sample; struct gasmix oldgasmix; struct event *ev; cylinder_t *cyl; int oldpo2 = 0; int lasttime = 0; int lastdepth = 0; if (!diveplan || !diveplan->dp) return; #if DEBUG_PLAN & 4 printf("in create_dive_from_plan\n"); dump_plan(diveplan); #endif // reset the cylinders and clear out the samples and events of the // displayed dive so we can restart reset_cylinders(&displayed_dive, track_gas); dc = &displayed_dive.dc; free(dc->sample); dc->sample = NULL; dc->samples = 0; dc->alloc_samples = 0; while ((ev = dc->events)) { dc->events = dc->events->next; free(ev); } dp = diveplan->dp; cyl = &displayed_dive.cylinder[0]; oldgasmix = cyl->gasmix; sample = prepare_sample(dc); sample->po2.mbar = dp->po2; if (track_gas && cyl->type.workingpressure.mbar) sample->cylinderpressure.mbar = cyl->end.mbar; finish_sample(dc); while (dp) { struct gasmix gasmix = dp->gasmix; int po2 = dp->po2; int time = dp->time; int depth = dp->depth; if (time == 0) { /* special entries that just inform the algorithm about * additional gases that are available */ if (verify_gas_exists(gasmix) < 0) goto gas_error_exit; dp = dp->next; continue; } /* Check for SetPoint change */ if (oldpo2 != po2) { if (lasttime) /* this is a bad idea - we should get a different SAMPLE_EVENT type * reserved for this in libdivecomputer... overloading SMAPLE_EVENT_PO2 * with a different meaning will only cause confusion elsewhere in the code */ add_event(dc, lasttime, SAMPLE_EVENT_PO2, 0, po2, "SP change"); oldpo2 = po2; } /* Make sure we have the new gas, and create a gas change event */ if (gasmix_distance(&gasmix, &oldgasmix) > 0) { int idx; if ((idx = verify_gas_exists(gasmix)) < 0) goto gas_error_exit; /* need to insert a first sample for the new gas */ add_gas_switch_event(&displayed_dive, dc, lasttime + 1, idx); cyl = &displayed_dive.cylinder[idx]; sample = prepare_sample(dc); sample[-1].po2.mbar = po2; sample->time.seconds = lasttime + 1; sample->depth.mm = lastdepth; if (track_gas && cyl->type.workingpressure.mbar) sample->cylinderpressure.mbar = cyl->sample_end.mbar; finish_sample(dc); oldgasmix = gasmix; } /* Create sample */ sample = prepare_sample(dc); /* set po2 at beginning of this segment */ /* and keep it valid for last sample - where it likely doesn't matter */ sample[-1].po2.mbar = po2; sample->po2.mbar = po2; sample->time.seconds = lasttime = time; sample->depth.mm = lastdepth = depth; if (track_gas) { update_cylinder_pressure(&displayed_dive, sample[-1].depth.mm, depth, time - sample[-1].time.seconds, dp->entered ? diveplan->bottomsac : diveplan->decosac, cyl, !dp->entered); if (cyl->type.workingpressure.mbar) sample->cylinderpressure.mbar = cyl->end.mbar; } finish_sample(dc); dp = dp->next; } #if DEBUG_PLAN & 32 save_dive(stdout, &displayed_dive); #endif return; gas_error_exit: report_error(translate("gettextFromC", "Too many gas mixes")); return; }
// TODO: This looks like should be ported to C code. or a big part of it. bool DivelogsDeWebServices::prepare_dives_for_divelogs(const QString &tempfile, const bool selected) { static const char errPrefix[] = "divelog.de-upload:"; if (!amount_selected) { report_error(tr("no dives were selected").toUtf8()); return false; } xsltStylesheetPtr xslt = NULL; struct zip *zip; xslt = get_stylesheet("divelogs-export.xslt"); if (!xslt) { qDebug() << errPrefix << "missing stylesheet"; return false; } int error_code; zip = zip_open(QFile::encodeName(tempfile), ZIP_CREATE, &error_code); if (!zip) { char buffer[1024]; zip_error_to_str(buffer, sizeof buffer, error_code, errno); report_error(tr("failed to create zip file for upload: %s").toUtf8(), buffer); return false; } /* walk the dive list in chronological order */ int i; struct dive *dive; for_each_dive (i, dive) { FILE *f; char filename[PATH_MAX]; int streamsize; char *membuf; xmlDoc *transformed; struct zip_source *s; /* * Get the i'th dive in XML format so we can process it. * We need to save to a file before we can reload it back into memory... */ if (selected && !dive->selected) continue; f = tmpfile(); if (!f) { report_error(tr("cannot create temporary file: %s").toUtf8(), qt_error_string().toUtf8().data()); goto error_close_zip; } save_dive(f, dive); fseek(f, 0, SEEK_END); streamsize = ftell(f); rewind(f); membuf = (char *)malloc(streamsize + 1); if (!membuf || (streamsize = fread(membuf, 1, streamsize, f)) == 0) { report_error(tr("internal error: %s").toUtf8(), qt_error_string().toUtf8().data()); fclose(f); free((void *)membuf); goto error_close_zip; } membuf[streamsize] = 0; fclose(f); /* * Parse the memory buffer into XML document and * transform it to divelogs.de format, finally dumping * the XML into a character buffer. */ xmlDoc *doc = xmlReadMemory(membuf, streamsize, "divelog", NULL, 0); if (!doc) { qWarning() << errPrefix << "could not parse back into memory the XML file we've just created!"; report_error(tr("internal error").toUtf8()); free((void *)membuf); goto error_close_zip; } free((void *)membuf); transformed = xsltApplyStylesheet(xslt, doc, NULL); xmlDocDumpMemory(transformed, (xmlChar **)&membuf, &streamsize); xmlFreeDoc(doc); xmlFreeDoc(transformed); /* * Save the XML document into a zip file. */ snprintf(filename, PATH_MAX, "%d.xml", i + 1); s = zip_source_buffer(zip, membuf, streamsize, 1); if (s) { int64_t ret = zip_add(zip, filename, s); if (ret == -1) qDebug() << errPrefix << "failed to include dive:" << i; } }
/* This tests that a previously calculated plan isn't affecting the calculations of the next plan. * It is NOT a 'repetitive dive' test (i.e. with a surface interval and considering tissue * saturation from the previous dive). */ void TestPlan::testVpmbMetricRepeat() { char *cache = NULL; setupPrefsVpmb(); prefs.unit_system = METRIC; prefs.units.length = units::METERS; struct diveplan testPlan = { 0 }; setupPlanVpmb30m20min(&testPlan); setCurrentAppState("PlanDive"); plan(&testPlan, &cache, 1, 0); #if DEBUG free(displayed_dive.notes); displayed_dive.notes = NULL; save_dive(stdout, &displayed_dive); #endif // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(first_ceiling_pressure.mbar, &displayed_dive) * 0.001)); // check benchmark run time of 27 minutes, and known Subsurface runtime of 27 minutes QVERIFY(compareDecoTime(displayed_dive.dc.duration.seconds, 27u * 60u + 20u, 27u * 60u + 20u)); int firstDiveRunTimeSeconds = displayed_dive.dc.duration.seconds; setupPlanVpmb100mTo70m30min(&testPlan); plan(&testPlan, &cache, 1, 0); #if DEBUG free(displayed_dive.notes); displayed_dive.notes = NULL; save_dive(stdout, &displayed_dive); #endif // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(first_ceiling_pressure.mbar, &displayed_dive) * 0.001)); // check first gas change to 21/35 at 66m struct event *ev = displayed_dive.dc.events; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->gas.mix.o2.permille, 210); QCOMPARE(ev->gas.mix.he.permille, 350); QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 66000); // check second gas change to EAN50 at 21m ev = ev->next; QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 50); QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 21000); // check third gas change to Oxygen at 6m ev = ev->next; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 3); QCOMPARE(ev->value, 100); QCOMPARE(get_depth_at_time(&displayed_dive.dc, ev->time.seconds), 6000); // we don't have a benchmark, known Subsurface runtime is 126 minutes QVERIFY(compareDecoTime(displayed_dive.dc.duration.seconds, 126u * 60u + 20u, 126u * 60u + 20u)); setupPlanVpmb30m20min(&testPlan); plan(&testPlan, &cache, 1, 0); #if DEBUG free(displayed_dive.notes); displayed_dive.notes = NULL; save_dive(stdout, &displayed_dive); #endif // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(first_ceiling_pressure.mbar, &displayed_dive) * 0.001)); // check runtime is exactly the same as the first time int finalDiveRunTimeSeconds = displayed_dive.dc.duration.seconds; QCOMPARE(finalDiveRunTimeSeconds, firstDiveRunTimeSeconds); }