| 
									
										
										
										
											2017-04-27 20:18:03 +02:00
										 |  |  | // SPDX-License-Identifier: GPL-2.0
 | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | /* planner.c
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * code that allows us to plan future dives | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * (c) Dirk Hohndel 2013 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-12-07 23:54:14 +01:00
										 |  |  | #include <assert.h>
 | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | #include <ctype.h>
 | 
					
						
							| 
									
										
										
										
											2013-10-05 00:29:09 -07:00
										 |  |  | #include <string.h>
 | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | #include "dive.h"
 | 
					
						
							| 
									
										
										
										
											2016-03-23 09:53:44 -07:00
										 |  |  | #include "deco.h"
 | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | #include "divelist.h"
 | 
					
						
							| 
									
										
										
										
											2013-04-07 20:20:25 -07:00
										 |  |  | #include "planner.h"
 | 
					
						
							| 
									
										
										
										
											2013-10-06 08:55:58 -07:00
										 |  |  | #include "gettext.h"
 | 
					
						
							| 
									
										
										
										
											2014-04-29 22:37:19 -07:00
										 |  |  | #include "libdivecomputer/parser.h"
 | 
					
						
							| 
									
										
										
										
											2017-01-07 03:01:14 +01:00
										 |  |  | #include "qthelperfromc.h"
 | 
					
						
							| 
									
										
										
										
											2017-03-31 08:14:36 +02:00
										 |  |  | #include "version.h"
 | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-20 11:01:09 +10:00
										 |  |  | #define TIMESTEP 2 /* second */
 | 
					
						
							| 
									
										
										
										
											2014-04-18 16:08:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | int decostoplevels_metric[] = { 0, 3000, 6000, 9000, 12000, 15000, 18000, 21000, 24000, 27000, | 
					
						
							|  |  |  | 				  30000, 33000, 36000, 39000, 42000, 45000, 48000, 51000, 54000, 57000, | 
					
						
							|  |  |  | 				  60000, 63000, 66000, 69000, 72000, 75000, 78000, 81000, 84000, 87000, | 
					
						
							|  |  |  | 				  90000, 100000, 110000, 120000, 130000, 140000, 150000, 160000, 170000, | 
					
						
							|  |  |  | 				  180000, 190000, 200000, 220000, 240000, 260000, 280000, 300000, | 
					
						
							|  |  |  | 				  320000, 340000, 360000, 380000 }; | 
					
						
							|  |  |  | int decostoplevels_imperial[] = { 0, 3048, 6096, 9144, 12192, 15240, 18288, 21336, 24384, 27432, | 
					
						
							|  |  |  | 				30480, 33528, 36576, 39624, 42672, 45720, 48768, 51816, 54864, 57912, | 
					
						
							|  |  |  | 				60960, 64008, 67056, 70104, 73152, 76200, 79248, 82296, 85344, 88392, | 
					
						
							|  |  |  | 				91440, 101600, 111760, 121920, 132080, 142240, 152400, 162560, 172720, | 
					
						
							|  |  |  | 				182880, 193040, 203200, 223520, 243840, 264160, 284480, 304800, | 
					
						
							|  |  |  | 				325120, 345440, 365760, 386080 }; | 
					
						
							| 
									
										
										
											
												Planner deco stops are at 10ft increments when measured in feet
When using feet as depth unit, deco stop levels should be at 10 ft rather
than 3 m increments.
For shallow stops, rounding means the difference is not apparent. However,
with stops deeper than 30 feet, using 3 m increments leads stops at 39ft,
49ft, ..., 98ft, etc.
Apart from making plans look messy, the old behaviour makes it harder to
benchmark the planner against published profiles in imperial units.
This revised patch uses the help macro M_OR_FT(6, 20) to set the last stop
at the correct depth.
Signed-off-by: Rick Walsh <rickmwalsh@gmail.com>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
											
										 
											2015-07-04 23:10:34 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-16 15:17:39 -08:00
										 |  |  | double plangflow, plangfhigh; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-23 11:50:50 +01:00
										 |  |  | extern double regressiona(); | 
					
						
							|  |  |  | extern double regressionb(); | 
					
						
							|  |  |  | extern void reset_regression(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-27 18:30:05 +02:00
										 |  |  | char *disclaimer; | 
					
						
							| 
									
										
										
										
											2016-11-23 11:50:50 +01:00
										 |  |  | int plot_depth = 0; | 
					
						
							| 
									
										
										
										
											2013-01-07 12:49:07 -08:00
										 |  |  | #if DEBUG_PLAN
 | 
					
						
							|  |  |  | void dump_plan(struct diveplan *diveplan) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct divedatapoint *dp; | 
					
						
							|  |  |  | 	struct tm tm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!diveplan) { | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 		printf("Diveplan NULL\n"); | 
					
						
							| 
									
										
										
										
											2013-01-07 12:49:07 -08:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	utc_mkdate(diveplan->when, &tm); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-08 14:05:25 -08:00
										 |  |  | 	printf("\nDiveplan @ %04d-%02d-%02d %02d:%02d:%02d (surfpres %dmbar):\n", | 
					
						
							| 
									
										
										
										
											2016-04-28 15:13:30 -07:00
										 |  |  | 	       tm.tm_year, tm.tm_mon + 1, tm.tm_mday, | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 	       tm.tm_hour, tm.tm_min, tm.tm_sec, | 
					
						
							|  |  |  | 	       diveplan->surface_pressure); | 
					
						
							| 
									
										
										
										
											2013-01-07 12:49:07 -08:00
										 |  |  | 	dp = diveplan->dp; | 
					
						
							|  |  |  | 	while (dp) { | 
					
						
							| 
									
										
										
										
											2017-10-07 22:06:20 +02:00
										 |  |  | 		printf("\t%3u:%02u: %6dmm cylid: %2d setpoint: %d\n", FRACTION(dp->time, 60), dp->depth, dp->cylinderid, dp->setpoint); | 
					
						
							| 
									
										
										
										
											2013-01-07 12:49:07 -08:00
										 |  |  | 		dp = dp->next; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-30 15:40:13 -07:00
										 |  |  | bool diveplan_empty(struct diveplan *diveplan) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct divedatapoint *dp; | 
					
						
							|  |  |  | 	if (!diveplan || !diveplan->dp) | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 	dp = diveplan->dp; | 
					
						
							| 
									
										
										
										
											2014-06-03 11:38:24 +02:00
										 |  |  | 	while (dp) { | 
					
						
							| 
									
										
										
										
											2014-05-30 15:40:13 -07:00
										 |  |  | 		if (dp->time) | 
					
						
							|  |  |  | 			return false; | 
					
						
							|  |  |  | 		dp = dp->next; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-18 18:37:28 -07:00
										 |  |  | /* get the gas at a certain time during the dive */ | 
					
						
							| 
									
										
										
										
											2014-07-17 21:16:50 +02:00
										 |  |  | void get_gas_at_time(struct dive *dive, struct divecomputer *dc, duration_t time, struct gasmix *gas) | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-07-18 18:37:28 -07:00
										 |  |  | 	// we always start with the first gas, so that's our gas
 | 
					
						
							|  |  |  | 	// unless an event tells us otherwise
 | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 	struct event *event = dc->events; | 
					
						
							| 
									
										
										
										
											2014-07-18 18:37:28 -07:00
										 |  |  | 	*gas = dive->cylinder[0].gasmix; | 
					
						
							| 
									
										
										
										
											2014-07-17 21:16:49 +02:00
										 |  |  | 	while (event && event->time.seconds <= time.seconds) { | 
					
						
							| 
									
										
										
										
											2014-07-17 21:16:50 +02:00
										 |  |  | 		if (!strcmp(event->name, "gaschange")) { | 
					
						
							|  |  |  | 			int cylinder_idx = get_cylinder_index(dive, event); | 
					
						
							|  |  |  | 			*gas = dive->cylinder[cylinder_idx].gasmix; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 		event = event->next; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | /* get the cylinder index at a certain time during the dive */ | 
					
						
							|  |  |  | int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_t time) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	// we start with the first cylinder unless an event tells us otherwise
 | 
					
						
							|  |  |  | 	int cylinder_idx = 0; | 
					
						
							|  |  |  | 	struct event *event = dc->events; | 
					
						
							|  |  |  | 	while (event && event->time.seconds <= time.seconds) { | 
					
						
							|  |  |  | 		if (!strcmp(event->name, "gaschange")) | 
					
						
							|  |  |  | 			cylinder_idx = get_cylinder_index(dive, event); | 
					
						
							|  |  |  | 		event = event->next; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return cylinder_idx; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-01 14:17:06 -07:00
										 |  |  | int get_gasidx(struct dive *dive, struct gasmix *mix) | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-04-02 15:06:54 -05:00
										 |  |  | 	return find_best_gasmix_match(mix, dive->cylinder, 0); | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | void interpolate_transition(struct deco_state *ds, struct dive *dive, duration_t t0, duration_t t1, depth_t d0, depth_t d1, const struct gasmix *gasmix, o2pressure_t po2) | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-03-23 09:53:44 -07:00
										 |  |  | 	uint32_t j; | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-18 20:25:00 +02:00
										 |  |  | 	for (j = t0.seconds; j < t1.seconds; j++) { | 
					
						
							|  |  |  | 		int depth = interpolate(d0.mm, d1.mm, j - t0.seconds, t1.seconds - t0.seconds); | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		add_segment(ds, depth_to_bar(depth, dive), gasmix, 1, po2.mbar, dive, prefs.bottomsac); | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-08-29 21:51:35 +10:00
										 |  |  | 	if (d1.mm > d0.mm) | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		calc_crushing_pressure(ds, depth_to_bar(d1.mm, &displayed_dive)); | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | /* returns the tissue tolerance at the end of this (partial) dive */ | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | int tissue_at_end(struct deco_state *ds, struct dive *dive, struct deco_state **cached_datap) | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct divecomputer *dc; | 
					
						
							|  |  |  | 	struct sample *sample, *psample; | 
					
						
							| 
									
										
										
										
											2014-07-17 21:16:50 +02:00
										 |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2014-07-17 21:16:49 +02:00
										 |  |  | 	depth_t lastdepth = {}; | 
					
						
							|  |  |  | 	duration_t t0 = {}, t1 = {}; | 
					
						
							| 
									
										
										
										
											2014-06-01 14:17:06 -07:00
										 |  |  | 	struct gasmix gas; | 
					
						
							| 
									
										
										
										
											2017-10-02 11:17:10 +02:00
										 |  |  | 	int surface_interval = 0; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!dive) | 
					
						
							| 
									
										
										
										
											2017-01-03 15:14:42 +01:00
										 |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2013-01-06 11:13:46 -08:00
										 |  |  | 	if (*cached_datap) { | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		restore_deco_state(*cached_datap, ds, true); | 
					
						
							| 
									
										
										
										
											2013-01-06 11:13:46 -08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		surface_interval = init_decompression(ds, dive); | 
					
						
							|  |  |  | 		cache_deco_state(ds, cached_datap); | 
					
						
							| 
									
										
										
										
											2013-01-06 11:13:46 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 	dc = &dive->dc; | 
					
						
							|  |  |  | 	if (!dc->samples) | 
					
						
							| 
									
										
										
										
											2017-01-03 15:14:42 +01:00
										 |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 	psample = sample = dc->sample; | 
					
						
							| 
									
										
										
										
											2014-07-18 18:37:28 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 	for (i = 0; i < dc->samples; i++, sample++) { | 
					
						
							| 
									
										
										
										
											2015-09-14 11:22:32 +02:00
										 |  |  | 		o2pressure_t setpoint; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (i) | 
					
						
							|  |  |  | 			setpoint = sample[-1].setpoint; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			setpoint = sample[0].setpoint; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-17 21:16:49 +02:00
										 |  |  | 		t1 = sample->time; | 
					
						
							| 
									
										
										
										
											2014-07-17 21:16:50 +02:00
										 |  |  | 		get_gas_at_time(dive, dc, t0, &gas); | 
					
						
							| 
									
										
										
										
											2013-01-08 13:54:29 -08:00
										 |  |  | 		if (i > 0) | 
					
						
							| 
									
										
										
										
											2014-07-17 21:16:49 +02:00
										 |  |  | 			lastdepth = psample->depth; | 
					
						
							| 
									
										
										
										
											2015-09-12 12:10:54 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* The ceiling in the deeper portion of a multilevel dive is sometimes critical for the VPM-B
 | 
					
						
							|  |  |  | 		 * Boyle's law compensation.  We should check the ceiling prior to ascending during the bottom | 
					
						
							|  |  |  | 		 * portion of the dive.  The maximum ceiling might be reached while ascending, but testing indicates | 
					
						
							|  |  |  | 		 * that it is only marginally deeper than the ceiling at the start of ascent. | 
					
						
							|  |  |  | 		 * Do not set the first_ceiling_pressure variable (used for the Boyle's law compensation calculation) | 
					
						
							|  |  |  | 		 * at this stage, because it would interfere with calculating the ceiling at the end of the bottom | 
					
						
							|  |  |  | 		 * portion of the dive. | 
					
						
							|  |  |  | 		 * Remember the value for later. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2017-01-07 03:01:14 +01:00
										 |  |  | 		if ((decoMode() == VPMB) && (lastdepth.mm > sample->depth.mm)) { | 
					
						
							| 
									
										
										
										
											2015-09-12 12:10:54 +10:00
										 |  |  | 			pressure_t ceiling_pressure; | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 			nuclear_regeneration(ds, t0.seconds); | 
					
						
							|  |  |  | 			vpmb_start_gradient(ds); | 
					
						
							|  |  |  | 			ceiling_pressure.mbar = depth_to_mbar(deco_allowed_depth(tissue_tolerance_calc(ds, dive, | 
					
						
							| 
									
										
										
										
											2015-09-12 12:10:54 +10:00
										 |  |  | 													depth_to_bar(lastdepth.mm, dive)), | 
					
						
							|  |  |  | 										dive->surface_pressure.mbar / 1000.0, | 
					
						
							|  |  |  | 										dive, | 
					
						
							|  |  |  | 										1), | 
					
						
							|  |  |  | 								dive); | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 			if (ceiling_pressure.mbar > ds->max_bottom_ceiling_pressure.mbar) | 
					
						
							|  |  |  | 				ds->max_bottom_ceiling_pressure.mbar = ceiling_pressure.mbar; | 
					
						
							| 
									
										
										
										
											2015-09-12 12:10:54 +10:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		interpolate_transition(ds, dive, t0, t1, lastdepth, sample->depth, &gas, setpoint); | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 		psample = sample; | 
					
						
							|  |  |  | 		t0 = t1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-03 15:14:42 +01:00
										 |  |  | 	return surface_interval; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-15 13:09:36 -07:00
										 |  |  | /* if a default cylinder is set, use that */ | 
					
						
							| 
									
										
										
										
											2013-11-13 21:44:18 +09:00
										 |  |  | void fill_default_cylinder(cylinder_t *cyl) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-03-15 13:09:36 -07:00
										 |  |  | 	const char *cyl_name = prefs.default_cylinder; | 
					
						
							| 
									
										
										
										
											2013-12-07 14:29:05 -08:00
										 |  |  | 	struct tank_info_t *ti = tank_info; | 
					
						
							| 
									
										
										
										
											2014-06-30 20:26:18 +02:00
										 |  |  | 	pressure_t pO2 = {.mbar = 1600}; | 
					
						
							| 
									
										
										
										
											2013-12-07 14:29:05 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-15 13:09:36 -07:00
										 |  |  | 	if (!cyl_name) | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2013-12-07 14:29:05 -08:00
										 |  |  | 	while (ti->name != NULL) { | 
					
						
							|  |  |  | 		if (strcmp(ti->name, cyl_name) == 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		ti++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (ti->name == NULL) | 
					
						
							| 
									
										
										
										
											2014-03-15 13:09:36 -07:00
										 |  |  | 		/* didn't find it */ | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2013-12-07 14:29:05 -08:00
										 |  |  | 	cyl->type.description = strdup(ti->name); | 
					
						
							|  |  |  | 	if (ti->ml) { | 
					
						
							|  |  |  | 		cyl->type.size.mliter = ti->ml; | 
					
						
							|  |  |  | 		cyl->type.workingpressure.mbar = ti->bar * 1000; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		cyl->type.workingpressure.mbar = psi_to_mbar(ti->psi); | 
					
						
							|  |  |  | 		if (ti->psi) | 
					
						
							| 
									
										
										
										
											2017-03-09 23:07:30 +07:00
										 |  |  | 			cyl->type.size.mliter = lrint(cuft_to_l(ti->cuft) * 1000 / bar_to_atm(psi_to_bar(ti->psi))); | 
					
						
							| 
									
										
										
										
											2013-11-23 21:54:51 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-06-30 20:26:18 +02:00
										 |  |  | 	// MOD of air
 | 
					
						
							| 
									
										
										
										
											2015-07-06 00:07:39 +02:00
										 |  |  | 	cyl->depth = gas_mod(&cyl->gasmix, pO2, &displayed_dive, 1); | 
					
						
							| 
									
										
										
										
											2013-11-13 21:44:18 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-29 14:36:14 -07:00
										 |  |  | /* calculate the new end pressure of the cylinder, based on its current end pressure and the
 | 
					
						
							|  |  |  |  * latest segment. */ | 
					
						
							| 
									
										
										
										
											2014-07-01 09:37:49 +02:00
										 |  |  | static void update_cylinder_pressure(struct dive *d, int old_depth, int new_depth, int duration, int sac, cylinder_t *cyl, bool in_deco) | 
					
						
							| 
									
										
										
										
											2014-05-29 14:36:14 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	volume_t gas_used; | 
					
						
							|  |  |  | 	pressure_t delta_p; | 
					
						
							|  |  |  | 	depth_t mean_depth; | 
					
						
							| 
									
										
										
										
											2015-01-15 23:22:12 +01:00
										 |  |  | 	int factor = 1000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (d->dc.divemode == PSCR) | 
					
						
							|  |  |  | 		factor = prefs.pscr_ratio; | 
					
						
							| 
									
										
										
										
											2014-05-29 14:36:14 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-01 09:59:38 -07:00
										 |  |  | 	if (!cyl) | 
					
						
							| 
									
										
										
										
											2014-05-29 14:36:14 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	mean_depth.mm = (old_depth + new_depth) / 2; | 
					
						
							| 
									
										
										
										
											2017-03-09 23:07:30 +07:00
										 |  |  | 	gas_used.mliter = lrint(depth_to_atm(mean_depth.mm, d) * sac / 60 * duration * factor / 1000); | 
					
						
							| 
									
										
										
										
											2014-06-01 09:59:38 -07:00
										 |  |  | 	cyl->gas_used.mliter += gas_used.mliter; | 
					
						
							| 
									
										
										
										
											2014-07-01 09:37:49 +02:00
										 |  |  | 	if (in_deco) | 
					
						
							|  |  |  | 		cyl->deco_gas_used.mliter += gas_used.mliter; | 
					
						
							| 
									
										
										
										
											2014-06-02 13:14:59 -07:00
										 |  |  | 	if (cyl->type.size.mliter) { | 
					
						
							| 
									
										
										
										
											2017-03-09 23:07:30 +07:00
										 |  |  | 		delta_p.mbar = lrint(gas_used.mliter * 1000.0 / cyl->type.size.mliter * gas_compressibility_factor(&cyl->gasmix, cyl->end.mbar / 1000.0)); | 
					
						
							| 
									
										
										
										
											2014-06-01 09:59:38 -07:00
										 |  |  | 		cyl->end.mbar -= delta_p.mbar; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-03 13:39:06 -07:00
										 |  |  | /* simply overwrite the data in the displayed_dive
 | 
					
						
							|  |  |  |  * return false if something goes wrong */ | 
					
						
							| 
									
										
										
										
											2017-08-28 23:59:58 +02:00
										 |  |  | static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, bool track_gas) | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct divedatapoint *dp; | 
					
						
							|  |  |  | 	struct divecomputer *dc; | 
					
						
							| 
									
										
										
										
											2013-01-28 19:54:05 -08:00
										 |  |  | 	struct sample *sample; | 
					
						
							| 
									
										
										
										
											2014-07-04 08:50:59 +02:00
										 |  |  | 	struct event *ev; | 
					
						
							| 
									
										
										
										
											2014-05-29 14:36:14 -07:00
										 |  |  | 	cylinder_t *cyl; | 
					
						
							| 
									
										
										
										
											2013-01-28 19:54:05 -08:00
										 |  |  | 	int oldpo2 = 0; | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 	int lasttime = 0; | 
					
						
							| 
									
										
										
										
											2017-03-10 13:37:54 +01:00
										 |  |  | 	depth_t lastdepth = {.mm = 0}; | 
					
						
							| 
									
										
										
										
											2017-10-08 05:21:31 +02:00
										 |  |  | 	int lastcylid; | 
					
						
							| 
									
										
										
										
											2017-08-28 23:59:58 +02:00
										 |  |  | 	enum dive_comp_type type = dive->dc.divemode; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!diveplan || !diveplan->dp) | 
					
						
							| 
									
										
										
										
											2014-07-03 13:39:06 -07:00
										 |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2013-01-08 08:52:48 -08:00
										 |  |  | #if DEBUG_PLAN & 4
 | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 	printf("in create_dive_from_plan\n"); | 
					
						
							| 
									
										
										
										
											2013-01-08 08:52:48 -08:00
										 |  |  | 	dump_plan(diveplan); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2017-08-28 23:59:58 +02:00
										 |  |  | 	dive->salinity = diveplan->salinity; | 
					
						
							| 
									
										
										
										
											2014-07-04 08:50:59 +02:00
										 |  |  | 	// reset the cylinders and clear out the samples and events of the
 | 
					
						
							|  |  |  | 	// displayed dive so we can restart
 | 
					
						
							| 
									
										
										
										
											2017-08-28 23:59:58 +02:00
										 |  |  | 	reset_cylinders(dive, track_gas); | 
					
						
							|  |  |  | 	dc = &dive->dc; | 
					
						
							|  |  |  | 	dc->when = dive->when = diveplan->when; | 
					
						
							| 
									
										
										
										
											2016-08-03 13:19:12 +02:00
										 |  |  | 	dc->surface_pressure.mbar = diveplan->surface_pressure; | 
					
						
							|  |  |  | 	dc->salinity = diveplan->salinity; | 
					
						
							| 
									
										
										
										
											2014-07-03 14:45:01 -07:00
										 |  |  | 	free(dc->sample); | 
					
						
							|  |  |  | 	dc->sample = NULL; | 
					
						
							|  |  |  | 	dc->samples = 0; | 
					
						
							|  |  |  | 	dc->alloc_samples = 0; | 
					
						
							| 
									
										
										
										
											2014-07-04 08:50:59 +02:00
										 |  |  | 	while ((ev = dc->events)) { | 
					
						
							|  |  |  | 		dc->events = dc->events->next; | 
					
						
							|  |  |  | 		free(ev); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 	dp = diveplan->dp; | 
					
						
							| 
									
										
										
										
											2017-10-08 05:21:31 +02:00
										 |  |  | 	/* Create first sample at time = 0, not based on dp because 
 | 
					
						
							|  |  |  | 	 * there is no real dp for time = 0, set first cylinder to 0 | 
					
						
							|  |  |  | 	 * O2 setpoint for this sample will be filled later from next dp */ | 
					
						
							|  |  |  | 	cyl = &dive->cylinder[0]; | 
					
						
							| 
									
										
										
										
											2013-01-28 19:54:05 -08:00
										 |  |  | 	sample = prepare_sample(dc); | 
					
						
							| 
									
										
										
										
											2015-01-16 13:49:12 +01:00
										 |  |  | 	sample->sac.mliter = prefs.bottomsac; | 
					
						
							| 
									
										
										
										
											2014-07-16 23:04:30 -07:00
										 |  |  | 	if (track_gas && cyl->type.workingpressure.mbar) | 
					
						
							| 
									
										
										
										
											2017-07-20 14:39:02 -07:00
										 |  |  | 		sample->pressure[0].mbar = cyl->end.mbar; | 
					
						
							| 
									
										
										
										
											2014-08-19 21:07:43 -05:00
										 |  |  | 	sample->manually_entered = true; | 
					
						
							| 
									
										
										
										
											2013-01-28 19:54:05 -08:00
										 |  |  | 	finish_sample(dc); | 
					
						
							| 
									
										
										
										
											2017-10-08 05:21:31 +02:00
										 |  |  | 	lastcylid = 0; | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | 	while (dp) { | 
					
						
							| 
									
										
										
										
											2014-10-19 07:07:07 -07:00
										 |  |  | 		int po2 = dp->setpoint; | 
					
						
							|  |  |  | 		if (dp->setpoint) | 
					
						
							|  |  |  | 			type = CCR; | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | 		int time = dp->time; | 
					
						
							| 
									
										
										
										
											2017-03-10 13:37:54 +01:00
										 |  |  | 		depth_t depth = dp->depth; | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-08 15:03:37 -08:00
										 |  |  | 		if (time == 0) { | 
					
						
							|  |  |  | 			/* special entries that just inform the algorithm about
 | 
					
						
							|  |  |  | 			 * additional gases that are available */ | 
					
						
							|  |  |  | 			dp = dp->next; | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-02 18:40:29 +01:00
										 |  |  | 		/* Check for SetPoint change */ | 
					
						
							| 
									
										
										
										
											2013-02-02 18:03:26 +01:00
										 |  |  | 		if (oldpo2 != po2) { | 
					
						
							| 
									
										
										
										
											2015-03-08 20:28:45 +01:00
										 |  |  | 			/* 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 */ | 
					
						
							| 
									
										
										
										
											2017-02-21 18:04:00 +01:00
										 |  |  | 			add_event(dc, lasttime, SAMPLE_EVENT_PO2, 0, po2, QT_TRANSLATE_NOOP("gettextFromC", "SP change")); | 
					
						
							| 
									
										
										
										
											2013-02-02 18:03:26 +01:00
										 |  |  | 			oldpo2 = po2; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-01 16:27:19 -07:00
										 |  |  | 		/* Make sure we have the new gas, and create a gas change event */ | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 		if (dp->cylinderid != lastcylid) { | 
					
						
							| 
									
										
										
										
											2014-06-01 10:02:38 -07:00
										 |  |  | 			/* need to insert a first sample for the new gas */ | 
					
						
							| 
									
										
										
										
											2017-08-28 23:59:58 +02:00
										 |  |  | 			add_gas_switch_event(dive, dc, lasttime + 1, dp->cylinderid); | 
					
						
							|  |  |  | 			cyl = &dive->cylinder[dp->cylinderid]; | 
					
						
							| 
									
										
										
										
											2014-05-29 14:36:14 -07:00
										 |  |  | 			sample = prepare_sample(dc); | 
					
						
							| 
									
										
										
										
											2014-10-19 07:07:07 -07:00
										 |  |  | 			sample[-1].setpoint.mbar = po2; | 
					
						
							| 
									
										
										
										
											2014-06-01 10:02:38 -07:00
										 |  |  | 			sample->time.seconds = lasttime + 1; | 
					
						
							| 
									
										
										
										
											2017-03-10 13:37:54 +01:00
										 |  |  | 			sample->depth = lastdepth; | 
					
						
							| 
									
										
										
										
											2014-08-19 21:07:43 -05:00
										 |  |  | 			sample->manually_entered = dp->entered; | 
					
						
							| 
									
										
										
										
											2015-01-16 13:49:12 +01:00
										 |  |  | 			sample->sac.mliter = dp->entered ? prefs.bottomsac : prefs.decosac; | 
					
						
							| 
									
										
										
										
											2014-05-29 14:36:14 -07:00
										 |  |  | 			finish_sample(dc); | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 			lastcylid = dp->cylinderid; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | 		/* Create sample */ | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 		sample = prepare_sample(dc); | 
					
						
							| 
									
										
										
										
											2013-01-28 19:54:05 -08:00
										 |  |  | 		/* set po2 at beginning of this segment */ | 
					
						
							|  |  |  | 		/* and keep it valid for last sample - where it likely doesn't matter */ | 
					
						
							| 
									
										
										
										
											2014-11-25 14:27:09 +01:00
										 |  |  | 		sample[-1].setpoint.mbar = po2; | 
					
						
							| 
									
										
										
										
											2014-12-12 14:51:25 +01:00
										 |  |  | 		sample->setpoint.mbar = po2; | 
					
						
							| 
									
										
										
										
											2014-06-01 10:02:38 -07:00
										 |  |  | 		sample->time.seconds = lasttime = time; | 
					
						
							| 
									
										
										
										
											2017-03-10 13:37:54 +01:00
										 |  |  | 		sample->depth = lastdepth = depth; | 
					
						
							| 
									
										
										
										
											2014-08-19 21:07:43 -05:00
										 |  |  | 		sample->manually_entered = dp->entered; | 
					
						
							| 
									
										
										
										
											2015-01-16 13:49:12 +01:00
										 |  |  | 		sample->sac.mliter = dp->entered ? prefs.bottomsac : prefs.decosac; | 
					
						
							| 
									
										
										
										
											2014-11-25 14:27:09 +01:00
										 |  |  | 		if (track_gas && !sample[-1].setpoint.mbar) {    /* Don't track gas usage for CCR legs of dive */ | 
					
						
							| 
									
										
										
										
											2017-08-28 23:59:58 +02:00
										 |  |  | 			update_cylinder_pressure(dive, sample[-1].depth.mm, depth.mm, time - sample[-1].time.seconds, | 
					
						
							| 
									
										
										
										
											2014-07-04 11:26:31 -07:00
										 |  |  | 					dp->entered ? diveplan->bottomsac : diveplan->decosac, cyl, !dp->entered); | 
					
						
							| 
									
										
										
										
											2014-07-16 23:04:30 -07:00
										 |  |  | 			if (cyl->type.workingpressure.mbar) | 
					
						
							| 
									
										
										
										
											2017-07-20 14:39:02 -07:00
										 |  |  | 				sample->pressure[0].mbar = cyl->end.mbar; | 
					
						
							| 
									
										
										
										
											2014-07-04 11:26:31 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | 		finish_sample(dc); | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 		dp = dp->next; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-01-11 00:01:15 +01:00
										 |  |  | 	dc->divemode = type; | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | #if DEBUG_PLAN & 32
 | 
					
						
							| 
									
										
										
										
											2014-07-03 13:39:06 -07:00
										 |  |  | 	save_dive(stdout, &displayed_dive); | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2014-07-03 13:39:06 -07:00
										 |  |  | 	return; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-19 11:41:09 -05:00
										 |  |  | void free_dps(struct diveplan *diveplan) | 
					
						
							| 
									
										
										
										
											2013-01-06 22:09:12 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-08-19 11:41:09 -05:00
										 |  |  | 	if (!diveplan) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	struct divedatapoint *dp = diveplan->dp; | 
					
						
							| 
									
										
										
										
											2013-01-06 22:09:12 -08:00
										 |  |  | 	while (dp) { | 
					
						
							|  |  |  | 		struct divedatapoint *ndp = dp->next; | 
					
						
							|  |  |  | 		free(dp); | 
					
						
							|  |  |  | 		dp = ndp; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-08-19 11:41:09 -05:00
										 |  |  | 	diveplan->dp = NULL; | 
					
						
							| 
									
										
										
										
											2013-01-06 22:09:12 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | struct divedatapoint *create_dp(int time_incr, int depth, int cylinderid, int po2) | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct divedatapoint *dp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dp = malloc(sizeof(struct divedatapoint)); | 
					
						
							|  |  |  | 	dp->time = time_incr; | 
					
						
							| 
									
										
										
										
											2017-03-10 13:37:54 +01:00
										 |  |  | 	dp->depth.mm = depth; | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 	dp->cylinderid = cylinderid; | 
					
						
							| 
									
										
										
										
											2017-03-15 22:28:36 +01:00
										 |  |  | 	dp->minimum_gas.mbar = 0; | 
					
						
							| 
									
										
										
										
											2014-10-19 07:07:07 -07:00
										 |  |  | 	dp->setpoint = po2; | 
					
						
							| 
									
										
										
										
											2014-01-15 19:54:41 +01:00
										 |  |  | 	dp->entered = false; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 	dp->next = NULL; | 
					
						
							|  |  |  | 	return dp; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void add_to_end_of_diveplan(struct diveplan *diveplan, struct divedatapoint *dp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct divedatapoint **lastdp = &diveplan->dp; | 
					
						
							|  |  |  | 	struct divedatapoint *ldp = *lastdp; | 
					
						
							| 
									
										
										
										
											2013-01-08 15:03:37 -08:00
										 |  |  | 	int lasttime = 0; | 
					
						
							| 
									
										
										
										
											2013-01-30 08:10:46 +11:00
										 |  |  | 	while (*lastdp) { | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 		ldp = *lastdp; | 
					
						
							| 
									
										
										
										
											2013-01-08 15:03:37 -08:00
										 |  |  | 		if (ldp->time > lasttime) | 
					
						
							|  |  |  | 			lasttime = ldp->time; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 		lastdp = &(*lastdp)->next; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	*lastdp = dp; | 
					
						
							| 
									
										
										
										
											2013-11-12 11:19:04 +09:00
										 |  |  | 	if (ldp && dp->time != 0) | 
					
						
							| 
									
										
										
										
											2013-01-08 15:03:37 -08:00
										 |  |  | 		dp->time += lasttime; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, int depth, int cylinderid, int po2, bool entered) | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 	struct divedatapoint *dp = create_dp(duration, depth, cylinderid, po2); | 
					
						
							| 
									
										
										
										
											2014-03-12 16:49:42 +01:00
										 |  |  | 	dp->entered = entered; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 	add_to_end_of_diveplan(diveplan, dp); | 
					
						
							| 
									
										
										
										
											2017-02-03 03:10:57 -08:00
										 |  |  | 	return dp; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | struct gaschanges { | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | 	int depth; | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 	int gasidx; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-30 17:09:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | static struct gaschanges *analyze_gaslist(struct diveplan *diveplan, int *gaschangenr, int depth, int *asc_cylinder) | 
					
						
							| 
									
										
										
										
											2013-01-08 08:52:48 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 	int nr = 0; | 
					
						
							|  |  |  | 	struct gaschanges *gaschanges = NULL; | 
					
						
							|  |  |  | 	struct divedatapoint *dp = diveplan->dp; | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | 	int best_depth = displayed_dive.cylinder[*asc_cylinder].depth.mm; | 
					
						
							| 
									
										
										
										
											2017-02-09 18:12:44 +01:00
										 |  |  | 	bool total_time_zero = true; | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 	while (dp) { | 
					
						
							| 
									
										
										
										
											2017-02-09 18:12:44 +01:00
										 |  |  | 		if (dp->time == 0 && total_time_zero) { | 
					
						
							| 
									
										
										
										
											2017-03-10 13:37:54 +01:00
										 |  |  | 			if (dp->depth.mm <= depth) { | 
					
						
							| 
									
										
										
										
											2014-05-30 17:09:05 +02:00
										 |  |  | 				int i = 0; | 
					
						
							|  |  |  | 				nr++; | 
					
						
							|  |  |  | 				gaschanges = realloc(gaschanges, nr * sizeof(struct gaschanges)); | 
					
						
							|  |  |  | 				while (i < nr - 1) { | 
					
						
							| 
									
										
										
										
											2017-03-10 13:37:54 +01:00
										 |  |  | 					if (dp->depth.mm < gaschanges[i].depth) { | 
					
						
							| 
									
										
										
										
											2014-05-30 17:09:05 +02:00
										 |  |  | 						memmove(gaschanges + i + 1, gaschanges + i, (nr - i - 1) * sizeof(struct gaschanges)); | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					i++; | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2017-03-10 13:37:54 +01:00
										 |  |  | 				gaschanges[i].depth = dp->depth.mm; | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 				gaschanges[i].gasidx = dp->cylinderid; | 
					
						
							| 
									
										
										
										
											2014-05-30 17:09:05 +02:00
										 |  |  | 				assert(gaschanges[i].gasidx != -1); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				/* is there a better mix to start deco? */ | 
					
						
							| 
									
										
										
										
											2017-03-10 13:37:54 +01:00
										 |  |  | 				if (dp->depth.mm < best_depth) { | 
					
						
							|  |  |  | 					best_depth = dp->depth.mm; | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 					*asc_cylinder = dp->cylinderid; | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2014-05-30 17:09:05 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-02-09 18:12:44 +01:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			total_time_zero = false; | 
					
						
							| 
									
										
										
										
											2013-01-08 08:52:48 -08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 		dp = dp->next; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	*gaschangenr = nr; | 
					
						
							|  |  |  | #if DEBUG_PLAN & 16
 | 
					
						
							| 
									
										
										
										
											2014-06-01 18:25:44 -07:00
										 |  |  | 	for (nr = 0; nr < *gaschangenr; nr++) { | 
					
						
							|  |  |  | 		int idx = gaschanges[nr].gasidx; | 
					
						
							|  |  |  | 		printf("gaschange nr %d: @ %5.2lfm gasidx %d (%s)\n", nr, gaschanges[nr].depth / 1000.0, | 
					
						
							| 
									
										
										
										
											2014-07-03 13:39:06 -07:00
										 |  |  | 		       idx, gasname(&displayed_dive.cylinder[idx].gasmix)); | 
					
						
							| 
									
										
										
										
											2014-06-01 18:25:44 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 	return gaschanges; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* sort all the stops into one ordered list */ | 
					
						
							| 
									
										
										
										
											2016-03-23 09:53:44 -07:00
										 |  |  | static int *sort_stops(int *dstops, int dnr, struct gaschanges *gstops, int gnr) | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i, gi, di; | 
					
						
							|  |  |  | 	int total = dnr + gnr; | 
					
						
							| 
									
										
										
										
											2016-03-23 09:53:44 -07:00
										 |  |  | 	int *stoplevels = malloc(total * sizeof(int)); | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* no gaschanges */ | 
					
						
							|  |  |  | 	if (gnr == 0) { | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | 		memcpy(stoplevels, dstops, dnr * sizeof(int)); | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 		return stoplevels; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	i = total - 1; | 
					
						
							|  |  |  | 	gi = gnr - 1; | 
					
						
							|  |  |  | 	di = dnr - 1; | 
					
						
							|  |  |  | 	while (i >= 0) { | 
					
						
							|  |  |  | 		if (dstops[di] > gstops[gi].depth) { | 
					
						
							|  |  |  | 			stoplevels[i] = dstops[di]; | 
					
						
							|  |  |  | 			di--; | 
					
						
							|  |  |  | 		} else if (dstops[di] == gstops[gi].depth) { | 
					
						
							|  |  |  | 			stoplevels[i] = dstops[di]; | 
					
						
							|  |  |  | 			di--; | 
					
						
							|  |  |  | 			gi--; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			stoplevels[i] = gstops[gi].depth; | 
					
						
							|  |  |  | 			gi--; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		i--; | 
					
						
							|  |  |  | 		if (di < 0) { | 
					
						
							|  |  |  | 			while (gi >= 0) | 
					
						
							|  |  |  | 				stoplevels[i--] = gstops[gi--].depth; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (gi < 0) { | 
					
						
							|  |  |  | 			while (di >= 0) | 
					
						
							|  |  |  | 				stoplevels[i--] = dstops[di--]; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	while (i >= 0) | 
					
						
							|  |  |  | 		stoplevels[i--] = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if DEBUG_PLAN & 16
 | 
					
						
							|  |  |  | 	int k; | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 	for (k = gnr + dnr - 1; k >= 0; k--) { | 
					
						
							|  |  |  | 		printf("stoplevel[%d]: %5.2lfm\n", k, stoplevels[k] / 1000.0); | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 		if (stoplevels[k] == 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2013-01-08 08:52:48 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 	return stoplevels; | 
					
						
							| 
									
										
										
										
											2013-01-08 08:52:48 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | int ascent_velocity(int depth, int avg_depth, int bottom_time) | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-03-07 16:54:49 -03:00
										 |  |  | 	(void) bottom_time; | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | 	/* We need to make this configurable */ | 
					
						
							| 
									
										
										
										
											2014-04-26 17:29:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* As an example (and possibly reasonable default) this is the Tech 1 provedure according
 | 
					
						
							|  |  |  | 	 * to http://www.globalunderwaterexplorers.org/files/Standards_and_Procedures/SOP_Manual_Ver2.0.2.pdf */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-25 00:08:36 +02:00
										 |  |  | 	if (depth * 4 > avg_depth * 3) { | 
					
						
							|  |  |  | 		return prefs.ascrate75; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if (depth * 2 > avg_depth) { | 
					
						
							|  |  |  | 			return prefs.ascrate50; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			if (depth > 6000) | 
					
						
							|  |  |  | 				return prefs.ascratestops; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				return prefs.ascratelast6m; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-08 19:18:54 +02:00
										 |  |  | void track_ascent_gas(int depth, cylinder_t *cylinder, int avg_depth, int bottom_time, bool safety_stop) | 
					
						
							| 
									
										
										
										
											2015-04-04 18:38:56 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	while (depth > 0) { | 
					
						
							| 
									
										
										
										
											2015-04-04 18:41:09 +02:00
										 |  |  | 		int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP; | 
					
						
							| 
									
										
										
										
											2015-04-04 18:38:56 +02:00
										 |  |  | 		if (deltad > depth) | 
					
						
							|  |  |  | 			deltad = depth; | 
					
						
							| 
									
										
										
										
											2015-04-09 20:26:56 +02:00
										 |  |  | 		update_cylinder_pressure(&displayed_dive, depth, depth - deltad, TIMESTEP, prefs.decosac, cylinder, true); | 
					
						
							| 
									
										
										
										
											2015-05-21 19:55:28 +02:00
										 |  |  | 		if (depth <= 5000 && depth >= (5000 - deltad) && safety_stop) { | 
					
						
							| 
									
										
										
										
											2015-04-09 20:26:56 +02:00
										 |  |  | 			update_cylinder_pressure(&displayed_dive, 5000, 5000, 180, prefs.decosac, cylinder, true); | 
					
						
							| 
									
										
										
										
											2015-04-08 19:18:54 +02:00
										 |  |  | 			safety_stop = false; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-04-04 18:38:56 +02:00
										 |  |  | 		depth -= deltad; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-27 22:06:02 +10:00
										 |  |  | // Determine whether ascending to the next stop will break the ceiling.  Return true if the ascent is ok, false if it isn't.
 | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | bool trial_ascent(struct deco_state *ds, int wait_time, int trial_depth, int stoplevel, int avg_depth, int bottom_time, struct gasmix *gasmix, int po2, double surface_pressure, struct dive *dive) | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bool clear_to_ascend = true; | 
					
						
							| 
									
										
										
										
											2017-05-26 00:45:53 +02:00
										 |  |  | 	struct deco_state *trial_cache = NULL; | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-30 00:07:22 +10:00
										 |  |  | 	// For consistency with other VPM-B implementations, we should not start the ascent while the ceiling is
 | 
					
						
							|  |  |  | 	// deeper than the next stop (thus the offgasing during the ascent is ignored).
 | 
					
						
							|  |  |  | 	// However, we still need to make sure we don't break the ceiling due to on-gassing during ascent.
 | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 	cache_deco_state(ds, &trial_cache); | 
					
						
							| 
									
										
										
										
											2017-08-24 23:45:33 +02:00
										 |  |  | 	if (wait_time) | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		add_segment(ds, depth_to_bar(trial_depth, dive), | 
					
						
							| 
									
										
										
										
											2017-08-24 23:45:33 +02:00
										 |  |  | 			    gasmix, | 
					
						
							|  |  |  | 			    wait_time, po2, dive, prefs.decosac); | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 	if (decoMode() == VPMB && (deco_allowed_depth(tissue_tolerance_calc(ds, dive,depth_to_bar(stoplevel, dive)), | 
					
						
							|  |  |  | 						      surface_pressure, dive, 1) | 
					
						
							|  |  |  | 				   > stoplevel)) { | 
					
						
							|  |  |  | 		restore_deco_state(trial_cache, ds, false); | 
					
						
							| 
									
										
										
										
											2017-05-26 00:45:53 +02:00
										 |  |  | 		free(trial_cache); | 
					
						
							| 
									
										
										
										
											2015-08-30 00:07:22 +10:00
										 |  |  | 		return false; | 
					
						
							| 
									
										
										
										
											2017-05-26 00:45:53 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-08-18 09:07:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 	while (trial_depth > stoplevel) { | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | 		int deltad = ascent_velocity(trial_depth, avg_depth, bottom_time) * TIMESTEP; | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 		if (deltad > trial_depth) /* don't test against depth above surface */ | 
					
						
							|  |  |  | 			deltad = trial_depth; | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		add_segment(ds, depth_to_bar(trial_depth, dive), | 
					
						
							| 
									
										
										
										
											2015-08-31 23:25:28 +02:00
										 |  |  | 			    gasmix, | 
					
						
							| 
									
										
										
										
											2017-08-24 23:45:33 +02:00
										 |  |  | 			    TIMESTEP, po2, dive, prefs.decosac); | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		if (deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(trial_depth, dive)), | 
					
						
							| 
									
										
										
										
											2017-08-24 23:45:33 +02:00
										 |  |  | 				       surface_pressure, dive, 1) > trial_depth - deltad) { | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 			/* We should have stopped */ | 
					
						
							|  |  |  | 			clear_to_ascend = false; | 
					
						
							| 
									
										
										
										
											2015-07-03 23:24:20 +02:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 		trial_depth -= deltad; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 	restore_deco_state(trial_cache, ds, false); | 
					
						
							| 
									
										
										
										
											2015-06-21 20:24:07 -07:00
										 |  |  | 	free(trial_cache); | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 	return clear_to_ascend; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-27 22:06:02 +10:00
										 |  |  | /* Determine if there is enough gas for the dive.  Return true if there is enough.
 | 
					
						
							|  |  |  |  * Also return true if this cannot be calculated because the cylinder doesn't have | 
					
						
							|  |  |  |  * size or a starting pressure. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2015-04-04 18:38:56 +02:00
										 |  |  | bool enough_gas(int current_cylinder) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	cylinder_t *cyl; | 
					
						
							|  |  |  | 	cyl = &displayed_dive.cylinder[current_cylinder]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!cyl->start.mbar) | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 	if (cyl->type.size.mliter) | 
					
						
							| 
									
										
										
										
											2017-02-03 03:10:57 -08:00
										 |  |  | 		return (float)(cyl->end.mbar - prefs.reserve_gas) * cyl->type.size.mliter / 1000.0 > (float) cyl->deco_gas_used.mliter; | 
					
						
							| 
									
										
										
										
											2015-04-04 18:38:56 +02:00
										 |  |  | 	else | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-24 23:45:33 +02:00
										 |  |  | /* Do a binary search for the time the ceiling is clear to ascent to target_depth.
 | 
					
						
							|  |  |  |  * Minimal solution is min + 1, and the solution should be an integer multiple of stepsize. | 
					
						
							|  |  |  |  * leap is a guess for the maximum but there is no guarantee that leap is an upper limit. | 
					
						
							|  |  |  |  * So we always test at the upper bundary, not in the middle! | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | int wait_until(struct deco_state *ds, struct dive *dive, int clock, int min, int leap, int stepsize, int depth, int target_depth, int avg_depth, int bottom_time, struct gasmix *gasmix, int po2, double surface_pressure) | 
					
						
							| 
									
										
										
										
											2017-08-24 23:45:33 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	// Round min + leap up to the next multiple of stepsize
 | 
					
						
							|  |  |  | 	int upper = min + leap + stepsize - 1 - (min + leap - 1) % stepsize; | 
					
						
							|  |  |  | 	// Is the upper boundary too small?
 | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 	if (!trial_ascent(ds, upper - clock, depth, target_depth, avg_depth, bottom_time, gasmix, po2, surface_pressure, dive)) | 
					
						
							|  |  |  | 		return wait_until(ds, dive, clock, upper, leap, stepsize, depth, target_depth, avg_depth, bottom_time, gasmix, po2, surface_pressure); | 
					
						
							| 
									
										
										
										
											2017-08-24 23:45:33 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (upper - min <= stepsize) | 
					
						
							|  |  |  | 		return upper; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 	return wait_until(ds, dive, clock, min, leap / 2, stepsize, depth, target_depth, avg_depth, bottom_time, gasmix, po2, surface_pressure); | 
					
						
							| 
									
										
										
										
											2017-08-24 23:45:33 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-09 21:55:59 +02:00
										 |  |  | // Work out the stops. Return value is if there were any mandatory stops.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-27 22:49:41 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | void printdecotable(struct decostop *table) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	while (table->depth) { | 
					
						
							|  |  |  | 		printf("depth=%d time=%d\n", table->depth, table->time); | 
					
						
							|  |  |  | 		++table; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, int timestep, struct decostop *decostoptable, struct deco_state **cached_datap, bool is_planner, bool show_disclaimer) | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 	int bottom_depth; | 
					
						
							|  |  |  | 	int bottom_gi; | 
					
						
							|  |  |  | 	int bottom_stopidx; | 
					
						
							|  |  |  | 	bool is_final_plan = true; | 
					
						
							| 
									
										
										
										
											2017-11-03 07:32:28 +11:00
										 |  |  | 	int bottom_time; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 	int previous_deco_time; | 
					
						
							| 
									
										
										
										
											2017-05-26 00:45:53 +02:00
										 |  |  | 	struct deco_state *bottom_cache = NULL; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 	struct sample *sample; | 
					
						
							| 
									
										
										
										
											2014-06-01 17:45:00 -07:00
										 |  |  | 	int po2; | 
					
						
							| 
									
										
										
										
											2013-10-07 22:37:32 -07:00
										 |  |  | 	int transitiontime, gi; | 
					
						
							| 
									
										
										
										
											2017-11-02 14:15:46 +01:00
										 |  |  | 	int current_cylinder, stop_cylinder; | 
					
						
							| 
									
										
										
										
											2016-03-23 09:53:44 -07:00
										 |  |  | 	int stopidx; | 
					
						
							| 
									
										
										
										
											2014-04-18 13:06:21 -07:00
										 |  |  | 	int depth; | 
					
						
							| 
									
										
										
										
											2013-11-12 11:19:04 +09:00
										 |  |  | 	struct gaschanges *gaschanges = NULL; | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 	int gaschangenr; | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | 	int *decostoplevels; | 
					
						
							|  |  |  | 	int decostoplevelcount; | 
					
						
							| 
									
										
										
										
											2016-03-23 09:53:44 -07:00
										 |  |  | 	int *stoplevels = NULL; | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | 	bool stopping = false; | 
					
						
							| 
									
										
										
										
											2015-06-22 22:43:20 +10:00
										 |  |  | 	bool pendinggaschange = false; | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | 	int clock, previous_point_time; | 
					
						
							| 
									
										
										
										
											2017-10-27 10:06:11 +11:00
										 |  |  | 	int avg_depth, max_depth; | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | 	int last_ascend_rate; | 
					
						
							| 
									
										
										
										
											2014-05-30 17:09:05 +02:00
										 |  |  | 	int best_first_ascend_cylinder; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 	struct gasmix gas, bottom_gas; | 
					
						
							| 
									
										
										
										
											2017-11-02 14:15:46 +01:00
										 |  |  | 	bool o2break_next = false; | 
					
						
							| 
									
										
										
										
											2017-10-10 09:46:34 +02:00
										 |  |  | 	int break_cylinder = -1, breakfrom_cylinder = 0; | 
					
						
							| 
									
										
										
										
											2017-11-08 20:59:47 +01:00
										 |  |  | 	bool last_segment_min_switch = false; | 
					
						
							| 
									
										
										
										
											2014-08-05 12:58:23 +02:00
										 |  |  | 	int error = 0; | 
					
						
							| 
									
										
										
										
											2015-05-09 21:55:59 +02:00
										 |  |  | 	bool decodive = false; | 
					
						
							| 
									
										
										
										
											2016-11-23 11:50:50 +01:00
										 |  |  | 	int first_stop_depth = 0; | 
					
						
							| 
									
										
										
										
											2017-08-24 23:45:33 +02:00
										 |  |  | 	int laststoptime = timestep; | 
					
						
							| 
									
										
										
										
											2017-08-25 00:08:14 +02:00
										 |  |  | 	bool o2breaking = false; | 
					
						
							| 
									
										
										
										
											2017-08-27 22:49:41 +02:00
										 |  |  | 	int decostopcounter = 0; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-19 14:38:38 +02:00
										 |  |  | 	set_gf(diveplan->gflow, diveplan->gfhigh); | 
					
						
							| 
									
										
										
										
											2017-08-29 11:41:30 +02:00
										 |  |  | 	lock_planner(); | 
					
						
							| 
									
										
										
										
											2016-09-24 18:02:08 +10:00
										 |  |  | 	set_vpmb_conservatism(diveplan->vpmb_conservatism); | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 	if (!diveplan->surface_pressure) | 
					
						
							| 
									
										
										
										
											2013-01-14 23:53:38 +01:00
										 |  |  | 		diveplan->surface_pressure = SURFACE_PRESSURE; | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 	dive->surface_pressure.mbar = diveplan->surface_pressure; | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 	clear_deco(ds, dive->surface_pressure.mbar / 1000.0); | 
					
						
							|  |  |  | 	ds->max_bottom_ceiling_pressure.mbar = ds->first_ceiling_pressure.mbar = 0; | 
					
						
							| 
									
										
										
										
											2017-08-28 23:59:58 +02:00
										 |  |  | 	create_dive_from_plan(diveplan, dive, is_planner); | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-27 22:06:02 +10:00
										 |  |  | 	// Do we want deco stop array in metres or feet?
 | 
					
						
							| 
									
										
										
											
												Planner deco stops are at 10ft increments when measured in feet
When using feet as depth unit, deco stop levels should be at 10 ft rather
than 3 m increments.
For shallow stops, rounding means the difference is not apparent. However,
with stops deeper than 30 feet, using 3 m increments leads stops at 39ft,
49ft, ..., 98ft, etc.
Apart from making plans look messy, the old behaviour makes it harder to
benchmark the planner against published profiles in imperial units.
This revised patch uses the help macro M_OR_FT(6, 20) to set the last stop
at the correct depth.
Signed-off-by: Rick Walsh <rickmwalsh@gmail.com>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
											
										 
											2015-07-04 23:10:34 +10:00
										 |  |  | 	if (prefs.units.length == METERS ) { | 
					
						
							| 
									
										
										
										
											2015-07-26 13:34:43 +10:00
										 |  |  | 		decostoplevels = decostoplevels_metric; | 
					
						
							|  |  |  | 		decostoplevelcount = sizeof(decostoplevels_metric) / sizeof(int); | 
					
						
							| 
									
										
										
										
											2015-07-04 22:14:10 +10:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2015-07-26 13:34:43 +10:00
										 |  |  | 		decostoplevels = decostoplevels_imperial; | 
					
						
							|  |  |  | 		decostoplevelcount = sizeof(decostoplevels_imperial) / sizeof(int); | 
					
						
							| 
									
										
										
										
											2015-07-04 22:14:10 +10:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-05 14:17:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-27 22:06:02 +10:00
										 |  |  | 	/* If the user has selected last stop to be at 6m/20', we need to get rid of the 3m/10' stop.
 | 
					
						
							|  |  |  | 	 * Otherwise reinstate the last stop 3m/10' stop. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
											
												Planner deco stops are at 10ft increments when measured in feet
When using feet as depth unit, deco stop levels should be at 10 ft rather
than 3 m increments.
For shallow stops, rounding means the difference is not apparent. However,
with stops deeper than 30 feet, using 3 m increments leads stops at 39ft,
49ft, ..., 98ft, etc.
Apart from making plans look messy, the old behaviour makes it harder to
benchmark the planner against published profiles in imperial units.
This revised patch uses the help macro M_OR_FT(6, 20) to set the last stop
at the correct depth.
Signed-off-by: Rick Walsh <rickmwalsh@gmail.com>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
											
										 
											2015-07-04 23:10:34 +10:00
										 |  |  | 	if (prefs.last_stop) | 
					
						
							| 
									
										
										
										
											2015-07-26 13:34:43 +10:00
										 |  |  | 		*(decostoplevels + 1) = 0; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		*(decostoplevels + 1) = M_OR_FT(3,10); | 
					
						
							| 
									
										
										
											
												Planner deco stops are at 10ft increments when measured in feet
When using feet as depth unit, deco stop levels should be at 10 ft rather
than 3 m increments.
For shallow stops, rounding means the difference is not apparent. However,
with stops deeper than 30 feet, using 3 m increments leads stops at 39ft,
49ft, ..., 98ft, etc.
Apart from making plans look messy, the old behaviour makes it harder to
benchmark the planner against published profiles in imperial units.
This revised patch uses the help macro M_OR_FT(6, 20) to set the last stop
at the correct depth.
Signed-off-by: Rick Walsh <rickmwalsh@gmail.com>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
											
										 
											2015-07-04 23:10:34 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-18 16:08:33 +02:00
										 |  |  | 	/* Let's start at the last 'sample', i.e. the last manually entered waypoint. */ | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 	sample = &dive->dc.sample[dive->dc.samples - 1]; | 
					
						
							| 
									
										
										
										
											2014-07-17 21:16:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-02 17:59:20 +11:00
										 |  |  | 	/* Keep time during the ascend */ | 
					
						
							| 
									
										
										
										
											2017-11-03 07:32:28 +11:00
										 |  |  | 	bottom_time = clock = previous_point_time = dive->dc.sample[dive->dc.samples - 1].time.seconds; | 
					
						
							| 
									
										
										
										
											2017-11-02 17:59:20 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 	current_cylinder = get_cylinderid_at_time(dive, &dive->dc, sample->time); | 
					
						
							|  |  |  | 	gas = dive->cylinder[current_cylinder].gasmix; | 
					
						
							| 
									
										
										
										
											2014-07-17 21:16:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-19 07:07:07 -07:00
										 |  |  | 	po2 = sample->setpoint.mbar; | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 	depth = dive->dc.sample[dive->dc.samples - 1].depth.mm; | 
					
						
							| 
									
										
										
										
											2015-04-02 10:49:24 +02:00
										 |  |  | 	average_max_depth(diveplan, &avg_depth, &max_depth); | 
					
						
							| 
									
										
										
										
											2017-11-03 07:32:28 +11:00
										 |  |  | 	last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); | 
					
						
							| 
									
										
										
										
											2013-09-18 22:40:34 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-10 07:50:19 +01:00
										 |  |  | 	/* if all we wanted was the dive just get us back to the surface */ | 
					
						
							| 
									
										
										
										
											2014-07-04 11:26:31 -07:00
										 |  |  | 	if (!is_planner) { | 
					
						
							| 
									
										
										
										
											2013-12-10 07:50:19 +01:00
										 |  |  | 		transitiontime = depth / 75; /* this still needs to be made configurable */ | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 		plan_add_segment(diveplan, transitiontime, 0, current_cylinder, po2, false); | 
					
						
							| 
									
										
										
										
											2017-08-28 23:59:58 +02:00
										 |  |  | 		create_dive_from_plan(diveplan, dive, is_planner); | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		unlock_planner(); | 
					
						
							| 
									
										
										
										
											2017-02-03 03:10:57 -08:00
										 |  |  | 		return false; | 
					
						
							| 
									
										
										
										
											2013-09-18 22:40:34 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-08 14:05:25 -08:00
										 |  |  | #if DEBUG_PLAN & 4
 | 
					
						
							| 
									
										
										
										
											2014-06-01 18:25:44 -07:00
										 |  |  | 	printf("gas %s\n", gasname(&gas)); | 
					
						
							| 
									
										
										
										
											2014-07-03 21:02:39 -07:00
										 |  |  | 	printf("depth %5.2lfm \n", depth / 1000.0); | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 	printf("current_cylinder %i\n", current_cylinder); | 
					
						
							| 
									
										
										
										
											2013-01-08 08:52:48 -08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-30 17:09:05 +02:00
										 |  |  | 	best_first_ascend_cylinder = current_cylinder; | 
					
						
							| 
									
										
										
										
											2014-04-18 16:08:33 +02:00
										 |  |  | 	/* Find the gases available for deco */ | 
					
						
							| 
									
										
										
										
											2014-11-21 11:10:19 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (po2) {	// Don't change gas in CCR mode
 | 
					
						
							|  |  |  | 		gaschanges = NULL; | 
					
						
							|  |  |  | 		gaschangenr = 0; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		gaschanges = analyze_gaslist(diveplan, &gaschangenr, depth, &best_first_ascend_cylinder); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-04-18 16:08:33 +02:00
										 |  |  | 	/* Find the first potential decostopdepth above current depth */ | 
					
						
							| 
									
										
										
										
											2015-07-26 13:34:43 +10:00
										 |  |  | 	for (stopidx = 0; stopidx < decostoplevelcount; stopidx++) | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | 		if (*(decostoplevels + stopidx) >= depth) | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2013-11-08 21:40:59 +09:00
										 |  |  | 	if (stopidx > 0) | 
					
						
							|  |  |  | 		stopidx--; | 
					
						
							| 
									
										
										
										
											2014-04-18 16:08:33 +02:00
										 |  |  | 	/* Stoplevels are either depths of gas changes or potential deco stop depths. */ | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 	stoplevels = sort_stops(decostoplevels, stopidx + 1, gaschanges, gaschangenr); | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | 	stopidx += gaschangenr; | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	gi = gaschangenr - 1; | 
					
						
							| 
									
										
										
										
											2015-07-03 23:07:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-29 21:54:22 +10:00
										 |  |  | 	/* Set tissue tolerance and initial vpmb gradient at start of ascent phase */ | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 	diveplan->surface_interval = tissue_at_end(ds, dive, cached_datap); | 
					
						
							|  |  |  | 	nuclear_regeneration(ds, clock); | 
					
						
							|  |  |  | 	vpmb_start_gradient(ds); | 
					
						
							| 
									
										
										
										
											2015-08-29 21:54:22 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-03 03:10:57 -08:00
										 |  |  | 	if (decoMode() == RECREATIONAL) { | 
					
						
							| 
									
										
										
										
											2015-04-02 10:49:24 +02:00
										 |  |  | 		bool safety_stop = prefs.safetystop && max_depth >= 10000; | 
					
						
							| 
									
										
										
										
											2017-11-03 07:32:28 +11:00
										 |  |  | 		track_ascent_gas(depth, &dive->cylinder[current_cylinder], avg_depth, bottom_time, safety_stop); | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 		// How long can we stay at the current depth and still directly ascent to the surface?
 | 
					
						
							| 
									
										
										
										
											2015-10-12 22:03:50 +02:00
										 |  |  | 		do { | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 			add_segment(ds, depth_to_bar(depth, dive), | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 				    &dive->cylinder[current_cylinder].gasmix, | 
					
						
							|  |  |  | 				    timestep, po2, dive, prefs.bottomsac); | 
					
						
							|  |  |  | 			update_cylinder_pressure(dive, depth, depth, timestep, prefs.bottomsac, &dive->cylinder[current_cylinder], false); | 
					
						
							|  |  |  | 			clock += timestep; | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		} while (trial_ascent(ds, 0, depth, 0, avg_depth, bottom_time, &dive->cylinder[current_cylinder].gasmix, | 
					
						
							| 
									
										
										
										
											2017-08-24 23:45:33 +02:00
										 |  |  | 				      po2, diveplan->surface_pressure / 1000.0, dive) && | 
					
						
							| 
									
										
										
										
											2015-10-12 22:03:50 +02:00
										 |  |  | 			 enough_gas(current_cylinder)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// We did stay one DECOTIMESTEP too many.
 | 
					
						
							|  |  |  | 		// In the best of all worlds, we would roll back also the last add_segment in terms of caching deco state, but
 | 
					
						
							|  |  |  | 		// let's ignore that since for the eventual ascent in recreational mode, nobody looks at the ceiling anymore,
 | 
					
						
							|  |  |  | 		// so we don't really have to compute the deco state.
 | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 		update_cylinder_pressure(dive, depth, depth, -timestep, prefs.bottomsac, &dive->cylinder[current_cylinder], false); | 
					
						
							|  |  |  | 		clock -= timestep; | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 		plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, true); | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 		previous_point_time = clock; | 
					
						
							|  |  |  | 		do { | 
					
						
							|  |  |  | 			/* Ascend to surface */ | 
					
						
							| 
									
										
										
										
											2017-11-03 07:32:28 +11:00
										 |  |  | 			int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP; | 
					
						
							|  |  |  | 			if (ascent_velocity(depth, avg_depth, bottom_time) != last_ascend_rate) { | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 				plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, false); | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 				previous_point_time = clock; | 
					
						
							| 
									
										
										
										
											2017-11-03 07:32:28 +11:00
										 |  |  | 				last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | 			if (depth - deltad < 0) | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 				deltad = depth; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			clock += TIMESTEP; | 
					
						
							|  |  |  | 			depth -= deltad; | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | 			if (depth <= 5000 && depth >= (5000 - deltad) && safety_stop) { | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 				plan_add_segment(diveplan, clock - previous_point_time, 5000, current_cylinder, po2, false); | 
					
						
							| 
									
										
										
										
											2015-04-02 11:17:57 +02:00
										 |  |  | 				previous_point_time = clock; | 
					
						
							|  |  |  | 				clock += 180; | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 				plan_add_segment(diveplan, clock - previous_point_time, 5000, current_cylinder, po2, false); | 
					
						
							| 
									
										
										
										
											2015-04-02 11:17:57 +02:00
										 |  |  | 				previous_point_time = clock; | 
					
						
							|  |  |  | 				safety_stop = false; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 		} while (depth > 0); | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 		plan_add_segment(diveplan, clock - previous_point_time, 0, current_cylinder, po2, false); | 
					
						
							| 
									
										
										
										
											2017-08-28 23:59:58 +02:00
										 |  |  | 		create_dive_from_plan(diveplan, dive, is_planner); | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 		add_plan_to_notes(diveplan, dive, show_disclaimer, error); | 
					
						
							|  |  |  | 		fixup_dc_duration(&dive->dc); | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		free(stoplevels); | 
					
						
							|  |  |  | 		free(gaschanges); | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		unlock_planner(); | 
					
						
							| 
									
										
										
										
											2017-02-03 03:10:57 -08:00
										 |  |  | 		return false; | 
					
						
							| 
									
										
										
										
											2015-03-31 14:52:37 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-30 17:09:05 +02:00
										 |  |  | 	if (best_first_ascend_cylinder != current_cylinder) { | 
					
						
							|  |  |  | 		current_cylinder = best_first_ascend_cylinder; | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 		gas = dive->cylinder[current_cylinder].gasmix; | 
					
						
							| 
									
										
										
										
											2014-10-19 07:07:07 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-30 17:09:05 +02:00
										 |  |  | #if DEBUG_PLAN & 16
 | 
					
						
							|  |  |  | 		printf("switch to gas %d (%d/%d) @ %5.2lfm\n", best_first_ascend_cylinder, | 
					
						
							| 
									
										
										
										
											2014-07-10 22:29:00 +02:00
										 |  |  | 		       (get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[best_first_ascend_cylinder].depth / 1000.0); | 
					
						
							| 
									
										
										
										
											2014-05-30 17:09:05 +02:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-07-03 23:54:57 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// VPM-B or Buehlmann Deco
 | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 	tissue_at_end(ds, dive, cached_datap); | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 	previous_deco_time = 100000000; | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 	ds->deco_time = 10000000; | 
					
						
							|  |  |  | 	cache_deco_state(ds, &bottom_cache);  // Lets us make several iterations
 | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 	bottom_depth = depth; | 
					
						
							|  |  |  | 	bottom_gi = gi; | 
					
						
							|  |  |  | 	bottom_gas = gas; | 
					
						
							|  |  |  | 	bottom_stopidx = stopidx; | 
					
						
							| 
									
										
										
										
											2015-08-22 14:41:53 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 	//CVA
 | 
					
						
							|  |  |  | 	do { | 
					
						
							| 
									
										
										
										
											2017-08-27 22:49:41 +02:00
										 |  |  | 		decostopcounter = 0; | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		is_final_plan = (decoMode() == BUEHLMANN) || (previous_deco_time - ds->deco_time < 10);  // CVA time converges
 | 
					
						
							|  |  |  | 		if (ds->deco_time != 10000000) | 
					
						
							|  |  |  | 			vpmb_next_gradient(ds, ds->deco_time, diveplan->surface_pressure / 1000.0); | 
					
						
							| 
									
										
										
										
											2015-08-15 14:16:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		previous_deco_time = ds->deco_time; | 
					
						
							|  |  |  | 		restore_deco_state(bottom_cache, ds, true); | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		depth = bottom_depth; | 
					
						
							|  |  |  | 		gi = bottom_gi; | 
					
						
							| 
									
										
										
										
											2017-11-03 07:32:28 +11:00
										 |  |  | 		clock = previous_point_time = bottom_time; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 		gas = bottom_gas; | 
					
						
							|  |  |  | 		stopping = false; | 
					
						
							|  |  |  | 		decodive = false; | 
					
						
							| 
									
										
										
										
											2016-11-23 11:50:50 +01:00
										 |  |  | 		first_stop_depth = 0; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 		stopidx = bottom_stopidx; | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		ds->first_ceiling_pressure.mbar = depth_to_mbar( | 
					
						
							|  |  |  | 					deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(depth, dive)), diveplan->surface_pressure / 1000.0, dive, 1), | 
					
						
							|  |  |  | 					dive); | 
					
						
							|  |  |  | 		if (ds->max_bottom_ceiling_pressure.mbar > ds->first_ceiling_pressure.mbar) | 
					
						
							|  |  |  | 			ds->first_ceiling_pressure.mbar = ds->max_bottom_ceiling_pressure.mbar; | 
					
						
							| 
									
										
										
										
											2015-08-27 14:13:12 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-03 07:32:28 +11:00
										 |  |  | 		last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); | 
					
						
							| 
									
										
										
										
											2017-10-09 23:38:38 +02:00
										 |  |  | 		/* 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, &dive->cylinder[best_first_ascend_cylinder].gasmix)) | 
					
						
							|  |  |  | 			current_cylinder = best_first_ascend_cylinder; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			current_cylinder = get_gasidx(dive, &gas); | 
					
						
							|  |  |  | 		if (current_cylinder == -1) { | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 			report_error(translate("gettextFromC", "Can't find gas %s"), gasname(&gas)); | 
					
						
							|  |  |  | 			current_cylinder = 0; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-11-23 11:50:50 +01:00
										 |  |  | 		reset_regression(); | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 		while (1) { | 
					
						
							|  |  |  | 			/* We will break out when we hit the surface */ | 
					
						
							|  |  |  | 			do { | 
					
						
							|  |  |  | 				/* Ascend to next stop depth */ | 
					
						
							| 
									
										
										
										
											2017-11-03 07:32:28 +11:00
										 |  |  | 				int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP; | 
					
						
							|  |  |  | 				if (ascent_velocity(depth, avg_depth, bottom_time) != last_ascend_rate) { | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 					if (is_final_plan) | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 						plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, false); | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 					previous_point_time = clock; | 
					
						
							|  |  |  | 					stopping = false; | 
					
						
							| 
									
										
										
										
											2017-11-03 07:32:28 +11:00
										 |  |  | 					last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | 				if (depth - deltad < stoplevels[stopidx]) | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 					deltad = depth - stoplevels[stopidx]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 				add_segment(ds, depth_to_bar(depth, dive), | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 								&dive->cylinder[current_cylinder].gasmix, | 
					
						
							|  |  |  | 								TIMESTEP, po2, dive, prefs.decosac); | 
					
						
							| 
									
										
										
										
											2017-11-08 20:59:47 +01:00
										 |  |  | 				last_segment_min_switch = false; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 				clock += TIMESTEP; | 
					
						
							|  |  |  | 				depth -= deltad; | 
					
						
							| 
									
										
										
										
											2016-11-23 11:50:50 +01:00
										 |  |  | 				/* Print VPM-Gradient as gradient factor, this has to be done from within deco.c */ | 
					
						
							|  |  |  | 				if (decodive) | 
					
						
							|  |  |  | 					plot_depth = depth; | 
					
						
							| 
									
										
										
										
											2016-03-23 08:16:33 -07:00
										 |  |  | 			} while (depth > 0 && depth > stoplevels[stopidx]); | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if (depth <= 0) | 
					
						
							|  |  |  | 				break; /* We are at the surface */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (gi >= 0 && stoplevels[stopidx] <= gaschanges[gi].depth) { | 
					
						
							|  |  |  | 				/* We have reached a gas change.
 | 
					
						
							|  |  |  | 				 * Record this in the dive plan */ | 
					
						
							|  |  |  | 				if (is_final_plan) | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 					plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, false); | 
					
						
							| 
									
										
										
										
											2014-04-26 21:06:48 +02:00
										 |  |  | 				previous_point_time = clock; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 				stopping = true; | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 				/* Check we need to change cylinder.
 | 
					
						
							|  |  |  | 				 * We might not if the cylinder was chosen by the user | 
					
						
							|  |  |  | 				 * or user has selected only to switch only at required stops. | 
					
						
							|  |  |  | 				 * If current gas is hypoxic, we want to switch asap */ | 
					
						
							| 
									
										
										
										
											2015-08-03 17:29:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 				if (current_cylinder != gaschanges[gi].gasidx) { | 
					
						
							|  |  |  | 					if (!prefs.switch_at_req_stop || | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 							!trial_ascent(ds, 0, depth, stoplevels[stopidx - 1], avg_depth, bottom_time, | 
					
						
							| 
									
										
										
										
											2017-08-24 23:45:33 +02:00
										 |  |  | 							&dive->cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0, dive) || get_o2(&dive->cylinder[current_cylinder].gasmix) < 160) { | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 						current_cylinder = gaschanges[gi].gasidx; | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 						gas = dive->cylinder[current_cylinder].gasmix; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | #if DEBUG_PLAN & 16
 | 
					
						
							|  |  |  | 						printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi].gasidx, | 
					
						
							|  |  |  | 							(get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[gi].depth / 1000.0); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2017-11-02 14:15:46 +01:00
										 |  |  | 						/* Stop for the minimum duration to switch gas unless we switch to o2 */ | 
					
						
							| 
									
										
										
										
											2017-11-08 20:59:47 +01:00
										 |  |  | 						if (!last_segment_min_switch && get_o2(&dive->cylinder[current_cylinder].gasmix) != 1000) { | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 							add_segment(ds, depth_to_bar(depth, dive), | 
					
						
							| 
									
										
										
										
											2017-10-30 22:10:57 +01:00
										 |  |  | 								&dive->cylinder[current_cylinder].gasmix, | 
					
						
							|  |  |  | 								prefs.min_switch_duration, po2, dive, prefs.decosac); | 
					
						
							|  |  |  | 							clock += prefs.min_switch_duration; | 
					
						
							| 
									
										
										
										
											2017-11-08 20:59:47 +01:00
										 |  |  | 							last_segment_min_switch = true; | 
					
						
							| 
									
										
										
										
											2017-10-30 22:10:57 +01:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 					} else { | 
					
						
							| 
									
										
										
										
											2015-07-27 22:06:02 +10:00
										 |  |  | 						/* The user has selected the option to switch gas only at required stops.
 | 
					
						
							|  |  |  | 						 * Remember that we are waiting to switch gas | 
					
						
							|  |  |  | 						 */ | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 						pendinggaschange = true; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2015-08-03 17:29:05 +02:00
										 |  |  | 				gi--; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			--stopidx; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* Save the current state and try to ascend to the next stopdepth */ | 
					
						
							|  |  |  | 			while (1) { | 
					
						
							|  |  |  | 				/* Check if ascending to next stop is clear, go back and wait if we hit the ceiling on the way */ | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 				if (trial_ascent(ds, 0, depth, stoplevels[stopidx], avg_depth, bottom_time, | 
					
						
							| 
									
										
										
										
											2017-08-28 23:59:58 +02:00
										 |  |  | 						&dive->cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0, dive)) { | 
					
						
							|  |  |  | 					decostoptable[decostopcounter].depth = depth; | 
					
						
							|  |  |  | 					decostoptable[decostopcounter].time = 0; | 
					
						
							|  |  |  | 					decostopcounter++; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 					break; /* We did not hit the ceiling */ | 
					
						
							| 
									
										
										
										
											2017-08-28 23:59:58 +02:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				/* Add a minute of deco time and then try again */ | 
					
						
							| 
									
										
										
										
											2016-11-23 11:50:50 +01:00
										 |  |  | 				if (!decodive) { | 
					
						
							|  |  |  | 					decodive = true; | 
					
						
							|  |  |  | 					first_stop_depth = depth; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 				if (!stopping) { | 
					
						
							|  |  |  | 					/* The last segment was an ascend segment.
 | 
					
						
							|  |  |  | 					 * Add a waypoint for start of this deco stop */ | 
					
						
							|  |  |  | 					if (is_final_plan) | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 						plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, false); | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 					previous_point_time = clock; | 
					
						
							|  |  |  | 					stopping = true; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2015-07-27 22:06:02 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				/* Are we waiting to switch gas?
 | 
					
						
							|  |  |  | 				 * Occurs when the user has selected the option to switch only at required stops | 
					
						
							|  |  |  | 				 */ | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 				if (pendinggaschange) { | 
					
						
							|  |  |  | 					current_cylinder = gaschanges[gi + 1].gasidx; | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 					gas = dive->cylinder[current_cylinder].gasmix; | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | #if DEBUG_PLAN & 16
 | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 					printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi + 1].gasidx, | 
					
						
							|  |  |  | 						(get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[gi + 1].depth / 1000.0); | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2017-11-02 14:15:46 +01:00
										 |  |  | 					/* Stop for the minimum duration to switch gas unless we switch to o2 */ | 
					
						
							| 
									
										
										
										
											2017-11-08 20:59:47 +01:00
										 |  |  | 					if (!last_segment_min_switch && get_o2(&dive->cylinder[current_cylinder].gasmix) != 1000) { | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 						add_segment(ds, depth_to_bar(depth, dive), | 
					
						
							| 
									
										
										
										
											2017-10-30 22:10:57 +01:00
										 |  |  | 							&dive->cylinder[current_cylinder].gasmix, | 
					
						
							|  |  |  | 							prefs.min_switch_duration, po2, dive, prefs.decosac); | 
					
						
							|  |  |  | 						clock += prefs.min_switch_duration; | 
					
						
							| 
									
										
										
										
											2017-11-08 20:59:47 +01:00
										 |  |  | 						last_segment_min_switch = true; | 
					
						
							| 
									
										
										
										
											2017-10-30 22:10:57 +01:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 					pendinggaschange = false; | 
					
						
							| 
									
										
										
										
											2015-06-22 22:43:20 +10:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2013-10-06 17:32:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 				int new_clock = wait_until(ds, dive, clock, clock, laststoptime * 2 + 1, timestep, depth, stoplevels[stopidx], avg_depth, bottom_time, &dive->cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0); | 
					
						
							| 
									
										
										
										
											2017-08-24 23:45:33 +02:00
										 |  |  | 				laststoptime = new_clock - clock; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 				/* Finish infinite deco */ | 
					
						
							| 
									
										
										
										
											2017-02-03 03:10:57 -08:00
										 |  |  | 				if (clock >= 48 * 3600 && depth >= 6000) { | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 					error = LONGDECO; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2017-08-25 00:08:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				o2breaking = false; | 
					
						
							| 
									
										
										
										
											2017-11-02 14:15:46 +01:00
										 |  |  | 				stop_cylinder = current_cylinder; | 
					
						
							| 
									
										
										
										
											2017-11-02 21:00:54 +01:00
										 |  |  | 				if (prefs.doo2breaks && prefs.last_stop) { | 
					
						
							| 
									
										
										
										
											2015-07-27 22:06:02 +10:00
										 |  |  | 					/* The backgas breaks option limits time on oxygen to 12 minutes, followed by 6 minutes on
 | 
					
						
							| 
									
										
										
										
											2017-10-10 09:46:34 +02:00
										 |  |  | 					 * backgas.  This could be customized if there were demand. | 
					
						
							| 
									
										
										
										
											2015-07-27 22:06:02 +10:00
										 |  |  | 					 */ | 
					
						
							| 
									
										
										
										
											2017-10-10 09:46:34 +02:00
										 |  |  | 					if (break_cylinder == -1) { | 
					
						
							|  |  |  | 						if (get_o2(&dive->cylinder[best_first_ascend_cylinder].gasmix) <= 320) | 
					
						
							|  |  |  | 							break_cylinder = best_first_ascend_cylinder; | 
					
						
							|  |  |  | 						else | 
					
						
							|  |  |  | 							break_cylinder = 0; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 					if (get_o2(&dive->cylinder[current_cylinder].gasmix) == 1000) { | 
					
						
							| 
									
										
										
										
											2017-08-25 00:08:14 +02:00
										 |  |  | 						if (laststoptime >= 12 * 60) { | 
					
						
							|  |  |  | 							laststoptime = 12 * 60; | 
					
						
							| 
									
										
										
										
											2017-10-18 23:51:14 +02:00
										 |  |  | 							new_clock = clock + laststoptime; | 
					
						
							| 
									
										
										
										
											2017-08-25 00:08:14 +02:00
										 |  |  | 							o2breaking = true; | 
					
						
							| 
									
										
										
										
											2017-11-02 14:15:46 +01:00
										 |  |  | 							o2break_next = true; | 
					
						
							| 
									
										
										
										
											2017-10-10 09:46:34 +02:00
										 |  |  | 							breakfrom_cylinder = current_cylinder; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 							if (is_final_plan) | 
					
						
							| 
									
										
										
										
											2017-10-18 23:51:14 +02:00
										 |  |  | 								plan_add_segment(diveplan, laststoptime, depth, current_cylinder, po2, false); | 
					
						
							| 
									
										
										
										
											2017-08-25 00:08:14 +02:00
										 |  |  | 							previous_point_time = clock + laststoptime; | 
					
						
							| 
									
										
										
										
											2017-10-10 09:46:34 +02:00
										 |  |  | 							current_cylinder = break_cylinder; | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 							gas = dive->cylinder[current_cylinder].gasmix; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2017-11-02 14:15:46 +01:00
										 |  |  | 					} else if (o2break_next) { | 
					
						
							|  |  |  | 						if (laststoptime >= 6 * 60) { | 
					
						
							|  |  |  | 							laststoptime = 6 * 60; | 
					
						
							|  |  |  | 							new_clock = clock + laststoptime; | 
					
						
							|  |  |  | 							o2breaking  = true; | 
					
						
							|  |  |  | 							o2break_next = false; | 
					
						
							|  |  |  | 							if (is_final_plan) | 
					
						
							|  |  |  | 								plan_add_segment(diveplan, laststoptime, depth, current_cylinder, po2, false); | 
					
						
							|  |  |  | 							previous_point_time = clock + laststoptime; | 
					
						
							|  |  |  | 							current_cylinder = breakfrom_cylinder; | 
					
						
							|  |  |  | 							gas = dive->cylinder[current_cylinder].gasmix; | 
					
						
							| 
									
										
										
										
											2014-07-02 22:07:38 +02:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 				add_segment(ds, depth_to_bar(depth, dive), &dive->cylinder[stop_cylinder].gasmix, | 
					
						
							| 
									
										
										
										
											2017-08-25 00:08:14 +02:00
										 |  |  | 					    laststoptime, po2, dive, prefs.decosac); | 
					
						
							| 
									
										
										
										
											2017-11-08 20:59:47 +01:00
										 |  |  | 				last_segment_min_switch = false; | 
					
						
							| 
									
										
										
										
											2017-10-18 23:51:14 +02:00
										 |  |  | 				decostoptable[decostopcounter].depth = depth; | 
					
						
							|  |  |  | 				decostoptable[decostopcounter].time = laststoptime; | 
					
						
							|  |  |  | 				++decostopcounter; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-30 22:46:01 +01:00
										 |  |  | 				clock += laststoptime; | 
					
						
							| 
									
										
										
										
											2017-08-25 00:08:14 +02:00
										 |  |  | 				if (!o2breaking) | 
					
						
							|  |  |  | 					break; | 
					
						
							| 
									
										
										
										
											2014-07-02 22:07:38 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 			if (stopping) { | 
					
						
							|  |  |  | 				/* Next we will ascend again. Add a waypoint if we have spend deco time */ | 
					
						
							|  |  |  | 				if (is_final_plan) | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 					plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, false); | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 				previous_point_time = clock; | 
					
						
							|  |  |  | 				stopping = false; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2014-04-17 10:54:55 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-11-02 19:34:45 +11:00
										 |  |  | 		/* When calculating deco_time, we should pretend the final ascent rate is always the same,
 | 
					
						
							|  |  |  | 		 * otherwise odd things can happen, such as CVA causing the final ascent to start *later* | 
					
						
							|  |  |  | 		 * if the ascent rate is slower, which is completely nonsensical. | 
					
						
							|  |  |  | 		 * Assume final ascent takes 20s, which is the time taken to ascend at 9m/min from 3m */ | 
					
						
							| 
									
										
										
										
											2017-11-22 20:42:33 +01:00
										 |  |  | 		ds->deco_time = clock - bottom_time - stoplevels[stopidx + 1] / last_ascend_rate + 20; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 	} while (!is_final_plan); | 
					
						
							| 
									
										
										
										
											2017-08-27 22:49:41 +02:00
										 |  |  | 	decostoptable[decostopcounter].depth = 0; | 
					
						
							| 
									
										
										
										
											2015-07-04 00:13:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-06 22:40:28 +10:00
										 |  |  | 	plan_add_segment(diveplan, clock - previous_point_time, 0, current_cylinder, po2, false); | 
					
						
							| 
									
										
										
										
											2017-02-03 03:10:57 -08:00
										 |  |  | 	if (decoMode() == VPMB) { | 
					
						
							| 
									
										
										
										
											2017-03-08 13:41:41 +07:00
										 |  |  | 		diveplan->eff_gfhigh = lrint(100.0 * regressionb()); | 
					
						
							|  |  |  | 		diveplan->eff_gflow = lrint(100.0 * (regressiona() * first_stop_depth + regressionb())); | 
					
						
							| 
									
										
										
										
											2016-11-23 11:50:50 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-28 23:59:58 +02:00
										 |  |  | 	create_dive_from_plan(diveplan, dive, is_planner); | 
					
						
							| 
									
										
										
										
											2017-08-23 22:43:33 +02:00
										 |  |  | 	add_plan_to_notes(diveplan, dive, show_disclaimer, error); | 
					
						
							|  |  |  | 	fixup_dc_duration(&dive->dc); | 
					
						
							| 
									
										
										
										
											2016-11-27 11:31:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-09 16:01:15 -08:00
										 |  |  | 	free(stoplevels); | 
					
						
							|  |  |  | 	free(gaschanges); | 
					
						
							| 
									
										
										
										
											2015-10-01 20:39:32 -04:00
										 |  |  | 	free(bottom_cache); | 
					
						
							| 
									
										
										
										
											2017-08-29 11:41:30 +02:00
										 |  |  | 	unlock_planner(); | 
					
						
							| 
									
										
										
										
											2015-05-09 21:55:59 +02:00
										 |  |  | 	return decodive; | 
					
						
							| 
									
										
										
										
											2013-01-04 23:11:42 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Get a value in tenths (so "10.2" == 102, "9" = 90) | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Return negative for errors. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | static int get_tenths(const char *begin, const char **endp) | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | 	char *end; | 
					
						
							|  |  |  | 	int value = strtol(begin, &end, 10); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (begin == end) | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | 		return -1; | 
					
						
							|  |  |  | 	value *= 10; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Fraction? We only look at the first digit */ | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | 	if (*end == '.') { | 
					
						
							| 
									
										
										
										
											2013-01-15 16:54:59 -08:00
										 |  |  | 		end++; | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | 		if (!isdigit(*end)) | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | 			return -1; | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | 		value += *end - '0'; | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | 		do { | 
					
						
							| 
									
										
										
										
											2013-01-15 16:54:59 -08:00
										 |  |  | 			end++; | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | 		} while (isdigit(*end)); | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-01-15 16:54:59 -08:00
										 |  |  | 	*endp = end; | 
					
						
							|  |  |  | 	return value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | static int get_permille(const char *begin, const char **end) | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	int value = get_tenths(begin, end); | 
					
						
							|  |  |  | 	if (value >= 0) { | 
					
						
							|  |  |  | 		/* Allow a percentage sign */ | 
					
						
							|  |  |  | 		if (**end == '%') | 
					
						
							|  |  |  | 			++*end; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-01 14:17:06 -07:00
										 |  |  | int validate_gas(const char *text, struct gasmix *gas) | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	int o2, he; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!text) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 00:29:09 -07:00
										 |  |  | 	while (isspace(*text)) | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | 		text++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-07 13:13:10 -08:00
										 |  |  | 	if (!*text) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 	if (!strcasecmp(text, translate("gettextFromC", "air"))) { | 
					
						
							|  |  |  | 		o2 = O2_IN_AIR; | 
					
						
							|  |  |  | 		he = 0; | 
					
						
							|  |  |  | 		text += strlen(translate("gettextFromC", "air")); | 
					
						
							| 
									
										
										
										
											2015-05-14 23:33:48 +02:00
										 |  |  | 	} else if (!strcasecmp(text, translate("gettextFromC", "oxygen"))) { | 
					
						
							| 
									
										
										
										
											2017-02-03 03:10:57 -08:00
										 |  |  | 		o2 = 1000; | 
					
						
							|  |  |  | 		he = 0; | 
					
						
							|  |  |  | 		text += strlen(translate("gettextFromC", "oxygen")); | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 	} else if (!strncasecmp(text, translate("gettextFromC", "ean"), 3)) { | 
					
						
							|  |  |  | 		o2 = get_permille(text + 3, &text); | 
					
						
							|  |  |  | 		he = 0; | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 		o2 = get_permille(text, &text); | 
					
						
							|  |  |  | 		he = 0; | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | 		if (*text == '/') | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 			he = get_permille(text + 1, &text); | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* We don't want any extra crud */ | 
					
						
							| 
									
										
										
										
											2013-10-05 00:29:09 -07:00
										 |  |  | 	while (isspace(*text)) | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | 		text++; | 
					
						
							|  |  |  | 	if (*text) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Validate the gas mix */ | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 	if (*text || o2 < 1 || o2 > 1000 || he < 0 || o2 + he > 1000) | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Let it rip */ | 
					
						
							| 
									
										
										
										
											2014-06-01 14:17:06 -07:00
										 |  |  | 	gas->o2.permille = o2; | 
					
						
							|  |  |  | 	gas->he.permille = he; | 
					
						
							| 
									
										
										
										
											2013-01-07 11:23:14 -08:00
										 |  |  | 	return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-07 20:20:25 -07:00
										 |  |  | int validate_po2(const char *text, int *mbar_po2) | 
					
						
							| 
									
										
										
										
											2013-01-23 20:12:24 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	int po2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!text) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	po2 = get_tenths(text, &text); | 
					
						
							|  |  |  | 	if (po2 < 0) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 00:29:09 -07:00
										 |  |  | 	while (isspace(*text)) | 
					
						
							| 
									
										
										
										
											2013-01-23 20:12:24 +01:00
										 |  |  | 		text++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-05 00:29:09 -07:00
										 |  |  | 	while (isspace(*text)) | 
					
						
							| 
									
										
										
										
											2013-01-23 20:12:24 +01:00
										 |  |  | 		text++; | 
					
						
							|  |  |  | 	if (*text) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*mbar_po2 = po2 * 100; | 
					
						
							|  |  |  | 	return 1; | 
					
						
							|  |  |  | } |