mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
Fix invalid bailout gas choice when planning a CCR dive.
Fixes a bug reported in https://groups.google.com/g/subsurface-divelog/c/8N3cTz2Zv5E: When planning a CCR dive with OC bailout, the diluent gas may be chosen as the first OC bailout gas, despite being set up with a use type of 'diluent', and likely not being available for open circuit breathing. `best_first_ascend_cylinder` is now initialised to an invalid value (instead of the first cylinder, which may or may not be a diluent cylinder), and its subsequent use is guarded by a validity check. Signed-off-by: Michael Keller <github@ike.ch>
This commit is contained in:
parent
eb5d4f2a15
commit
ec0c6833a0
4 changed files with 91 additions and 9 deletions
|
@ -1,3 +1,4 @@
|
|||
desktop: fix bug in bailout gas selection for CCR dives
|
||||
desktop: fix crash on cylinder update of multiple dives
|
||||
desktop: use dynamic tank use drop down in equipment tab and planner
|
||||
desktop: fix brightness configuration for OSTC4
|
||||
|
|
|
@ -405,7 +405,7 @@ static struct gaschanges *analyze_gaslist(struct diveplan *diveplan, struct dive
|
|||
int nr = 0;
|
||||
struct gaschanges *gaschanges = NULL;
|
||||
struct divedatapoint *dp = diveplan->dp;
|
||||
int best_depth = get_cylinder(dive, *asc_cylinder)->depth.mm;
|
||||
struct divedatapoint *best_ascent_dp = NULL;
|
||||
bool total_time_zero = true;
|
||||
while (dp) {
|
||||
if (dp->time == 0 && total_time_zero && (ccr == (bool) setpoint_change(dive, dp->cylinderid))) {
|
||||
|
@ -425,9 +425,8 @@ static struct gaschanges *analyze_gaslist(struct diveplan *diveplan, struct dive
|
|||
assert(gaschanges[i].gasidx != -1);
|
||||
} else {
|
||||
/* is there a better mix to start deco? */
|
||||
if (dp->depth.mm < best_depth) {
|
||||
best_depth = dp->depth.mm;
|
||||
*asc_cylinder = dp->cylinderid;
|
||||
if (!best_ascent_dp || dp->depth.mm < best_ascent_dp->depth.mm) {
|
||||
best_ascent_dp = dp;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -436,6 +435,9 @@ static struct gaschanges *analyze_gaslist(struct diveplan *diveplan, struct dive
|
|||
dp = dp->next;
|
||||
}
|
||||
*gaschangenr = nr;
|
||||
if (best_ascent_dp) {
|
||||
*asc_cylinder = best_ascent_dp->cylinderid;
|
||||
}
|
||||
#if DEBUG_PLAN & 16
|
||||
for (nr = 0; nr < *gaschangenr; nr++) {
|
||||
int idx = gaschanges[nr].gasidx;
|
||||
|
@ -678,7 +680,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
|
|||
int clock, previous_point_time;
|
||||
int avg_depth, max_depth;
|
||||
int last_ascend_rate;
|
||||
int best_first_ascend_cylinder;
|
||||
int best_first_ascend_cylinder = -1;
|
||||
struct gasmix gas, bottom_gas;
|
||||
bool o2break_next = false;
|
||||
int break_cylinder = -1, breakfrom_cylinder = 0;
|
||||
|
@ -752,7 +754,6 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
|
|||
printf("current_cylinder %i\n", current_cylinder);
|
||||
#endif
|
||||
|
||||
best_first_ascend_cylinder = current_cylinder;
|
||||
/* Find the gases available for deco */
|
||||
|
||||
gaschanges = analyze_gaslist(diveplan, dive, &gaschangenr, depth, &best_first_ascend_cylinder, divemode == CCR && !prefs.dobailout);
|
||||
|
@ -827,7 +828,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
|
|||
return false;
|
||||
}
|
||||
|
||||
if (best_first_ascend_cylinder != current_cylinder) {
|
||||
if (best_first_ascend_cylinder != -1 && best_first_ascend_cylinder != current_cylinder) {
|
||||
current_cylinder = best_first_ascend_cylinder;
|
||||
gas = get_cylinder(dive, current_cylinder)->gasmix;
|
||||
|
||||
|
@ -886,7 +887,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
|
|||
last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time);
|
||||
/* Always prefer the best_first_ascend_cylinder if it has the right gasmix.
|
||||
* Otherwise take first cylinder from list with rightgasmix */
|
||||
if (same_gasmix(gas, get_cylinder(dive, best_first_ascend_cylinder)->gasmix))
|
||||
if (best_first_ascend_cylinder != -1 && same_gasmix(gas, get_cylinder(dive, best_first_ascend_cylinder)->gasmix))
|
||||
current_cylinder = best_first_ascend_cylinder;
|
||||
else
|
||||
current_cylinder = get_gasidx(dive, gas);
|
||||
|
@ -1032,7 +1033,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i
|
|||
* backgas. This could be customized if there were demand.
|
||||
*/
|
||||
if (break_cylinder == -1) {
|
||||
if (get_o2(get_cylinder(dive, best_first_ascend_cylinder)->gasmix) <= 320)
|
||||
if (best_first_ascend_cylinder != -1 && get_o2(get_cylinder(dive, best_first_ascend_cylinder)->gasmix) <= 320)
|
||||
break_cylinder = best_first_ascend_cylinder;
|
||||
else
|
||||
break_cylinder = 0;
|
||||
|
|
|
@ -372,6 +372,39 @@ void setupPlanSeveralGases(struct diveplan *dp)
|
|||
plan_add_segment(dp, 5 * 60, 10000, 0, 0, true, OC);
|
||||
}
|
||||
|
||||
void setupPlanCcr(struct diveplan *dp)
|
||||
{
|
||||
dp->salinity = 10300;
|
||||
dp->surface_pressure = 1013;
|
||||
dp->gflow = 50;
|
||||
dp->gfhigh = 70;
|
||||
dp->bottomsac = prefs.bottomsac;
|
||||
dp->decosac = prefs.decosac;
|
||||
|
||||
pressure_t po2 = {1600};
|
||||
struct gasmix diluent = {{200}, {210}};
|
||||
struct gasmix ean53 = {{530}, {0}};
|
||||
struct gasmix tx19_33 = {{190}, {330}};
|
||||
cylinder_t *cyl0 = get_or_create_cylinder(&displayed_dive, 0);
|
||||
cylinder_t *cyl1 = get_or_create_cylinder(&displayed_dive, 1);
|
||||
cylinder_t *cyl2 = get_or_create_cylinder(&displayed_dive, 2);
|
||||
cyl0->gasmix = diluent;
|
||||
cyl0->depth = gas_mod(diluent, po2, &displayed_dive, M_OR_FT(3, 10));
|
||||
cyl0->type.size.mliter = 3000;
|
||||
cyl0->type.workingpressure.mbar = 200000;
|
||||
cyl0->cylinder_use = DILUENT;
|
||||
cyl1->gasmix = ean53;
|
||||
cyl1->depth = gas_mod(ean53, po2, &displayed_dive, M_OR_FT(3, 10));
|
||||
cyl2->gasmix = tx19_33;
|
||||
cyl2->depth = gas_mod(tx19_33, po2, &displayed_dive, M_OR_FT(3, 10));
|
||||
reset_cylinders(&displayed_dive, true);
|
||||
free_dps(dp);
|
||||
|
||||
plan_add_segment(dp, 0, cyl1->depth.mm, 1, 0, false, OC);
|
||||
plan_add_segment(dp, 0, cyl2->depth.mm, 2, 0, false, OC);
|
||||
plan_add_segment(dp, 20 * 60, M_OR_FT(60, 197), 0, 1300, true, CCR);
|
||||
}
|
||||
|
||||
/* We compare the calculated runtimes against two values:
|
||||
* - Known runtime calculated by Subsurface previously (to detect if anything has changed)
|
||||
* - Benchmark runtime (we should be close, but not always exactly the same)
|
||||
|
@ -885,4 +918,50 @@ void TestPlan::testVpmbMetricRepeat()
|
|||
QCOMPARE(finalDiveRunTimeSeconds, firstDiveRunTimeSeconds);
|
||||
}
|
||||
|
||||
|
||||
// Test that the correct gases are selected during a CCR dive with bailout ascent
|
||||
// Includes a regression test for https://groups.google.com/g/subsurface-divelog/c/8N3cTz2Zv5E
|
||||
|
||||
void TestPlan::testCcrBailoutGasSelection()
|
||||
{
|
||||
struct deco_state *cache = NULL;
|
||||
|
||||
setupPrefs();
|
||||
prefs.unit_system = METRIC;
|
||||
prefs.units.length = units::METERS;
|
||||
prefs.planner_deco_mode = BUEHLMANN;
|
||||
displayed_dive.dc.divemode = CCR;
|
||||
prefs.dobailout = true;
|
||||
|
||||
struct diveplan testPlan = {};
|
||||
setupPlanCcr(&testPlan);
|
||||
|
||||
plan(&test_deco_state, &testPlan, &displayed_dive, 60, stoptable, &cache, true, false);
|
||||
|
||||
#if DEBUG
|
||||
free(displayed_dive.notes);
|
||||
displayed_dive.notes = NULL;
|
||||
save_dive(stdout, &displayed_dive, false);
|
||||
#endif
|
||||
|
||||
// check diluent used
|
||||
cylinder_t *cylinder = get_cylinder(&displayed_dive, get_cylinderid_at_time(&displayed_dive, &displayed_dive.dc, { 20 * 60 - 1 }));
|
||||
QCOMPARE(cylinder->cylinder_use, DILUENT);
|
||||
QCOMPARE(get_o2(cylinder->gasmix), 200);
|
||||
|
||||
// check deep bailout used
|
||||
cylinder = get_cylinder(&displayed_dive, get_cylinderid_at_time(&displayed_dive, &displayed_dive.dc, { 20 * 60 + 1 }));
|
||||
QCOMPARE(cylinder->cylinder_use, OC_GAS);
|
||||
QCOMPARE(get_o2(cylinder->gasmix), 190);
|
||||
|
||||
// check shallow bailout used
|
||||
cylinder = get_cylinder(&displayed_dive, get_cylinderid_at_time(&displayed_dive, &displayed_dive.dc, { 30 * 60 }));
|
||||
QCOMPARE(cylinder->cylinder_use, OC_GAS);
|
||||
QCOMPARE(get_o2(cylinder->gasmix), 530);
|
||||
|
||||
// check expected run time of 51 minutes
|
||||
QVERIFY(compareDecoTime(displayed_dive.dc.duration.seconds, 51 * 60, 51 * 60));
|
||||
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestPlan)
|
||||
|
|
|
@ -19,6 +19,7 @@ private slots:
|
|||
void testVpmbMetric100m10min();
|
||||
void testVpmbMetricRepeat();
|
||||
void testMultipleGases();
|
||||
void testCcrBailoutGasSelection();
|
||||
};
|
||||
|
||||
#endif // TESTPLAN_H
|
||||
|
|
Loading…
Add table
Reference in a new issue