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
		Add a link
		
	
		Reference in a new issue