Mercurial > public > hwos_code
diff src/p2_deco.c @ 631:185ba2f91f59
3.09 beta 1 release
author | heinrichsweikamp |
---|---|
date | Fri, 28 Feb 2020 15:45:07 +0100 |
parents | 237931377539 |
children | 4050675965ea |
line wrap: on
line diff
--- a/src/p2_deco.c Fri Feb 21 10:51:36 2020 +0100 +++ b/src/p2_deco.c Fri Feb 28 15:45:07 2020 +0100 @@ -1,5 +1,5 @@ // *************************************************************************** -// p2_deco.c combined next generation V3.06.1 +// p2_deco.c combined next generation V3.08.8 // // Created on: 12.05.2009 // Author: heinrichs weikamp, contributions by Ralph Lembcke and others @@ -35,7 +35,7 @@ // 07/xx/2008 v102a: debug of bottom time routine // 09/xx/2008 v102d: Gradient Factor Model implementation // 10/10/2008 v104: renamed to build v103 for v118 stable -// 10/14/2008 v104: integration of char_I_depth_last_deco for Gradient Model +// 10/14/2008 v104: integration of char_I_last_stop_depth for Gradient Model // 03/31/2009 v107: integration of FONT Incon24 // 05/23/2010 v109: 5 gas changes & 1 min timer // 07/13/2010 v110: cns vault added @@ -47,7 +47,7 @@ // 2011/02/11: [jDG] Reworked gradient-factor implementation. // 2011/02/15: [jDG] Fixed inconsistencies introduced by gas switch delays. // 2011/03/21: [jDG] Added gas consumption (CF56 & CF57) evaluation for OCR mode. -// 2011/04/15: [jDG] Store GF_low_depth in 32 bits (w/o rounding), for a better stability. +// 2011/04/15: [jDG] Store GF_depth in 32 bits (w/o rounding), for a better stability. // 2011/04/25: [jDG] Added 1mn mode for CNS calculation, to allow it for deco planning. // 2011/04/27: [jDG] Fixed char_O_gradient_factor calculation when model uses gradient-factor. // 2011/05/02: [jDG] Added "Future TTS" function (CF58). @@ -59,7 +59,7 @@ // 2012/02/25: [jDG] Looking for a more stable LOW grad factor reference. // 2012/09/10: [mH] Fill char_O_deco_time_for_log for logbook write // 2012/10/05: [jDG] Better calc_gas_needs_ascent accuracy (average depth, switch between stop). -// 2013/03/05: [jDG] Should vault GF_low_depth too. +// 2013/03/05: [jDG] Should vault GF_depth too. // 2013/03/05: [jDG] Wrobell remark: ascent_to_first_stop works better with finer steps (2sec). // 2013/05/08: [jDG] A. Salm remark: NOAA tables for CNS are in ATA, not bar. // 2013/12/21: [jDG] Fix CNS calculation in deco plan w/o marked gas switch @@ -123,30 +123,29 @@ // 0.135 bar safety margin // constants and factors -#define ppWater 0.06270 // water vapor partial pressure in the lungs -#define METER_TO_BAR 0.09985 // conversion factor -#define BAR_TO_METER 10.0150 // conversion factor (1.0/METER_TO_BAR) -#define SURFACE_DESAT_FACTOR 0.70420 // surface desaturation safety factor -#define HYST 1.0E-06 // threshold for tissue graphics on-gassing / off-gassing visualization +#define ppWater 0.06270 // water vapor partial pressure in the lungs +#define METER_TO_BAR 0.09807 // conversion factor (1 m water column = 0.09807 bar) +#define BAR_TO_METER 10.19716 // conversion factor (1 bar = 10.19716 m ) +#define SURFACE_DESAT_FACTOR 0.70420 // surface desaturation safety factor +#define HYST 1.0E-06 // threshold for tissue graphics on-gassing / off-gassing visualization // thresholds #define CNS_LIMIT_WARNING 100 // threshold for CNS warning #define CNS_LIMIT_ATTENTION 70 // threshold for CNS attention #define PRESSURE_LIMIT_WARNING 200 // threshold for pressure reading warning : 20.0 bar #define PRESSURE_LIMIT_ATTENTION 500 // threshold for pressure reading attention: 50.0 bar -#define GAS_NEEDS_LIMIT_ATTENTION 0.70 // threshold for gas needs attention [1.00 = 100%] +#define GAS_NEEDS_ATTENTION 0.7 // threshold for gas needs attention [1 = 100%] #define O2_CONSUMPTION_LIMIT_ATTENTION 20 // threshold for O2 "SAC" attention: 2.0 l/min #define ppO2_GAP_TO_SETPOINT 10 // gap between setpoint and max. ppO2 of the pure diluent [cbar] #define ppO2_MARGIN_ON_MAX 3 // [cbar] margin on ppO2 max to compensate for surface pressures > 1.000 mbar -#define STOP_CHAINING_LIMIT 5 // max. number of chained stop table entries before deco calculation is aborted +#define STOP_CHAINING_LIMIT 3 // max. number of chained stop table entries before deco calculation is aborted // deco engine states and modes - (char_O_)main_status: controls current tissue and deco status calculation (as-is situation) #define CALC_VOLUME 0x01 // =1: calculate gas needs #define CALCULATE_BOTTOM 0x02 // =1: calculate gas needs in deco calculator mode, =0: in dive mode -#define CAVE_MODE 0x04 // =1: calculate ascent and gas needs using backtracking data -#define USE_Z_FACTOR 0x08 // =1: calculate with Z factor when converting gas volumes <-> pressures - +#define CAVE_MODE 0x04 // =1: calculate return path and gas needs using backtracking data +#define GAS_CONTINGENCY 0x08 // =1: use a second best gas if best gas is all used up #define TR_FUNCTIONS 0x10 // =1: calculate TR functions (pressure reading) processing #define EXTENDED_STOPS 0x20 // =1: allow placement of gas switches below the depth of the 1st stop @@ -166,12 +165,12 @@ #define CALC_ALT 0x02 // internal: calculating an alternative deco plan #define COMPLETED_ALT 0x02 // output: calculation of an alternative deco plan has completed #define INITIALIZE 0x04 // input: initialize deco engine -#define INITIALIZE_START_NORM 0x05 // input: initialize deco engine and start calculation of a normal deco plan +#define INITIALIZE_START_NORM 0x05 // input: initialize deco engine and start calculation of a normal deco plan #define INITIALIZE_START_ALT 0x06 // input: initialize deco engine and start calculation of an alternative deco plan #define DECO_CALCULATOR_MODE 0x08 // input: deco engine is run from deco calculator #define BAILOUT_MODE 0x10 // =1: allow gas switches before first deco stop -#define DELAYED_ASCENT 0x20 // =1: figure in a delayed ascent (fTTS) +#define DELAYED_ASCENT 0x20 // =1: figure in a delayed ascent / delayed turn of the dive (fTTS) // MODE_MASK 0xC0 // mask for simulated tissues mode selection // MODE_LOOP 0x40 // =1: CCR (MODE_PSCR needs to be cleared) or pSCR mode @@ -187,22 +186,22 @@ #define DECO_WARNING_OUTSIDE 0x10 // tissue pressures outside the Buhlmann model now #define DECO_WARNING_OUTSIDE_lock 0x20 // tissue pressures outside the model sometime during the dive #define DECO_ATTENTION_OUTSIDE 0x40 // tissue pressures are very close to the Buhlmann limit -#define DECO_WARNING_INCOMPLETE 0x80 // internal error: deco calculation incomplete +#define DECO_WARNING_INCOMPLETE 0x80 // deco calculation incomplete due to too long compute time // deco engine status (char_O_)deco_info #define DECO_MODE 0x01 // =1: deco ppO2 levels are permitted #define IND_DOUBLE_SWITCH_FLAG 0x02 // =1: switch to other tank advice active -// 0x04 // --- unused +#define GAS_NEEDS_fTTS 0x04 // =1: gas needs are calculated in fTTS mode #define DECO_ZONE 0x08 // =1: fTTS < TTS (not updated when in bailout mode) -#define DECO_CEILING 0x10 // =1: ceiling depth > 0 -#define DECO_STOPS 0x20 // =1: deco stops found -#define GAS_NEEDS_CAVE 0x40 // =1: indicated gas needs are calculated in cave mode -// 0x80 // --- unused +#define DECO_CEILING 0x10 // =1: deco obligation (ceiling > 0) +#define DECO_STOPS_NORM 0x20 // =1: deco stops found in normal plan +#define DECO_STOPS_ALT 0x40 // =1: deco stops found in alternative plan +#define GAS_NEEDS_CAVE 0x80 // =1: indicated gas needs are calculated in cave mode // deco engine control - tissue_increment -#define TIME_MASK 0x7F // =0: time increment is 2 seconds, 1..127: time increments is 1..127 minutes -#define TISSUE_SELECTOR 0x80 // =1: calculate on real tissues, =0: calculate on simulated tissues +#define TIME_MASK 0x7F // =0: time increment is 2 or 6 seconds, 1..127: time increments is 1..127 minutes +#define TISSUE_SELECTOR 0x80 // =0: calculate on simulated tissues, 1 : calculate on real tissues // deco engine control - next_planning_phase @@ -212,16 +211,30 @@ #define PHASE_30_EXTENDED_BOTTOM_TIME 0x30 // calculate extended bottom time #define PHASE_40_BOTTOM_GAS_NEED 0x40 // calculate gas needs for bottom segment #define PHASE_50_NDL_TIME 0x50 // calculate NDL time -#define PHASE_60_CAVE_RETURN 0x60 // calculate cave mode return -#define PHASE_70_OPEN_WATER_ASCENT 0x70 // calculate open water ascent +#define PHASE_70_ASCENT_OR_RETURN 0x70 // calculate open water ascent or cave return #define PHASE_80_RESULTS 0x80 // results - initialization #define PHASE_81_RESULTS_STOPS_TABLE 0x81 // results - publish stops table #define PHASE_82_RESULTS_NDL 0x82 // results - publish data / within NDL #define PHASE_83_RESULTS_DECO 0x83 // results - publish data / in deco #define PHASE_84_GAS_NEEDS_PRESSURES 0x84 // results - convert gas needs from volumes to pressures +#define PHASE_85_GAS_NEEDS_CAVE 0x85 // results - tag gas needs as calculated in cave or open water mode #define PHASE_90_FINISH 0x90 // finish calculation cycle +// gas & diluent - type and availability state +// 0x01 // | 0: disabled, 1: first, 2: normal/work, 3: deco +// 0x02 // | +#define GAS_TYPE_MASK 0x03 // bit mask covering the type enumerator +#define GAS_AVAIL_LOST 0x04 // =1: gas/diluent lost flag (permanently unavailable) +#define GAS_AVAIL_STAGED 0x08 // =1: gas/diluent staged flag (temporary unavailable) +#define GAS_AVAIL_MASK 0x0C // bit mask covering the availability flags +#define GAS_NEED_ATTENTION 0x10 // =1: gas need >= attention threshold +#define GAS_NEED_WARNING 0x20 // =1: gas need >= warning threshold +#define GAS_NEED_MASK 0x30 // bit mask covering the need flags +#define GAS_NEARLY_USED_UP 0x40 // =1: the gas is nearly used up (= at attention threshold) +#define GAS_FULLY_USED_UP 0x80 // =1: the gas is fully used up (= at warning threshold) + + // flags used with integer numbers #define INT_FLAG_INVALID 0x0400 // =1: value not valid #define INT_FLAG_NOT_COMPUTED_YET 0x0800 // =1: value not computed yet @@ -264,26 +277,31 @@ static void calc_tissues(void); // Updates the tissues dependent on the partial pressures of N2 and He. static void calc_CNS(void); // Updates the CNS value dependent on the partial pressure of the O2. static void calc_limit(PARAMETER float GF_current); - // Calculates ceiling, current GF (supersaturation) and some more data. + // Calculates ceiling, current supersaturation factor and some more data. // Functions for TR #ifdef _rx_functions static void calc_TR_functions(void); // Calculates SAC etc. #endif +// Functions for Cave Mode +#ifdef _cave_mode +static void read_backtrack_data(void); // Gets the data of the next backtracking data set +#endif + // Functions dedicated to Deco Calculations static void clear_deco_table(void); // Clears the deco stops table, invoked at the start of each calculation cycle. -static void gas_take_current(void); // Sets the first gas used for deco calculation, invoked at start of cycle, too. +static void gas_take_current(void); // Take the actual currently used gas for ascent & deco calculation static unsigned char gas_find_best(void); // Searches for the best gas available. static void gas_take_best(void); // Switches to the best gas that has been found found before by gas_find_best(). static void gas_set_ratios(void); // Sets the gas ratios for use in deco calculation (simulated tissues), // needs to be called after each gas change (gas_take_current/_better). static void calc_NDL_time_tissue(void); // Calculates the remaining NDL time for a given tissue. static unsigned char find_next_stop(void); // Finds the next stop when in a deco ascent. -static unsigned char update_deco_table(PARAMETER unsigned char time_increment); +static void update_deco_table(PARAMETER unsigned char time_increment); // Enters a new stop or extends an existing stop in the deco stops table. -static void calc_due_by_depth_time_sac(void); // Calculates gas volume required for a given depth, time and usage (SAC rate). -static void convert_gas_needs_to_press(void); // Converts gas volumes into pressures and sets respective flags. +static void calc_required_volume(void); // Calculates gas volume required for a given depth, time and usage (SAC rate). +static void convert_volume_to_pressure(void); // Converts gas volumes into pressures and sets respective flags. // Functions for Results Reporting static void publish_deco_table(void); // Copies the internal deco stops table to the export interface. @@ -305,7 +323,6 @@ static void adopt_Buhlmann_coefficients(void); // Computes average a and b coefficient by the N2/He tissue ratio. static void push_tissues_to_vault(void); // Stores the state of the real tissues during simulator runs. static void pull_tissues_from_vault(void); // Restores the state of the real tissues after a simulator run. -static void calc_sim_pres_respiration(void); // Calculate sim_pres_respiration from char_depth_sim. static void calc_N2_equilibrium(void); // Calculate partial pressure of N2 in respired air at surface pressure. static void get_saturation_factors(void); // Get, safeguard and convert the saturation and desaturation factors. static void apply_saturation_factors(void); // Applies saturation and desaturation factors. @@ -322,15 +339,19 @@ # pragma udata bank5=0x500 #endif -// Environmental and Gas Data (52 byte) +// Data that go into the deco data vault (4 byte) + +static float CNS_fraction_real; // || current real CNS (1.00 = 100%) + + +// Environmental and Gas Data (51 byte) static float pres_surface; // absolute pressure at the surface static float float_depth_real; // current real depth in meters, float static unsigned char char_depth_real; // current real depth in meters, integer -static unsigned char char_depth_sim_start; // start value of simulated depth in meters, integer +static unsigned char char_depth_start; // start value of simulated depth in meters, integer static unsigned char char_depth_sim; // current value of simulated depth in meters, integer -static unsigned char char_depth_last; // last value of simulated depth in meters, integer static float real_pres_respiration; // current real depth in absolute pressure static float real_O2_ratio; // real breathed gas oxygen ratio @@ -345,33 +366,32 @@ static float sim_pSCR_drop; // simulated ppO2 drop in pSCR loop -// general Deco Parameters (57 byte) +// general Deco Parameters (64 byte) static float GF_low; // gradient factor to determine 1st stop static float GF_high; // gradient factor to determine surfacing -static unsigned char GF_low_last; // last GF low, used to detect changes -static unsigned char GF_high_last; // last GF high, used to detect changes - -static unsigned char GF_low_depth; // GF low reference depth in current calculation cycle -static unsigned char GF_low_depth_norm; // GF low reference depth in normal plan -static unsigned char GF_low_depth_alt; // GF low reference depth in alternative plan - -static float GF_slope; // (GF_high - GF_low) / GF_low_depth in current calculation cycle -static float GF_slope_norm; // (GF_high - GF_low) / GF_low_depth_norm in normal plan -static float GF_slope_alt; // (GF_high - GF_low) / GF_low_depth_alt in alternative plan - -static float float_ascent_speed; // ascent speed from options_table (5.0 .. 10.0 m/min) -static float float_deco_distance; // additional depth below stop depth - not used any more +static unsigned char GF_low_last; // last GF low, used to detect a GF change +static unsigned char GF_high_last; // last GF high, used to detect a GF change + +static unsigned char GF_depth; // GF low reference depth in current calculation cycle +static unsigned char GF_depth_norm; // GF low reference depth in normal plan +static unsigned char GF_depth_alt; // GF low reference depth in alternative plan + +static float GF_slope; // (GF_high - GF_low) / GF_depth in current calculation cycle +static float GF_slope_norm; // (GF_high - GF_low) / GF_depth_norm in normal plan +static float GF_slope_alt; // (GF_high - GF_low) / GF_depth_alt in alternative plan + static float float_saturation_multiplier; // safety factor for on-gassing rates static float float_desaturation_multiplier; // safety factor for off-gassing rates static unsigned char split_N2_He[NUM_COMP]; // used for calculating the desaturation time - - -// real Context: what we are doing now (16 byte) - -static float CNS_fraction_real; // current real CNS (1.00 = 100%) +static unsigned char deco_gas_type[NUM_GAS]; // type and state of the deco gases +static unsigned char peer_tank[NUM_GAS]; // bit flag vector indicating peer tanks holding same gas + + +// real Context: what we are doing now (12 byte) + static unsigned short IBCD_tissue_vector; // 16 bit vector to memorize all tissues that experience IBCD static float pres_respiration_sac; // used in SAC calculation: current depth in absolute pressure @@ -394,38 +414,112 @@ static float ceiling; // minimum tolerated relative pressure (i.e. without surface pressure) static float lead_supersat; // supersaturation of the leading tissue, 1.0 = 100% -static unsigned char lead_tissue; // number of the leading tissue +static unsigned char lead_tissue; // number of the leading tissue (0-15) // Transfer Variables between calc_desaturation_time() and calc_desaturation_time_helper() (18 byte) static float desat_factor; // used to cache a pre-computed factor -static float var_ht; // buffer for a half-time factor -static float pres_target; // target pressure for a compartment -static float pres_actual; // current pressure of the compartment +static float var_ht; // half-time factor for the compartment +static float pres_target; // target pressure for the compartment +static float pres_actual; // current pressure of the compartment static unsigned short int_time; // time it takes for the compartment to reach the target pressure -// Gas in Use and Gas Needs (26 byte) +// Gas in Use and Gas Needs (67 byte) + +static unsigned char start_gas_num; // number of the gas/dil to start with static unsigned char sim_gas_last_num; // number of the last used gas static unsigned char sim_gas_current_num; // number of the currently used gas static unsigned char sim_gas_current_depth; // change depth of the currently used gas -static unsigned char sim_gas_best_num; // number of the better gas -static unsigned char sim_gas_best_depth; // change depth of the better gas +static unsigned char sim_gas_best_num; // number of the best gas available +static unsigned char sim_gas_best_depth; // change depth of the best gas available static unsigned char gas_needs_gas_index; // index to the gas and tank data arrays -static float gas_volume_need[NUM_GAS]; // gas volumes required for return/ascent in liters -static float gas_volume_avail[NUM_GAS]; // gas volumes available for return/ascent in liters - - -// Transfer Variables for calc_due_by_depth_time_sac() (7 byte) +static float gas_volume_need[NUM_GAS]; // gas volumes required for ascent / cave return in liters +static float gas_volume_avail[NUM_GAS]; // gas volumes available for ascent / cave return in liters +static float gas_volume_atten[NUM_GAS]; // attention threshold for gas volumes available + + +// Transfer Variables for calc_required_volume() (7 byte) static unsigned char gas_needs_depth; // depth of the stop or half-way point -static unsigned char gas_needs_time; // duration of the stop or ascent phase +static unsigned char gas_needs_time; // duration of the stop, ascent or travel phase static unsigned char gas_needs_usage_rate; // gas usage in l/min -static float gas_needs_volume_due; // computed due of gas volume required +static float gas_needs_volume_due; // computed amount of required gas volume + + +// 243 byte used, 13 byte left in this bank (4 bytes per float, 2 bytes per short, 1 byte per char) + + +//---- Bank 6 parameters ----------------------------------------------------- +#ifndef UNIX +# pragma udata bank6=0x600 +#endif + +// Timer5 Interface (3 byte) - Attention: keep order and keep at beginning of bank 6, i.e. at address 0x600 ! + +static volatile unsigned short tmr5_value; // | timer 5 value buffer MUST be at address 0x600 +static volatile unsigned char tmr5_overflow; // | timer 5 overflow flag MUST be at address 0x602 + + +// Modes, Sequencing and Indexing (14 byte) + +static unsigned char main_status; // shadow register for char_O_main_status +static unsigned char deco_status; // shadow register for char_O_deco_status +static unsigned char deco_info; // shadow register for char_O_deco_info +static unsigned char deco_warnings; // shadow register for char_O_deco_warnings +static unsigned char next_planning_phase; // next calculation phase to be executed +static unsigned char tissue_increment; // selector for real/simulated tissues and time increment +static unsigned char sequence_timer; // timer to sequence deco engine tasks +static unsigned char ci; // index to the Buhlmann tables (compartment index) +static unsigned char cns_i; // index to the CNS tables (ppO2 range index) +static unsigned char i; // general purpose loop counter and index +static unsigned char j; // general purpose loop counter and index +static unsigned char stop_index; // current stop table position +static unsigned char chained_stops; // counter for chained stop entries +static unsigned char backtrack_index; // index into the depth backtracking array char_I_backtrack_storage +static unsigned char backtrack_target_depth; // current backtracking target depth +static unsigned char backtrack_step_counter; // counter for number of 1/10 minute steps done + + +// Result Values from Calculation Functions (28 byte) + +static float ppO2_O2; // ppO2 calculated for breathing pure oxygen in OC mode +static float ppO2_OC; // ppO2 calculated for breathing current gas in OC mode +static float ppO2_pSCR; // ppO2 calculated for breathing current gas in pSCR mode + +static float ppO2; // partial pressure of breathed oxygen +static float ppN2; // partial pressure of breathed nitrogen +static float ppHe; // partial pressure of breathed helium + +static unsigned char char_ppO2; // partial pressure of breathed oxygen, 100 = 1.00 bar +static unsigned char NDL_time; // time in full minutes until reaching no-deco limit (NDL) +static unsigned short TTS_time; // time in 1/10 minutes until finishing ascent / cave return +static unsigned short TST_time; // time in full minutes of all stops in ascent / cave return + + +// Buhlmann Model Parameters (40 byte) + +static float var_N2_a; // Buhlmann a for current N2 tissue +static float var_N2_b; // Buhlmann b for current N2 tissue +static float var_He_a; // Buhlmann a for current He tissue +static float var_He_b; // Buhlmann b for current He tissue +static float var_a; // Buhlmann a adopted to current N2/He ratio +static float var_b; // Buhlmann b adopted to current N2/He ratio +static float var_N2_e; // exposition for current N2 tissue +static float var_He_e; // exposition for current He tissue +static float var_N2_ht; // half-time for current N2 tissue +static float var_He_ht; // half-time for current He tissue + + +// CNS Coefficients (10 byte) + +static float var_cns_gain; // two coefficients approximation, gain +static float var_cns_offset; // two coefficients approximation, offset +static unsigned short var_cns_value; // one coefficient approximation, value // Auxiliary Variables for Data Buffering (28 byte) @@ -439,87 +533,10 @@ static float old_pres_respiration; // auxiliary variable to buffer sim_pres_respiration -// 244 byte used, 12 byte left in this bank (4 bytes per float, 2 bytes per short, 1 byte per char) - - -//---- Bank 6 parameters ----------------------------------------------------- -#ifndef UNIX -# pragma udata bank6=0x600 -#endif - -// Timer5 Interface (3 byte) - Attention: keep order and keep at beginning of bank 6, i.e. at address 0x600 ! - -static volatile unsigned short tmr5_value; // timer 5 value buffer MUST be at address 0x600 -static volatile unsigned char tmr5_overflow; // timer 5 overflow flag MUST be at address 0x602 - - -// Modes, Sequencing and Indexing (12 byte) - -static unsigned char main_status; // shadow register for char_O_main_status -static unsigned char deco_status; // shadow register for char_O_deco_status -static unsigned char deco_info; // shadow register for char_O_deco_info -static unsigned char deco_warnings; // shadow register for char_O_deco_warnings -static unsigned char next_planning_phase; // next calculation phase to be executed -static unsigned char tissue_increment; // selector for real/simulated tissues and time increment -static unsigned char sequence_timer; // timer to sequence deco engine tasks -static unsigned char ci; // index to the Buhlmann tables (compartment index) -static unsigned char cns_i; // index to the CNS tables (ppO2 range index) -static unsigned char i; // general purpose loop counter and index -static unsigned char fast; // selects 1 minute or 2 second ascent steps -static unsigned char stop_index; // current stop table position -static unsigned char chained_stops; // counter for chained stop entries - - -// Result Values from Calculation Functions (28 byte) - -static float O2_ppO2; // ppO2 - calculated for pure oxygen at current depth -static float OC_ppO2; // ppO2 - calculated for breathing in OC mode -static float pSCR_ppO2; // ppO2 - calculated for breathing in pSCR mode - -static float ppO2; // partial pressure of breathed oxygen -static float ppN2; // partial pressure of breathed nitrogen -static float ppHe; // partial pressure of breathed helium - -static unsigned char char_ppO2; // partial pressure of breathed oxygen, as integer 100 = 1.00 bar -static unsigned char NDL_time; // time in minutes until reaching NDL -static unsigned short ascent_time; // time in minutes needed for the ascent - - -// Buhlmann Model Parameters (40 byte) - -static float var_N2_a; // Buhlmann a, for current N2 tissue -static float var_N2_b; // Buhlmann b, for current N2 tissue -static float var_He_a; // Buhlmann a, for current He tissue -static float var_He_b; // Buhlmann b, for current He tissue -static float var_a; // Buhlmann a, adopted to current N2/He ratio -static float var_b; // Buhlmann b, adopted to current N2/He ratio -static float var_N2_e; // exposition, for current N2 tissue -static float var_He_e; // exposition, for current He tissue -static float var_N2_ht; // half-time, for current N2 tissue -static float var_He_ht; // half-time, for current He tissue - - -// CNS Coefficients (10 byte) - -static float var_cns_a; // two coefficients approximation, gain -static float var_cns_b; // two coefficients approximation, offset -static unsigned short var_cns_c; // one coefficient approximation, value - - -// Vault to back-up & restore Tissue related Data (134 byte) - -static float vault_pres_tissue_N2[NUM_COMP]; // stores the nitrogen tissue pressures -static float vault_pres_tissue_He[NUM_COMP]; // stores the helium tissue pressures -static float vault_CNS_fraction_real; // stores current CNS (float representation) -static unsigned char vault_deco_warnings; // stores warnings status -static unsigned char vault_deco_info; // stores info status - - -// Transfer values for convert_float_int and convert_float_to_char() (7 byte) +// Transfer Values for convert_float_to_int() (6 byte) static float float_value; // input value, float static unsigned short int_value; // output value, 16 bit -static unsigned char char_value; // output value, 8 bit // Performance Profiling (4 byte) @@ -532,7 +549,7 @@ // 7 byte occupied by compiler-placed vars -// 245 byte used, 11 byte left in this bank (4 bytes per float, 2 bytes per short, 1 byte per char) +// 139 byte used, 117 byte left in this bank (4 bytes per float, 2 bytes per short, 1 byte per char) @@ -548,7 +565,15 @@ static unsigned char internal_deco_gas[NUM_STOPS]; // gases used on the stops (0 / 1-5) -// 96 byte used, 160 byte left in this bank (4 bytes per float, 2 bytes per short, 1 byte per char) +// Vault to back-up & restore Tissue related Data (134 byte) + +static float vault_pres_tissue_N2[NUM_COMP]; // stores the nitrogen tissue pressures +static float vault_pres_tissue_He[NUM_COMP]; // stores the helium tissue pressures +static float vault_CNS_fraction_real; // stores CNS percentage (1.0 = 100%) +static unsigned char vault_deco_warnings; // stores warnings status +static unsigned char vault_deco_info; // stores info status + +// 230 byte used, 26 byte left in this bank (4 bytes per float, 2 bytes per short, 1 byte per char) //---- Bank 7 parameters ----------------------------------------------------- @@ -591,12 +616,39 @@ // ********************************************************************************************************************************* #ifndef UNIX +# pragma romdata Buhlmann_ht = 0x1DC00 // needs to be in the UPPER bank +#endif + +rom const float Buhlmann_ht[2*16] = { +// Compartment half-times, in minutes +//--- N2 ---- He ---------------------- + 4.0, 1.51, + 8.0, 3.02, + 12.5, 4.72, + 18.5, 6.99, + 27.0, 10.21, + 38.3, 14.48, + 54.3, 20.53, + 77.0, 29.11, + 109.0, 41.20, + 146.0, 55.19, + 187.0, 70.69, + 239.0, 90.34, + 305.0, 115.29, + 390.0, 147.42, + 498.0, 188.24, + 635.0, 240.03 +}; + + +#ifndef UNIX # pragma romdata CNS_tables = 0x1DC80 // needs to be in the UPPER bank #endif -rom const float CNS_ab[2*11] = { -// CNS increment per 2 sec = 1 / (a*ppO2 + b) with ppO2 in [cbar] -// a b for ppO2 cbar range +rom const float CNS_2_approx[2*11] = { +// 2 coefficient approximation for ppO2 = 51 ... 160 cbar +// CNS increment per 2 sec = 1 / (gain*ppO2 + offset) with ppO2 in [cbar] +// gain offset for ppO2 cbar range -533.07, 54000, // 51 - 60 (index 0) -444.22, 48600, // 61 - 70 (index 1) -355.38, 42300, // 71 - 80 (index 2) @@ -610,9 +662,10 @@ -222.11, 37350 // 151 - 160 (index 10) }; -rom const unsigned short CNS_c[1*18] = { -// CNS increment per 2 sec = c / 100000.0 -// c in [1/100000] for ppO2 cbar range +rom const unsigned short CNS_1_approx[1*18] = { +// 1 coefficient approximation for ppO2 = 161 ... 250 cbar +// CNS increment per 2 sec = c / 100000.0 +// value in [1/100000] for ppO2 cbar range 75, // 161 - 165 (index 0) 102, // 166 - 170 (index 1) 136, // 171 - 175 (index 2) @@ -635,12 +688,13 @@ #ifndef UNIX -# pragma romdata Buhlmann_tables = 0x1DD00 // needs to be in the UPPER bank +# pragma romdata Buhlmann_ab = 0x1DD00 // needs to be in the UPPER bank #endif rom const float Buhlmann_ab[4*16] = { +// Compartment a and b factors // Data ZH-L16C, from Bühlmann Tauchmedizin 2002, option 1a (4mn) -// a for N2 b for N2 a of He b for He +// a for N2 b for N2 a for He b for He 1.2599, 0.5050, 1.7424, 0.4245, 1.0000, 0.6514, 1.3830, 0.5747, 0.8618, 0.7222, 1.1919, 0.6527, @@ -659,30 +713,14 @@ 0.2327, 0.9653, 0.5119, 0.9267 }; -rom const float Buhlmann_ht[2*16] = { -// Compartment half-life, in minutes -//--- N2 ---- He ---------------------- - 4.0, 1.51, - 8.0, 3.02, - 12.5, 4.72, - 18.5, 6.99, - 27.0, 10.21, - 38.3, 14.48, - 54.3, 20.53, - 77.0, 29.11, - 109.0, 41.20, - 146.0, 55.19, - 187.0, 70.69, - 239.0, 90.34, - 305.0, 115.29, - 390.0, 147.42, - 498.0, 188.24, - 635.0, 240.03 -}; - -rom const float e2secs[2*16] = { -// Integration constant for 2 seconds, -// result of 1 - 2^(-1/(2sec/60sec * HT)) + +#ifndef UNIX +# pragma romdata e_tables = 0x1DE00 // needs to be in the UPPER bank +#endif + +rom const float e2sec[2*16] = { +// Integration constants for 2 seconds, +// result of 1 - 2^(-(2sec/60sec / HT)) //---- N2 ------------- He ------------ 5.75958E-03, 1.51848E-02, 2.88395E-03, 7.62144E-03, @@ -703,9 +741,32 @@ //------------------------------------- }; +rom const float e6sec[2*16] = { +// Integration constants for 6 seconds, +// result of 1 - 2^(-(6sec/60sec / HT)) +//---- N2 ------------- He ------------ + 1.71794E-02, 4.48661E-02, + 8.62691E-03, 2.26905E-02, + 5.52983E-03, 1.45780E-02, + 3.73973E-03, 9.86726E-03, + 2.56392E-03, 6.76591E-03, + 1.80815E-03, 4.77549E-03, + 1.27570E-03, 3.37057E-03, + 8.99786E-04, 2.37830E-03, + 6.35713E-04, 1.68098E-03, + 4.74646E-04, 1.25514E-03, + 3.70598E-04, 9.80064E-04, + 2.89978E-04, 7.66971E-04, + 2.27236E-04, 6.01040E-04, + 1.77714E-04, 4.70075E-04, + 1.39176E-04, 3.68157E-04, + 1.09151E-04, 2.88734E-04 +//------------------------------------- +}; + rom const float e1min[2*16] = { -// Integration constant for 1 minute, -// result of 1 - 2^(-1/HT) +// Integration constants for 1 minute, +// result of 1 - 2^(-(1min / HT)) //----- N2 --------- e 1min He -------- 1.59104E-01, 3.68109E-01, 8.29960E-02, 2.05084E-01, @@ -727,8 +788,8 @@ }; rom const float e10min[2*16] = { -// Integration constant for 10 minutes, -// result of 1 - 2^(-10/ht) +// Integration constants for 10 minutes, +// result of 1 - 2^(-(10min / HT)) //---- N2 -------------- He ----------- 8.23223E-01, 9.89851E-01, 5.79552E-01, 8.99258E-01, @@ -853,7 +914,7 @@ ////////////////////////////////////////////////////////////////////////////// -// Read CNS coefficients a and b +// Read CNS coefficients gain and offset // static void read_CNS_ab_coefficient(void) { @@ -869,9 +930,9 @@ #endif { - overlay rom const float* ptr = &CNS_ab[2*cns_i]; - var_cns_a = *ptr++; - var_cns_b = *ptr++; + overlay rom const float* ptr = &CNS_2_approx[2*cns_i]; + var_cns_gain = *ptr++; + var_cns_offset = *ptr++; } } @@ -893,8 +954,8 @@ #endif { - overlay rom const unsigned short* ptr = &CNS_c[cns_i]; - var_cns_c = *ptr++; + overlay rom const unsigned short* ptr = &CNS_1_approx[cns_i]; + var_cns_value = *ptr++; } } @@ -937,7 +998,7 @@ #ifndef CROSS_COMPILE // Note: We don't use far ROM pointer, because handling // 24 bit is to complex, hence we have to set the - // UPPER page ourself... + // UPPER page by hand... // -> set to zero if tables are moved to lower pages! _asm movlw 1 @@ -950,15 +1011,27 @@ // Integration Intervals switch(period) { - case 0: //---- 2 sec ----------------------------------------------------- + case 0: //---- 2 or 6 seconds -------------------------------------------- { - overlay rom const float* ptr = &e2secs[2*ci]; - var_N2_e = *ptr++; - var_He_e = *ptr++; + // check which tissues are selected + if(tissue_increment & TISSUE_SELECTOR) + { + // real tissues - 2 seconds + overlay rom const float* ptr = &e2sec[2*ci]; + var_N2_e = *ptr++; + var_He_e = *ptr++; + } + else + { + // simulated tissues - 6 seconds + overlay rom const float* ptr = &e6sec[2*ci]; + var_N2_e = *ptr++; + var_He_e = *ptr++; + } } break; - case 1: //---- 1 min ----------------------------------------------------- + case 1: //---- 1 minutes ------------------------------------------------- { overlay rom const float* ptr = &e1min[2*ci]; var_N2_e = *ptr++; @@ -966,7 +1039,7 @@ } break; - case 2: //---- 10 min ---------------------------------------------------- + case 2: //---- 10 minutes ------------------------------------------------ { overlay rom const float* ptr = &e10min[2*ci]; var_N2_e = *ptr++; @@ -1040,20 +1113,6 @@ ////////////////////////////////////////////////////////////////////////////// -// Calculate sim_pres_respiration from char_depth_sim -// -// Input: char_depth_sim simulated depth in meters -// pres_surface surface pressure -// -// Output: sim_pres_respiration simulated depth in absolute pressure -// -static void calc_sim_pres_respiration(void) -{ - sim_pres_respiration = (float)char_depth_sim * METER_TO_BAR + pres_surface; -} - - -////////////////////////////////////////////////////////////////////////////// // Calculate partial pressure of N2 in respired air at surface pressure // // Input: pres_surface surface pressure @@ -1120,18 +1179,6 @@ } -////////////////////////////////////////////////////////////////////////////// -// convert_float_to_char -// -// Converts a float within range 0.0 - 255 into 8 bit integer -// -static void convert_float_to_char(void) -{ - if (float_value < 0.0) char_value = 0; - else if (float_value >= 254.5) char_value = 255; - else char_value = (unsigned char)(float_value + 0.5); -} - // ********************************************************************************************************************************* // @@ -1299,25 +1346,23 @@ // // INPUT, fixed during dive: // pres_surface : surface pressure (as absolute pressure) -// char_I_depth_last_deco : depth of the last deco stop +// char_I_last_stop_depth : depth of the last deco stop // // INPUT, may change during dive: // GF_high : GF high factor // GF_low : GF low factor -// -// INPUT & OUTPUT +// GF_depth : GF low reference depth +// GF_slope : GF slope +// +// MODIFIED // char_depth_sim : simulated depth in meters -// GF_low_depth : GF low depth in current calculation cycle -// GF_slope : GF slope in current calculation cycle -// GF_low_depth_norm/_alt : frozen GF low depth reference -// GF_slope_norm/_alt : frozen GF slope // // OUTPUT -// char_depth_last : depth we came from -// sim_pres_respiration : simulated depth in absolute pressure +// GF_depth_norm/_alt : updated GF low depth reference +// GF_slope_norm/_alt : updated GF slope // // RETURN -// TRUE: a stop is needed, FALSE: no stop needed +// TRUE: a deco stop is required, FALSE: no deco stop required // static unsigned char find_next_stop(void) { @@ -1325,63 +1370,72 @@ overlay unsigned char depth_limit; overlay unsigned char stop_depth; overlay unsigned char next_stop; - overlay unsigned char need_stop; - - - // memorize the depth we came from - char_depth_last = char_depth_sim; - - // calculate the ceiling (minimum relative pressure) for the current depth - if( char_I_deco_model == 0 ) calc_limit(1.0); // deco or not - straight Buhlmann - else if( NDL_time ) calc_limit(GF_high); // not in deco - else if( char_depth_sim >= GF_low_depth ) calc_limit(GF_low); // in deco with GF, below or at low depth reference - else calc_limit(GF_high - GF_slope * (float)char_depth_sim); // in deco with GF, above low depth reference - - // convert the ceiling from relative pressure to meters, - // rounded up (i.e. made deeper) to next full meter, - // limiting at surface. - depth_limit = (ceiling > 0.0) ? (unsigned char)(ceiling * BAR_TO_METER + 0.99) : 0; - - // calculate the stop depth, i.e. round up (make deeper) to - // the next multiple of 3 meters using integer arithmetics - stop_depth = 3 * ( (depth_limit + 2) / 3 ); - - // apply correction for the shallowest stop - if( stop_depth == 3 ) stop_depth = char_I_depth_last_deco; - - // compute depth in meters that will be reached in 1 minute of ascent - // at a speed of char_I_ascent_speed (5..10 m/min) - if( char_depth_sim > char_I_ascent_speed ) + + + // calculate the current deco ceiling (minimum relative pressure) + if( char_I_model == 0 ) calc_limit(1.0); // straight Buhlmann + else if( NDL_time ) calc_limit(GF_high); // with GF: not in deco + else if( char_depth_sim >= GF_depth ) calc_limit(GF_low); // in deco, below or at low depth reference + else calc_limit(GF_high - GF_slope * (float)char_depth_sim); // in deco, above low depth reference + + // convert the deco ceiling from relative pressure to meters, + // rounded up (i.e. made deeper) to the next full meter + depth_limit = (unsigned char)(ceiling * BAR_TO_METER + 0.99); + + +#ifdef _cave_mode + // in cave or open water mode? + if( main_status & CAVE_MODE ) { - depth_1min = char_depth_sim - char_I_ascent_speed; + // cave mode + + // does backtracking require descent or keeping depth? + if( backtrack_target_depth >= char_depth_sim ) + { + // YES - decent to target depth or stay at current depth + char_depth_sim = backtrack_target_depth; + + // - done, no stop required + return(0); + } + else + { + // NO + + // target depth requires an ascent - determine ascent limit due to deco obligation + stop_depth = depth_limit; + + // apply correction for the shallowest stop + if( stop_depth && (stop_depth < char_I_last_stop_depth) ) stop_depth = char_I_last_stop_depth; + } } else +#endif { - depth_1min = 0; + // open water vertical ascent mode - determine the stop depth + + // stop depth is depth limit rounded up (made deeper) to the next multiple of 3 meters + stop_depth = 3 * ( (depth_limit + 2) / 3 ); + + // apply correction for the shallowest stop + if( stop_depth == 3 ) stop_depth = char_I_last_stop_depth; } - // is the stop depth shallower than the depth that can be reached within 1 minute, - // or are we allowed to surface AND can we reach the surface within 1 minute? - if( ( ( stop_depth < depth_1min ) ) - || ( ( stop_depth == 0 ) && ( depth_1min == 0 ) ) - ) + // is the stop depth shallower than the current depth (can we ascent)? + if( stop_depth < char_depth_sim ) { - // YES - report the depth that will be reached within 1 minute of ascent - char_depth_sim = depth_1min; - - // - no stop needed - need_stop = 0; - - // - done - goto done; + // YES - ascent by 1 meter + char_depth_sim--; + + // done, no stop needed + return(0); } // ----------------------------------------------------------------------- - // we need to make a stop now, or need to stay at the current stop depth + // we need to make a stop because we are not allowed to ascent any further // ----------------------------------------------------------------------- - // set stop data - need_stop = 1; + // set depth to stop depth char_depth_sim = stop_depth; // Apply correction in case the stop is to be placed deeper than a @@ -1393,115 +1447,113 @@ // the duration of the deco stop itself. if( 0 < internal_deco_depth[stop_index] ) if( char_depth_sim > internal_deco_depth[stop_index] ) - char_depth_sim = internal_deco_depth[stop_index]; - - // done so far if using straight Buhlmann - if( char_I_deco_model == 0 ) goto done; + char_depth_sim = internal_deco_depth[stop_index]; + + // if using straight Buhlmann: done, stop needed + if( char_I_model == 0 ) return(1); // ----------------------------------------------------------------------- // we need to make or hold a stop and we are using the GF extension // ----------------------------------------------------------------------- - // is the stop depth deeper than the GF low depth reference used up to now? - if( stop_depth > GF_low_depth ) + // is the stop_depth deeper than the GF low depth reference used up to now? + if( stop_depth > GF_depth ) { - // YES - update the reference - GF_low_depth = stop_depth; - GF_slope = (GF_high - GF_low) / (float)GF_low_depth; + // YES - update the GF low depth reference + GF_depth = stop_depth; + GF_slope = (GF_high - GF_low) / (float)GF_depth; // store for use in next cycles if( deco_status & CALC_NORM ) { - GF_low_depth_norm = GF_low_depth; - GF_slope_norm = GF_slope; + GF_depth_norm = GF_depth; + GF_slope_norm = GF_slope; } else { - GF_low_depth_alt = GF_low_depth; - GF_slope_alt = GF_slope; + GF_depth_alt = GF_depth; + GF_slope_alt = GF_slope; } } - // We have a (first) stop. But with a steep GF slope, the stop(s) after this - // first stop may be allowed to ascent to, too. This is because the gradient +#ifdef _cave_mode + // if in cave mode: done, stop needed + if( main_status & CAVE_MODE ) return(1); +#endif + + // We have a stop depth candidate. But with a steep GF slope, the stop(s) after + // this first stop may be allowed to ascent to, too. This is because the gradient // factor that will be used at the next depth(s) will allow more tissue super- - // saturation, maybe so much more that the next stop(s) will be allowed to - // ascent to. So we have to probe the next stops that are within the reach of - // 1 minute of ascent as well. + // saturation, maybe so much more that the next stop(s) will be allowed to ascent + // to. So we have to probe the next stops that are within the reach of 1 minute + // of ascent as well. + + // compute depth in meters that is reachable within 1 minute of ascent at 10 m/min + depth_1min = ( char_depth_sim > 10 ) ? char_depth_sim - 10 : 0; // probe all stop depths that are in reach of 1 minute of ascent - next_stop = stop_depth; + next_stop = stop_depth; while(next_stop > 0) { - // compute the depth of the next stop ... - if ( next_stop <= char_I_depth_last_deco ) next_stop = 0; - else if ( next_stop == 6 ) next_stop = char_I_depth_last_deco; + // compute the depth of the next stop to probe + if ( next_stop <= char_I_last_stop_depth ) next_stop = 0; + else if ( next_stop == 6 ) next_stop = char_I_last_stop_depth; else next_stop -= 3; - // would the next stop be beyond the 1 minute limit? - if( next_stop < depth_1min ) - { - // YES - signal that probing is done - next_stop = 0; - } - else - { - // NO - compute the ceiling at the next stop depth - calc_limit(GF_high - GF_slope * (float)next_stop); - - // - limit ceiling to positive values, i.e. the surface - if( ceiling < 0.0 ) ceiling = 0.0; - - // - check if this stop depth would be allowed - if( next_stop >= (unsigned char)(ceiling * BAR_TO_METER + 0.99) ) - { - // YES - this stop depth is allowed - char_depth_sim = next_stop; - - // - no stop needed if stop depth actually is the surface - if( next_stop == 0 ) need_stop = 0; - } - } + // done if the next stop would be above the 1 minute limit + if( next_stop < depth_1min ) return(1); + + // compute the ceiling for the next stop depth + calc_limit(GF_high - GF_slope * (float)next_stop); + + // done if the next stop would be above the ceiling + if( next_stop < (unsigned char)(ceiling * BAR_TO_METER + 0.99) ) return(1); + + // the next stop depth is allowed, ascent to it and redo trying further stop + char_depth_sim = next_stop; } - - // ----------------------------------------------------------------------- - // common end for straight Buhlmann and Buhlmann with GF extension - // ----------------------------------------------------------------------- - -done: - - // calculate absolute pressure at the depth found - calc_sim_pres_respiration(); - - return need_stop; + // reached the surface, done with no further stop + return(0); } ////////////////////////////////////////////////////////////////////////////// -// Find current gas in the list and get its change depth -// -// Input: char_I_current_gas_num number of current gas (1..5 or 6) +// Take the actual currently used gas for ascent & deco calculation +// +// Input: start_gas_num number of the gas/dil to start with (1..5 or 6) // // Output: sim_gas_current_num 1..6 or 0 for the manually configured gas/dil // sim_gas_current_depth change depth (MOD) of the gas/dil in meters // static void gas_take_current(void) { - assert( 1 <= char_I_current_gas_num && char_I_current_gas_num <= 6 ); - - if( char_I_current_gas_num <= NUM_GAS ) // gas/diluent 1-5 + assert( 1 <= start_gas_num && start_gas_num <= 6 ); + + + // check origin of the gas/diluent to start with + if( start_gas_num <= NUM_GAS ) { - sim_gas_current_num = char_I_current_gas_num; - sim_gas_current_depth = char_I_deco_gas_change[sim_gas_current_num-1]; + // pre-configured gas/diluent + + // set gas number + sim_gas_current_num = sim_gas_last_num = start_gas_num; + + // set change depth + sim_gas_current_depth = char_I_deco_gas_change[start_gas_num-1]; // capture case of non-configured change depth if( sim_gas_current_depth == 0 ) sim_gas_current_depth = 255; } else { - sim_gas_current_num = 0; + // on-the-fly configured gas/diluent ("gas 6") + + // set gas number + sim_gas_current_num = sim_gas_last_num = 0; + + // set change depth sim_gas_current_depth = char_I_gas6_depth; } } @@ -1512,8 +1564,8 @@ // // Input: char_depth_sim simulated depth in meters // sim_gas_current_num number of the currently used gas/dil -// char_I_deco_gas_type[] types of the gases/dils -// char_I_deco_gas_change[] change depths of the gases/dils +// deco_gas_type[] types and state of the gases/dils +// char_I_deco_gas_change[] change depths of the gases/dils // // Modified: sim_gas_best_num index of the gas (1..5) - only if return value is true // sim_gas_best_depth switch depth - only if return value is true @@ -1524,34 +1576,51 @@ { overlay unsigned char switch_depth = 255; overlay unsigned char switch_gas = 0; - overlay unsigned char j; + // loop over all gases to find the shallowest one below or at current depth for( j = 0; j < NUM_GAS; ++j ) { - // Is this gas available? - if( char_I_deco_gas_type[j] > 0 ) - - // Is the change depth of the this gas deeper than or + // is this gas available? + if( ( deco_gas_type[j] & GAS_TYPE_MASK ) // gas enabled (type > 0) ? + && !( deco_gas_type[j] & GAS_AVAIL_MASK ) // neither lost nor staged ? + ) + + // is it not a deco gas unless: + // extended stops are activated + // OR we are on a deco stop + // OR we are in bailout but not in cave mode + if( ( ( deco_gas_type[j] & GAS_TYPE_MASK ) < 3 ) + || ( ( main_status & EXTENDED_STOPS ) ) + || ( ( deco_status & CALC_NORM ) && ( deco_info & DECO_STOPS_NORM ) ) + || ( ( deco_status & CALC_ALT ) && ( deco_info & DECO_STOPS_ALT ) ) + || ( ( deco_status & BAILOUT_MODE ) && !( main_status & CAVE_MODE ) ) + ) + + // is the change depth of the this gas deeper than or // at least equal to the current depth? if( char_I_deco_gas_change[j] >= char_depth_sim ) - // Is the change depth of this gas shallower than or - // at least equal to the change depth of the best gas - // found so far, or is it the first better gas found? - if( char_I_deco_gas_change[j] <= switch_depth ) + // is the change depth of this gas shallower than + // the change depth of the best gas found so far, + // or is it the first better gas found? + if( char_I_deco_gas_change[j] < switch_depth ) #ifdef _gas_contingency - // Is there still enough of this gas or doesn't it matter? - if( ( gas_volume_need[j] < gas_volume_avail[j] ) || ( !char_I_gas_contingency ) ) + // is there still enough of this gas or shall we don't care? + if( !(deco_gas_type[j] & GAS_FULLY_USED_UP ) + || !( main_status & GAS_CONTINGENCY ) + || !( main_status & CALC_VOLUME ) + ) #endif - // If there is a yes to all these questions, we have a better gas! + // if there is a yes to all these questions, we have a better gas! { // memorize this gas (1..5) and its change depth switch_gas = j+1; switch_depth = char_I_deco_gas_change[j]; } + } // continue looping through all gases to eventually find an even better gas // has a best gas been found? @@ -1585,6 +1654,9 @@ // static void gas_take_best(void) { + // memorize current gas as last gas used + sim_gas_last_num = sim_gas_current_num; + // set new gas sim_gas_current_num = sim_gas_best_num; sim_gas_current_depth = sim_gas_best_depth; @@ -1655,7 +1727,7 @@ ////////////////////////////////////////////////////////////////////////////// // Compute respired ppO2, ppN2 and ppHe // -// Input: tissue_increment : selector for targeting simulated or real tissues +// Input: tissue_increment : selector for simulated/real tissues and gases // main_status : breathing mode for real tissues // deco_status : breathing mode for simulated tissues // sim_/real_O2_ratio : (simulated) O2 ratio breathed @@ -1665,7 +1737,6 @@ // sim_/real_pSCR_drop : (simulated) pSCR O2 drop // pres_surface : surface pressure [bar] // char_I_const_ppO2 : ppO2 reported from sensors or setpoint [cbar] -// float_deco_distance : safety factor, additional depth below stop depth [bar] - not used any more // ppWater : water-vapor pressure inside respiratory tract [bar] // // Output: ppN2 : respired N2 partial pressure @@ -1743,21 +1814,21 @@ //---- OC, CCR and Bailout Mode Gas Calculations ----------------------------------- // calculate ppO2 of pure oxygen - O2_ppO2 = calc_pres_respiration - ppWater; + ppO2_O2 = calc_pres_respiration - ppWater; // capture failure condition in case real_pres_respiration is < ppWater (should never happen...) - if( O2_ppO2 < 0.0 ) O2_ppO2 = 0.0; + if( ppO2_O2 < 0.0 ) ppO2_O2 = 0.0; // calculate ppO2 of the pure gas (OC, diluent) - OC_ppO2 = O2_ppO2 * calc_O2_ratio; + ppO2_OC = ppO2_O2 * calc_O2_ratio; #ifdef _ccr_pscr // calculate pSCR ppO2 - pSCR_ppO2 = OC_ppO2 - calc_pSCR_drop; - - // capture failure condition in case pSCR_ppO2 becomes negative - if( pSCR_ppO2 < 0.0 ) pSCR_ppO2 = 0.0; + ppO2_pSCR = ppO2_OC - calc_pSCR_drop; + + // capture failure condition in case ppO2_pSCR becomes negative + if( ppO2_pSCR < 0.0 ) ppO2_pSCR = 0.0; //---- Loop modes : adjust ppN2 and ppHe for change in ppO2 due to setpoint (CCR) or drop (pSCR) --- @@ -1783,10 +1854,8 @@ { //---- pSCR Mode -------------------------------------------------------------------------- - // Use the sensor value if available, but only in real tissue context! - // In all other cases use calculated ppO2. - if ( char_I_const_ppO2 && (tissue_increment & TISSUE_SELECTOR)) ppO2 = const_ppO2; - else ppO2 = pSCR_ppO2; + // Use the sensor value if available and in real tissue context, else use calculated ppO2. + ppO2 = ( char_I_const_ppO2 && (tissue_increment & TISSUE_SELECTOR)) ? const_ppO2 : ppO2_pSCR; } else { @@ -1821,7 +1890,7 @@ //---- OC mode --------------------------------------------------------------------------------- // breathed ppO2 is ppO2 of pure gas - ppO2 = OC_ppO2; + ppO2 = ppO2_OC; } @@ -1834,9 +1903,6 @@ //---- calculate ppN2 and ppHe --------------------------------------------------------------------- - // add deco safety distance when working on simulated tissues - not used any more - // if( !(tissue_increment & TISSUE_SELECTOR) ) calc_pres_respiration += float_deco_distance; - // compute ppN2 and ppHe, capture potential failure conditions first: if( calc_pres_respiration > ppWater ) { @@ -1912,10 +1978,15 @@ int_O_CNS_alt = 0 + INT_FLAG_INVALID; // initialize NDL times - char_O_NDL_norm = 240; - char_O_NDL_alt = 240; - - // initialize ascent times + int_O_NDL_norm = 240; + int_O_NDL_alt = 240; + + // initialize stop times + int_O_TST_norm = 0; + int_O_TST_alt = 0 + INT_FLAG_INVALID + INT_FLAG_NOT_COMPUTED_YET + INT_FLAG_ZERO; + + + // initialize ascent / return times int_O_TTS_norm = 0; int_O_TTS_alt = 0 + INT_FLAG_INVALID + INT_FLAG_NOT_COMPUTED_YET; @@ -1951,8 +2022,8 @@ // int_O_CNS_norm CNS value at end of normal dive plan // int_O_CNS_alt CNS value at end of alternative dive plan // char_O_deco_warnings deco warnings vector -// char_O_NDL_norm remaining NDL time in normal dive plan -// char_O_NDL_alt remaining NDL time in alternative dive plan +// int_O_NDL_norm remaining NDL time in normal dive plan +// int_O_NDL_alt remaining NDL time in alternative dive plan // int_O_TTS_norm ascent time (TTS) in normal dive plan // int_O_TTS_alt ascent time (TTS) in alternative dive plan // int_O_lead_supersat supersaturation of the leading tissue @@ -1974,7 +2045,7 @@ real_pres_tissue_N2[ci] = N2_equilibrium; // N2 // reset tissue pressures for scaled tissue graphics - char_O_tissue_pres_He[ci] = 0; // He + char_O_tissue_pres_He[ci] = 0; // He char_O_tissue_pres_N2[ci] = 10; // N2 char_O_tissue_pressure[ci] = 10; // combined } @@ -1984,15 +2055,17 @@ int_O_CNS_current = int_O_CNS_norm = int_O_CNS_alt = 0; // reset some more vars to their defaults - char_O_NDL_norm = 240; - char_O_NDL_alt = 240; - int_O_TTS_norm = 0; - int_O_TTS_alt = 0; - int_O_lead_supersat = 0; + int_O_NDL_norm = 240; + int_O_NDL_alt = 240; + int_O_TST_norm = 0; + int_O_TST_alt = 0 + INT_FLAG_ZERO; + int_O_TTS_norm = 0; + int_O_TTS_alt = 0; + int_O_lead_supersat = 0; // reset all warning and info flags - char_O_deco_warnings = 0; - char_O_deco_info = 0; + char_O_deco_warnings = 0; + char_O_deco_info = 0; } @@ -2009,12 +2082,11 @@ // char_I_SAC_work gas usage rate during working phase in l/min // char_I_SAC_deco gas usage rate during deco stops phase in l/min // -// char_I_deco_model selector for GF extension -// char_I_ascent_speed ascent speed +// char_I_model selector for GF extension // char_I_saturation_multiplier safety factor for tissue saturation // char_I_desaturation_multiplier safety factor for tissue desaturation // -// char_I_pressure_gas[] amount of gas available for ascent in bar +// char_I_pressure_gas[] amount of gas available for ascent / cave return in bar // int_I_pressure_drop[] pressure drop used to calculate SAC rate // char_I_gas_avail_size[] size of the tanks in liters // @@ -2027,15 +2099,15 @@ // char_O_deco_info deco engine information vector // char_O_deco_warnings deco engine warnings vector // -// char_O_NDL_norm remaining NDL time in normal dive plan -// char_O_NDL_alt remaining NDL time in alternative dive plan +// int_O_NDL_norm remaining NDL time in normal dive plan +// int_O_NDL_alt remaining NDL time in alternative dive plan // int_O_TTS_norm ascent time (TTS) in normal dive plan // int_O_TTS_alt ascent time (TTS) in alternative dive plan // int_O_CNS_norm CNS value at end of normal dive plan // int_O_CNS_alt CNS value at end of alternative dive plan // -// int_O_gas_need_vol calculated gas volumes needed for ascent -// int_O_gas_need_pres calculated gas pressures needed for ascent +// int_O_gas_need_vol[] calculated gas volumes needed for ascent / cave return +// int_O_gas_need_pres[] calculated gas pressures needed for ascent / cave return // // int_O_SAC_measured measured surface air consumption (SAC) rate in l/min // @@ -2048,8 +2120,8 @@ overlay unsigned short int_ppO2_max_warn; overlay unsigned short int_ppO2_max_att; overlay unsigned short int_ppO2_max_dil; - overlay float EAD; - overlay float END; + overlay float EAD_pres; + overlay float END_pres; //============================================================================================= @@ -2179,8 +2251,8 @@ calc_CNS(); // calculate ceiling (at GF_high or 100%) and leading tissue supersaturation - if ( char_I_deco_model ) calc_limit(GF_high); // GF factors enabled - else calc_limit( 1.0 ); // classic Buhlmann + if ( char_I_model ) calc_limit(GF_high); // GF factors enabled + else calc_limit( 1.0 ); // classic Buhlmann // convert the ceiling value to integer convert_ceiling_for_display(); @@ -2203,28 +2275,31 @@ { //---- Calculate and Export EAD and END ------------------------------------------------------ - // calculate EAD (Equivalent Air Depth): equivalent depth for the same N2 level with plain air - EAD = (ppN2 / 0.7902 + ppWater - pres_surface) * BAR_TO_METER; - - // calculate END (Equivalent Narcotic Depth): here O2 is treated as narcotic, too - // Source cited: The Physiology and Medicine of Diving by Peter Bennett and David Elliott, - // 4th edition, 1993, W.B.Saunders Company Ltd, London. - END = (real_pres_respiration - ppHe - pres_surface) * BAR_TO_METER; - - // export EAD - float_value = EAD; convert_float_to_char(); char_O_EAD = char_value; - - // export END - float_value = END; convert_float_to_char(); char_O_END = char_value; + // calculate EAD (Equivalent Air Depth) as pressure in [bar]: + // equivalent depth for the same N2 level with plain air + EAD_pres = ppN2 / 0.7902 + ppWater - pres_surface; + + // calculate END (Equivalent Narcotic Depth) as pressure in [bar]: + // as before, but with O2 treated as narcotic, too + // Source: The Physiology and Medicine of Diving by Peter Bennett and David Elliott, + // 4th edition, 1993, W.B.Saunders Company Ltd, London. + END_pres = real_pres_respiration - ppHe - pres_surface; + + + // export EAD, factor 10 is because conversion delivers in [cbar] but we need [mbar] + float_value = EAD_pres; convert_float_to_int(); int_O_EAD_pres = int_value * 10; + + // export END, factor 10 is because conversion delivers in [cbar] but we need [mbar] + float_value = END_pres; convert_float_to_int(); int_O_END_pres = int_value * 10; //---- Compute ppO2 Values in [cbar] --------------------------------------------------------- float_value = ppO2; convert_float_to_int(); int_O_breathed_ppO2 = int_value; // breathed gas #ifdef _ccr_pscr - float_value = O2_ppO2; convert_float_to_int(); int_O_O2_ppO2 = int_value; // pure oxygen - float_value = OC_ppO2; convert_float_to_int(); int_O_pure_ppO2 = int_value; // pure gas - float_value = pSCR_ppO2; convert_float_to_int(); int_O_pSCR_ppO2 = int_value; // pSCR calculated + float_value = ppO2_O2; convert_float_to_int(); int_O_O2_ppO2 = int_value; // pure oxygen + float_value = ppO2_OC; convert_float_to_int(); int_O_pure_ppO2 = int_value; // pure gas + float_value = ppO2_pSCR; convert_float_to_int(); int_O_pSCR_ppO2 = int_value; // pSCR calculated #endif @@ -2236,19 +2311,22 @@ // (7 meters chosen as to be 2 stop depth intervals plus 1 additional meter below) if ( ( deco_info & DECO_MODE ) > 0 ) if ( ( char_depth_real ) > char_O_deco_depth[0] + 7 ) - deco_info &= ~DECO_MODE; + deco_info &= ~DECO_MODE; // Set the deco mode flag if: // deco mode is not set // AND breathing an OC deco gas (gas type 3) // OR breathing a gas or diluent that officially is disabled (type 0) // OR there is a deco stop + // + // Remark: when breathing a gas, its lost & staged flags are cleared + // if ( ( deco_info & DECO_MODE ) == 0 ) if ( ( char_I_current_gas_type == 3 ) || ( char_I_current_gas_type == 0 ) || ( char_O_deco_depth[0] > 0 ) ) - deco_info |= DECO_MODE; + deco_info |= DECO_MODE; //---- Compute ppO2 Warnings ------------------------------------------------------------------ @@ -2277,7 +2355,7 @@ int_ppO2_max_dil = int_ppO2_max_warn; // when enabled and in CCR mode, the upper diluent warning threshold gets adjust according to the current setpoint - if( char_I_dil_ppO2_check ) + if( char_I_dil_check ) if( (main_status & MODE_MASK) == MODE_CCR ) { overlay unsigned short max_dil; @@ -2365,47 +2443,28 @@ // initialize all output variables to defaults init_output_vars(); - // safeguard input parameters that are constant during the course of the dive - if( char_I_ascent_speed < 5 ) char_I_ascent_speed = 5; - if( char_I_ascent_speed > 10 ) char_I_ascent_speed = 10; - - // convert input parameters to float numbers - float_ascent_speed = 1.00 * char_I_ascent_speed; - - // initialize values that will be recalculated later on periodically - deco_warnings = 0; // reset all deco warnings - deco_info = 0; // reset all deco infos - IBCD_tissue_vector = 0; // reset tissue IBCD vector - NDL_tissue_start_norm = 0; // initialize the tissue to start with when calculating normal NDL time - NDL_tissue_start_alt = 0; // initialize the tissue to start with when calculating alternative NDL time + deco_warnings = 0; // reset all deco warnings + deco_info = 0; // reset all deco infos + IBCD_tissue_vector = 0; // reset tissue IBCD vector + NDL_tissue_start_norm = 0; // initialize the tissue to start with when calculating normal NDL time + NDL_tissue_start_alt = 0; // initialize the tissue to start with when calculating alternative NDL time // enforce initialization of GF data on first cyclic initialization - GF_high_last = 255; - GF_low_last = 255; - -#ifdef _gas_contingency - // shall check for gas pan out? - if( char_I_gas_contingency ) + GF_high_last = 255; + GF_low_last = 255; + + + // calculate volumes available for each gas + for( i = 0; i < NUM_GAS; i++ ) { - overlay float reduction = 0.5 * (float)char_I_SAC_deco * (1.0 + (float)char_I_depth_last_deco / 10.0); - - // YES - calculate volumes available for each gas - for( i = 0; i < NUM_GAS; i++ ) - { - // total available volume = tank size * fill press, char_I_gas_avail_pres is in multiples of 10 bar - gas_volume_avail[i] = (float)char_I_gas_avail_size[i] * (float)char_I_gas_avail_pres[i] * 10.0; - - // reduce total available volumes by due for 1/2 minute on last stop - gas_volume_avail[i] -= reduction; - } + // total available volume = tank size * fill press, char_I_gas_avail_pres is in multiples of 10 bar + gas_volume_avail[i] = (float)char_I_gas_avail_size[i] * (float)char_I_gas_avail_pres[i] * 10.0; + + // attention threshold + gas_volume_atten[i] = gas_volume_avail[i] * GAS_NEEDS_ATTENTION; } -#endif - -#ifdef _cave_mode - char_I_backtrack_time = 0; //clear backtracking time (index to char_I_backtrack_depth) - char_I_backtrack_depth = 0; //prime first entry with a depth of 0 meter -#endif + #ifdef _profiling int_O_profiling_overrun_max = 0; @@ -2433,6 +2492,10 @@ // clear the internal stops table clear_deco_table(); + // clear deco stops info + if( deco_status & CALC_NORM ) deco_info &= ~DECO_STOPS_NORM; + else deco_info &= ~DECO_STOPS_ALT; + // initialize the simulated tissues with the current state of the real tissues for( i = 0; i < NUM_COMP; i++ ) { @@ -2440,8 +2503,47 @@ sim_pres_tissue_He[i] = real_pres_tissue_He[i]; } + // initialize the gas types + for( i = 0; i < NUM_GAS; i++ ) + { + deco_gas_type[i] = char_I_deco_gas_type[i]; + } + +#ifdef _gas_contingency + // if in gas contingency mode, + // check if there are multiple tanks with the same gas + // (or at least with the same change depth...) + if( main_status & GAS_CONTINGENCY ) + { + for( i = 0; i < NUM_GAS; i++ ) + { + // default to no peer tanks existing + peer_tank[i] = 0; + + // tank enabled? + if( char_I_deco_gas_type[i] ) + { + // YES - check for peer tanks + for( j = 0; j < NUM_GAS; j++ ) + { + // do not check a tank against itself + if( i == j ) continue; + + // is the other tank also enabled and does it have the same change depth? + if( char_I_deco_gas_type[j] & GAS_TYPE_MASK ) + if( char_I_deco_gas_change[i] == char_I_deco_gas_change[j] ) + { + // YES - memorize it as a peer tank + peer_tank[i] |= (1 << j); + } + } + } + } + } +#endif + // initialize GF parameters if using GF model - if( char_I_deco_model != 0 ) + if( char_I_model != 0 ) { // update GF parameters (GFs may have been switched between GF and aGF) if( (char_I_GF_Low_percentage != GF_low_last) || (char_I_GF_High_percentage != GF_high_last) ) @@ -2456,33 +2558,37 @@ // reset low depth references and slopes - GF_low_depth_norm = 0; - GF_low_depth_alt = 0; - GF_slope_norm = 0.0; - GF_slope_alt = 0.0; + GF_depth_norm = 0; + GF_depth_alt = 0; + GF_slope_norm = 0.0; + GF_slope_alt = 0.0; } // retrieve GF parameters for current calculation cycle if( deco_status & CALC_NORM ) { - GF_low_depth = GF_low_depth_norm; - GF_slope = GF_slope_norm; + GF_depth = GF_depth_norm; + GF_slope = GF_slope_norm; } else { - GF_low_depth = GF_low_depth_alt; - GF_slope = GF_slope_alt; + GF_depth = GF_depth_alt; + GF_slope = GF_slope_alt; } } // initialize the simulated CNS value with the current CNS value of the real tissues CNS_fraction_sim = CNS_fraction_real; - // initialize the simulated depth with the current depth, - // memorize current depth as start depth of the simulation + // initialize the simulated pressure with the current real pressure sim_pres_respiration = real_pres_respiration; - char_depth_sim = char_depth_real; - char_depth_sim_start = char_depth_real; + + // initialize the simulated depth with the current real depth + char_depth_sim = char_depth_start = char_depth_real; + + // cache the gas/dil to start calculations with + start_gas_num = char_I_current_gas_num; + // Lookup the gas that is currently breathed with the real tissues and set it as // the gas to be used with the simulated tissues, too. This gas will be used until @@ -2501,8 +2607,9 @@ // initialize the no decompression limit (NDL) time to 240 minutes NDL_time = 240; - // initialize the total ascent time to 0 minutes - ascent_time = 0; + // clear the Total Time to Surface (TTS) and the Total Stops Time (TST) + TTS_time = 0; + TST_time = 0; // retrieve the tissue that had the shortest NDL time during last calculation NDL_tissue_start = ( deco_status & CALC_NORM ) ? NDL_tissue_start_norm : NDL_tissue_start_alt; @@ -2511,12 +2618,11 @@ NDL_tissue = NDL_tissue_start; NDL_tissue_lead = NDL_tissue_start; - // initialization for calculating the initial ascent - // start with 1 minute ascent steps when calculating the initial ascent - fast = 1; - - // initialization for convert_gas_needs_to_press() - gas_needs_gas_index = 0; + // initialization for convert_volume_to_pressure() + gas_needs_gas_index = 0; + + // tag gas needs as not calculated in fTTS mode by default + deco_info &= ~GAS_NEEDS_fTTS; // shall calculate gas needs? if( main_status & CALC_VOLUME ) @@ -2538,6 +2644,19 @@ #endif } + +#ifdef _cave_mode + if( main_status & CAVE_MODE ) + { + // get the position of the first data set to start the backtracking from + backtrack_index = char_I_backtrack_index; + + // get the first backtracking data set + read_backtrack_data(); + } +#endif + + #ifdef _profiling profiling_runs = 0; #endif @@ -2558,6 +2677,9 @@ // case PHASE_30_EXTENDED_BOTTOM_TIME: + // tag gas needs as calculated in fTTS mode + deco_info |= GAS_NEEDS_fTTS; + // program interval on simulated tissues (flag bit 7 = 0) tissue_increment = char_I_extra_time; @@ -2585,17 +2707,21 @@ // on gas 1-5 ? if( sim_gas_current_num ) { - // YES - set the bottom depth - gas_needs_depth = char_depth_sim_start; - - // take either the whole bottom time or just the fTTS/bailout extra time + // YES - take either the whole bottom time or just the fTTS/bailout extra time gas_needs_time = ( main_status & CALCULATE_BOTTOM ) ? char_I_bottom_time : char_I_extra_time; - // calculate gas demand - calc_due_by_depth_time_sac(); - - // take the result - gas_volume_need[sim_gas_current_num-1] = gas_needs_volume_due; + // any time to do? + if( gas_needs_time ) + { + // YES - set the bottom depth + gas_needs_depth = char_depth_start; + + // calculate required gas volume + calc_required_volume(); + + // take the result + gas_volume_need[sim_gas_current_num-1] = gas_needs_volume_due; + } } @@ -2633,260 +2759,324 @@ if( deco_status & CALC_NORM ) NDL_tissue_start_norm = NDL_tissue_lead; else NDL_tissue_start_alt = NDL_tissue_lead; - // done with calculating NDL time, set next calculation phase: - // - calculate return and ascent in cave mode if configured, else - // - proceed with no-stop ascent if within NDL time, or - // - proceed with deco ascent if beyond NDL time. -#ifdef _cave_mode - if ( main_status & CAVE_MODE ) next_planning_phase = PHASE_60_CAVE_RETURN; - else -#endif - next_planning_phase = PHASE_70_OPEN_WATER_ASCENT; + // done with calculating NDL time, next phase will calculate the ascent / cave return + next_planning_phase = PHASE_70_ASCENT_OR_RETURN; } break; -#ifdef _cave_mode - // - //---- Cave Mode Return ------------------------------------------------------------------- - // - case PHASE_60_CAVE_RETURN: - - // TODO - - // the next calculation phase will gather all results - next_planning_phase = PHASE_80_RESULTS; - - break; -#endif - - // - //---- Open Water Ascent ------------------------------------------------------------------ + //---- Ascent or Return (cave mode) ------------------------------------------------------- // - case PHASE_70_OPEN_WATER_ASCENT: - - // program 1 minute interval on simulated tissues - tissue_increment = 1; - - // memorize current gas in case there will be a gas change - sim_gas_last_num = sim_gas_current_num; - - // find_next_stop(): - // stays at the current stop depth, ascents to the next stop depth, or - // ascents to the depth that is reachable within one minute of ascent - // without needing to stop. - // - // return value : 1/true if a stop is required, else 0/false - // char_depth_sim : current depth (depth achieved) - // char_depth_last : last depth (depth we came from) - // - if( find_next_stop() ) + case PHASE_70_ASCENT_OR_RETURN: + { - overlay unsigned char silent_stop = 0; - overlay unsigned char gas_change = 0; - - //---- stop required -------------------- - - // check if there is a better gas to switch to - if( gas_find_best() ) + overlay unsigned char doing_deco_stop = 0; + overlay unsigned char doing_gas_change = 0; + + + // target simulated tissues, default is 1 minute interval + tissue_increment = 1; + + // check if a deco stop is required: + // - stays at the current depth if a stop is required, + // - ascents to the next stop if possible, else + // - ascents by 1 meter + if( find_next_stop() ) + { + //---- stop required -------------------- + + // memorize doing a deco stop + doing_deco_stop = 1; + + // set flag for deco stops found + if( deco_status & CALC_NORM ) deco_info |= DECO_STOPS_NORM; + else deco_info |= DECO_STOPS_ALT; + + // encountered a deco stop, so switch to deco usage rate (SAC deco) + gas_needs_usage_rate = char_I_SAC_deco; + + // check if there is a better gas to switch to + if( gas_find_best() ) + { + // YES - memorize doing a gas change + doing_gas_change = 1; + + // take the gas + gas_take_best(); + + // set the new calculation ratios for N2, He and O2 + gas_set_ratios(); + + // add the gas change time to the stop time + tissue_increment += char_I_gas_change_time; + } + + // add the stop to an existing stop or add a new stop + update_deco_table(tissue_increment); + } + else { - // YES - memorize it - gas_change = 1; - - // take the gas - gas_take_best(); - - // set the new calculation ratios for N2, He and O2 - gas_set_ratios(); - - // add the gas change time to the stop time - tissue_increment += char_I_gas_change_time; - - // extended stops option enabled and - // gas change depth deeper than the current depth ? - if( main_status & EXTENDED_STOPS ) - if( sim_gas_current_depth > char_depth_sim ) + //---- no stop required ----------------- + + // switch to 1/10 minute interval + tissue_increment = 0; + + // memorize not doing a deco stop + doing_deco_stop = 0; + + // check if there is a better gas to switch to, but only: + // + // if extended stops are activated OR if cave mode is enabled OR if in bailout + // AND if the actual depth (char_depth_start) is deeper or at the change + // depth of the better gas (change depth has not been passed yet) *) + // AND if the depth of the last stop is above (shallower) or at the change + // depth of the better gas (do not switch on final ascent) *) + // + // *) skipped when calculating in cave mode + // + // Attention: do not use a && formula over all 'if' terms, the + // conditions need to be evaluated in the given order! + // + if( ( main_status & EXTENDED_STOPS ) + || ( main_status & CAVE_MODE ) + || ( deco_status & BAILOUT_MODE ) + ) + if( gas_find_best() ) +#ifdef _cave_mode + if( ( char_depth_start >= sim_gas_best_depth ) || ( main_status & CAVE_MODE ) ) + if( ( char_I_last_stop_depth <= sim_gas_best_depth ) || ( main_status & CAVE_MODE ) ) +#else + if( ( char_depth_start >= sim_gas_best_depth ) ) + if( ( char_I_last_stop_depth <= sim_gas_best_depth ) ) +#endif { - // YES - make a "silent" stop at the gas change depth - // to figure in the gas change - silent_stop = 1; - - // locate the stop at the shallower one of the - // gas change depth or the depth we came from - char_depth_sim = (sim_gas_current_depth < char_depth_last) ? - sim_gas_current_depth : char_depth_last; - - // calculate sim_pres_respiration for - // the adjusted value of char_depth_sim - calc_sim_pres_respiration(); - - // as we didn't travel the full distance, - // account for the gas change time only + // YES - memorize doing a gas change + doing_gas_change = 1; + + // take the gas + gas_take_best(); + + // set the new calculation values for N2, He and O2 + gas_set_ratios(); + + // create a stop lasting the gas change time tissue_increment = char_I_gas_change_time; - // if run from the deco calculator, - // put the gas change into the stops table or - // abort deco calculation if the table is full - if( deco_status & DECO_CALCULATOR_MODE ) - if( !update_deco_table(tissue_increment) ) - next_planning_phase = PHASE_80_RESULTS; + // if in deco and run from the deco calculator: + // create a stop for the gas change in the stops table + if( !NDL_time && (deco_status & DECO_CALCULATOR_MODE) ) + update_deco_table(tissue_increment); + } + + } // stop / no stop + + +#ifdef _cave_mode + // cave mode actions + if( main_status & CAVE_MODE ) + { + // doing a deco stop? + if( doing_deco_stop ) + { + // YES - not moving, reset the 1/10 minute steps counter + backtrack_step_counter = 10; } - } // better gas - - - // if the stop is not a silent one, - // add the stop to an existing stop or add a new stop, - // or abort deco calculation if the deco table is full - if( !silent_stop ) - if( !update_deco_table(tissue_increment) ) - next_planning_phase = PHASE_80_RESULTS; - + else + { + // NO - on the move, switch back to SAC work + gas_needs_usage_rate = char_I_SAC_work; + + // - decrement the 1/10 minute steps counter if not already zero + if( backtrack_step_counter ) backtrack_step_counter--; + + // - target backtracking depth reached? + if( char_depth_sim == backtrack_target_depth ) + { + // YES - on target depth + + // target depth reached within first 1/10 minute? + if( backtrack_step_counter == 9 ) + { + // YES - will not change depth any more while remaining 9/10 of + // the minute, so can do the full full minute in one step + tissue_increment = 1; + backtrack_step_counter = 0; + } + + // on the move for a minute or more now? + // (incl. doing full minute in one step) + if( backtrack_step_counter == 0 ) + { + // YES - get the data of the next backtracking data set + read_backtrack_data(); + } + } + } + } // cave mode +#endif // shall calculate gas needs? if( main_status & CALC_VOLUME ) { - // encountered a stop, so switch to deco usage rate (SAC deco) - gas_needs_usage_rate = char_I_SAC_deco; - - // set the depth for gas need calculation to the shallower one - // of the start depth (current real depth) and the stop depth - // (assumed depth to be) as we may be shallower than we should be - gas_needs_depth = ( char_depth_sim_start < char_depth_sim ) ? - char_depth_sim_start : char_depth_sim; - - // did a gas change occur and last gas is 1-5 and a gas change time set? - if( gas_change && sim_gas_last_num && char_I_gas_change_time ) + overlay unsigned char index_last_gas = sim_gas_last_num-1; + overlay unsigned char index_curr_gas = sim_gas_current_num-1; + +#ifdef _cave_mode + // in cave mode? + if( main_status & CAVE_MODE ) + { + // YES - set the depth for the gas needs calculation to the + // simulated stop / on-the-move depth + gas_needs_depth = char_depth_sim; + } + else +#endif + { + // NO - set the depth for the gas needs calculation to the shallower + // one of the actual depth (char_depth_start, current real depth) + // and the simulated (stop) depth, as we may on purpose dive + // shallower than we should to conserve on a low running gas supply + gas_needs_depth = ( char_depth_start < char_depth_sim ) ? + char_depth_start : char_depth_sim; + } + + // doing a gas change and a gas change time is set? + if( doing_gas_change && char_I_gas_change_time ) { // YES - set time it takes for switching the gas gas_needs_time = char_I_gas_change_time; - // calculate gas demand - calc_due_by_depth_time_sac(); - - // add the demand to the overall demand on the last gas - gas_volume_need[sim_gas_last_num-1] += gas_needs_volume_due; + // - calculate required gas volume for the gas change + calc_required_volume(); + + // - add gas change demand to overall demand on the last gas + if( sim_gas_last_num ) gas_volume_need[index_last_gas] += gas_needs_volume_due; + + // - add gas change demand to overall demand on the current gas + if( sim_gas_current_num ) gas_volume_need[index_curr_gas] += gas_needs_volume_due; } - // current gas is 1-5 ? + // current gas is 1-5 ? (i.e. not 0 aka gas 6) if( sim_gas_current_num ) { - // YES - time is 1 minute plus the gas change time (if set) + // set time: doing a deco stop -> 1 minute, encoded by tissue_increment = 1 + // no deco stop -> 1/10 minute, encoded by tissue_increment = 0 gas_needs_time = tissue_increment; - // calculate gas demand - calc_due_by_depth_time_sac(); + // calculate required gas volume for the stop, ascent or travel + calc_required_volume(); // add the demand to the overall demand on the current gas - gas_volume_need[sim_gas_current_num-1] += gas_needs_volume_due; + gas_volume_need[index_curr_gas] += gas_needs_volume_due; } - } // gas need - } - else - { - //---- no stop required ----------------- - - // switch to a better gas, but only: - // - // if extended stops are activated OR if in bailout OR if within NDL - // AND if the actual depth is below (deeper) or at the change depth of the - // better gas (switch depth has not been passed yet) - // AND if the depth of the last stop is above (shallower) or at the change - // depth of the better gas (do not switch on final ascent) - // - // Attention: do not use a && formula over all 'if' terms, the - // conditions need to be evaluated in the given order! - // - if( (main_status & EXTENDED_STOPS) || (deco_status & BAILOUT_MODE) || NDL_time ) - if( gas_find_best() ) - if( char_depth_real >= sim_gas_best_depth ) - if( char_I_depth_last_deco <= sim_gas_best_depth ) - { - // YES - take the gas - gas_take_best(); - - // set the new calculation values for N2, He and O2 - gas_set_ratios(); - - // set char_depth_sim to the gas change depth - char_depth_sim = sim_gas_current_depth; - - // calculate sim_pres_respiration for - // the adjusted value of char_depth_sim - calc_sim_pres_respiration(); - - // as we didn't travel the full distance, - // account for the gas change time only - tissue_increment = char_I_gas_change_time; - - // if in deco and - // if run from the deco calculator: - // create a stop for the gas change in the stops table, - // abort deco calculation if the deco table is full - if( !NDL_time ) - if( deco_status & DECO_CALCULATOR_MODE ) - if( !update_deco_table(tissue_increment) ) - next_planning_phase = PHASE_80_RESULTS; - - // shall calculate gas needs and gas change time is set? - if( main_status & CALC_VOLUME ) - if( char_I_gas_change_time ) + +#ifdef _gas_contingency + // in gas contingency mode? + if( main_status & GAS_CONTINGENCY ) { - // YES - set depth to current depth - gas_needs_depth = char_depth_sim; - - // set time it takes for switching the gas - gas_needs_time = tissue_increment; - - // calculate gas demand - calc_due_by_depth_time_sac(); - - // add gas demand to the overall demand on the new gas - gas_volume_need[sim_gas_current_num-1] += gas_needs_volume_due; - - // was the last gas one of the gases 1-5 ? - if( sim_gas_last_num ) + overlay unsigned char all_peer_tanks_used_up = 1; + + + // when doing a gas change and the there is an overdraw on the last gas, + // then transfer the overdraw to the current gas if the current gas has + // an equal or deeper change depth than the overdrawn gas + if( doing_gas_change ) + if( gas_volume_need[index_last_gas] >= gas_volume_avail[index_last_gas] ) + if( char_I_deco_gas_change[index_last_gas] <= char_I_deco_gas_change[index_curr_gas] ) + { + overlay float overdraw; + + // calculate overdraw + overdraw = gas_volume_need[index_last_gas] - gas_volume_avail[index_last_gas]; + + // transfer overdraw + gas_volume_need[index_last_gas] -= overdraw; + gas_volume_need[index_curr_gas] += overdraw; + + // tag last gas as fully used up + deco_gas_type[index_last_gas] |= GAS_FULLY_USED_UP; + } + + // if there are peer tanks with the current gas (i.e. other tanks that have the same + // change depth), check if there is at least one tank that is not yet fully used up + if( peer_tank[index_curr_gas] ) { - // YES - add the same demand to the overall demand on the last gas - gas_volume_need[sim_gas_last_num-1] += gas_needs_volume_due; + // scan all tanks + for( i = 0; i < NUM_GAS; i++ ) + { + // check if + // - tank is a peer tank + // - tank is currently neither staged nor lost + // - tank is not fully used up yet + if( (peer_tank[index_curr_gas] & (1 << i) ) ) + if( !(deco_gas_type[i] & GAS_AVAIL_MASK ) ) + if( !(deco_gas_type[i] & GAS_FULLY_USED_UP) ) + { + // found a peer tank that is available and not fully used up yet + all_peer_tanks_used_up = 0; + } + } + } + + // select which threshold is sensible to check for + if ( deco_gas_type[index_curr_gas] & GAS_FULLY_USED_UP ) + { + // already found as fully used up, nothing to do any more } - } // gas switching needs - } // gas switch - - // shall calculate gas needs and - // last (or still current) gas is 1-5 ? - if( main_status & CALC_VOLUME ) - if( sim_gas_last_num ) + else if( deco_gas_type[index_curr_gas] & GAS_NEARLY_USED_UP ) + { + // check for fully used up threshold + if( gas_volume_need[index_curr_gas] >= gas_volume_avail[index_curr_gas] ) + { + // tag the gas as fully used up + deco_gas_type[index_curr_gas] |= GAS_FULLY_USED_UP; + + // set warning if all peer tanks are fully used up, too + if( all_peer_tanks_used_up ) deco_gas_type[index_curr_gas] |= GAS_NEED_WARNING; + } + } + else + { + // check for nearly used up threshold + if( gas_volume_need[index_curr_gas] >= gas_volume_atten[index_curr_gas] ) + { + // tag the gas as nearly used up + deco_gas_type[index_curr_gas] |= GAS_NEARLY_USED_UP; + + // set attention if all peer tanks are already fully used up + if( all_peer_tanks_used_up ) deco_gas_type[index_curr_gas] |= GAS_NEED_ATTENTION; + } + } + } +#endif // _gas_contingency + + } // gas needs + + // update the total stops time + if( doing_deco_stop ) { - // YES - compute distance traveled - gas_needs_depth = char_depth_last - char_depth_sim; - - // at least some positive distance traveled? - if( gas_needs_depth > 1 ) - { - // YES - set depth to average depth along the distance - gas_needs_depth += 1; - gas_needs_depth /= 2; - gas_needs_depth += char_depth_sim; - - // ascent time is 1 minute - gas_needs_time = 1; - - // calculate gas demand - calc_due_by_depth_time_sac(); - - // add to overall demand - gas_volume_need[sim_gas_last_num-1] += gas_needs_volume_due; - } - } // gas travel needs - - } // stop / no stop - - // --- one or more minutes have passed by now --- - - // update the ascent time - ascent_time += tissue_increment; + // total stops time is counted in full minutes, add 1 minute + TST_time += 1; + } + + // update the total ascent / cave return time + if( tissue_increment ) + { + // total time to surface is counted in 1/10 minutes, add 1 minute + TTS_time += 10 * tissue_increment; + } + else + { + // total time to surface is counted in 1/10 minutes, add 1/10 minute + TTS_time += 1; + } + + } // overlay + + // calculate absolute pressure at the current depth + sim_pres_respiration = (float)char_depth_sim * METER_TO_BAR + pres_surface; // compute current ppO2, ppN2 and ppHe calc_alveolar_pressures(); @@ -2894,11 +3084,13 @@ // update the tissues calc_tissues(); - // update the CNS value + // update the CNS calc_CNS(); - // finish stops calculation if the surface is reached - if( char_depth_sim == 0 ) next_planning_phase = PHASE_80_RESULTS; + // finish stops calculation if the surface is reached or + // if the deco table is full / calculations took too long + if( (char_depth_sim == 0) || (deco_warnings & DECO_WARNING_INCOMPLETE) ) + next_planning_phase = PHASE_80_RESULTS; break; @@ -2911,17 +3103,22 @@ // convert the CNS value to integer convert_sim_CNS_for_display(); + // normal or alternative plan? if( deco_status & CALC_NORM ) { - // export the integer CNS value + // normal plan - export the integer CNS value int_O_CNS_norm = int_sim_CNS_fraction; } else { - // export the integer CNS value + // alternative plan - export the integer CNS value int_O_CNS_alt = int_sim_CNS_fraction; } + // limit total time to surface to display max. and rescale to full minutes + if( TTS_time < 9995 ) TTS_time = (TTS_time + 5) / 10; + else TTS_time = 999 | INT_FLAG_INVALID; + // The next calculation phase will // - publish the stops table if in normal plan mode, // - proceed with remaining results dependent on if within NDL, or @@ -2946,20 +3143,16 @@ // the deco obligation will vanish during the ascent, create an // artificial stop to signal that expedite surfacing ("popping // up") is not allowed anymore. - if( char_O_deco_depth[0] == 0 ) // simulated ascent reveals no required stops - if( int_O_ceiling > 0 ) // real tissues have a ceiling + if( char_O_deco_depth[0] == 0 ) // simulated ascent reveals no required stops + if( int_O_ceiling > 0 ) // real tissues have a ceiling { // set a pro forma stop at the configured last stop depth - char_O_deco_depth[0] = char_I_depth_last_deco; + char_O_deco_depth[0] = char_I_last_stop_depth; // set a stop time of 0 minutes, this will be displayed as "..'" char_O_deco_time[0] = 0; } - // update deco info vector - if( char_O_deco_depth[0] ) deco_info |= DECO_STOPS; // set flag for deco stops found - else deco_info &= ~DECO_STOPS; // clear flag for deco stops found - // The next calculation phase will publish the main results dependent on being // - within NDL, // - in deco. @@ -2974,29 +3167,31 @@ // case PHASE_82_RESULTS_NDL: - // results to publish depend on normal or alternative plan + // normal or alternative plan? if( deco_status & CALC_NORM ) { - // output the NDL time - char_O_NDL_norm = NDL_time; - - // clear the normal ascent time - int_O_TTS_norm = 0; + // normal plan - output the NDL and TTS time + int_O_NDL_norm = NDL_time; + int_O_TTS_norm = TTS_time; + + // clear the stops time + int_O_TST_norm = 0; } else { - // output the NDL time - char_O_NDL_alt = NDL_time; - - // clear the alternative ascent time - int_O_TTS_alt = 0; + // alternative plan - output the NDL time + int_O_NDL_alt = NDL_time; + int_O_TTS_alt = TTS_time; + + // clear the alternative TTS and stops time + int_O_TST_alt = 0 + INT_FLAG_ZERO; } // The next calculation phase will - // - finish the calculation cycle if no gas needs calculation configured, else - // - calculate the gas needs pressures - if ( !(main_status & CALC_VOLUME ) ) next_planning_phase = PHASE_90_FINISH; - else next_planning_phase = PHASE_84_GAS_NEEDS_PRESSURES; + // - convert the gas needs from volume to pressure if gas needs calculation is configured + // - else finish the calculation cycle + if ( main_status & CALC_VOLUME ) next_planning_phase = PHASE_84_GAS_NEEDS_PRESSURES; + else next_planning_phase = PHASE_90_FINISH; break; @@ -3006,35 +3201,35 @@ // case PHASE_83_RESULTS_DECO: - // limit ascent time to display max. - if( ascent_time > 999) ascent_time = 999; - - // tag ascent time as invalid if there is an overflow in the stops table - if( deco_warnings & DECO_WARNING_INCOMPLETE ) ascent_time |= INT_FLAG_INVALID; - - // results to publish depend on normal or alternative plan + // limit total stops time to display max. + if( TST_time > 999 ) TST_time = 999 | INT_FLAG_INVALID; + + + // normal or alternative plan? if( deco_status & CALC_NORM ) { - // clear the normal NDL time - char_O_NDL_norm = 0; - - // export the ascent time - int_O_TTS_norm = ascent_time; + // normal plan - clear the normal NDL time + int_O_NDL_norm = 0; + + // export the TTS and total stops time + int_O_TTS_norm = TTS_time; + int_O_TST_norm = TST_time; } else { - // clear the alternative NDL time - char_O_NDL_alt = 0; - - // export the ascent time - int_O_TTS_alt = ascent_time; + // alternative plan - clear the alternative NDL time + int_O_NDL_alt = 0; + + // export the TTS and total stops time + int_O_TTS_alt = TTS_time; + int_O_TST_alt = TST_time; } // The next calculation phase will - // - finish the calculation cycle if no gas needs calculation configured, else - // - calculate the gas needs along the ascent - if ( !(main_status & CALC_VOLUME ) ) next_planning_phase = PHASE_90_FINISH; - else next_planning_phase = PHASE_84_GAS_NEEDS_PRESSURES; + // - convert the gas needs from volume to pressure if gas needs calculation is configured + // - else finish the calculation cycle + if ( main_status & CALC_VOLUME ) next_planning_phase = PHASE_84_GAS_NEEDS_PRESSURES; + else next_planning_phase = PHASE_90_FINISH; break; @@ -3046,26 +3241,59 @@ // convert required volume of the gas pointed to by gas_needs_gas_index // into the respective pressure and set the flags - convert_gas_needs_to_press(); + convert_volume_to_pressure(); // increment index to address next gas gas_needs_gas_index++; // if all gases have been converted, advance to next calculation phase +#ifdef _cave_mode + if( gas_needs_gas_index == NUM_GAS ) next_planning_phase = PHASE_85_GAS_NEEDS_CAVE; +#else if( gas_needs_gas_index == NUM_GAS ) next_planning_phase = PHASE_90_FINISH; +#endif break; +#ifdef _cave_mode + // + //--- Results - tag Gas Needs as Cave or Open Water Mode ---------------------------------- + // + case PHASE_85_GAS_NEEDS_CAVE: + + // in cave mode? + if( main_status & CAVE_MODE ) + { + // YES - tag gas needs as calculated in cave mode (return along recorded depth profile) + deco_info |= GAS_NEEDS_CAVE; + } + else + { + // NO - tag gas needs as calculated in open water mode (vertical ascent) + deco_info &= ~GAS_NEEDS_CAVE; + } + + // advance to next calculation phase + next_planning_phase = PHASE_90_FINISH; + + break; +#endif + + // //--- finish Calculation Cycle ------------------------------------------------------------ // case PHASE_90_FINISH: // Check if deco obligation is steady state or decreasing. - // This works only when an alternative plan is enabled and if it is not a bailout plan, - // thus BAILOUT_MODE must not be set while doing the alternative plan. - if( (deco_status & CALC_ALT) && !(deco_status & BAILOUT_MODE) ) + // Update the result only: + // - if an alternative plan is enabled, and + // - if a valid alternative plan TTS exists, and + // - if it is not a bailout plan + if( (deco_status & CALC_ALT ) ) + if( (int_O_TTS_alt & INT_FLAG_INVALID) ) + if( !(deco_status & BAILOUT_MODE ) ) { if( int_O_TTS_alt < int_O_TTS_norm ) deco_info |= DECO_ZONE; if( int_O_TTS_alt > int_O_TTS_norm ) deco_info &= ~DECO_ZONE; @@ -3215,7 +3443,7 @@ // // INPUT: ppN2 partial pressure of inspired N2 // ppHe partial pressure of inspired He -// tissue_increment integration time and tissue selector (real or simulated) +// tissue_increment tissue selector (real or simulated) and interval time // // MODIFIED: real_pres_tissue_N2[] tissue N2 pressures (in real tissues context) // real_pres_tissue_He[] tissue He pressures (in real tissues context) @@ -3242,12 +3470,12 @@ for( ci=0; ci < NUM_COMP; ci++ ) // iterate through all compartments { - i = tissue_increment & TIME_MASK; // extract number of minutes to do (if i > 0) - // or if one 2 second period is to do (if i = 0) - - if( i == 0 ) // check if we shall do one 2-seconds period + i = tissue_increment & TIME_MASK; // i > 0: do a number of i full minutes + // I = 0: do 2 (real tissues) or 6 (simulated tissues) seconds + + if( i == 0 ) // check if we shall do one 2 or 6 seconds interval { - read_Buhlmann_times(0); // YES - program coefficients for a 2 seconds period + read_Buhlmann_times(0); // YES - program coefficients for a 2 or 6 seconds period period = 1; // - set period length (in cycles) i = 1; // - and one cycle to do } @@ -3436,8 +3664,8 @@ // static void calc_limit(PARAMETER float GF_parameter) { - overlay float pres_respiration_min_total = 0.0; - overlay unsigned char surface_mode = 0; // 0: off, 1: on + overlay float pres_ambient_min_overall = 0.0; + overlay unsigned char surface_mode = 0; // 0: off, 1: on // check mode @@ -3466,7 +3694,7 @@ // loop over all tissues for( ci = 0; ci < NUM_COMP; ci++ ) { - overlay float pres_respiration_min_tissue; + overlay float pres_ambient_min_tissue; // get the coefficients for tissue ci @@ -3569,29 +3797,32 @@ } // real tissues // calculate the minimum ambient pressure that the tissue can withstand - if( char_I_deco_model == 0 ) + if( char_I_model == 0 ) { // straight Buhlmann - pres_respiration_min_tissue = (pres_tissue - var_a) * var_b; + pres_ambient_min_tissue = (pres_tissue - var_a) * var_b; } else { // Buhlmann with Eric Baker's varying gradient factor correction // note: this equation [1] is the inverse of equation [2] - pres_respiration_min_tissue = ( pres_tissue - (var_a * GF_parameter) ) - / ( 1.0 - GF_parameter + (GF_parameter / var_b ) ); + pres_ambient_min_tissue = ( pres_tissue - (var_a * GF_parameter) ) + / ( 1.0 - GF_parameter + (GF_parameter / var_b ) ); } // check if this tissue requires a higher ambient pressure than was found to be needed up to now - if( pres_respiration_min_tissue > pres_respiration_min_total ) + if( pres_ambient_min_tissue > pres_ambient_min_overall ) { - pres_respiration_min_total = pres_respiration_min_tissue; - lead_tissue = ci; + pres_ambient_min_overall = pres_ambient_min_tissue; + lead_tissue = ci; } } // for - // compute ceiling for the real tissues in bar relative pressure - ceiling = pres_respiration_min_total - pres_surface; + // compute ceiling in bar relative pressure + ceiling = pres_ambient_min_overall - pres_surface; + + // limit ceiling to positive values + if( ceiling < 0.0 ) ceiling = 0.0; #ifdef _helium // IBCD is checked for real tissues only @@ -3681,7 +3912,7 @@ // compute the maximum tissue pressure allowed to be exposed to an // ambient pressure equaling the surface pressure - if( char_I_deco_model != 0 ) + if( char_I_model != 0 ) { // GF model enabled, this equation [2] is the inverse of equation [1] pres_limit = (1.0 - GF_high + GF_high / var_b) * pres_surface + GF_high * var_a; @@ -3859,7 +4090,7 @@ // internal_deco_time [] time (in minutes) of each stop // internal_deco_gas [] gas used (index 1-5) at each stop // -static unsigned char update_deco_table(PARAMETER unsigned char time_increment) +static void update_deco_table(PARAMETER unsigned char time_increment) { assert( char_depth_sim > 0 ); // no stop at surface @@ -3873,9 +4104,11 @@ if( internal_deco_time[stop_index] < (100 - time_increment) ) { // YES - time increment fits into current stop entry, - // increment stop time and return with status 'success' + // increment stop time internal_deco_time[stop_index] += time_increment; - return 1; + + // done + return; } else { @@ -3885,9 +4118,11 @@ // running deco calculation. Too many chained entries? if( ++chained_stops >= STOP_CHAINING_LIMIT ) { - // YES - set overflow warning and return with status 'failed' + // YES - set warning that calculations took too long deco_warnings |= DECO_WARNING_INCOMPLETE; - return 0; + + // done + return; } } } @@ -3909,18 +4144,24 @@ } else { - // YES - set overflow warning and return with status 'failed' - deco_warnings |= DECO_WARNING_INCOMPLETE; - return 0; + // YES - if run in deco calculator mode, set a warning that there is an overflow in the stops table + if( main_status & CALCULATE_BOTTOM ) deco_warnings |= DECO_WARNING_INCOMPLETE; + + // limit runtime via reached TTS (scaled in 1/10 minutes here) + if( TTS_time > 9999 ) deco_warnings |= DECO_WARNING_INCOMPLETE; + + // done + return; } } - // initial use of a new (or the very first) stop entry, - // store all stop data and return with status 'success' + // initial use of a new (or the very first) stop entry, store all stop data internal_deco_time [stop_index] = time_increment; internal_deco_depth[stop_index] = char_depth_sim; internal_deco_gas [stop_index] = sim_gas_current_num; - return 1; + + // done + return; } @@ -4133,7 +4374,7 @@ // adjust target pressure by GF-high in case the GF model is in use, but not // for the no-fly time as it's target pressure is hard to reach anyhow - if( char_I_deco_model && char_I_altitude_wait ) + if( char_I_model && char_I_altitude_wait ) pres_tissue_max = P_ambient_altitude + 0.01 * char_I_GF_High_percentage * (pres_tissue_max - P_ambient_altitude); @@ -4331,9 +4572,10 @@ // // Modified: tissue pressures N2 and He pressures of the tissues // CNS_fraction_real current real CNS value -// ceiling minimum allowed depth in mbar relative pressure +// ceiling minimum allowed depth in bar relative pressure // lead_supersat supersaturation of the leading tissue (float) // int_O_lead_supersat supersaturation of the leading tissue (integer) +// char_O_lead_tissue number of the leading tissue // static void calc_interval(PARAMETER unsigned char time_interval) { @@ -4348,6 +4590,9 @@ if( int_I_pres_surface < 500) pres_surface = 0.500; else pres_surface = 0.001 * int_I_pres_surface; + // safeguard time interval + if( time_interval > 254 ) time_interval = 254; + // set breathed pressure to surface pressure real_pres_respiration = pres_surface; @@ -4426,7 +4671,7 @@ // calc_CNS // // Input: char_ppO2 current ppO2 [in 0.1 bars] -// tissue_increment time increment and tissue selector +// tissue_increment tissue selector and time interval // // Modified: CNS_fraction_real accumulated CNS (real tissue context) // CNS_fraction_sim accumulated CNS (simulated tissue context) @@ -4436,13 +4681,13 @@ overlay float CNS_fraction_inc; // increment of CNS load, 0.01 = 1% - // calculate CNS increment for 2 seconds interval + // calculate CNS increment for a 2 seconds interval if( char_ppO2 > 160 ) { // step-wise CNS increment - // calculate index for increment look-up - cns_i = (char_ppO2 - 161) / 5; // integer division + // calculate index for increment look-up (uses integer division) + cns_i = (char_ppO2 - 161) / 5; // indexes > 17 use increment of index 17 if( cns_i > 17 ) cns_i = 17; @@ -4451,31 +4696,32 @@ read_CNS_c_coefficient(); // re-scale coefficient from storage format in [1/100000] to productive value - CNS_fraction_inc = (float)var_cns_c / 100000.0; + CNS_fraction_inc = (float)var_cns_value / 100000.0; } else if( char_ppO2 > 50 ) { // range wise CNS increment approximation - // calculate index for approximation coefficients look-up - cns_i = (char_ppO2 - 51) / 10; // integer division + // calculate index for approximation coefficients look-up (uses integer division) + cns_i = (char_ppO2 - 51) / 10; // read coefficients read_CNS_ab_coefficient(); // calculate the CNS increment - CNS_fraction_inc = 1.0 / (var_cns_a * char_ppO2 + var_cns_b ); + CNS_fraction_inc = 1.0 / (var_cns_gain * char_ppO2 + var_cns_offset ); } else - { // no increment up to 0.5 bar ppO2 + { // no increment for a ppO2 of up to 0.5 bar CNS_fraction_inc = 0.0; } - // apply a time factor in case of minute-based interval (factor = N * 30.0) - if( tissue_increment & TIME_MASK ) - { - CNS_fraction_inc *= (float)(tissue_increment & TIME_MASK) * 30.0; - } + // apply a time factor in case of: + // - simulated tissues and interval = 0 (i.e. 6 seconds to do) -> factor 3 + // - any tissues and interval > 0 (i.e. i minutes to do) -> factor 30 * i + if( tissue_increment == 0 ) CNS_fraction_inc *= 3.0; + else if( tissue_increment & TIME_MASK ) CNS_fraction_inc *= 30.0 * (float)(tissue_increment & TIME_MASK); + // update the CNS accumulator if ( tissue_increment & TISSUE_SELECTOR ) CNS_fraction_real += CNS_fraction_inc; // real tissues @@ -4484,24 +4730,32 @@ ////////////////////////////////////////////////////////////////////////////// -// calc_due_by_depth_time_sac (Helper Function saving Code Space) +// calc_required_volume // // Calculates the gas volume required for a given depth, time and usage (SAC) // rate. It uses a fixed surface pressure of 1.0 bar to deliver stable results // when used through the deco calculator. // // Input: gas_needs_depth depth in meters -// gas_needs_time time in minutes +// gas_needs_time time in minutes (0 encodes 1/10 minute) // gas_needs_usage_rate gas usage in liters per minute at surface pressure // // Output: gas_needs_volume_due required gas volume in liters // -static void calc_due_by_depth_time_sac(void) +static void calc_required_volume(void) { - gas_needs_volume_due = ((float)gas_needs_depth * METER_TO_BAR + pres_surface) * gas_needs_time * gas_needs_usage_rate; + // calculate volume for 1 minute + gas_needs_volume_due = ((float)gas_needs_depth * METER_TO_BAR + pres_surface) * gas_needs_usage_rate; + + // multiply 1-minute-volume with time factor if time factor <> 1 + if ( gas_needs_time == 0 ) gas_needs_volume_due *= 0.1; // 1/10 minute + else if ( gas_needs_time > 1 ) gas_needs_volume_due *= gas_needs_time; // multiple minutes } + +#ifdef _rx_functions + ////////////////////////////////////////////////////////////////////////////// // calc_TR_functions // @@ -4511,7 +4765,6 @@ // // Output: todo // -#ifdef _rx_functions static void calc_TR_functions(void) { // pressure warnings for reading 1, but only if enabled and pressure value available @@ -4663,8 +4916,7 @@ // strip flags int_O_tank_pressure &= 0x0FFF; - // TODO: decide if log shall be in 0.1 bar of full bar only - // scale to full bar only + // scale to recording format of full bar only int_O_tank_pressure /= 10; @@ -4734,11 +4986,115 @@ } } } -#endif +#endif // _rx_functions + + +#ifdef _cave_mode + +////////////////////////////////////////////////////////////////////////////// +// read_backtrack_data +// +// Gets the data of the next backtracking data set +// +// Modified: backtrack_index last current position in backtracking storage +// +// Output: backtrack_step_counter number of 1/10 min calculation steps to do on next target depth +// backtrack_target_depth next target depth +// +static void read_backtrack_data(void) +{ + overlay unsigned char firstround = 1; + + + // load the step counter with the default of ten 1/10 minute steps to go between two depth samples + backtrack_step_counter = 10; + + // repeat until having read the whole data set or having reached the first storage position + while(backtrack_index) + { + // backtracking data recording format: + // --------------------------------------------------------------------------- + // 0ddddddd -> datum is a depth recording with d = 0..127 depth in meters + // 10tttttt -> delta time with t = 0.. 59 time in seconds + // 110ggggg -> gas staging status with g = 1 gas staged + // 111nnnnn -> waypoint marker with n = 1.. 31 waypoint number + // + // gas availability vector: LSB : gas 1 (and so on) + // waypoint marker : 1 - 30: user waypoints, last waypoint is turn point + // : 31: spare for turn point if all 30 waypoints used up + // : 0: reserved + + // read recording entry and then advance (backward direction!) the reading index + overlay unsigned char datum = char_I_backtrack_storage[backtrack_index--]; + + // is it a target depth? + if( datum < 128 ) + { + // YES - assign the target depth + backtrack_target_depth = datum; + + // a depth entry closes a data set, so done with this data set + return; + } + + // is it a delta time entry? + else if( datum < 128 + 64 ) + { + // YES - When a delta time entry is contained in a data set, the time + // stored in the delta time entry is the time that has elapsed + // between storage of the depth that is part of this data set + // and the previous depth recording. + // This entry is stored whenever the delta time is shorter than + // the default 1 minute depth sampling interval time. + + // assign the delta time to the step counter: remove entry tag and + // convert datum from 0..59 seconds to 0..9 steps by integer division + backtrack_step_counter = (datum - 128) / 6; + } + + // is it a gas staging status entry? + else if( datum < 128 + 64 + 32 ) + { + // YES - extract gas staging status + overlay unsigned char gas_status = datum - (128 + 64); + + // decode and update gas staging status + // + // bit set : gas is staged (not available) during next section of backtracking, + // bit cleared: gas is not staged ( available) during next section of backtracking. + // + // bit 0 corresponds to gas 1, ..., bit 4 corresponds to gas 5 + // + for( i = 0; i < NUM_GAS; i++ ) + { + if ( gas_status & (1 << i) ) deco_gas_type[i] |= GAS_AVAIL_STAGED; // staged + else deco_gas_type[i] &= ~GAS_AVAIL_STAGED; // not staged + } + } + + // must be waypoint marker entry then + else + { + // set step counter to zero if first entry read is a waypoint entry + if( firstround ) backtrack_step_counter = 0; + } + + // first round done + firstround = 0; + + } // while + + // first storage position reached, by convention it contains + // the final target depth which is zero meters aka the surface + backtrack_target_depth = 0; +} + +#endif // _cave_mode + ////////////////////////////////////////////////////////////////////////////// -// convert_gas_needs_to_press +// convert_volume_to_pressure // // Converts gas volumes into pressures and sets respective flags // @@ -4748,13 +5104,12 @@ // char_I_gas_avail_size[] size of the tanks in liters // char_I_pressure_gas[] gas configured on reading 1/2 (TR only) // -// Output: int_O_gas_need_vol[] required gas amount in liters, including flags -// int_O_gas_need_pres[] required gas amount in bar, including flags +// Output: int_O_gas_need_vol[] required gas amount in liters +// int_O_gas_need_pres[] required gas amount in bar, including flags // int_O_pressure_need[] required gas amount for reading 1/2 (TR only) // -static void convert_gas_needs_to_press(void) +static void convert_volume_to_pressure(void) { - // just to make the code more readable... i = gas_needs_gas_index; @@ -4765,29 +5120,55 @@ } else { - overlay unsigned short int_pres_warn; - overlay unsigned short int_pres_attn; - - // set warning and attention thresholds - int_pres_warn = 10.0 * (unsigned short)char_I_gas_avail_pres[i]; - int_pres_attn = GAS_NEEDS_LIMIT_ATTENTION * int_pres_warn; - - // convert ascent gas volume need from float to integer [in liter] - int_O_gas_need_vol[i] = (unsigned short)gas_volume_need[i]; + // convert gas volume need from float to integer [in liter] + int_O_gas_need_vol[i] = (unsigned short)( gas_volume_need[i] + 0.5 ); // compute how much pressure in the tank will be needed [in bar] - int_O_gas_need_pres[i] = (unsigned short)( gas_volume_need[i] / char_I_gas_avail_size[i] + 0.999 ); + int_O_gas_need_pres[i] = (unsigned short)( gas_volume_need[i] / char_I_gas_avail_size[i] + 0.5 ); // limit result to 999 bar because of display constraints if( int_O_gas_need_pres[i] > 999 ) int_O_gas_need_pres[i] = 999 | INT_FLAG_HIGH; - // set flags for fast evaluation by dive mode - if ( int_O_gas_need_pres[i] == 0 ) int_O_gas_need_pres[i] |= INT_FLAG_ZERO; - else if ( int_O_gas_need_pres[i] >= int_pres_warn ) int_O_gas_need_pres[i] |= INT_FLAG_WARNING; - else if ( int_O_gas_need_pres[i] >= int_pres_attn ) int_O_gas_need_pres[i] |= INT_FLAG_ATTENTION; + // set flags for fast evaluation by output routine + if( int_O_gas_need_pres[i] == 0 ) int_O_gas_need_pres[i] |= INT_FLAG_ZERO; + else +#ifdef _gas_contingency + if( main_status & GAS_CONTINGENCY ) + { + // take warning and attention computed en-route + if ( deco_gas_type[i] & GAS_NEED_WARNING ) + { + // tag the tank with a warning + int_O_gas_need_pres[i] |= INT_FLAG_WARNING; + + // tag the peer tanks with a warning, too + for( j = 0; j < NUM_GAS; j++ ) + { + if( peer_tank[i] & (1 << j) ) int_O_gas_need_pres[j] |= INT_FLAG_WARNING; + } + } + else if( deco_gas_type[i] & GAS_NEED_ATTENTION ) + { + // tag the tank with an attention + int_O_gas_need_pres[i] |= INT_FLAG_ATTENTION; + + // tag the peer tanks with an attention, too + for( j = 0; j < NUM_GAS; j++ ) + { + if( peer_tank[i] & (1 << j) ) int_O_gas_need_pres[j] |= INT_FLAG_ATTENTION; + } + } + } + else +#endif + { + // compute warning and attention now + if ( gas_volume_need[i] >= gas_volume_avail[i] ) int_O_gas_need_pres[i] |= INT_FLAG_WARNING; + else if( gas_volume_need[i] >= gas_volume_atten[i] ) int_O_gas_need_pres[i] |= INT_FLAG_ATTENTION; + } } - // set invalid flag if there is an overflow in the stops table + // set invalid flag if there is an overflow in the stops table or calculation took too long if( deco_warnings & DECO_WARNING_INCOMPLETE ) int_O_gas_need_pres[i] |= INT_FLAG_INVALID; #ifdef _rx_functions @@ -4795,7 +5176,7 @@ if( main_status & TR_FUNCTIONS ) { // char_I_pressure_gas[] uses gas indexes from 1-10, loop variable i runs from 0 to 4 - overlay unsigned char j = i+1; + j = i+1; // check if the current gas is configured on pressure reading 1 or 2 if( (char_I_pressure_gas[0] == j) || (char_I_pressure_gas[1] == j) ) @@ -4809,7 +5190,7 @@ // limit to 400 bar and multiply by 10 to get required pressure in 0.1 bar int_pres_need = (int_pres_need > 400) ? 4000 | INT_FLAG_OUT_OF_RANGE : 10 * int_pres_need; - // tag as not available if there is an overflow in the stops table + // tag as not available if there is an overflow in the stops table or calculation took too long if( deco_warnings & DECO_WARNING_INCOMPLETE ) int_pres_need |= INT_FLAG_NOT_AVAIL; // copy to reading data (in both readings the same gas could be configured) @@ -4857,7 +5238,7 @@ if ( int_sim_CNS_fraction >= CNS_LIMIT_WARNING ) int_sim_CNS_fraction |= INT_FLAG_WARNING; else if ( int_sim_CNS_fraction >= CNS_LIMIT_ATTENTION ) int_sim_CNS_fraction |= INT_FLAG_ATTENTION; - // set invalid flag if there is an overflow in the stops table + // set invalid flag if there is an overflow in the stops table or calculation took too long if ( deco_warnings & DECO_WARNING_INCOMPLETE ) int_sim_CNS_fraction |= INT_FLAG_INVALID; } @@ -4888,8 +5269,8 @@ int_O_lead_supersat |= INT_FLAG_WARNING; // make GF factor shown in red deco_warnings |= DECO_WARNING_OUTSIDE; // make depth shown in red } - else if( (char_I_deco_model != 0) && (int_O_lead_supersat > char_I_GF_High_percentage) - || (char_I_deco_model == 0) && (int_O_lead_supersat > 99 ) ) + else if( (char_I_model != 0) && (int_O_lead_supersat > char_I_GF_High_percentage) + || (char_I_model == 0) && (int_O_lead_supersat > 99 ) ) { int_O_lead_supersat |= INT_FLAG_ATTENTION; // make GF factor shown in yellow deco_warnings |= DECO_ATTENTION_OUTSIDE; // make depth shown in yellow