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
2014-06-06 19:13:54 +00:00
# define TIMESTEP 3 /* 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-06-01 21:17:06 +00:00
void get_gas_from_events ( struct divecomputer * dc , int time , struct gasmix * gas )
2013-01-10 00:01:15 +00:00
{
2013-11-18 19:55:56 +00:00
// we don't modify the values passed in if nothing is found
2014-06-01 21:17:06 +00:00
// so don't call with uninitialized gasmix !
2013-01-10 00:01:15 +00:00
struct event * event = dc - > events ;
while ( event & & event - > time . seconds < = time ) {
2014-06-01 21:17:06 +00:00
if ( ! strcmp ( event - > name , " gaschange " ) )
* gas = * get_gasmix_from_event ( event ) ;
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 )
2014-06-01 21:17:06 +00:00
if ( gasmix_distance ( & dive - > cylinder [ gasidx ] . gasmix , mix ) < 200 )
2013-01-10 00:01:15 +00:00
return gasidx ;
return - 1 ;
}
2014-06-22 14:41:44 +00:00
double interpolate_transition ( struct dive * dive , int t0 , int t1 , int d0 , int d1 , const struct gasmix * gasmix , int 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
for ( j = t0 ; j < t1 ; j + + ) {
int depth = interpolate ( d0 , d1 , j - t0 , t1 - t0 ) ;
2014-06-22 14:41:44 +00:00
tissue_tolerance = add_segment ( depth_to_mbar ( depth , dive ) / 1000.0 , gasmix , 1 , po2 , dive ) ;
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-04-18 20:06:21 +00:00
int i , t0 , t1 , gasidx , lastdepth ;
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 ;
2013-01-08 21:54:29 +00:00
lastdepth = t0 = 0 ;
2013-02-19 22:28:07 +00:00
/* we always start with gas 0 (unless an event tells us otherwise) */
2014-06-01 21:17:06 +00:00
gas = dive - > cylinder [ 0 ] . gasmix ;
2013-01-05 07:11:42 +00:00
for ( i = 0 ; i < dc - > samples ; i + + , sample + + ) {
t1 = sample - > time . seconds ;
2014-06-01 21:17:06 +00:00
get_gas_from_events ( & dive - > dc , t0 , & gas ) ;
if ( ( gasidx = get_gasidx ( dive , & gas ) ) = = - 1 ) {
2014-06-02 01:25:44 +00:00
report_error ( translate ( " gettextFromC " , " Can't find gas %s " ) , gasname ( & gas ) ) ;
2013-01-10 00:01:15 +00:00
gasidx = 0 ;
}
2013-01-08 21:54:29 +00:00
if ( i > 0 )
lastdepth = psample - > depth . mm ;
2014-06-03 17:21:41 +00:00
tissue_tolerance = interpolate_transition ( dive , t0 , t1 , lastdepth , sample - > depth . mm , & dive - > cylinder [ gasidx ] . gasmix , sample - > po2 . mbar ) ;
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 ;
2014-06-01 23:27:19 +00:00
if ( gasmix_distance ( & cyl - > gasmix , & mix_in ) < 200 )
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 ;
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 ;
gas_used . mliter = depth_to_atm ( mean_depth . mm , d ) * sac / 60 * duration ;
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 ;
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-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-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-06-03 17:21:41 +00:00
sample - > po2 . mbar = dp - > po2 ;
2014-07-04 18:26:31 +00:00
if ( track_gas )
sample - > cylinderpressure . mbar = cyl - > end . mbar ;
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 ;
2013-02-02 17:03:26 +00:00
int po2 = dp - > po2 ;
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 ) {
if ( lasttime )
2014-05-05 16:28:58 +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 */
2014-04-30 05:37:19 +00:00
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-06-03 17:21:41 +00:00
sample [ - 1 ] . po2 . mbar = po2 ;
2014-06-01 17:02:38 +00:00
sample - > time . seconds = lasttime + 1 ;
sample - > depth . mm = lastdepth ;
2014-07-04 18:26:31 +00:00
if ( track_gas )
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-06-03 17:21:41 +00:00
sample [ - 1 ] . po2 . mbar = po2 ;
sample - > po2 . mbar = po2 ;
2014-06-01 17:02:38 +00:00
sample - > time . seconds = lasttime = time ;
sample - > depth . mm = lastdepth = depth ;
2014-07-04 18:26:31 +00:00
if ( track_gas ) {
update_cylinder_pressure ( & displayed_dive , sample [ - 1 ] . depth . mm , depth , time - sample [ - 1 ] . time . seconds ,
dp - > entered ? diveplan - > bottomsac : diveplan - > decosac , cyl , ! dp - > entered ) ;
sample - > cylinderpressure . mbar = cyl - > end . mbar ;
}
2013-01-07 21:13:10 +00:00
finish_sample ( dc ) ;
2013-01-05 07:11:42 +00:00
dp = dp - > next ;
}
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
}
2013-01-07 06:09:12 +00:00
void free_dps ( struct divedatapoint * dp )
{
while ( dp ) {
struct divedatapoint * ndp = dp - > next ;
free ( dp ) ;
dp = ndp ;
}
}
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 ;
2013-01-23 19:12:24 +00:00
dp - > po2 = 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 ;
}
2013-01-07 06:09:12 +00:00
struct divedatapoint * get_nth_dp ( struct diveplan * diveplan , int idx )
{
struct divedatapoint * * ldpp , * dp = diveplan - > dp ;
int i = 0 ;
2014-06-09 21:05:57 +00:00
struct gasmix air = { } ;
2013-01-07 06:09:12 +00:00
ldpp = & diveplan - > dp ;
while ( dp & & i + + < idx ) {
ldpp = & dp - > next ;
dp = dp - > next ;
}
while ( i + + < = idx ) {
2014-06-01 22:25:19 +00:00
* ldpp = dp = create_dp ( 0 , 0 , air , 0 ) ;
2013-01-07 06:09:12 +00:00
ldpp = & ( ( * ldpp ) - > next ) ;
}
return dp ;
}
2013-01-05 07:11:42 +00:00
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 ;
2014-04-17 08:54:55 +00:00
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-06-01 04:24:27 +00:00
static void add_plan_to_notes ( struct diveplan * diveplan , struct dive * dive , bool show_disclaimer )
2013-01-16 01:17:24 +00:00
{
2014-06-22 17:28:08 +00:00
char buffer [ 20000 ] , temp [ 1000 ] ;
2014-07-10 20:11:46 +00:00
int len , lastdepth = 0 , lasttime = 0 ;
2013-01-16 01:17:24 +00:00
struct divedatapoint * dp = diveplan - > dp ;
2014-06-03 09:38:24 +00:00
bool gaschange = ! plan_verbatim ;
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-06-04 21:34:09 +00:00
" ALGORITHM AND A DIVE PLANNER IMPLEMENTION BASED ON THAT WHICH HAS "
" 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-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_runtime )
2014-06-22 17:28:08 +00:00
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " <th style='padding-left: 10px;'>%s</th> " ,
translate ( " gettextFromC " , " runtime " ) ) ;
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 " ) ) ;
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-06-01 23:10:07 +00:00
struct gasmix gasmix , newgasmix ;
2013-01-16 01:17:24 +00:00
const char * depth_unit ;
double depthvalue ;
int decimals ;
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
nextdp = dp - > next ;
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 ;
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 & &
gasmix_distance ( & gasmix , & newgasmix ) = = 0 & &
nextdp & &
dp - > depth ! = lastdepth & &
nextdp - > depth ! = dp - > depth )
2013-01-16 01:17:24 +00:00
continue ;
2014-06-03 11:00:51 +00:00
if ( dp - > time - lasttime < 10 & & ! ( gaschange & & dp - > next & & dp - > depth ! = dp - > next - > depth ) )
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 ( nextdp & & gasmix_distance ( & gasmix , & newgasmix ) )
gaschange = true ;
if ( plan_verbatim ) {
if ( dp - > depth ! = lastdepth ) {
2014-06-04 22:15:11 +00:00
if ( plan_display_transitions | | dp - > entered | | ! dp - > next | | ( gaschange & & dp - > next & & dp - > depth ! = nextdp - > depth ) ) {
2014-06-22 17:28:08 +00:00
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 ) ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " %s<br> " , temp ) ;
2014-06-04 21:54:33 +00:00
lasttime = dp - > time ;
}
2014-06-02 14:25:58 +00:00
} else {
2014-06-04 22:15:11 +00:00
if ( dp - > depth ! = nextdp - > depth ) {
2014-06-22 17:28:08 +00:00
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s " ) ,
2014-06-04 22:15:11 +00:00
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 22:15:11 +00:00
lasttime = dp - > time ;
}
2014-06-03 09:38:24 +00:00
}
} else {
2014-06-04 22:15:11 +00:00
if ( ( dp - > depth = = lastdepth & & dp - > depth ! = nextdp - > depth ) | | plan_display_transitions | | dp - > entered | | ! dp - > next | | ( gaschange & & dp - > next & & 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_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 ) ;
}
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 ) ;
}
2014-06-02 14:25:58 +00:00
if ( gaschange ) {
2014-06-22 17:28:08 +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-06-02 14:25:58 +00:00
gaschange = 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-06-04 21:54:33 +00:00
lasttime = dp - > time ;
2014-06-02 14:25:58 +00:00
}
}
2014-06-03 09:38:24 +00:00
if ( gaschange ) {
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-06-22 17:28:08 +00:00
snprintf ( temp , sizeof ( temp ) , translate ( " gettextFromC " , " Switch gas to %s " ) , gasname ( & newgasmix ) ) ;
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " %s<br> " , temp ) ;
2014-06-03 09:38:24 +00:00
gaschange = false ;
}
2014-06-02 00:34:40 +00:00
gasmix = newgasmix ;
2013-01-16 01:17:24 +00:00
}
lastdepth = dp - > depth ;
2014-06-04 22:15:11 +00:00
} while ( ( dp = nextdp ) ! = NULL ) ;
2014-06-22 17:28:08 +00:00
2014-06-27 06:04:46 +00:00
snprintf ( temp , sizeof ( temp ) , " %s " , translate ( " gettextFromC " , " Gas consumption: " ) ) ;
2014-06-22 17:28:08 +00:00
len + = snprintf ( buffer + len , sizeof ( buffer ) - len , " </tbody></table></div><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 ;
while ( dp ) {
if ( dp - > time ! = 0 ) {
2014-07-10 20:29:00 +00:00
int pO2 = depth_to_atm ( dp - > depth , dive ) * get_o2 ( & dp - > gasmix ) ;
2014-06-22 17:28:08 +00:00
2014-07-10 20:29:00 +00:00
if ( pO2 > dp - > entered ? prefs . bottompo2 : prefs . decopo2 ) {
2014-06-02 19:29:40 +00:00
const char * depth_unit ;
int decimals ;
double depth_value = get_depth_units ( dp - > depth , & decimals , & depth_unit ) ;
len = strlen ( buffer ) ;
2014-06-22 17:28:08 +00:00
snprintf ( temp , sizeof ( temp ) ,
translate ( " gettextFromC " , " high pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s " ) ,
2014-06-09 18:23:05 +00:00
pO2 / 1000.0 , FRACTION ( dp - > time , 60 ) , gasname ( & dp - > gasmix ) , decimals , depth_value , depth_unit ) ;
2014-06-22 17:28:08 +00:00
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
}
}
dp = dp - > next ;
}
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 ) ;
}
2014-04-26 15:29:40 +00:00
int ascend_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
}
2014-07-04 18:26:31 +00:00
void 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 ;
2014-04-17 08:54:55 +00:00
int * stoplevels = NULL ;
char * trial_cache = NULL ;
bool stopping = false ;
bool clear_to_ascend ;
int clock , previous_point_time ;
2014-06-09 21:18:00 +00:00
int avg_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 ;
int breakcylinder ;
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
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 ] ;
2013-01-08 16:52:48 +00:00
/* we start with gas 0, then check if that was changed */
2014-07-03 20:39:06 +00:00
gas = displayed_dive . cylinder [ 0 ] . gasmix ;
get_gas_from_events ( & displayed_dive . dc , sample - > time . seconds , & gas ) ;
po2 = displayed_dive . dc . sample [ displayed_dive . dc . samples - 1 ] . po2 . mbar ;
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 ;
2014-04-26 15:29:40 +00:00
avg_depth = average_depth ( diveplan ) ;
2014-04-26 19:06:48 +00:00
last_ascend_rate = ascend_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 ) ;
2013-09-19 03:40:34 +00:00
return ;
}
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-07-03 20:39:06 +00:00
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 ;
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-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 */
2014-04-26 15:29:40 +00:00
int deltad = ascend_velocity ( depth , avg_depth , bottom_time ) * TIMESTEP ;
2014-04-26 19:06:48 +00:00
if ( ascend_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 ;
last_ascend_rate = ascend_velocity ( depth , avg_depth , bottom_time ) ;
}
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 ,
TIMESTEP , po2 , & displayed_dive ) ;
2014-04-18 14:08:33 +00:00
clock + = TIMESTEP ;
2014-04-17 08:54:55 +00:00
depth - = deltad ;
} while ( depth > stoplevels [ stopidx ] ) ;
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
2013-01-10 00:01:15 +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 ;
current_cylinder = gaschanges [ gi ] . gasidx ;
2014-07-03 20:39:06 +00:00
gas = displayed_dive . cylinder [ current_cylinder ] . gasmix ;
2013-01-10 00:01:15 +00:00
# if DEBUG_PLAN & 16
printf ( " switch to gas %d (%d/%d) @ %5.2lfm \n " , gaschanges [ gi ] . gasidx ,
2014-07-10 20:29:00 +00:00
( get_o2 ( & gas ) + 5 ) / 10 , ( get_he ( & gas ) + 5 ) / 10 , gaschanges [ gi ] . depth / 1000.0 ) ;
2013-01-10 00:01:15 +00:00
# endif
gi - - ;
}
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-04-17 08:54:55 +00:00
int trial_depth = depth ;
cache_deco_state ( tissue_tolerance , & trial_cache ) ;
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 */
2014-04-17 08:54:55 +00:00
clear_to_ascend = true ;
while ( trial_depth > stoplevels [ stopidx ] ) {
2014-04-26 15:29:40 +00:00
int deltad = ascend_velocity ( trial_depth , avg_depth , bottom_time ) * TIMESTEP ;
2014-07-11 17:10:15 +00:00
if ( deltad > trial_depth ) /* don't test against depth above surface */
deltad = trial_depth ;
2014-07-03 20:39:06 +00:00
tissue_tolerance = add_segment ( depth_to_mbar ( trial_depth , & displayed_dive ) / 1000.0 ,
& displayed_dive . cylinder [ current_cylinder ] . gasmix ,
TIMESTEP , po2 , & displayed_dive ) ;
if ( deco_allowed_depth ( tissue_tolerance , diveplan - > surface_pressure / 1000.0 , & displayed_dive , 1 ) > trial_depth - deltad ) {
2014-04-17 08:54:55 +00:00
/* We should have stopped */
clear_to_ascend = false ;
break ;
}
trial_depth - = deltad ;
}
restore_deco_state ( trial_cache ) ;
2014-04-18 14:08:33 +00:00
2014-06-03 09:38:24 +00:00
if ( clear_to_ascend )
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 */
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 ;
}
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 ,
DECOTIMESTEP , po2 , & displayed_dive ) ;
2014-04-17 08:54:55 +00:00
cache_deco_state ( tissue_tolerance , & trial_cache ) ;
2014-04-18 14:08:33 +00:00
clock + = DECOTIMESTEP ;
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
trial_depth = depth ;
}
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
/* We made it to the surface */
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-07-03 20:39:06 +00:00
add_plan_to_notes ( diveplan , & displayed_dive , show_disclaimer ) ;
2013-10-07 00:32:50 +00:00
2013-01-10 00:01:15 +00:00
free ( stoplevels ) ;
free ( gaschanges ) ;
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 " ) ) ;
} 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 ;
}