2013-01-05 07:11:42 +00:00
/* planner.c
*
* code that allows us to plan future dives
*
* ( c ) Dirk Hohndel 2013
*/
2013-12-07 22:54:14 +00:00
# include <assert.h>
2013-01-07 19:23:14 +00:00
# include <unistd.h>
# include <ctype.h>
2013-10-05 07:29:09 +00:00
# include <string.h>
2013-01-05 07:11:42 +00:00
# include "dive.h"
# include "divelist.h"
2013-04-08 03:20:25 +00:00
# include "planner.h"
2013-10-06 15:55:58 +00:00
# include "gettext.h"
2014-04-30 05:37:19 +00:00
# include "libdivecomputer/parser.h"
2013-01-05 07:11:42 +00:00
2015-06-20 01:01:09 +00:00
# define TIMESTEP 2 /* second */
2014-04-18 14:08:33 +00:00
# define DECOTIMESTEP 60 /* seconds. Unit of deco stop times */
2014-04-17 08:54:55 +00:00
int decostoplevels [ ] = { 0 , 3000 , 6000 , 9000 , 12000 , 15000 , 18000 , 21000 , 24000 , 27000 ,
2013-10-08 05:37:32 +00:00
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 ,
2014-02-28 04:09:57 +00:00
320000 , 340000 , 360000 , 380000 } ;
2013-01-16 23:17:39 +00:00
double plangflow , plangfhigh ;
2014-06-02 14:25:58 +00:00
bool plan_verbatim = false , plan_display_runtime = true , plan_display_duration = false , plan_display_transitions = false ;
2013-01-05 07:11:42 +00:00
2014-06-04 21:34:09 +00:00
const char * disclaimer ;
2013-01-07 20:49:07 +00:00
# if DEBUG_PLAN
void dump_plan ( struct diveplan * diveplan )
{
struct divedatapoint * dp ;
struct tm tm ;
if ( ! diveplan ) {
2014-02-28 04:09:57 +00:00
printf ( " Diveplan NULL \n " ) ;
2013-01-07 20:49:07 +00:00
return ;
}
utc_mkdate ( diveplan - > when , & tm ) ;
2013-01-08 22:05:25 +00:00
printf ( " \n Diveplan @ %04d-%02d-%02d %02d:%02d:%02d (surfpres %dmbar): \n " ,
2014-02-28 04:09:57 +00:00
tm . tm_year + 1900 , tm . tm_mon + 1 , tm . tm_mday ,
tm . tm_hour , tm . tm_min , tm . tm_sec ,
diveplan - > surface_pressure ) ;
2013-01-07 20:49:07 +00:00
dp = diveplan - > dp ;
while ( dp ) {
2014-06-05 15:28:14 +00:00
printf ( " \t %3u:%02u: %dmm gas: %d o2 %d h2 \n " , FRACTION ( dp - > time , 60 ) , dp - > depth , get_o2 ( & dp - > gasmix ) , get_he ( & dp - > gasmix ) ) ;
2013-01-07 20:49:07 +00:00
dp = dp - > next ;
}
}
# endif
2014-05-30 22:40:13 +00:00
bool diveplan_empty ( struct diveplan * diveplan )
{
struct divedatapoint * dp ;
if ( ! diveplan | | ! diveplan - > dp )
return true ;
dp = diveplan - > dp ;
2014-06-03 09:38:24 +00:00
while ( dp ) {
2014-05-30 22:40:13 +00:00
if ( dp - > time )
return false ;
dp = dp - > next ;
}
return true ;
}
2013-10-05 07:29:09 +00:00
void set_last_stop ( bool last_stop_6m )
2013-05-02 18:35:21 +00:00
{
2014-01-15 18:54:41 +00:00
if ( last_stop_6m = = true )
2013-05-02 18:35:21 +00:00
decostoplevels [ 1 ] = 6000 ;
else
decostoplevels [ 1 ] = 3000 ;
}
2014-06-02 14:25:58 +00:00
void set_verbatim ( bool verbatim )
{
plan_verbatim = verbatim ;
}
void set_display_runtime ( bool display )
{
plan_display_runtime = display ;
}
void set_display_duration ( bool display )
{
plan_display_duration = display ;
}
void set_display_transitions ( bool display )
{
plan_display_transitions = display ;
}
2014-07-19 01:37:28 +00:00
/* get the gas at a certain time during the dive */
2014-07-17 19:16:50 +00:00
void get_gas_at_time ( struct dive * dive , struct divecomputer * dc , duration_t time , struct gasmix * gas )
2013-01-10 00:01:15 +00:00
{
2014-07-19 01:37:28 +00:00
// we always start with the first gas, so that's our gas
// unless an event tells us otherwise
2013-01-10 00:01:15 +00:00
struct event * event = dc - > events ;
2014-07-19 01:37:28 +00:00
* gas = dive - > cylinder [ 0 ] . gasmix ;
2014-07-17 19:16:49 +00:00
while ( event & & event - > time . seconds < = time . seconds ) {
2014-07-17 19:16:50 +00:00
if ( ! strcmp ( event - > name , " gaschange " ) ) {
int cylinder_idx = get_cylinder_index ( dive , event ) ;
* gas = dive - > cylinder [ cylinder_idx ] . gasmix ;
}
2013-01-10 00:01:15 +00:00
event = event - > next ;
}
}
2014-06-01 21:17:06 +00:00
int get_gasidx ( struct dive * dive , struct gasmix * mix )
2013-01-10 00:01:15 +00:00
{
int gasidx = - 1 ;
while ( + + gasidx < MAX_CYLINDERS )
2015-03-07 15:37:12 +00:00
if ( gasmix_distance ( & dive - > cylinder [ gasidx ] . gasmix , mix ) < 100 )
2013-01-10 00:01:15 +00:00
return gasidx ;
return - 1 ;
}
2014-07-18 18:25:00 +00:00
double interpolate_transition ( 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 08:54:55 +00:00
{
int j ;
2014-06-11 16:37:12 +00:00
double tissue_tolerance = 0.0 ;
2014-04-17 08:54:55 +00:00
2014-07-18 18:25:00 +00:00
for ( j = t0 . seconds ; j < t1 . seconds ; j + + ) {
int depth = interpolate ( d0 . mm , d1 . mm , j - t0 . seconds , t1 . seconds - t0 . seconds ) ;
2014-10-24 14:40:21 +00:00
tissue_tolerance = add_segment ( depth_to_mbar ( depth , dive ) / 1000.0 , gasmix , 1 , po2 . mbar , dive , prefs . bottomsac ) ;
2014-04-17 08:54:55 +00:00
}
return tissue_tolerance ;
}
2013-01-05 07:11:42 +00:00
/* returns the tissue tolerance at the end of this (partial) dive */
2014-03-14 18:26:07 +00:00
double tissue_at_end ( struct dive * dive , char * * cached_datap )
2013-01-05 07:11:42 +00:00
{
struct divecomputer * dc ;
struct sample * sample , * psample ;
2014-07-17 19:16:50 +00:00
int i ;
2014-07-17 19:16:49 +00:00
depth_t lastdepth = { } ;
duration_t t0 = { } , t1 = { } ;
2013-01-05 07:11:42 +00:00
double tissue_tolerance ;
2014-06-01 21:17:06 +00:00
struct gasmix gas ;
2013-01-05 07:11:42 +00:00
if ( ! dive )
return 0.0 ;
2013-01-06 19:13:46 +00:00
if ( * cached_datap ) {
tissue_tolerance = restore_deco_state ( * cached_datap ) ;
} else {
tissue_tolerance = init_decompression ( dive ) ;
cache_deco_state ( tissue_tolerance , cached_datap ) ;
}
2013-01-05 07:11:42 +00:00
dc = & dive - > dc ;
if ( ! dc - > samples )
2013-01-06 19:13:46 +00:00
return tissue_tolerance ;
2013-01-05 07:11:42 +00:00
psample = sample = dc - > sample ;
2014-07-19 01:37:28 +00:00
2013-01-05 07:11:42 +00:00
for ( i = 0 ; i < dc - > samples ; i + + , sample + + ) {
2014-07-17 19:16:49 +00:00
t1 = sample - > time ;
2014-07-17 19:16:50 +00:00
get_gas_at_time ( dive , dc , t0 , & gas ) ;
2013-01-08 21:54:29 +00:00
if ( i > 0 )
2014-07-17 19:16:49 +00:00
lastdepth = psample - > depth ;
2014-10-19 14:07:07 +00:00
tissue_tolerance = interpolate_transition ( dive , t0 , t1 , lastdepth , sample - > depth , & gas , sample - > setpoint ) ;
2013-01-05 07:11:42 +00:00
psample = sample ;
t0 = t1 ;
}
return tissue_tolerance ;
}
2014-03-15 20:09:36 +00:00
/* if a default cylinder is set, use that */
2013-11-13 12:44:18 +00:00
void fill_default_cylinder ( cylinder_t * cyl )
{
2014-03-15 20:09:36 +00:00
const char * cyl_name = prefs . default_cylinder ;
2013-12-07 22:29:05 +00:00
struct tank_info_t * ti = tank_info ;
2014-06-30 18:26:18 +00:00
pressure_t pO2 = { . mbar = 1600 } ;
2013-12-07 22:29:05 +00:00
2014-03-15 20:09:36 +00:00
if ( ! cyl_name )
return ;
2013-12-07 22:29:05 +00:00
while ( ti - > name ! = NULL ) {
if ( strcmp ( ti - > name , cyl_name ) = = 0 )
break ;
ti + + ;
}
if ( ti - > name = = NULL )
2014-03-15 20:09:36 +00:00
/* didn't find it */
return ;
2013-12-07 22:29:05 +00: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 )
cyl - > type . size . mliter = cuft_to_l ( ti - > cuft ) * 1000 / bar_to_atm ( psi_to_bar ( ti - > psi ) ) ;
2013-11-24 05:54:51 +00:00
}
2014-06-30 18:26:18 +00:00
// MOD of air
cyl - > depth = gas_mod ( & cyl - > gasmix , pO2 , 1 ) ;
2013-11-13 12:44:18 +00:00
}
2014-05-30 00:36:39 +00:00
/* make sure that the gas we are switching to is represented in our
* list of cylinders */
2014-07-03 20:39:06 +00:00
static int verify_gas_exists ( struct gasmix mix_in )
2013-01-05 07:11:42 +00:00
{
int i ;
cylinder_t * cyl ;
for ( i = 0 ; i < MAX_CYLINDERS ; i + + ) {
2014-07-03 20:39:06 +00:00
cyl = displayed_dive . cylinder + i ;
2013-01-05 07:11:42 +00:00
if ( cylinder_nodata ( cyl ) )
2014-05-31 20:49:51 +00:00
continue ;
2015-03-07 15:37:12 +00:00
if ( gasmix_distance ( & cyl - > gasmix , & mix_in ) < 100 )
2013-01-05 07:11:42 +00:00
return i ;
}
2014-06-02 01:25:44 +00:00
fprintf ( stderr , " this gas %s should have been on the cylinder list \n Things will fail now \n " , gasname ( & mix_in ) ) ;
2014-05-29 21:36:14 +00:00
return - 1 ;
}
/* calculate the new end pressure of the cylinder, based on its current end pressure and the
* latest segment . */
2014-07-01 07:37:49 +00: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 21:36:14 +00:00
{
volume_t gas_used ;
pressure_t delta_p ;
depth_t mean_depth ;
2015-01-15 22:22:12 +00:00
int factor = 1000 ;
if ( d - > dc . divemode = = PSCR )
factor = prefs . pscr_ratio ;
2014-05-29 21:36:14 +00:00
2014-06-01 16:59:38 +00:00
if ( ! cyl )
2014-05-29 21:36:14 +00:00
return ;
mean_depth . mm = ( old_depth + new_depth ) / 2 ;
2015-01-15 22:22:12 +00:00
gas_used . mliter = depth_to_atm ( mean_depth . mm , d ) * sac / 60 * duration * factor / 1000 ;
2014-06-01 16:59:38 +00:00
cyl - > gas_used . mliter + = gas_used . mliter ;
2014-07-01 07:37:49 +00:00
if ( in_deco )
cyl - > deco_gas_used . mliter + = gas_used . mliter ;
2014-06-02 20:14:59 +00:00
if ( cyl - > type . size . mliter ) {
2014-06-01 16:59:38 +00:00
delta_p . mbar = gas_used . mliter * 1000.0 / cyl - > type . size . mliter ;
cyl - > end . mbar - = delta_p . mbar ;
}
2013-01-05 07:11:42 +00:00
}
2014-07-03 20:39:06 +00:00
/* simply overwrite the data in the displayed_dive
* return false if something goes wrong */
2014-07-04 18:26:31 +00:00
static void create_dive_from_plan ( struct diveplan * diveplan , bool track_gas )
2013-01-05 07:11:42 +00:00
{
struct divedatapoint * dp ;
struct divecomputer * dc ;
2013-01-29 03:54:05 +00:00
struct sample * sample ;
2014-06-01 23:27:19 +00:00
struct gasmix oldgasmix ;
2014-07-04 06:50:59 +00:00
struct event * ev ;
2014-05-29 21:36:14 +00:00
cylinder_t * cyl ;
2013-01-29 03:54:05 +00:00
int oldpo2 = 0 ;
2013-01-10 00:01:15 +00:00
int lasttime = 0 ;
2014-06-01 17:02:38 +00:00
int lastdepth = 0 ;
2015-01-10 23:01:15 +00:00
enum dive_comp_type type = displayed_dive . dc . divemode ;
2013-01-05 07:11:42 +00:00
if ( ! diveplan | | ! diveplan - > dp )
2014-07-03 20:39:06 +00:00
return ;
2013-01-08 16:52:48 +00:00
# if DEBUG_PLAN & 4
2013-01-10 00:01:15 +00:00
printf ( " in create_dive_from_plan \n " ) ;
2013-01-08 16:52:48 +00:00
dump_plan ( diveplan ) ;
# endif
2014-11-12 22:33:40 +00:00
displayed_dive . salinity = diveplan - > salinity ;
2014-07-04 06:50:59 +00:00
// reset the cylinders and clear out the samples and events of the
// displayed dive so we can restart
2014-07-04 18:40:02 +00:00
reset_cylinders ( & displayed_dive , track_gas ) ;
2014-07-03 20:39:06 +00:00
dc = & displayed_dive . dc ;
2014-07-24 16:03:56 +00:00
dc - > when = displayed_dive . when = diveplan - > when ;
2014-07-03 21:45:01 +00:00
free ( dc - > sample ) ;
dc - > sample = NULL ;
dc - > samples = 0 ;
dc - > alloc_samples = 0 ;
2014-07-04 06:50:59 +00:00
while ( ( ev = dc - > events ) ) {
dc - > events = dc - > events - > next ;
free ( ev ) ;
}
2013-01-05 07:11:42 +00:00
dp = diveplan - > dp ;
2014-07-03 20:39:06 +00:00
cyl = & displayed_dive . cylinder [ 0 ] ;
2014-06-01 23:27:19 +00:00
oldgasmix = cyl - > gasmix ;
2013-01-29 03:54:05 +00:00
sample = prepare_sample ( dc ) ;
2014-10-19 14:07:07 +00:00
sample - > setpoint . mbar = dp - > setpoint ;
2015-01-16 12:49:12 +00:00
sample - > sac . mliter = prefs . bottomsac ;
2014-10-19 14:07:07 +00:00
oldpo2 = dp - > setpoint ;
2014-07-17 06:04:30 +00:00
if ( track_gas & & cyl - > type . workingpressure . mbar )
2014-07-04 18:26:31 +00:00
sample - > cylinderpressure . mbar = cyl - > end . mbar ;
2014-08-20 02:07:43 +00:00
sample - > manually_entered = true ;
2013-01-29 03:54:05 +00:00
finish_sample ( dc ) ;
2013-01-07 21:13:10 +00:00
while ( dp ) {
2014-06-01 23:27:19 +00:00
struct gasmix gasmix = dp - > gasmix ;
2014-10-19 14:07:07 +00:00
int po2 = dp - > setpoint ;
if ( dp - > setpoint )
type = CCR ;
2013-01-07 21:13:10 +00:00
int time = dp - > time ;
int depth = dp - > depth ;
2013-01-08 23:03:37 +00:00
if ( time = = 0 ) {
/* special entries that just inform the algorithm about
* additional gases that are available */
2014-07-03 20:39:06 +00:00
if ( verify_gas_exists ( gasmix ) < 0 )
2013-04-08 03:20:25 +00:00
goto gas_error_exit ;
2013-01-08 23:03:37 +00:00
dp = dp - > next ;
continue ;
}
2013-01-07 21:13:10 +00:00
2013-02-02 17:40:29 +00:00
/* Check for SetPoint change */
2013-02-02 17:03:26 +00:00
if ( oldpo2 ! = po2 ) {
2015-03-08 19:28:45 +00: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 */
add_event ( dc , lasttime , SAMPLE_EVENT_PO2 , 0 , po2 , " SP change " ) ;
2013-02-02 17:03:26 +00:00
oldpo2 = po2 ;
}
2014-06-01 23:27:19 +00:00
/* Make sure we have the new gas, and create a gas change event */
if ( gasmix_distance ( & gasmix , & oldgasmix ) > 0 ) {
2013-11-20 19:09:52 +00:00
int idx ;
2014-07-03 20:39:06 +00:00
if ( ( idx = verify_gas_exists ( gasmix ) ) < 0 )
2013-04-08 03:20:25 +00:00
goto gas_error_exit ;
2014-06-01 17:02:38 +00:00
/* need to insert a first sample for the new gas */
2014-07-03 20:39:06 +00:00
add_gas_switch_event ( & displayed_dive , dc , lasttime + 1 , idx ) ;
cyl = & displayed_dive . cylinder [ idx ] ;
2014-05-29 21:36:14 +00:00
sample = prepare_sample ( dc ) ;
2014-10-19 14:07:07 +00:00
sample [ - 1 ] . setpoint . mbar = po2 ;
2014-06-01 17:02:38 +00:00
sample - > time . seconds = lasttime + 1 ;
sample - > depth . mm = lastdepth ;
2014-08-20 02:07:43 +00:00
sample - > manually_entered = dp - > entered ;
2015-01-16 12:49:12 +00:00
sample - > sac . mliter = dp - > entered ? prefs . bottomsac : prefs . decosac ;
2014-07-17 06:04:30 +00:00
if ( track_gas & & cyl - > type . workingpressure . mbar )
2014-07-04 18:26:31 +00:00
sample - > cylinderpressure . mbar = cyl - > sample_end . mbar ;
2014-05-29 21:36:14 +00:00
finish_sample ( dc ) ;
2014-06-01 23:27:19 +00:00
oldgasmix = gasmix ;
2013-01-05 07:11:42 +00:00
}
2013-01-07 21:13:10 +00:00
/* Create sample */
2013-01-05 07:11:42 +00:00
sample = prepare_sample ( dc ) ;
2013-01-29 03:54:05 +00:00
/* set po2 at beginning of this segment */
/* and keep it valid for last sample - where it likely doesn't matter */
2014-11-25 13:27:09 +00:00
sample [ - 1 ] . setpoint . mbar = po2 ;
2014-12-12 13:51:25 +00:00
sample - > setpoint . mbar = po2 ;
2014-06-01 17:02:38 +00:00
sample - > time . seconds = lasttime = time ;
sample - > depth . mm = lastdepth = depth ;
2014-08-20 02:07:43 +00:00
sample - > manually_entered = dp - > entered ;
2015-01-16 12:49:12 +00:00
sample - > sac . mliter = dp - > entered ? prefs . bottomsac : prefs . decosac ;
2014-11-25 13:27:09 +00:00
if ( track_gas & & ! sample [ - 1 ] . setpoint . mbar ) { /* Don't track gas usage for CCR legs of dive */
2014-07-04 18:26:31 +00:00
update_cylinder_pressure ( & displayed_dive , sample [ - 1 ] . depth . mm , depth , time - sample [ - 1 ] . time . seconds ,
dp - > entered ? diveplan - > bottomsac : diveplan - > decosac , cyl , ! dp - > entered ) ;
2014-07-17 06:04:30 +00:00
if ( cyl - > type . workingpressure . mbar )
sample - > cylinderpressure . mbar = cyl - > end . mbar ;
2014-07-04 18:26:31 +00:00
}
2013-01-07 21:13:10 +00:00
finish_sample ( dc ) ;
2013-01-05 07:11:42 +00:00
dp = dp - > next ;
}
2015-01-10 23:01:15 +00:00
dc - > divemode = type ;
2013-01-10 00:01:15 +00:00
# if DEBUG_PLAN & 32
2014-07-03 20:39:06 +00:00
save_dive ( stdout , & displayed_dive ) ;
2013-01-10 00:01:15 +00:00
# endif
2014-07-03 20:39:06 +00:00
return ;
2013-04-08 03:20:25 +00:00
gas_error_exit :
2014-03-14 18:26:07 +00:00
report_error ( translate ( " gettextFromC " , " Too many gas mixes " ) ) ;
2014-07-03 20:39:06 +00:00
return ;
2013-01-05 07:11:42 +00:00
}
2014-08-19 16:41:09 +00:00
void free_dps ( struct diveplan * diveplan )
2013-01-07 06:09:12 +00:00
{
2014-08-19 16:41:09 +00:00
if ( ! diveplan )
return ;
struct divedatapoint * dp = diveplan - > dp ;
2013-01-07 06:09:12 +00:00
while ( dp ) {
struct divedatapoint * ndp = dp - > next ;
free ( dp ) ;
dp = ndp ;
}
2014-08-19 16:41:09 +00:00
diveplan - > dp = NULL ;
2013-01-07 06:09:12 +00:00
}
2014-06-01 22:25:19 +00:00
struct divedatapoint * create_dp ( int time_incr , int depth , struct gasmix gasmix , int po2 )
2013-01-05 07:11:42 +00:00
{
struct divedatapoint * dp ;
dp = malloc ( sizeof ( struct divedatapoint ) ) ;
dp - > time = time_incr ;
dp - > depth = depth ;
2014-06-01 22:25:19 +00:00
dp - > gasmix = gasmix ;
2014-10-19 14:07:07 +00:00
dp - > setpoint = po2 ;
2014-01-15 18:54:41 +00:00
dp - > entered = false ;
2013-01-05 07:11:42 +00: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 23:03:37 +00:00
int lasttime = 0 ;
2013-01-29 21:10:46 +00:00
while ( * lastdp ) {
2013-01-05 07:11:42 +00:00
ldp = * lastdp ;
2013-01-08 23:03:37 +00:00
if ( ldp - > time > lasttime )
lasttime = ldp - > time ;
2013-01-05 07:11:42 +00:00
lastdp = & ( * lastdp ) - > next ;
}
* lastdp = dp ;
2013-11-12 02:19:04 +00:00
if ( ldp & & dp - > time ! = 0 )
2013-01-08 23:03:37 +00:00
dp - > time + = lasttime ;
2013-01-05 07:11:42 +00:00
}
2014-06-01 22:25:19 +00:00
struct divedatapoint * plan_add_segment ( struct diveplan * diveplan , int duration , int depth , struct gasmix gasmix , int po2 , bool entered )
2013-01-05 07:11:42 +00:00
{
2014-06-01 22:25:19 +00:00
struct divedatapoint * dp = create_dp ( duration , depth , gasmix , po2 ) ;
2014-03-12 15:49:42 +00:00
dp - > entered = entered ;
2013-01-05 07:11:42 +00:00
add_to_end_of_diveplan ( diveplan , dp ) ;
2014-02-28 04:09:57 +00:00
return ( dp ) ;
2013-01-05 07:11:42 +00:00
}
2013-01-10 00:01:15 +00:00
struct gaschanges {
2014-04-17 08:54:55 +00:00
int depth ;
2013-01-10 00:01:15 +00:00
int gasidx ;
} ;
2014-05-30 15:09:05 +00:00
2014-07-03 20:39:06 +00:00
static struct gaschanges * analyze_gaslist ( struct diveplan * diveplan , int * gaschangenr , int depth , int * asc_cylinder )
2013-01-08 16:52:48 +00:00
{
2014-06-01 21:17:06 +00:00
struct gasmix gas ;
2013-01-10 00:01:15 +00:00
int nr = 0 ;
struct gaschanges * gaschanges = NULL ;
struct divedatapoint * dp = diveplan - > dp ;
2014-07-03 20:39:06 +00:00
int best_depth = displayed_dive . cylinder [ * asc_cylinder ] . depth . mm ;
2013-01-10 00:01:15 +00:00
while ( dp ) {
2014-05-30 15:09:05 +00:00
if ( dp - > time = = 0 ) {
2014-06-01 22:25:19 +00:00
gas = dp - > gasmix ;
2014-05-30 15:09:05 +00:00
if ( dp - > depth < = depth ) {
int i = 0 ;
nr + + ;
gaschanges = realloc ( gaschanges , nr * sizeof ( struct gaschanges ) ) ;
while ( i < nr - 1 ) {
if ( dp - > depth < gaschanges [ i ] . depth ) {
memmove ( gaschanges + i + 1 , gaschanges + i , ( nr - i - 1 ) * sizeof ( struct gaschanges ) ) ;
break ;
}
i + + ;
2013-01-10 00:01:15 +00:00
}
2014-05-30 15:09:05 +00:00
gaschanges [ i ] . depth = dp - > depth ;
2014-07-03 20:39:06 +00:00
gaschanges [ i ] . gasidx = get_gasidx ( & displayed_dive , & gas ) ;
2014-05-30 15:09:05 +00:00
assert ( gaschanges [ i ] . gasidx ! = - 1 ) ;
} else {
/* is there a better mix to start deco? */
if ( dp - > depth < best_depth ) {
best_depth = dp - > depth ;
2014-07-03 20:39:06 +00:00
* asc_cylinder = get_gasidx ( & displayed_dive , & gas ) ;
2013-01-10 00:01:15 +00:00
}
2014-05-30 15:09:05 +00:00
}
2013-01-08 16:52:48 +00:00
}
2013-01-10 00:01:15 +00:00
dp = dp - > next ;
}
* gaschangenr = nr ;
# if DEBUG_PLAN & 16
2014-06-02 01:25:44 +00: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 20:39:06 +00:00
idx , gasname ( & displayed_dive . cylinder [ idx ] . gasmix ) ) ;
2014-06-02 01:25:44 +00:00
}
2013-01-10 00:01:15 +00:00
# endif
return gaschanges ;
}
/* sort all the stops into one ordered list */
2014-04-17 08:54:55 +00:00
static unsigned int * sort_stops ( int * dstops , int dnr , struct gaschanges * gstops , int gnr )
2013-01-10 00:01:15 +00:00
{
int i , gi , di ;
int total = dnr + gnr ;
2015-05-28 12:59:08 +00:00
unsigned int * stoplevels = malloc ( total * sizeof ( int ) ) ;
2013-01-10 00:01:15 +00:00
/* no gaschanges */
if ( gnr = = 0 ) {
2014-04-17 08:54:55 +00:00
memcpy ( stoplevels , dstops , dnr * sizeof ( int ) ) ;
2013-01-10 00:01:15 +00: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-28 04:09:57 +00:00
for ( k = gnr + dnr - 1 ; k > = 0 ; k - - ) {
printf ( " stoplevel[%d]: %5.2lfm \n " , k , stoplevels [ k ] / 1000.0 ) ;
2013-01-10 00:01:15 +00:00
if ( stoplevels [ k ] = = 0 )
break ;
2013-01-08 16:52:48 +00:00
}
2013-01-10 00:01:15 +00:00
# endif
return stoplevels ;
2013-01-08 16:52:48 +00:00
}
2014-08-05 10:58:23 +00:00
static void add_plan_to_notes ( struct diveplan * diveplan , struct dive * dive , bool show_disclaimer , int error )
2013-01-16 01:17:24 +00:00
{
2015-05-07 20:59:12 +00:00
char buffer [ 2000000 ] , temp [ 100000 ] ;
2015-06-20 05:04:05 +00:00
int len , lastdepth = 0 , lasttime = 0 , lastsetpoint = - 1 , newdepth = 0 , lastprintdepth = 0 , lastprintsetpoint = - 1 ;
struct gasmix lastprintgasmix = { - 1 , - 1 } ;
2013-01-16 01:17:24 +00:00
struct divedatapoint * dp = diveplan - > dp ;
2015-06-22 21:07:43 +00:00
bool gaschange_after = ! plan_verbatim ;
bool gaschange_before ;
2014-06-11 16:37:12 +00:00
struct divedatapoint * nextdp = NULL ;
2013-01-16 01:17:24 +00:00
2014-06-22 17:28:08 +00:00
disclaimer = translate ( " gettextFromC " , " DISCLAIMER / WARNING: THIS IS A NEW IMPLEMENTATION OF THE BUHLMANN "
2014-09-12 16:29:56 +00:00
" ALGORITHM AND A DIVE PLANNER IMPLEMENTATION BASED ON THAT WHICH HAS "
2014-06-04 21:34:09 +00:00
" RECEIVED ONLY A LIMITED AMOUNT OF TESTING. WE STRONGLY RECOMMEND NOT TO "
2014-06-22 17:28:08 +00:00
" PLAN DIVES SIMPLY BASED ON THE RESULTS GIVEN HERE. " ) ;
2014-06-04 21:34:09 +00:00
2013-01-16 01:17:24 +00:00
if ( ! dp )
return ;
2014-08-05 10:58:23 +00:00
if ( error ) {
2014-08-06 04:39:23 +00:00
snprintf ( temp , sizeof ( temp ) , " %s " ,
2014-08-05 10:58:23 +00:00
translate ( " gettextFromC " , " Decompression calculation aborted due to excessive time " ) ) ;
snprintf ( buffer , sizeof ( buffer ) , " <span style='color: red;'>%s </span> %s<br> " ,
translate ( " gettextFromC " , " Warning: " ) , temp ) ;
dive - > notes = strdup ( buffer ) ;
return ;
}
2014-06-22 17:28:08 +00:00
len = show_disclaimer ? snprintf ( buffer , sizeof ( buffer ) , " <div><b>%s<b></div><br> " , disclaimer ) : 0 ;
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " based on GFlow = %d and GFhigh = %d " ) ,
diveplan - > gflow , diveplan - > gfhigh ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <div><b>%s</b><br>%s</div><br> " ,
translate ( " gettextFromC " , " Subsurface dive plan " ) , temp ) ;
2014-06-03 09:38:24 +00:00
if ( ! plan_verbatim ) {
2014-06-22 17:28:08 +00:00
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <div><table><thead><tr><th>%s</th> " ,
translate ( " gettextFromC " , " depth " ) ) ;
2014-06-03 09:38:24 +00:00
if ( plan_display_duration )
2014-06-22 17:28:08 +00:00
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <th style='padding-left: 10px;'>%s</th> " ,
translate ( " gettextFromC " , " duration " ) ) ;
2015-06-16 11:15:46 +00:00
if ( plan_display_runtime )
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <th style='padding-left: 10px;'>%s</th> " ,
translate ( " gettextFromC " , " runtime " ) ) ;
2014-06-22 17:28:08 +00:00
len + = snprintf ( buffer + len , sizeof ( buffer ) - len ,
" <th style='padding-left: 10px; float: left;'>%s</th></tr></thead><tbody style='float: left;'> " ,
translate ( " gettextFromC " , " gas " ) ) ;
2014-06-03 09:38:24 +00:00
}
2013-01-16 01:17:24 +00:00
do {
2014-07-19 01:37:28 +00:00
struct gasmix gasmix , newgasmix = { } ;
2013-01-16 01:17:24 +00:00
const char * depth_unit ;
double depthvalue ;
int decimals ;
2015-06-20 05:04:05 +00:00
bool isascent = ( dp - > depth < lastdepth ) ;
2013-01-16 01:17:24 +00:00
2014-10-19 14:07:07 +00:00
nextdp = dp - > next ;
2013-01-16 01:17:24 +00:00
if ( dp - > time = = 0 )
continue ;
2014-06-01 23:10:07 +00:00
gasmix = dp - > gasmix ;
2013-01-16 01:17:24 +00:00
depthvalue = get_depth_units ( dp - > depth , & decimals , & depth_unit ) ;
2014-06-01 14:55:07 +00:00
/* analyze the dive points ahead */
2013-01-16 01:17:24 +00:00
while ( nextdp & & nextdp - > time = = 0 )
nextdp = nextdp - > next ;
2014-07-05 11:37:49 +00:00
if ( nextdp )
2014-06-01 23:10:07 +00:00
newgasmix = nextdp - > gasmix ;
2015-06-23 13:20:30 +00:00
gaschange_after = ( nextdp & & ( gasmix_distance ( & gasmix , & newgasmix ) | | dp - > setpoint ! = nextdp - > setpoint ) ) ;
gaschange_before = ( gasmix_distance ( & lastprintgasmix , & gasmix ) | | lastprintsetpoint ! = dp - > setpoint ) ;
2013-01-16 01:17:24 +00:00
/* do we want to skip this leg as it is devoid of anything useful? */
2014-06-02 00:34:40 +00:00
if ( ! dp - > entered & &
nextdp & &
dp - > depth ! = lastdepth & &
2015-06-23 13:20:30 +00:00
nextdp - > depth ! = dp - > depth & &
! gaschange_before & &
! gaschange_after )
2013-01-16 01:17:24 +00:00
continue ;
2015-06-22 21:07:43 +00:00
if ( dp - > time - lasttime < 10 & & ! ( gaschange_after & & dp - > next & & dp - > depth ! = dp - > next - > depth ) )
2014-06-03 11:00:51 +00:00
continue ;
2014-07-10 20:11:45 +00:00
2013-01-16 01:17:24 +00:00
len = strlen ( buffer ) ;
2014-06-03 09:38:24 +00:00
if ( plan_verbatim ) {
2014-12-05 14:13:05 +00:00
if ( dp - > depth ! = lastprintdepth ) {
2015-06-22 21:07:43 +00:00
if ( plan_display_transitions | | dp - > entered | | ! dp - > next | | ( gaschange_after & & dp - > next & & dp - > depth ! = nextdp - > depth ) ) {
2014-11-21 11:11:17 +00:00
if ( dp - > setpoint )
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " Transition to %.*f %s in %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar) " ) ,
decimals , depthvalue , depth_unit ,
FRACTION ( dp - > time - lasttime , 60 ) ,
FRACTION ( dp - > time , 60 ) ,
gasname ( & gasmix ) ,
( double ) dp - > setpoint / 1000.0 ) ;
else
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " Transition to %.*f %s in %d:%02d min - runtime %d:%02u on %s " ) ,
decimals , depthvalue , depth_unit ,
FRACTION ( dp - > time - lasttime , 60 ) ,
FRACTION ( dp - > time , 60 ) ,
gasname ( & gasmix ) ) ;
2014-06-22 17:28:08 +00:00
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " %s<br> " , temp ) ;
2014-06-04 21:54:33 +00:00
}
2015-02-03 19:37:35 +00:00
newdepth = dp - > depth ;
lasttime = dp - > time ;
2014-06-02 14:25:58 +00:00
} else {
2015-06-22 21:07:43 +00:00
if ( ( nextdp & & dp - > depth ! = nextdp - > depth ) | | gaschange_after ) {
2014-11-21 11:11:17 +00:00
if ( dp - > setpoint )
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar) " ) ,
decimals , depthvalue , depth_unit ,
FRACTION ( dp - > time - lasttime , 60 ) ,
FRACTION ( dp - > time , 60 ) ,
gasname ( & gasmix ) ,
( double ) dp - > setpoint / 1000.0 ) ;
else
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s " ) ,
decimals , depthvalue , depth_unit ,
FRACTION ( dp - > time - lasttime , 60 ) ,
FRACTION ( dp - > time , 60 ) ,
gasname ( & gasmix ) ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " %s<br> " , temp ) ;
2014-11-21 13:53:51 +00:00
newdepth = dp - > depth ;
2014-06-04 22:15:11 +00:00
lasttime = dp - > time ;
}
2014-06-03 09:38:24 +00:00
}
} else {
2015-06-22 21:07:43 +00:00
if ( plan_display_transitions | | dp - > entered | | ! dp - > next | | ( nextdp & & dp - > depth ! = nextdp - > depth ) | |
! isascent & & gaschange_before & & ( ( nextdp & & dp - > depth ! = nextdp - > depth ) | | gaschange_after ) | |
( isascent & & gaschange_after & & nextdp & & dp - > depth ! = nextdp - > depth ) ) {
2014-06-22 17:28:08 +00:00
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " %3.0f%s " ) , depthvalue , depth_unit ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <tr><td style='padding-left: 10px; float: right;'>%s</td> " , temp ) ;
if ( plan_display_duration ) {
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " %3dmin " ) , ( dp - > time - lasttime + 30 ) / 60 ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <td style='padding-left: 10px; float: right;'>%s</td> " , temp ) ;
2015-06-16 11:15:46 +00:00
}
if ( plan_display_runtime ) {
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " %3dmin " ) , ( dp - > time + 30 ) / 60 ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <td style='padding-left: 10px; float: right;'>%s</td> " , temp ) ;
2014-06-22 17:28:08 +00:00
}
2014-11-21 13:53:51 +00:00
2015-06-22 21:07:43 +00:00
if ( isascent & & gaschange_after & & dp - > next & & nextdp & & dp - > depth ! = nextdp - > depth ) {
2015-06-20 05:04:05 +00:00
if ( dp - > setpoint ) {
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " (SP = %.1fbar) " ) , ( double ) nextdp - > setpoint / 1000.0 ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <td style='padding-left: 10px; color: red; float: left;'><b>%s %s</b></td> " , gasname ( & newgasmix ) ,
2014-11-21 13:53:51 +00:00
temp ) ;
2015-06-20 05:04:05 +00:00
} else {
2014-11-21 13:53:51 +00:00
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <td style='padding-left: 10px; color: red; float: left;'><b>%s</b></td> " , gasname ( & newgasmix ) ) ;
2014-11-21 11:11:17 +00:00
}
2015-06-20 05:04:05 +00:00
lastprintsetpoint = nextdp - > setpoint ;
lastprintgasmix = newgasmix ;
2015-06-22 21:07:43 +00:00
gaschange_after = false ;
} else if ( gaschange_before ) {
2015-06-20 05:04:05 +00:00
if ( dp - > setpoint ) {
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " (SP = %.1fbar) " ) , ( double ) dp - > setpoint / 1000.0 ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <td style='padding-left: 10px; color: red; float: left;'><b>%s %s</b></td> " , gasname ( & gasmix ) ,
temp ) ;
} else {
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <td style='padding-left: 10px; color: red; float: left;'><b>%s</b></td> " , gasname ( & gasmix ) ) ;
}
lastprintsetpoint = dp - > setpoint ;
lastprintgasmix = gasmix ;
2015-06-22 21:07:43 +00:00
gaschange_after = false ;
2014-06-03 09:38:24 +00:00
} else {
2014-06-22 17:28:08 +00:00
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <td> </td> " ) ;
2014-06-02 14:25:58 +00:00
}
2014-06-03 09:38:24 +00:00
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " </tr> " ) ;
2014-11-21 13:53:51 +00:00
newdepth = dp - > depth ;
2014-06-04 21:54:33 +00:00
lasttime = dp - > time ;
2014-06-02 14:25:58 +00:00
}
}
2015-06-22 21:07:43 +00:00
if ( gaschange_after ) {
2014-06-01 14:55:07 +00:00
// gas switch at this waypoint
2014-06-03 09:38:24 +00:00
if ( plan_verbatim ) {
2014-11-21 11:11:17 +00:00
if ( lastsetpoint > = 0 ) {
2014-11-21 13:53:51 +00:00
if ( nextdp & & nextdp - > setpoint )
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " Switch gas to %s (SP = %.1fbar) " ) , gasname ( & newgasmix ) , ( double ) nextdp - > setpoint / 1000.0 ) ;
2014-11-21 11:11:17 +00:00
else
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " Switch gas to %s " ) , gasname ( & newgasmix ) ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " %s<br> " , temp ) ;
}
2015-06-22 21:07:43 +00:00
gaschange_after = false ;
2014-06-02 00:34:40 +00:00
gasmix = newgasmix ;
2014-11-21 13:53:51 +00:00
}
2013-01-16 01:17:24 +00:00
}
2014-12-05 14:13:05 +00:00
lastprintdepth = newdepth ;
lastdepth = dp - > depth ;
2014-11-21 11:11:17 +00:00
lastsetpoint = dp - > setpoint ;
2014-06-04 22:15:11 +00:00
} while ( ( dp = nextdp ) ! = NULL ) ;
2014-07-15 06:03:10 +00:00
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " </tbody></table></div> " ) ;
dive - > cns = 0 ;
dive - > maxcns = 0 ;
update_cylinder_related_info ( dive ) ;
snprintf ( temp , sizeof ( temp ) , " %s " , translate ( " gettextFromC " , " CNS " ) ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <div><br>%s: %i%% " , temp , dive - > cns ) ;
snprintf ( temp , sizeof ( temp ) , " %s " , translate ( " gettextFromC " , " OTU " ) ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <br>%s: %i</div> " , temp , dive - > otu ) ;
2014-06-22 17:28:08 +00:00
2015-01-10 23:01:15 +00:00
if ( dive - > dc . divemode = = CCR )
2014-10-24 13:40:59 +00:00
snprintf ( temp , sizeof ( temp ) , " %s " , translate ( " gettextFromC " , " Gas consumption (CCR legs excluded): " ) ) ;
else
snprintf ( temp , sizeof ( temp ) , " %s " , translate ( " gettextFromC " , " Gas consumption: " ) ) ;
2014-07-15 06:03:10 +00:00
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <div><br>%s<br> " , temp ) ;
2014-07-10 20:11:46 +00:00
for ( int gasidx = 0 ; gasidx < MAX_CYLINDERS ; gasidx + + ) {
2014-07-06 19:43:53 +00:00
double volume , pressure , deco_volume , deco_pressure ;
2014-07-01 07:37:49 +00:00
const char * unit , * pressure_unit ;
2014-06-22 17:28:08 +00:00
char warning [ 1000 ] = " " ;
2014-05-29 21:36:14 +00:00
cylinder_t * cyl = & dive - > cylinder [ gasidx ] ;
if ( cylinder_none ( cyl ) )
break ;
2014-06-22 17:28:08 +00:00
2014-06-01 16:59:38 +00:00
volume = get_volume_units ( cyl - > gas_used . mliter , NULL , & unit ) ;
2014-07-01 07:37:49 +00:00
deco_volume = get_volume_units ( cyl - > deco_gas_used . mliter , NULL , & unit ) ;
2014-06-01 16:59:38 +00:00
if ( cyl - > type . size . mliter ) {
2014-07-01 07:37:49 +00:00
deco_pressure = get_pressure_units ( 1000.0 * cyl - > deco_gas_used . mliter / cyl - > type . size . mliter , & pressure_unit ) ;
2014-07-06 19:43:53 +00:00
pressure = get_pressure_units ( 1000.0 * cyl - > gas_used . mliter / cyl - > type . size . mliter , & pressure_unit ) ;
2014-06-01 14:55:07 +00:00
/* Warn if the plan uses more gas than is available in a cylinder
* This only works if we have working pressure for the cylinder
* 10 bar is a made up number - but it seemed silly to pretend you could breathe cylinder down to 0 */
if ( cyl - > end . mbar < 10000 )
2014-06-22 17:28:08 +00:00
snprintf ( warning , sizeof ( warning ) , " — <span style='color: red;'>%s </span> %s " ,
translate ( " gettextFromC " , " Warning: " ) ,
translate ( " gettextFromC " , " this is more gas than available in the specified cylinder! " ) ) ;
2014-07-06 19:43:53 +00:00
else
if ( ( float ) cyl - > end . mbar * cyl - > type . size . mliter / 1000.0 < ( float ) cyl - > deco_gas_used . mliter )
snprintf ( warning , sizeof ( warning ) , " — <span style='color: red;'>%s </span> %s " ,
translate ( " gettextFromC " , " Warning: " ) ,
translate ( " gettextFromC " , " not enough reserve for gas sharing on ascent! " ) ) ;
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " %.0f%s/%.0f%s of %s (%.0f%s/%.0f%s in planned ascent) " ) , volume , unit , pressure , pressure_unit , gasname ( & cyl - > gasmix ) , deco_volume , unit , deco_pressure , pressure_unit ) ;
2014-07-01 07:37:49 +00:00
} else {
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " %.0f%s (%.0f%s during planned ascent) of %s " ) , volume , unit , deco_volume , unit , gasname ( & cyl - > gasmix ) ) ;
2014-06-01 14:55:07 +00:00
}
2014-06-22 17:28:08 +00:00
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " %s%s<br> " , temp , warning ) ;
2013-01-16 01:24:17 +00:00
}
2014-06-02 19:29:40 +00:00
dp = diveplan - > dp ;
2015-01-15 22:22:12 +00:00
if ( dive - > dc . divemode ! = CCR ) {
while ( dp ) {
if ( dp - > time ! = 0 ) {
struct gas_pressures pressures ;
fill_pressures ( & pressures , depth_to_atm ( dp - > depth , dive ) , & dp - > gasmix , 0.0 , dive - > dc . divemode ) ;
if ( pressures . o2 > ( dp - > entered ? prefs . bottompo2 : prefs . decopo2 ) / 1000.0 ) {
const char * depth_unit ;
int decimals ;
double depth_value = get_depth_units ( dp - > depth , & decimals , & depth_unit ) ;
len = strlen ( buffer ) ;
snprintf ( temp , sizeof ( temp ) ,
translate ( " gettextFromC " , " high pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s " ) ,
pressures . o2 , FRACTION ( dp - > time , 60 ) , gasname ( & dp - > gasmix ) , decimals , depth_value , depth_unit ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <span style='color: red;'>%s </span> %s<br> " ,
translate ( " gettextFromC " , " Warning: " ) , temp ) ;
} else if ( pressures . o2 < 0.16 ) {
const char * depth_unit ;
int decimals ;
double depth_value = get_depth_units ( dp - > depth , & decimals , & depth_unit ) ;
len = strlen ( buffer ) ;
snprintf ( temp , sizeof ( temp ) ,
translate ( " gettextFromC " , " low pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s " ) ,
pressures . o2 , FRACTION ( dp - > time , 60 ) , gasname ( & dp - > gasmix ) , decimals , depth_value , depth_unit ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <span style='color: red;'>%s </span> %s<br> " ,
translate ( " gettextFromC " , " Warning: " ) , temp ) ;
}
2014-06-02 19:29:40 +00:00
}
2015-01-15 22:22:12 +00:00
dp = dp - > next ;
2014-06-02 19:29:40 +00:00
}
}
2014-06-22 17:28:08 +00:00
snprintf ( buffer + len , sizeof ( buffer ) - len , " </div> " ) ;
2013-01-16 01:17:24 +00:00
dive - > notes = strdup ( buffer ) ;
}
2015-04-04 16:41:09 +00:00
int ascent_velocity ( int depth , int avg_depth , int bottom_time )
2014-04-17 08:54:55 +00:00
{
/* We need to make this configurable */
2014-04-26 15:29:40 +00: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-24 22:08:36 +00: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 08:54:55 +00:00
}
2015-04-08 17:18:54 +00:00
void track_ascent_gas ( int depth , cylinder_t * cylinder , int avg_depth , int bottom_time , bool safety_stop )
2015-04-04 16:38:56 +00:00
{
while ( depth > 0 ) {
2015-04-04 16:41:09 +00:00
int deltad = ascent_velocity ( depth , avg_depth , bottom_time ) * TIMESTEP ;
2015-04-04 16:38:56 +00:00
if ( deltad > depth )
deltad = depth ;
2015-04-09 18:26:56 +00:00
update_cylinder_pressure ( & displayed_dive , depth , depth - deltad , TIMESTEP , prefs . decosac , cylinder , true ) ;
2015-05-21 17:55:28 +00:00
if ( depth < = 5000 & & depth > = ( 5000 - deltad ) & & safety_stop ) {
2015-04-09 18:26:56 +00:00
update_cylinder_pressure ( & displayed_dive , 5000 , 5000 , 180 , prefs . decosac , cylinder , true ) ;
2015-04-08 17:18:54 +00:00
safety_stop = false ;
}
2015-04-04 16:38:56 +00:00
depth - = deltad ;
}
}
2015-03-31 12:52:37 +00:00
bool trial_ascent ( int trial_depth , int stoplevel , int avg_depth , int bottom_time , double tissue_tolerance , struct gasmix * gasmix , int po2 , double surface_pressure )
{
bool clear_to_ascend = true ;
char * trial_cache = NULL ;
cache_deco_state ( tissue_tolerance , & trial_cache ) ;
while ( trial_depth > stoplevel ) {
2015-04-04 16:41:09 +00:00
int deltad = ascent_velocity ( trial_depth , avg_depth , bottom_time ) * TIMESTEP ;
2015-03-31 12:52:37 +00:00
if ( deltad > trial_depth ) /* don't test against depth above surface */
deltad = trial_depth ;
tissue_tolerance = add_segment ( depth_to_mbar ( trial_depth , & displayed_dive ) / 1000.0 ,
gasmix ,
TIMESTEP , po2 , & displayed_dive , prefs . decosac ) ;
if ( deco_allowed_depth ( tissue_tolerance , surface_pressure , & displayed_dive , 1 ) > trial_depth - deltad ) {
/* We should have stopped */
clear_to_ascend = false ;
break ;
}
trial_depth - = deltad ;
}
restore_deco_state ( trial_cache ) ;
2015-06-22 03:24:07 +00:00
free ( trial_cache ) ;
2015-03-31 12:52:37 +00:00
return clear_to_ascend ;
}
2015-04-04 16:38:56 +00: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 )
2015-04-09 15:41:41 +00:00
return ( float ) ( cyl - > end . mbar - prefs . reserve_gas ) * cyl - > type . size . mliter / 1000.0 > ( float ) cyl - > deco_gas_used . mliter ;
2015-04-04 16:38:56 +00:00
else
return true ;
}
2015-05-09 19:55:59 +00:00
// Work out the stops. Return value is if there were any mandatory stops.
bool plan ( struct diveplan * diveplan , char * * cached_datap , bool is_planner , bool show_disclaimer )
2013-01-05 07:11:42 +00:00
{
struct sample * sample ;
2014-06-02 00:45:00 +00:00
int po2 ;
2013-10-08 05:37:32 +00:00
int transitiontime , gi ;
2014-04-17 08:54:55 +00:00
int current_cylinder ;
unsigned int stopidx ;
2014-04-18 20:06:21 +00:00
int depth ;
2014-06-11 16:37:12 +00:00
double tissue_tolerance = 0.0 ;
2013-11-12 02:19:04 +00:00
struct gaschanges * gaschanges = NULL ;
2013-01-10 00:01:15 +00:00
int gaschangenr ;
2015-05-28 12:59:08 +00:00
unsigned int * stoplevels = NULL ;
2014-04-17 08:54:55 +00:00
bool stopping = false ;
2015-06-22 12:43:20 +00:00
bool pendinggaschange = false ;
2014-04-17 08:54:55 +00:00
bool clear_to_ascend ;
int clock , previous_point_time ;
2015-04-02 08:49:24 +00:00
int avg_depth , max_depth , bottom_time = 0 ;
2014-04-26 19:06:48 +00:00
int last_ascend_rate ;
2014-05-30 15:09:05 +00:00
int best_first_ascend_cylinder ;
2014-06-01 21:17:06 +00:00
struct gasmix gas ;
2014-07-02 20:07:38 +00:00
int o2time = 0 ;
int breaktime = - 1 ;
2014-08-06 17:51:53 +00:00
int breakcylinder = 0 ;
2014-08-05 10:58:23 +00:00
int error = 0 ;
2015-05-09 19:55:59 +00:00
bool decodive = false ;
2013-01-05 07:11:42 +00:00
2014-06-10 16:08:07 +00:00
set_gf ( diveplan - > gflow , diveplan - > gfhigh , prefs . gf_low_at_maxdepth ) ;
2013-01-05 07:11:42 +00:00
if ( ! diveplan - > surface_pressure )
2013-01-14 22:53:38 +00:00
diveplan - > surface_pressure = SURFACE_PRESSURE ;
2014-07-04 18:26:31 +00:00
create_dive_from_plan ( diveplan , is_planner ) ;
2013-01-05 07:11:42 +00:00
2015-04-05 12:17:26 +00:00
if ( prefs . verbatim_plan )
plan_verbatim = true ;
if ( prefs . display_runtime )
plan_display_runtime = true ;
if ( prefs . display_duration )
plan_display_duration = true ;
if ( prefs . display_transitions )
plan_display_transitions = true ;
if ( prefs . last_stop )
decostoplevels [ 1 ] = 6000 ;
2014-04-18 14:08:33 +00:00
/* Let's start at the last 'sample', i.e. the last manually entered waypoint. */
2014-07-03 20:39:06 +00:00
sample = & displayed_dive . dc . sample [ displayed_dive . dc . samples - 1 ] ;
2014-07-17 19:16:50 +00:00
get_gas_at_time ( & displayed_dive , & displayed_dive . dc , sample - > time , & gas ) ;
2014-10-19 14:07:07 +00:00
po2 = sample - > setpoint . mbar ;
2014-07-03 20:39:06 +00:00
if ( ( current_cylinder = get_gasidx ( & displayed_dive , & gas ) ) = = - 1 ) {
2014-06-02 01:25:44 +00:00
report_error ( translate ( " gettextFromC " , " Can't find gas %s " ) , gasname ( & gas ) ) ;
2014-04-17 08:54:55 +00:00
current_cylinder = 0 ;
}
2014-07-03 20:39:06 +00:00
depth = displayed_dive . dc . sample [ displayed_dive . dc . samples - 1 ] . depth . mm ;
2015-04-02 08:49:24 +00:00
average_max_depth ( diveplan , & avg_depth , & max_depth ) ;
2015-04-04 16:41:09 +00:00
last_ascend_rate = ascent_velocity ( depth , avg_depth , bottom_time ) ;
2013-09-19 03:40:34 +00:00
2013-12-10 06:50:19 +00:00
/* if all we wanted was the dive just get us back to the surface */
2014-07-04 18:26:31 +00:00
if ( ! is_planner ) {
2013-12-10 06:50:19 +00:00
transitiontime = depth / 75 ; /* this still needs to be made configurable */
2014-06-01 22:25:19 +00:00
plan_add_segment ( diveplan , transitiontime , 0 , gas , po2 , false ) ;
2014-07-04 18:26:31 +00:00
create_dive_from_plan ( diveplan , is_planner ) ;
2015-05-09 19:55:59 +00:00
return ( false ) ;
2013-09-19 03:40:34 +00:00
}
2014-07-03 20:39:06 +00:00
tissue_tolerance = tissue_at_end ( & displayed_dive , cached_datap ) ;
2014-04-17 08:54:55 +00:00
2013-01-08 22:05:25 +00:00
# if DEBUG_PLAN & 4
2014-06-02 01:25:44 +00:00
printf ( " gas %s \n " , gasname ( & gas ) ) ;
2014-07-04 04:02:39 +00:00
printf ( " depth %5.2lfm \n " , depth / 1000.0 ) ;
2013-01-08 16:52:48 +00:00
# endif
2014-04-17 08:54:55 +00:00
2014-05-30 15:09:05 +00:00
best_first_ascend_cylinder = current_cylinder ;
2014-04-18 14:08:33 +00:00
/* Find the gases available for deco */
2014-11-21 10:10:19 +00: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 14:08:33 +00:00
/* Find the first potential decostopdepth above current depth */
2013-01-10 00:01:15 +00:00
for ( stopidx = 0 ; stopidx < sizeof ( decostoplevels ) / sizeof ( int ) ; stopidx + + )
2014-04-17 08:54:55 +00:00
if ( decostoplevels [ stopidx ] > = depth )
2013-01-05 07:11:42 +00:00
break ;
2013-11-08 12:40:59 +00:00
if ( stopidx > 0 )
stopidx - - ;
2014-04-18 14:08:33 +00:00
/* Stoplevels are either depths of gas changes or potential deco stop depths. */
2013-01-10 00:01:15 +00:00
stoplevels = sort_stops ( decostoplevels , stopidx + 1 , gaschanges , gaschangenr ) ;
2014-04-17 08:54:55 +00:00
stopidx + = gaschangenr ;
2013-01-10 00:01:15 +00:00
2014-04-18 14:08:33 +00:00
/* Keep time during the ascend */
2014-07-03 20:39:06 +00:00
bottom_time = clock = previous_point_time = displayed_dive . dc . sample [ displayed_dive . dc . samples - 1 ] . time . seconds ;
2013-01-10 00:01:15 +00:00
gi = gaschangenr - 1 ;
2015-03-31 12:52:37 +00:00
if ( prefs . recreational_mode ) {
2015-04-02 08:49:24 +00:00
bool safety_stop = prefs . safetystop & & max_depth > = 10000 ;
2015-04-08 17:18:54 +00:00
track_ascent_gas ( depth , & displayed_dive . cylinder [ current_cylinder ] , avg_depth , bottom_time , safety_stop ) ;
2015-03-31 12:52:37 +00:00
// How long can we stay at the current depth and still directly ascent to the surface?
while ( trial_ascent ( depth , 0 , avg_depth , bottom_time , tissue_tolerance , & displayed_dive . cylinder [ current_cylinder ] . gasmix ,
2015-04-04 16:38:56 +00:00
po2 , diveplan - > surface_pressure / 1000.0 ) & &
enough_gas ( current_cylinder ) ) {
2015-03-31 12:52:37 +00:00
tissue_tolerance = add_segment ( depth_to_mbar ( depth , & displayed_dive ) / 1000.0 ,
& displayed_dive . cylinder [ current_cylinder ] . gasmix ,
DECOTIMESTEP , po2 , & displayed_dive , prefs . bottomsac ) ;
2015-04-04 16:38:56 +00:00
update_cylinder_pressure ( & displayed_dive , depth , depth , DECOTIMESTEP , prefs . bottomsac , & displayed_dive . cylinder [ current_cylinder ] , false ) ;
2015-03-31 12:52:37 +00:00
clock + = DECOTIMESTEP ;
}
clock - = DECOTIMESTEP ;
2015-04-04 16:38:56 +00:00
plan_add_segment ( diveplan , clock - previous_point_time , depth , gas , po2 , true ) ;
2015-03-31 12:52:37 +00:00
previous_point_time = clock ;
do {
/* Ascend to surface */
2015-04-04 16:41:09 +00:00
int deltad = ascent_velocity ( depth , avg_depth , bottom_time ) * TIMESTEP ;
if ( ascent_velocity ( depth , avg_depth , bottom_time ) ! = last_ascend_rate ) {
2015-03-31 12:52:37 +00:00
plan_add_segment ( diveplan , clock - previous_point_time , depth , gas , po2 , false ) ;
previous_point_time = clock ;
2015-04-04 16:41:09 +00:00
last_ascend_rate = ascent_velocity ( depth , avg_depth , bottom_time ) ;
2015-03-31 12:52:37 +00:00
}
if ( depth - deltad < 0 )
deltad = depth ;
tissue_tolerance = add_segment ( depth_to_mbar ( depth , & displayed_dive ) / 1000.0 ,
& displayed_dive . cylinder [ current_cylinder ] . gasmix ,
TIMESTEP , po2 , & displayed_dive , prefs . decosac ) ;
clock + = TIMESTEP ;
depth - = deltad ;
2015-05-21 17:55:28 +00:00
if ( depth < = 5000 & & depth > = ( 5000 - deltad ) & & safety_stop ) {
2015-04-02 09:17:57 +00:00
plan_add_segment ( diveplan , clock - previous_point_time , 5000 , gas , po2 , false ) ;
previous_point_time = clock ;
clock + = 180 ;
plan_add_segment ( diveplan , clock - previous_point_time , 5000 , gas , po2 , false ) ;
previous_point_time = clock ;
safety_stop = false ;
}
2015-03-31 12:52:37 +00:00
} while ( depth > 0 ) ;
plan_add_segment ( diveplan , clock - previous_point_time , 0 , gas , po2 , false ) ;
create_dive_from_plan ( diveplan , is_planner ) ;
add_plan_to_notes ( diveplan , & displayed_dive , show_disclaimer , error ) ;
fixup_dc_duration ( & displayed_dive . dc ) ;
free ( stoplevels ) ;
free ( gaschanges ) ;
2015-05-09 19:55:59 +00:00
return ( false ) ;
2015-03-31 12:52:37 +00:00
}
2014-04-17 08:54:55 +00:00
2014-05-30 15:09:05 +00:00
if ( best_first_ascend_cylinder ! = current_cylinder ) {
stopping = true ;
current_cylinder = best_first_ascend_cylinder ;
2014-07-03 20:39:06 +00:00
gas = displayed_dive . cylinder [ current_cylinder ] . gasmix ;
2014-10-19 14:07:07 +00:00
2014-05-30 15:09:05 +00:00
# if DEBUG_PLAN & 16
printf ( " switch to gas %d (%d/%d) @ %5.2lfm \n " , best_first_ascend_cylinder ,
2014-07-10 20:29:00 +00:00
( get_o2 ( & gas ) + 5 ) / 10 , ( get_he ( & gas ) + 5 ) / 10 , gaschanges [ best_first_ascend_cylinder ] . depth / 1000.0 ) ;
2014-05-30 15:09:05 +00:00
# endif
}
2014-04-17 08:54:55 +00:00
while ( 1 ) {
/* We will break out when we hit the surface */
do {
2014-04-18 14:08:33 +00:00
/* Ascend to next stop depth */
2015-04-04 16:41:09 +00:00
int deltad = ascent_velocity ( depth , avg_depth , bottom_time ) * TIMESTEP ;
if ( ascent_velocity ( depth , avg_depth , bottom_time ) ! = last_ascend_rate ) {
2014-06-01 22:25:19 +00:00
plan_add_segment ( diveplan , clock - previous_point_time , depth , gas , po2 , false ) ;
2014-04-26 19:06:48 +00:00
previous_point_time = clock ;
stopping = false ;
2015-04-04 16:41:09 +00:00
last_ascend_rate = ascent_velocity ( depth , avg_depth , bottom_time ) ;
2014-04-26 19:06:48 +00:00
}
2014-04-17 08:54:55 +00:00
if ( depth - deltad < stoplevels [ stopidx ] )
deltad = depth - stoplevels [ stopidx ] ;
2014-07-03 20:39:06 +00:00
tissue_tolerance = add_segment ( depth_to_mbar ( depth , & displayed_dive ) / 1000.0 ,
& displayed_dive . cylinder [ current_cylinder ] . gasmix ,
2014-10-24 14:40:21 +00:00
TIMESTEP , po2 , & displayed_dive , prefs . decosac ) ;
2014-04-18 14:08:33 +00:00
clock + = TIMESTEP ;
2014-04-17 08:54:55 +00:00
depth - = deltad ;
2015-06-14 22:25:43 +00:00
} while ( depth > 0 & & depth > stoplevels [ stopidx ] ) ;
2014-04-17 08:54:55 +00:00
if ( depth < = 0 )
2014-06-03 09:38:24 +00:00
break ; /* We are at the surface */
2014-04-17 08:54:55 +00:00
2015-06-22 12:43:20 +00:00
if ( gi > = 0 & & stoplevels [ stopidx ] < = gaschanges [ gi ] . depth ) {
2014-04-18 14:08:33 +00:00
/* We have reached a gas change.
* Record this in the dive plan */
2014-06-01 22:25:19 +00:00
plan_add_segment ( diveplan , clock - previous_point_time , depth , gas , po2 , false ) ;
2014-04-17 08:54:55 +00:00
previous_point_time = clock ;
stopping = true ;
2015-06-19 14:02:38 +00:00
/* Check we need to change cylinder.
2015-06-22 12:43:20 +00:00
* 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-06-19 14:02:38 +00:00
if ( current_cylinder ! = gaschanges [ gi ] . gasidx ) {
2015-06-22 12:43:20 +00:00
if ( ! prefs . switch_at_req_stop | |
! trial_ascent ( depth , stoplevels [ stopidx - 1 ] , avg_depth , bottom_time , tissue_tolerance ,
& displayed_dive . cylinder [ current_cylinder ] . gasmix , po2 , diveplan - > surface_pressure / 1000.0 ) | | get_o2 ( & displayed_dive . cylinder [ current_cylinder ] . gasmix ) < 160 ) {
current_cylinder = gaschanges [ gi ] . gasidx ;
gas = displayed_dive . cylinder [ current_cylinder ] . gasmix ;
2013-01-10 00:01:15 +00:00
# if DEBUG_PLAN & 16
2015-06-22 12:43:20 +00:00
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 ) ;
2013-01-10 00:01:15 +00:00
# endif
2015-06-22 12:43:20 +00:00
/* Stop for the minimum duration to switch gas */
tissue_tolerance = add_segment ( depth_to_mbar ( depth , & displayed_dive ) / 1000.0 ,
& displayed_dive . cylinder [ current_cylinder ] . gasmix ,
prefs . min_switch_duration , po2 , & displayed_dive , prefs . decosac ) ;
clock + = prefs . min_switch_duration ;
} else {
pendinggaschange = true ;
}
2013-01-10 00:01:15 +00:00
gi - - ;
2015-06-22 12:43:20 +00:00
}
2013-01-10 00:01:15 +00:00
}
2013-10-07 00:32:50 +00:00
2014-04-17 08:54:55 +00:00
- - stopidx ;
2014-04-18 14:08:33 +00:00
/* Save the current state and try to ascend to the next stopdepth */
2014-06-03 09:38:24 +00:00
while ( 1 ) {
2014-04-18 14:08:33 +00:00
/* Check if ascending to next stop is clear, go back and wait if we hit the ceiling on the way */
2015-03-31 12:52:37 +00:00
if ( trial_ascent ( depth , stoplevels [ stopidx ] , avg_depth , bottom_time , tissue_tolerance ,
& displayed_dive . cylinder [ current_cylinder ] . gasmix , po2 , diveplan - > surface_pressure / 1000.0 ) )
2014-06-03 09:38:24 +00:00
break ; /* We did not hit the ceiling */
2014-04-17 08:54:55 +00:00
2014-04-18 14:08:33 +00:00
/* Add a minute of deco time and then try again */
2015-05-09 19:55:59 +00:00
decodive = true ;
2014-04-17 08:54:55 +00:00
if ( ! stopping ) {
2014-04-18 14:08:33 +00:00
/* The last segment was an ascend segment.
* Add a waypoint for start of this deco stop */
2014-06-01 22:25:19 +00:00
plan_add_segment ( diveplan , clock - previous_point_time , depth , gas , po2 , false ) ;
2014-04-17 08:54:55 +00:00
previous_point_time = clock ;
stopping = true ;
}
2015-06-20 01:40:36 +00:00
2015-06-22 12:43:20 +00:00
if ( pendinggaschange ) {
current_cylinder = gaschanges [ gi + 1 ] . gasidx ;
gas = displayed_dive . cylinder [ current_cylinder ] . gasmix ;
# if DEBUG_PLAN & 16
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 ) ;
# endif
/* Stop for the minimum duration to switch gas */
tissue_tolerance = add_segment ( depth_to_mbar ( depth , & displayed_dive ) / 1000.0 ,
& displayed_dive . cylinder [ current_cylinder ] . gasmix ,
prefs . min_switch_duration , po2 , & displayed_dive , prefs . decosac ) ;
clock + = prefs . min_switch_duration ;
pendinggaschange = false ;
}
2015-06-20 01:40:36 +00:00
/* Deco stop should end when runtime is at a whole minute */
int this_decotimestep ;
this_decotimestep = DECOTIMESTEP - clock % DECOTIMESTEP ;
2014-07-03 20:39:06 +00:00
tissue_tolerance = add_segment ( depth_to_mbar ( depth , & displayed_dive ) / 1000.0 ,
& displayed_dive . cylinder [ current_cylinder ] . gasmix ,
2015-06-20 01:40:36 +00:00
this_decotimestep , po2 , & displayed_dive , prefs . decosac ) ;
clock + = this_decotimestep ;
2014-08-05 10:58:23 +00:00
/* Finish infinite deco */
if ( clock > = 48 * 3600 & & depth > = 6000 ) {
error = LONGDECO ;
break ;
}
2014-07-02 20:07:38 +00:00
if ( prefs . doo2breaks ) {
if ( get_o2 ( & displayed_dive . cylinder [ current_cylinder ] . gasmix ) = = 1000 ) {
o2time + = DECOTIMESTEP ;
if ( o2time > = 12 * 60 ) {
breaktime = 0 ;
breakcylinder = current_cylinder ;
plan_add_segment ( diveplan , clock - previous_point_time , depth , gas , po2 , false ) ;
previous_point_time = clock ;
current_cylinder = 0 ;
gas = displayed_dive . cylinder [ current_cylinder ] . gasmix ;
}
} else {
if ( breaktime > = 0 ) {
breaktime + = DECOTIMESTEP ;
if ( breaktime > = 6 * 60 ) {
o2time = 0 ;
plan_add_segment ( diveplan , clock - previous_point_time , depth , gas , po2 , false ) ;
previous_point_time = clock ;
current_cylinder = breakcylinder ;
gas = displayed_dive . cylinder [ current_cylinder ] . gasmix ;
breaktime = - 1 ;
}
}
}
}
2014-04-17 08:54:55 +00:00
}
if ( stopping ) {
2014-04-18 14:08:33 +00:00
/* Next we will ascend again. Add a waypoint if we have spend deco time */
2014-06-01 22:25:19 +00:00
plan_add_segment ( diveplan , clock - previous_point_time , depth , gas , po2 , false ) ;
2014-04-17 08:54:55 +00:00
previous_point_time = clock ;
stopping = false ;
}
}
2014-04-18 14:08:33 +00:00
2014-08-04 14:36:07 +00:00
/* We made it to the surface
* Create the final dive , add the plan to the notes and fixup some internal
* data that we need to be there when plotting the dive */
2014-06-01 22:25:19 +00:00
plan_add_segment ( diveplan , clock - previous_point_time , 0 , gas , po2 , false ) ;
2014-07-04 18:26:31 +00:00
create_dive_from_plan ( diveplan , is_planner ) ;
2014-08-05 10:58:23 +00:00
add_plan_to_notes ( diveplan , & displayed_dive , show_disclaimer , error ) ;
2014-08-04 14:36:07 +00:00
fixup_dc_duration ( & displayed_dive . dc ) ;
2013-10-07 00:32:50 +00:00
2013-01-10 00:01:15 +00:00
free ( stoplevels ) ;
free ( gaschanges ) ;
2015-05-09 19:55:59 +00:00
return decodive ;
2013-01-05 07:11:42 +00:00
}
2013-01-07 19:23:14 +00:00
/*
* Get a value in tenths ( so " 10.2 " = = 102 , " 9 " = 90 )
*
* Return negative for errors .
*/
2013-01-07 21:13:10 +00:00
static int get_tenths ( const char * begin , const char * * endp )
2013-01-07 19:23:14 +00:00
{
2013-01-07 21:13:10 +00:00
char * end ;
int value = strtol ( begin , & end , 10 ) ;
if ( begin = = end )
2013-01-07 19:23:14 +00:00
return - 1 ;
value * = 10 ;
/* Fraction? We only look at the first digit */
2013-01-07 21:13:10 +00:00
if ( * end = = ' . ' ) {
2013-01-16 00:54:59 +00:00
end + + ;
2013-01-07 21:13:10 +00:00
if ( ! isdigit ( * end ) )
2013-01-07 19:23:14 +00:00
return - 1 ;
2013-01-07 21:13:10 +00:00
value + = * end - ' 0 ' ;
2013-01-07 19:23:14 +00:00
do {
2013-01-16 00:54:59 +00:00
end + + ;
2013-01-07 21:13:10 +00:00
} while ( isdigit ( * end ) ) ;
2013-01-07 19:23:14 +00:00
}
2013-01-16 00:54:59 +00:00
* endp = end ;
return value ;
}
2013-01-07 21:13:10 +00:00
static int get_permille ( const char * begin , const char * * end )
2013-01-07 19:23:14 +00:00
{
int value = get_tenths ( begin , end ) ;
if ( value > = 0 ) {
/* Allow a percentage sign */
if ( * * end = = ' % ' )
+ + * end ;
}
return value ;
}
2014-06-01 21:17:06 +00:00
int validate_gas ( const char * text , struct gasmix * gas )
2013-01-07 19:23:14 +00:00
{
int o2 , he ;
if ( ! text )
return 0 ;
2013-10-05 07:29:09 +00:00
while ( isspace ( * text ) )
2013-01-07 19:23:14 +00:00
text + + ;
2013-01-07 21:13:10 +00:00
if ( ! * text )
return 0 ;
2014-02-28 04:09:57 +00:00
if ( ! strcasecmp ( text , translate ( " gettextFromC " , " air " ) ) ) {
o2 = O2_IN_AIR ;
he = 0 ;
text + = strlen ( translate ( " gettextFromC " , " air " ) ) ;
2015-05-14 21:33:48 +00:00
} else if ( ! strcasecmp ( text , translate ( " gettextFromC " , " oxygen " ) ) ) {
o2 = 1000 ;
he = 0 ;
text + = strlen ( translate ( " gettextFromC " , " oxygen " ) ) ;
2014-02-28 04:09:57 +00:00
} else if ( ! strncasecmp ( text , translate ( " gettextFromC " , " ean " ) , 3 ) ) {
o2 = get_permille ( text + 3 , & text ) ;
he = 0 ;
2013-01-07 19:23:14 +00:00
} else {
2014-02-28 04:09:57 +00:00
o2 = get_permille ( text , & text ) ;
he = 0 ;
2013-01-07 19:23:14 +00:00
if ( * text = = ' / ' )
2014-02-28 04:09:57 +00:00
he = get_permille ( text + 1 , & text ) ;
2013-01-07 19:23:14 +00:00
}
/* We don't want any extra crud */
2013-10-05 07:29:09 +00:00
while ( isspace ( * text ) )
2013-01-07 19:23:14 +00:00
text + + ;
if ( * text )
return 0 ;
/* Validate the gas mix */
2014-02-28 04:09:57 +00:00
if ( * text | | o2 < 1 | | o2 > 1000 | | he < 0 | | o2 + he > 1000 )
2013-01-07 19:23:14 +00:00
return 0 ;
/* Let it rip */
2014-06-01 21:17:06 +00:00
gas - > o2 . permille = o2 ;
gas - > he . permille = he ;
2013-01-07 19:23:14 +00:00
return 1 ;
}
2013-04-08 03:20:25 +00:00
int validate_po2 ( const char * text , int * mbar_po2 )
2013-01-23 19:12:24 +00:00
{
int po2 ;
if ( ! text )
return 0 ;
po2 = get_tenths ( text , & text ) ;
if ( po2 < 0 )
return 0 ;
2013-10-05 07:29:09 +00:00
while ( isspace ( * text ) )
2013-01-23 19:12:24 +00:00
text + + ;
2013-10-05 07:29:09 +00:00
while ( isspace ( * text ) )
2013-01-23 19:12:24 +00:00
text + + ;
if ( * text )
return 0 ;
* mbar_po2 = po2 * 100 ;
return 1 ;
}