Mercurial > public > hwos_code
diff src/p2_deco.c @ 623:c40025d8e750
3.03 beta released
author | heinrichsweikamp |
---|---|
date | Mon, 03 Jun 2019 14:01:48 +0200 |
parents | 7b3903536213 |
children | cd58f7fc86db |
line wrap: on
line diff
--- a/src/p2_deco.c Wed Apr 10 10:51:07 2019 +0200 +++ b/src/p2_deco.c Mon Jun 03 14:01:48 2019 +0200 @@ -1,5 +1,5 @@ // *************************************************************************** -// p2_deco.c REFACTORED VERSION V2.99f +// p2_deco.c combined next generation V3.03.4 // // Created on: 12.05.2009 // Author: heinrichs weikamp, contributions by Ralph Lembcke and others @@ -12,42 +12,42 @@ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or +// the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. +// along with this program. If not, see <http://www.gnu.org/licenses/>. // ////////////////////////////////////////////////////////////////////////////// -// history: +// History: // 01/03/08 v100: first release candidate // 03/13/08 v101: start of programming ppO2 code // 03/13/25 v101a: backup of interim version with ppO2 calculation // 03/13/25 v101: open circuit gas change during deco -// 03/13/25 v101: CNS_fraction calculation +// 03/13/25 v101: CNS_fraction_real calculation // 03/13/26 v101: optimization of tissue calc routines -// 07/xx/08 v102a: debug of bottom time routine -// 09/xx/08 v102d: Gradient Factor Model implementation -// 10/10/08 v104: renamed to build v103 for v118 stable -// 10/14/08 v104: integration of char_I_depth_last_deco for Gradient Model -// 03/31/09 v107: integration of FONT Incon24 -// 05/23/10 v109: 5 gas changes & 1 min timer -// 07/13/10 v110: cns vault added -// 12/25/10 v110: split in three files (deco.c, main.c, definitions.h) +// 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 +// 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 +// 12/25/2010 v110: split in three files (deco.c, main.c, definitions.h) // 2011/01/20: [jDG] Create a common file included in ASM and C code. -// 2011/01/24: [jDG] Make ascenttime an short. No more overflow! +// 2011/01/24: [jDG] Make ascent time an short. No more overflow! // 2011/01/25: [jDG] Fusion deco array for both models. // 2011/01/25: [jDG] Use CF(54) to reverse deco order. // 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 low_depth in 32bits (w/o rounding), for a better stability. +// 2011/04/15: [jDG] Store GF_low_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). @@ -58,8 +58,8 @@ // 2012/02/24: [jDG] Remove missed stop bug. // 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 gas_volumes accuracy (average depth, switch between stop). -// 2013/03/05: [jDG] Should vault low_depth too. +// 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] 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 @@ -87,10 +87,12 @@ // // ********************************************************************************************************************************* + #include <math.h> #include "p2_definitions.h" #define TEST_MAIN #include "shared_definitions.h" +#include "configuration.inc" // ********************************************************************************************************************************* @@ -99,10 +101,11 @@ // // ********************************************************************************************************************************* -// conditional compiles -#define _rx_functions // if defined, compile transmitter functions (default: included *) -//#define _cave_mode // if defined, compile cave mode into firmware (default: not included *) ## OPTION IS UNDER CONSTRUCTION ## - // * option needs to be included / excluded in hwos.inc, too! + +// deco engine scheduling +#define INVOKES_PER_SECOND 2 // number of invocations of the deco engine per second (use powers of 2 only: 1, 2, 4, ...) +#define BUDGET_PER_SECOND 640 // [ms] total time budget per second for the deco engine, each invocation will preempt after BUDGET_PER_SECOND / INVOKES_PER_SECOND + // ambient pressure at different mountain heights #define P_ambient_1000m 0.880 // [bar] based on 990 hPa and 20°C at sea level, 15°C at altitude @@ -122,7 +125,6 @@ #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_WARNING_THRESHOLD 100 // threshold for CNS warning #define CNS_ATTENTION_THRESHOLD 70 // threshold for CNS attention @@ -131,39 +133,47 @@ #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 O2_CONSUMPTION_LIMIT_ATTENTION 20 // threshold for O2 "SAC" attention: 2.0 l/min - -// deco engine states and modes - char_O_main_status: controls current tissue and deco status calculation (as-is situation) -#define DECO_COMPLETED_NORM 0x01 // the calculation of a normal deco plan has just been completed -#define DECO_COMPLETED_ALT 0x02 // the calculation of an alternative deco plan has just been completed -//#define DECO_MODE_MASK 0x0C // mask for mode selection ==> current diving mode -//#define DECO_MODE_LOOP 0x04 // see below -//#define DECO_MODE_CCR 0x04 // see below -//#define DECO_MODE_PSCR 0x08 // see below - -#define DECO_USE_Z_FACTOR 0x10 // =1: figure in Z factor when converting gas volumes <-> pressures -#define DECO_CAVE_MODE 0x20 // =1: activate ascent gas needs calculation under cave constraints -#define DECO_BOTTOM_CALCULATE 0x40 // =1: switch to deco calculator interface -#define DECO_TR_FUNCTIONS 0x80 // =1: activate TR functions (pressure reading) processing - -// deco engine states and modes - char_O_deco_status: controls deco plan calculation (to-be scenario) -#define DECO_STATUS_MASK 0x03 // bit mask for values below -#define DECO_STATUS_START 0x00 // value commands the start of a new deco calculation cycle -#define DECO_STATUS_FINISHED 0x00 // value indicates completion of deco calculation -#define DECO_STATUS_STOPS 0x01 // value indicated calculation is ongoing, currently calculating the stops -#define DECO_STATUS_RESULTS 0x02 // value indicates calculation is ongoing, currently calculating the results -#define DECO_STATUS_INIT 0x03 // value to be set once for the first invocation at the begin of a new dive - -#define DECO_MODE_MASK 0x0C // mask for mode selection ==> diving mode during ascent -#define DECO_MODE_LOOP 0x04 // =1: CCR (DECO_MODE_PSCR needs to be cleared) or pSCR mode -#define DECO_MODE_CCR 0x04 // to be used with == operator in combination with DECO_MODE_MASK only! -#define DECO_MODE_PSCR 0x08 // =1: pSCR mode (DECO_MODE_LOOP needs to be set, too) - -#define DECO_PLAN_ALTERNATE 0x10 // =1: calculate the 2nd (alternative) deco plan -#define DECO_BAILOUT_MODE 0x20 // =1: do a bailout calculation, i.e. allow gas switches before first deco stop -#define DECO_VOLUME_CALCULATE 0x40 // =1: calculate ascent gas needs -#define DECO_ASCENT_DELAYED 0x80 // =1: calculate a delayed ascent (fTTS) - -// deco engine warnings - char_O_deco_warnings +#define ppO2_MARGIN_ON_MAX 3 // [cbar] margin on ppO2 max to compensate for surface pressures > 1.000 mbar + +// 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 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 + +#define MODE_MASK 0xC0 // mask for real tissues mode selection +#define MODE_LOOP 0x40 // =1: CCR (MODE_PSCR needs to be cleared) or pSCR mode +#define MODE_CCR 0x40 // to be used with == operator in combination with MODE_MASK only! +#define MODE_PSCR 0x80 // =1: pSCR mode (MODE_LOOP needs to be set, too) + +// deco engine states and modes - (char_O_)deco_status: controls deco plan calculation (to-be scenario) +#define PLAN_MASK 0x03 // bit mask covering normal & alternative plan flag +#define COMMAND_MASK 0x07 // bit mask covering all command flags +#define CALCULATING 0x00 // calculations are ongoing +#define START_NORM 0x01 // input: start calculation of a normal deco plan +#define CALC_NORM 0x01 // internal: calculating a normal deco plan +#define COMPLETED_NORM 0x01 // output: calculation of a normal deco plan has completed +#define START_ALT 0x02 // input: start calculation of an alternative deco plan +#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_ALT 0x06 // input: initialize deco engine and start calculation of an alternative deco plan +// 0x08 // unused - reserved for further deco engine commands + +#define BAILOUT_MODE 0x10 // =1: allow gas switches before first deco stop +#define DELAYED_ASCENT 0x20 // =1: figure in a delayed ascent (fTTS) + +// MODE_MASK 0xC0 // mask for simulated tissues mode selection +// MODE_LOOP 0x40 // =1: CCR (MODE_PSCR needs to be cleared) or pSCR mode +// MODE_CCR 0x40 // to be used with == operator in combination with MODE_MASK only! +// MODE_PSCR 0x80 // =1: pSCR mode (MODE_LOOP needs to be set, too) + + +// deco engine warnings - (char_O_)deco_warnings #define DECO_WARNING_IBCD 0x01 // IBCD occurring now #define DECO_WARNING_IBCD_lock 0x02 // IBCD has occurred during the dive #define DECO_WARNING_MBUBBLES 0x04 // micro bubbles likely to develop now @@ -173,21 +183,53 @@ #define DECO_ATTENTION_OUTSIDE 0x40 // tissue pressures are very close to the Buhlmann limit #define DECO_WARNING_STOPTABLE_OVERFLOW 0x80 // internal error: no more space in the deco stops table -// deco engine status (char_O_deco_info) +// deco engine status (char_O_)deco_info #define DECO_FLAG 0x01 // =1: deco ppO2 levels are permitted #define IND_DOUBLE_SWITCH_FLAG 0x02 // =1: switch to other tank advice active -#define DECO_STEADY 0x04 // =1: fTTS = TTS (not updated when in bailout mode) -#define DECO_DECREASING 0x08 // =1: fTTS < TTS (not updated when in bailout mode) +// 0x04 // --- unused +#define DECO_ZONE 0x08 // =1: fTTS < TTS (not updated when in bailout mode) #define DECO_CEILING 0x10 // =1: ceiling depth > 0 -#define GAS_NEEDS_CAVE 0x20 // =1: indicated gas needs are calculated in cave mode +#define DECO_STOPS 0x20 // =1: deco stops found +#define GAS_NEEDS_CAVE 0x40 // =1: indicated gas needs are calculated in cave mode +// 0x80 // --- unused + // deco engine control - tissue_increment -#define TIME_MASK 0x7F // (127 decimal, bits 0-6 set) -#define TISSUE_FLAG 0x80 // (128 decimal, bit 7 set) +#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 + + +// deco engine control - next_planning_phase +#define PHASE_00_DONE 0x00 // calculation cycle finished +#define PHASE_10_DIVE_INIT 0x10 // once-per-dive initialization of the deco engine +#define PHASE_11_CYCLIC_INIT 0x11 // once-every-cycle initialization of the deco engine +#define PHASE_20_EXTENDED_BOTTOM_TIME 0x20 // calculate extended bottom time +#define PHASE_30_NDL_TIME 0x30 // calculate NDL time +#define PHASE_40_CAVE_ASCENT 0x40 // calculate cave mode return/ascent +#define PHASE_60_DECO_ASCENT 0x60 // calculate open water deco ascent +#define PHASE_70_RESULTS 0x70 // results - initialization +#define PHASE_71_RESULTS_STOPS_TABLE 0x71 // results - publish stops table +#define PHASE_72_RESULTS_NDL 0x72 // results - publish data / within NDL +#define PHASE_73_RESULTS_DECO 0x73 // results - publish data / in deco +#define PHASE_80_GAS_NEEDS_SWITCHES 0x80 // calculate gas needs - find gas switches in NDL bailout mode +#define PHASE_81_GAS_NEEDS_ASCENT 0x81 // calculate gas needs - needs of bottom segment and ascent +#define PHASE_82_GAS_NEEDS_PRESSURES 0x82 // calculate gas needs - conversion from volumes to pressures +#define PHASE_90_FINISH 0x90 // finish calculation cycle + + +// gas needs calculation - gas_needs_next_phase +#define GAS_NEEDS_INIT 0x00 // initialization +#define GAS_NEEDS_BOTTOM_SEGMENT 0x10 // demand during bottom segment +#define GAS_NEEDS_INITIAL_ASCENT 0x20 // demand of initial ascent +#define GAS_NEEDS_STOP 0x30 // demand on a stop +#define GAS_NEEDS_INTERMEDIATE_ASCENT 0x40 // demand on ascent between two stops +#define GAS_NEEDS_FINAL_ASCENT 0x50 // demand during final ascent +#define GAS_NEEDS_DONE 0x60 // calculation finished // 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 #define INT_FLAG_ZERO 0x0800 // =1: value is zero #define INT_FLAG_LOW 0x1000 // =1: value is below a lower warning threshold #define INT_FLAG_NOT_AVAIL 0x1000 // =1: value is not available (not computed) @@ -207,19 +249,20 @@ // // ********************************************************************************************************************************* -// Functions used in surface mode +// Functions used in Surface Mode static void calc_interval(PARAMETER unsigned char time_increment); // Calculates the tissue off-gassing under surface conditions. static void calc_desaturation_time(void); // Calculates the desaturation and no-fly times. static void clear_tissue(void); // Resets all tissues to surface pressure equilibrium state. - -// Main entry point in dive mode +static void init_output_vars(void); // Initializes all deco engine output variables to defaults + +// Main entry point in Dive Mode static void calc_hauptroutine(void); // Sequences all calculations for the real tissues and the deco calculation. -// Functions dedicated to the real tissues +// Functions dedicated to the real Tissues static void calc_hauptroutine_data_input(void);// Initializes environment data and sets gas ratios for the real tissues. -// Functions combined for real tissues & deco calculations +// Functions combined for real Tissues & Deco Calculations static void calc_alveolar_pressures(void); // Computes the partial pressures from the gas ratios and many more parameters, // needs either calc_hauptroutine_data_input() be called beforehand or // gas_find_current()/gas_find_better() and gas_set_ratios(). @@ -228,41 +271,50 @@ static void calc_limit(PARAMETER float GF_current); // Calculates ceiling, current GF (supersaturation) and some more data. -// Functions dedicated to deco calculations +// Functions for TR +#ifdef _rx_functions +static void calc_TR_functions(void); // Calculates SAC etc. +#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_find_current(void); // Sets the first gas used for deco calculation, invoked at start of cycle, too. static unsigned char gas_find_better(void); // Checks for, and eventually switches to, a better gas. 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_find_current/_better). -static void calc_NDL_time(void); // Calculates remaining NDL time. -static void find_NDL_gas_changes(void); // Finds the gas changes in an OC bailout ascent that is within NDL -static void calc_ascent_to_first_stop(void); // Calculates ascent to the first deco stop. -static void calc_hauptroutine_calc_deco(void); // Calculates the subsequent ascent until reaching surface. -static unsigned char calc_nextdecodepth(void); // Calculates the depth of the next required deco stop. +static void calc_NDL_time_tissue(void); // Calculates the remaining NDL time for a given tissue. +static void find_NDL_gas_changes(void); // Finds the gas changes in an OC bailout ascent that is within NDL. +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); // Enters a new stop or extends an existing stop in the deco stops table. static void calc_ascenttime(void); // Calculates the ascent time from current depth and deco stop times. -static void gas_volumes(void); // Calculates required gas volumes and pressures from the data in stops table. - -// Functions for results reporting +static void calc_gas_needs_ascent(void); // Calculates required gas volumes and pressures from the data in 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. + +// Functions for Results Reporting static void publish_deco_table(void); // Copies the internal deco stops table to the export interface. -static void convert_CNS_for_display(void); // Converts the current CNS value from float to integer. +static void convert_cur_CNS_for_display(void); // Converts the current CNS value from float to integer. static void convert_sim_CNS_for_display(void); // Converts the end-of-dive CNS value from float to integer. -static void convert_GF_for_display(void); // Converts leading tissue supersaturation value from float to integer, 1.0 = 100%. +static void convert_sat_for_display(void); // Converts leading tissue saturation value from float to integer, 1.0 = 100%. static void convert_ceiling_for_display(void); // Converts ceiling from float to integer in mbar relative pressure. -// internal helper functions -static unsigned short tmr5(void); // Reads a hardware timer which is used for preemptive scheduling. -static void read_Buhlmann_coefficients(void); // Reads the a and b coefficients from a ROM table. +// internal helper Functions +static void load_tmr5(void); // Loads a hardware timer which is used for preemptive scheduling. +static void read_tmr5(void); // Reads a hardware timer which is used for preemptive scheduling. +static void read_CNS_ab_coefficient(void); // Reads the CNS a and b coefficients from a ROM table. +static void read_CNS_c_coefficient(void); // Reads the CNS c coefficient from a ROM table. +static void read_Buhlmann_coefficients(void); // Reads the Buhlmann a and b coefficients from a ROM table. static void read_Buhlmann_times(PARAMETER char period); // Reads pre-computed tissue increment factors from a ROM table. static void read_Buhlmann_ht(void); // Reads the half-times from a ROM table. static void adopt_Buhlmann_coefficients(void); // Computes average a and b coefficient by the N2/He tissue ratio. -static void temp_tissue_safety(void); // Applies safety margins to the tissue increments. 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_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 // ********************************************************************************************************************************* @@ -276,110 +328,15 @@ # pragma udata bank5=0x500 #endif -// general deco parameters - -static float GF_low; // initialized from deco parameters -static float GF_high; // initialized from deco parameters -static float GF_delta; // initialized from deco parameters - -static float locked_GF_step_norm; // GF_delta / low_depth_norm in normal plan -static float locked_GF_step_alt; // GF_delta / low_depth_alt in alternative plan - -static float low_depth_norm; // depth of deepest stop in normal plan -static float low_depth_alt; // depth of deepest stop 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 for tissue, CNS and gas volume calculation -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 - -static float CNS_fraction; // current CNS (1.00 = 100%) - -static unsigned short deco_tissue_vector; // 16 bit vector to memorize all tissues that are in decompression -static unsigned short IBCD_tissue_vector; // 16 bit vector to memorize all tissues that experience IBCD - -static float pres_respiration_sac; // current depth in absolute pressure, used in SAC calculation -static float float_sac; // used in SAC calculation -static unsigned int max_sac_rate; // used in SAC calculation to determine SAC rate attention - - -// simulation context: used to predict ascent - -static float sim_CNS_fraction; // CNS after predicted ascent, 0.01 = 1%, as float - -static unsigned int int_sim_CNS_fraction; // CNS after predicted ascent, 1 = 1%, as integer - -static unsigned char sim_depth_limit; // depth of next stop in meters, used in deco calculations -static unsigned char NDL_lead_tissue_norm; // used to cache the tissue to start with when calculating the NDL -static unsigned char NDL_lead_tissue_alt; // used to cache the tissue to start with when calculating the NDL - - -// result values from calculation functions - -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_number; // number of the leading tissue - -// stops table - -static unsigned char internal_deco_depth[NUM_STOPS]; // depths of the stops -static unsigned char internal_deco_time[NUM_STOPS]; // durations of the stops -static unsigned char internal_deco_gas[NUM_STOPS]; // gases used on the stops - - -// transfer variables between calc_desaturation_time() and calc_desaturation_time_helper() - -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 unsigned int int_time; // time it takes for the compartment to reach the target pressure - - -// transfer variables between gas_volumes() and gas_volumes_helper_1/_2() - -static float float_depth; // depth of the stop or half-way point -static float float_time; // duration of the stop or ascent phase -static unsigned char char_usage; // gas usage in l/min -static unsigned char gas_num; // number of the gas/tank -static float volume; // computed volume of gas -static unsigned int int_volume; // required gas volume in liter -static unsigned int int_pres_need; // required gas volume in bar - - -// auxiliary variables for data buffering - -static float N2_equilibrium; // used for N2 tissue graphics scaling -static float temp_tissue; // auxiliary variable to buffer tissue pressures -static float float_pSCR_factor; // pre-computed factor for pSCR ppO2 drop calculation -static float calc_pres_tissue_N2; // auxiliary variable to buffer tissue N2 pressure -static float calc_pres_tissue_He; // auxiliary variable to buffer tissue He pressure -static float pres_tissue; // auxiliary variable to buffer total tissue pressure - -// 10 byte free space left in this bank (4 bytes per float, 2 bytes per int/short, 1 byte per char) - - -//---- Bank 6 parameters ----------------------------------------------------- -#ifndef UNIX -# pragma udata bank6=0x600 -#endif - -// indexing and sequencing - -static unsigned char ci; // used as index to the Buhlmann tables -static unsigned char twosectimer = 0; // used for timing the tissue updating -static unsigned char tissue_increment; // selector for real/simulated tissues and time increment - - -// environmental and gas data +// Environmental and Gas Data (52 byte) static float pres_surface; // absolute pressure at the surface -static unsigned char char_bottom_depth; // bottom depth in meters, used by ascent time and gas needs calculation +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; // current simulated depth in meters, integer +static unsigned char char_depth_last; // last simulated depth in meters, integer +static unsigned char char_depth_bottom; // bottom 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 @@ -394,7 +351,153 @@ static float sim_pSCR_drop; // simulated ppO2 drop in pSCR loop -// result values from calculation functions +// general Deco Parameters (57 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 for tissue, CNS and gas volume calculation +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 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 +static float float_sac; // used in SAC calculation: SAC value in float +static unsigned short max_sac_rate; // used in SAC calculation: threshold for SAC rate attention + + +// simulated Context: used to calculate Ascent (11 byte) + +static float CNS_fraction_sim; // CNS after predicted ascent, 0.01 = 1%, as float +static unsigned short int_sim_CNS_fraction; // CNS after predicted ascent, 1 = 1%, as integer +static unsigned char NDL_tissue_start_norm; // tissue to start with when calculating the normal NDL time +static unsigned char NDL_tissue_start_alt; // tissue to start with when calculating the alternative NDL time +static unsigned char NDL_tissue_start; // tissue to start with in current cycle +static unsigned char NDL_tissue_lead; // tissue with the shortest NDL time found in current cycle +static unsigned char NDL_tissue; // tissue for which the NDL is calculated right now + +// Result Values from Calculation Functions (9 byte) + +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 + + +// 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 unsigned short int_time; // time it takes for the compartment to reach the target pressure + + +// Gas in Use and Gas Needs (30 byte) + +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 gas_needs_stop_time; // duration of the stop in minutes +static unsigned char gas_needs_stop_gas; // gas used now (1-5 or 0) +static unsigned char gas_needs_stop_gas_last; // gas used before (1-5 or 0) +static unsigned char gas_needs_stop_depth; // depth of the stop in meters +static unsigned char gas_needs_stop_depth_last; // depth of the last stop in meters +static unsigned char gas_needs_stop_index; // index to the stop table +static unsigned char gas_needs_gas_index; // index to the gas and tank data arrays +static unsigned char gas_needs_next_phase; // next phase within the ascent gas needs calculation + +static float gas_volume_need[NUM_GAS]; // gas volumes required for return/ascent in liters + + +// Transfer Variables between calc_gas_needs_ascent() and calc_due_by_depth_time_sac() (13 byte) + +static float gas_needs_float_depth; // depth of the stop or half-way point +static float gas_needs_float_time; // duration of the stop or ascent phase +static unsigned char gas_needs_stop_usage; // gas usage in l/min +static float gas_needs_volume_due; // computed due of gas volume required + + +// 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 + + +// Transfer values for convert_float_int and convert_float_to_char() (7 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 + + +// Auxiliary Variables for Data Buffering (28 byte) + +static float N2_equilibrium; // used for N2 tissue graphics scaling +static float temp_tissue; // auxiliary variable to buffer tissue pressures +static float float_pSCR_factor; // pre-computed factor for pSCR ppO2 drop calculation +static float calc_pres_tissue_N2; // auxiliary variable to buffer tissue N2 pressure +static float calc_pres_tissue_He; // auxiliary variable to buffer tissue He pressure +static float pres_tissue; // auxiliary variable to buffer total tissue pressure +static float old_pres_respiration; // auxiliary variable to buffer sim_pres_respiration + + +// Performance Profiling (4 byte) + +static unsigned short profiling_runtime; // performance measurement: runtime of current invocation +static unsigned char profiling_runs; // performance measurement: invocations per deco calculation cycle +static unsigned char profiling_phase; // performance measurement: current calculation phase + + +// 255 byte used, 1 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 (11 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 + + +// 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 @@ -406,36 +509,52 @@ 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 int ascent_time; // time in minutes needed for the ascent - - -// Buhlmann model parameters +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 -// gas in use - -static unsigned char sim_gas_current; // number of the currently used gas -static unsigned char sim_gas_current_depth; // change depth of the currently used gas - - -// vault to back-up & restore tissue data - -static float pres_tissue_N2_vault[NUM_COMP]; // stores the nitrogen tissue pressures -static float pres_tissue_He_vault[NUM_COMP]; // stores the helium tissue pressures -static float cns_vault_float; // stores current CNS (float representation) -static unsigned char deco_warnings_vault; // stores warnings status - - -// 8 byte free space left in this bank (4 bytes per float, 2 bytes per int/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 current CNS (float representation) +static unsigned char vault_deco_warnings; // stores warnings status +static unsigned char vault_deco_info; // stores info status + + +// 7 byte occupied by compiler-placed vars + + +// 223 byte used, 33 byte left in this bank (4 bytes per float, 2 bytes per short, 1 byte per char) + + + +//---- Bank 12 parameters ----------------------------------------------------- +#ifndef UNIX +# pragma udata bank12=0xc00 +#endif + +// stops table (96 byte) + +static unsigned char internal_deco_depth[NUM_STOPS]; // depths of the stops in meters +static unsigned char internal_deco_time[NUM_STOPS]; // durations of the stops in minutes +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) //---- Bank 7 parameters ----------------------------------------------------- @@ -443,15 +562,15 @@ # pragma udata bank7=0x700 #endif -// Keep order and position of the variables in bank 7 as they are backed-up to & restored from EEPROM - -float pres_tissue_N2[NUM_COMP]; // 16 floats = 64 bytes -float pres_tissue_He[NUM_COMP]; // 16 floats = 64 bytes - -float sim_pres_tissue_N2[NUM_COMP]; // 16 floats = 64 bytes -float sim_pres_tissue_He[NUM_COMP]; // 16 floats = 64 bytes - -// bank is full! +// Keep order and position of the variables in bank 7 as they are backed-up to & restored from EEPROM + +static float real_pres_tissue_N2[NUM_COMP]; // 16 floats = 64 bytes +static float real_pres_tissue_He[NUM_COMP]; // 16 floats = 64 bytes + +static float sim_pres_tissue_N2[NUM_COMP]; // 16 floats = 64 bytes +static float sim_pres_tissue_He[NUM_COMP]; // 16 floats = 64 bytes + +// 256 byte used, bank is full //---- Bank 8 parameters ----------------------------------------------------- @@ -478,6 +597,52 @@ // ********************************************************************************************************************************* #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 2sec = 1 / (a*ppO2 + b) with ppO2 in [cbar] +// a b 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) + -266.53, 35100, // 81 - 90 (index 3) + -177.69, 27000, // 91 - 100 (index 4) + -177.69, 27000, // 101 - 110 (index 5) + -88.84, 17100, // 111 - 120 (index 6) + -88.84, 17100, // 121 - 130 (index 7) + -88.84, 17100, // 131 - 140 (index 8) + -88.84, 17100, // 141 - 150 (index 9) + -222.11, 37350 // 151 - 160 (index 10) +}; + +rom const unsigned short CNS_c[1*20] = { +// CNS increment per 2sec = c / 100000.0 +// c in [1/100000] for ppO2 cbar range + 75, // 161 - 165 (index 0) + 102, // 166 - 170 (index 1) + 136, // 171 - 175 (index 2) + 180, // 176 - 180 (index 3) + 237, // 181 - 185 (index 4) + 310, // 186 - 190 (index 5) + 401, // 191 - 195 (index 6) + 517, // 196 - 200 (index 7) + 760, // 201 - 205 (index 8) + 1100, // 206 - 210 (index 9) + 1500, // 211 - 215 (index 10) + 2090, // 216 - 220 (index 11) + 2900, // 221 - 225 (index 12) + 3900, // 226 - 230 (index 13) + 4820, // 231 - 235 (index 14) + 4820, // 236 - 240 (index 15) + 4820, // 241 - 245 (index 16) + 4820, // 246 - 250 (index 17) + 4820, // 251 - 255 (index 18) + 0 // not used, just to fill up the memory block +}; + + +#ifndef UNIX # pragma romdata Buhlmann_tables = 0x1DD00 // needs to be in the UPPER bank #endif @@ -526,7 +691,7 @@ rom const float e2secs[2*16] = { // result of 1 - 2^(-1/(2sec*HT)) //---- N2 ------------- He ------------ - 5.75958E-03, 1.51848E-02, + 5.75958E-03, 1.51848E-02, 2.88395E-03, 7.62144E-03, 1.84669E-03, 4.88315E-03, 1.24813E-03, 3.29997E-03, @@ -607,7 +772,7 @@ ////////////////////////////////////////////////////////////////////////////// // Bump to blue-screen when an assert is wrong -#ifdef __DEBUG +#ifdef _DEBUG void assert_failed(PARAMETER short int line) { } @@ -616,12 +781,12 @@ ////////////////////////////////////////////////////////////////////////////// // When calling C code from ASM context, the data stack pointer and -// frames should be reset. Bank8 is used by stack +// frames should be reset. Bank 8 is used by stack. #ifdef CROSS_COMPILE # define RESET_C_STACK #else -# ifdef __DEBUG +# ifdef _DEBUG # define RESET_C_STACK fillDataStack(); void fillDataStack(void) { @@ -647,23 +812,101 @@ ////////////////////////////////////////////////////////////////////////////// -// Fast subroutine to read timer 5 -// Note: result is in 1/32 of milliseconds (30.51757813 us/bit to be precise) -static unsigned short tmr5(void) +// Reset timer 5 +// +// Note: TMR5 is configured in 16 bit mode: a value written to TMR5H is buffered +// and will be written to TMR5 together with a successive write to TMR5L. +// As we don't know in which bank the code will be executed, we use either +// the bank-save "movff" command, or address mapping via access bank (",0"). +// +static void load_tmr5(void) { #ifndef CROSS_COMPILE _asm - movff 0xf7c,PRODL // TMR5L - movff 0xf7d,PRODH // TMR5H - _endasm // result in PRODH:PRODL + movff 0x601,0xF7D // bank-safe load TMR5H from C variable tmr5_value first + movff 0x600,0xF7C // bank-safe load TMR5L from c variable tmr5_value thereafter + bcf 0xFBA,1,0 // clear timer 5 overrun flag (0xFBA = PIR5, bit 1 = TMR5IF) + _endasm #else - return 0; + return; +#endif +} + + +////////////////////////////////////////////////////////////////////////////// +// Read timer 5 +// +// Note: TMR5 reads in multiples of 1/32 ms, 30.51757813 us/bit to be precise. +// TMR5 is configured in 16 bit mode: on reading of TMR5L the contents of +// TMR5H is latched and can be read afterwards without potential rollover. +// As we don't know in which bank the code will be executed, we use either +// the bank-save "movff" command, or address mapping via access bank (",0"). +// +static void read_tmr5(void) +{ +#ifndef CROSS_COMPILE + _asm + movff 0xF7C,0x600 // copy TMR5L to C variable tmr5_value, low byte first + movff 0xF7D,0x601 // copy TMR5H to C variable tmr5_value, high byte thereafter + clrf WREG,0 // clear WREG to 0x00 = no overrun by default + btfsc 0xFBA,1,0 // did timer 5 overrun? (0xFBA = PIR5, bit 1 = TMR5IF) + setf WREG,0 // YES - set WREG to 0xff = overrun detected + movff WREG,0x602 // copy WREG to C variable tmr5_overflow + _endasm +#else + return; #endif } ////////////////////////////////////////////////////////////////////////////// -// read Buhlmann coefficients a and b for compartment ci +// Read CNS coefficients a and b +// +static void read_CNS_ab_coefficient(void) +{ +#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... + // -> set to zero if tables are moved to lower pages! + _asm + movlw 1 + movwf TBLPTRU,0 + _endasm +#endif + + { + overlay rom const float* ptr = &CNS_ab[2*cns_i]; + var_cns_a = *ptr++; + var_cns_b = *ptr++; + } +} + + +////////////////////////////////////////////////////////////////////////////// +// Read CNS coefficient c +// +static void read_CNS_c_coefficient(void) +{ +#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... + // -> set to zero if tables are moved to lower pages! + _asm + movlw 1 + movwf TBLPTRU,0 + _endasm +#endif + + { + overlay rom const unsigned short* ptr = &CNS_c[cns_i]; + var_cns_c = *ptr++; + } +} + +////////////////////////////////////////////////////////////////////////////// +// Read Buhlmann coefficients a and b for compartment ci // static void read_Buhlmann_coefficients(void) { @@ -692,7 +935,7 @@ ////////////////////////////////////////////////////////////////////////////// -// read Buhlmann increments for compartment ci +// Read Buhlmann increments for compartment ci // If period == 0 : 2 sec interval // 1 : 1 min interval // 2 : 10 min interval @@ -711,7 +954,7 @@ assert( ci < NUM_COMP ); - // Integration intervals + // Integration Intervals switch(period) { case 0: //---- 2 sec ----------------------------------------------------- @@ -736,16 +979,16 @@ var_N2_e = *ptr++; var_He_e = *ptr++; } - break; + break; default: - assert(0); // Never go there... + assert(0); // code execution shall never pass along here! } } ////////////////////////////////////////////////////////////////////////////// -// read Buhlmann half-times for compartment ci +// Read Buhlmann half-times for compartment ci // static void read_Buhlmann_ht(void) { @@ -774,13 +1017,112 @@ ////////////////////////////////////////////////////////////////////////////// -// compute adopted Buhlmann coefficients +// Calculate adopted Buhlmann coefficients +// +// Input: var_N2_a, var_N2_b coefficients for N2 +// var_He_a, var_He_b coefficients for He +// calc_pres_tissue_N2 partial pressure of N2 in tissue +// calc_pres_tissue_He partial pressure of He in tissue +// pres_tissue total pressure in tissue +// +// Output: var_a, var_b coefficients adopted by N2/He ratio // static void adopt_Buhlmann_coefficients(void) { // adopt a and b coefficients to current N2/He ratio inside the tissue - var_N2_a = (var_N2_a * calc_pres_tissue_N2 + var_He_a * calc_pres_tissue_He) / pres_tissue; - var_N2_b = (var_N2_b * calc_pres_tissue_N2 + var_He_b * calc_pres_tissue_He) / pres_tissue; + +#ifdef _helium + + var_a = (var_N2_a * calc_pres_tissue_N2 + var_He_a * calc_pres_tissue_He) / pres_tissue; + var_b = (var_N2_b * calc_pres_tissue_N2 + var_He_b * calc_pres_tissue_He) / pres_tissue; + +#else + + var_a = var_N2_a; + var_b = var_N2_b; + +#endif + +} + + +////////////////////////////////////////////////////////////////////////////// +// Calculate partial pressure of N2 in respired air at surface pressure +// +// Input: pres_surface surface pressure +// +// Output: N2_equilibrium partial pressure of N2 in surface air +// +static void calc_N2_equilibrium(void) +{ + N2_equilibrium = 0.7902 * (pres_surface - ppWater); +} + + +////////////////////////////////////////////////////////////////////////////// +// Get, safeguard and convert the saturation and desaturation factors +// +// Input: char_I_saturation_multiplier saturation factor (integer) +// char_I_desaturation_multiplier desaturation factor (integer) +// +// Output: float_saturation_multiplier saturation factor (float) +// float_desaturation_multiplier desaturation factor (float) +// +static void get_saturation_factors(void) +{ + // safeguard input parameters that are constant during the course of the dive + if( char_I_saturation_multiplier < 100 ) char_I_saturation_multiplier = 100; + if( char_I_saturation_multiplier > 140 ) char_I_saturation_multiplier = 140; + + if( char_I_desaturation_multiplier < 60 ) char_I_desaturation_multiplier = 60; + if( char_I_desaturation_multiplier > 100 ) char_I_desaturation_multiplier = 100; + + // convert input parameters to float numbers + float_saturation_multiplier = 0.01 * char_I_saturation_multiplier; + float_desaturation_multiplier = 0.01 * char_I_desaturation_multiplier; +} + + +////////////////////////////////////////////////////////////////////////////// +// apply_saturation_factors +// +// Apply safety factors for both ZH-L16 models. +// +// Modified: temp_tissue safeguarded tissue increment/decrement +// +static void apply_saturation_factors(void) +{ + assert( 0.0 < float_desaturation_multiplier && float_desaturation_multiplier <= 1.0 ); + assert( 1.0 <= float_saturation_multiplier && float_saturation_multiplier <= 2.0 ); + + if ( temp_tissue < 0.0 ) temp_tissue *= float_desaturation_multiplier; + else temp_tissue *= float_saturation_multiplier; +} + + +////////////////////////////////////////////////////////////////////////////// +// convert_float_to_int +// +// Converts a float within range 0.0 - 9.99 into 16 bit integer scaled in 1/100. +// +static void convert_float_to_int(void) +{ + if ( float_value < 0.005 ) int_value = 0; + else if ( float_value >= 9.985 ) int_value = 999; + else int_value = (unsigned short)(100 * float_value + 0.5); +} + + +////////////////////////////////////////////////////////////////////////////// +// 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); } @@ -810,6 +1152,20 @@ calc_hauptroutine(); } + +////////////////////////////////////////////////////////////////////////////// +// deco_init_output_vars +// +// called from divemode.asm +// +// Initializes all output variables to their default values. +// +void deco_init_output_vars(void) +{ + RESET_C_STACK + init_output_vars(); +} + ////////////////////////////////////////////////////////////////////////////// // deco_clear_tissue // @@ -826,6 +1182,7 @@ clear_tissue(); } + ////////////////////////////////////////////////////////////////////////////// // deco_calc_dive_interval // @@ -841,6 +1198,7 @@ calc_interval(char_I_dive_interval); } + ////////////////////////////////////////////////////////////////////////////// // deco_calc_dive_interval_1min // @@ -893,6 +1251,7 @@ calc_desaturation_time(); } + ////////////////////////////////////////////////////////////////////////////// // deco_push_tissues_to_vault // @@ -906,6 +1265,7 @@ push_tissues_to_vault(); } + ////////////////////////////////////////////////////////////////////////////// // deco_pull_tissues_from_vault // @@ -929,202 +1289,190 @@ ////////////////////////////////////////////////////////////////////////////// -// calc_nextdecodepth -// -// INPUT, changing during dive: -// sim_pres_respiration : current depth in absolute pressure +// find_next_stop // // INPUT, fixed during dive: -// pres_surface -// GF_delta -// GF_high -// GF_low -// char_I_depth_last_deco -// -// MODIFIED -// locked_GF_step_norm/_alt : used for GF model -// low_depth_norm/_alt : used for GF model +// pres_surface : surface pressure (as absolute pressure) +// char_I_depth_last_deco : depth of the last deco stop +// +// INPUT, changing during dive: +// float_depth_real : current real depth in meters (float) +// char_depth_real : current real depth in meters (integer) +// GF_high : GF high factor +// GF_low : GF low factor +// +// INPUT & OUTPUT +// 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 -// sim_depth_limit : depth of next stop in meters (if RETURN == true ) -// next possible depth without stop (if RETURN == false) -// -// RETURN TRUE if a stop is needed, else false -// -static unsigned char calc_nextdecodepth(void) +// char_depth_last : depth we came from +// sim_pres_respiration : simulated depth in absolute pressure +// +// RETURN +// TRUE: a stop is needed, FALSE: no stop needed +// +static unsigned char find_next_stop(void) { + overlay unsigned char depth_1min; + overlay unsigned char depth_limit; + overlay unsigned char first_stop; overlay unsigned char need_stop; - // compute current depth in meters - overlay float depth = (sim_pres_respiration - pres_surface) * BAR_TO_METER; - - // compute depth in meters after 1 minute of ascent at float_ascent_speed (5..10 m/min) - overlay float min_depth = (depth > float_ascent_speed) ? (depth - float_ascent_speed) : 0.0; - - - // target the simulated tissues - tissue_increment = 0; - - //---- check if a stop is needed for deco reasons ---------------------------- - - // switch on deco model - if( char_I_deco_model != 0 ) + + // ----------------------------------------------------------------------- + // we start with the assumption that a stop is not required + // ----------------------------------------------------------------------- + + need_stop = 0; + + // remember the depth we came from + char_depth_last = char_depth_sim; + + // calculate the limit for the current depth + if( char_I_deco_model == 0 ) calc_limit(1.0); // straight Buhlmann + else if( char_depth_sim >= GF_low_depth ) calc_limit(GF_low); // with GF, below low depth reference + else calc_limit(GF_high - GF_slope * (float)char_depth_sim); // with GF, above low depth reference + + // check if we can surface directly + if( ceiling <= 0.0 ) { - //---- ZH-L16 + GRADIENT FACTOR Model ------------------------------------ - - overlay float locked_GF_step; - overlay float low_depth; - overlay float limit_depth; - - overlay unsigned char first_stop = 0; - - - // calculate minimum depth we can ascent to in bar relative pressure - calc_limit(GF_low); - - // check if we can surface directly - if( ceiling <= 0.0 ) + // YES - ascent to surface is allowed + char_depth_sim = 0; + + // - done + goto done; + } + + // ----------------------------------------------------------------------- + // a stop is required, but maybe not yet within the running minute + // ----------------------------------------------------------------------- + + // convert the depth we can ascent to from relative pressure to meters, + // rounded up (i.e. made deeper) to next full meter. + depth_limit = (unsigned char)(ceiling * BAR_TO_METER + 0.99); + + // calculate the stop depth, i.e. round up to the next multiple of 3 meters + // using integer arithmetics + first_stop = 3 * ( (depth_limit + 2) / 3 ); + + // apply correction for the shallowest stop + if( first_stop == 3 ) first_stop = 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 ) + { + depth_1min = char_depth_sim - char_I_ascent_speed; + } + else + { + depth_1min = 0; + } + + // is the stop shallower than the depth that can be reached within 1 minute? + if( depth_1min > first_stop ) + { + // YES - report the depth that will be reached within 1 minute of ascent + char_depth_sim = depth_1min; + + // - done + goto done; + } + + // ----------------------------------------------------------------------- + // we need to make a stop now + // ----------------------------------------------------------------------- + + // set stop data + need_stop = 1; + char_depth_sim = first_stop; + + // done so far if using straight Buhlmann + if( char_I_deco_model == 0 ) goto done; + + // ----------------------------------------------------------------------- + // we need to make a stop now and we are using the GF extension + // ----------------------------------------------------------------------- + + // is the depth limit deeper than the GF low depth reference used up to now? + if( depth_limit > GF_low_depth ) + { + // YES - update the reference + GF_low_depth = depth_limit; + GF_slope = (GF_high - GF_low) / (float)GF_low_depth; + + // store for use in next cycles + if( deco_status & CALC_NORM ) { - min_depth = 0.0; // set minimum depth to 0 meters = surface - goto no_deco_stop; // done - } - - // convert minimum depth we can ascent to from relative pressure to depth in meters - limit_depth = ceiling * BAR_TO_METER; - - // recall low_depth dependent on current plan - low_depth = (char_O_deco_status & DECO_PLAN_ALTERNATE) ? low_depth_alt : low_depth_norm; - - // Store the deepest point needing a deco stop as the LOW reference for GF. - // NOTE: following stops will be validated using this LOW-HIGH GF scale, - // so if we want to keep coherency, we should not validate this stop - // yet, but apply the search to it, as for all the following stops afterward. - if( limit_depth > low_depth ) - { - // update GF parameters - low_depth = limit_depth; - locked_GF_step = GF_delta / low_depth; - - // store updated GF parameters dependent on current plan - if( char_O_deco_status & DECO_PLAN_ALTERNATE ) - { - low_depth_alt = low_depth; - locked_GF_step_alt = locked_GF_step; - } - else - { - low_depth_norm = low_depth; - locked_GF_step_norm = locked_GF_step; - } + GF_low_depth_norm = GF_low_depth; + GF_slope_norm = GF_slope; } else { - // recall locked_GF_step dependent of current plan - locked_GF_step = (char_O_deco_status & DECO_PLAN_ALTERNATE) ? locked_GF_step_alt : locked_GF_step_norm; + GF_low_depth_alt = GF_low_depth; + GF_slope_alt = GF_slope; } - - // invalidate this stop if we can ascent for 1 minute without going above minimum required deco depth - if( limit_depth < min_depth ) goto no_deco_stop; - - - //---- if program execution passes here, we need a deco stop -------------------------------- - - // round to multiple of 3 meters (limit depth is in meters of depth) - first_stop = 3 * (unsigned char)(0.4999 + limit_depth * 0.333333); - - // check a constraint - assert( first_stop < 128 ); - - // apply correction for the shallowest stop, use char_I_depth_last_deco (3..6 m) instead - if( first_stop == 3 ) first_stop = char_I_depth_last_deco; - - // We have a stop candidate. - // But maybe ascending to the next stop will diminish the constraint, - // because the GF might decrease more than the pressure gradient... - while( first_stop > 0 ) + } + + // keep the stop as it is when it is the first stop + // (i.e. there are no stops in the stops table yet) + if( internal_deco_depth[0] == 0 ) goto done; + + // keep the stop as it is when extended stops are activated + if( main_status & EXTENDED_STOPS ) goto done; + + // 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 + // 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. + + // no need to probe for a stop that is beyond 1 minute of ascent + while( first_stop >= (depth_1min + 3) ) + { + overlay unsigned char next_stop; + + // compute the depth of the next stop + if ( first_stop <= char_I_depth_last_deco ) next_stop = 0; + else if ( first_stop == 6 ) next_stop = char_I_depth_last_deco; + else next_stop = first_stop - 3; + + // compute the depth limit at the next stop depth + calc_limit(GF_high - GF_slope * (float)next_stop); + + // check if ascent to the next stop is allowed + if( next_stop < (unsigned char)(ceiling * BAR_TO_METER + 0.99) ) { - // next depth - overlay unsigned char next_stop; - - // invalidate this stop if we can ascent one more minute without going above minimum required deco depth - if( first_stop <= (unsigned char)min_depth ) goto no_deco_stop; - - // compute depth of next stop - if ( first_stop <= char_I_depth_last_deco ) next_stop = 0; - else if ( first_stop == 6 ) next_stop = char_I_depth_last_deco; - else next_stop = first_stop - 3; - - // compute limit with the GF of the new stop candidate - if( (low_depth == 0.0) || (next_stop > low_depth) ) calc_limit(GF_low); - else calc_limit(GF_high - next_stop * locked_GF_step); - - // check if ascent to the next stop candidate is possible - if( ceiling * BAR_TO_METER >= next_stop ) - goto deco_stop_found; // no - ascent to next_stop forbidden - - // else, validate that stop and loop... - first_stop = next_stop; - } - -no_deco_stop: - need_stop = 0; // set flag for stop needed to 'no' - sim_depth_limit = (unsigned char)min_depth; // report depth we can ascent to without stop - goto done; - -deco_stop_found: - need_stop = 1; // set flag for stop needed to 'yes' - sim_depth_limit = (unsigned char)first_stop; // stop depth, in meters - -done: - ; - } - else - { - //---- ZH-L16 model ------------------------------------------------- - - overlay float limit_depth; - - - // calculate minimum depth we can ascent to in bar relative pressure - calc_limit(1.0); - - // check if we can surface directly - if( ceiling >= 0 ) - { - // no - set flag for stop needed to 'yes' - need_stop = 1; - - // convert stop depth in relative pressure to stop index - limit_depth = ceiling * BAR_TO_METER / 3.0; - - // convert stop index to depth in meters, rounded to multiple of 3 meters - sim_depth_limit = 3 * (short)(limit_depth + 0.99); - - // correct last stop to 4m/5m/6m - if( sim_depth_limit == 3 ) sim_depth_limit = char_I_depth_last_deco; + // NO - the next stop would be too shallow + break; } else { - // yes - set flag for stop needed to 'no' - need_stop = 0; - - // set depth we can ascent to as 0 = surface - sim_depth_limit = 0; + // YES - the next stop is allowed + char_depth_sim = next_stop; + + // - ascent to next stop + first_stop = next_stop; + + // - loop to probe the stop following next + continue; } } - // ---- After the first deco stop, gas changes are only done at deco stops now! ----------------------- - - // check if a stop is found and there is a better gas to switch to - if( need_stop ) - if( gas_find_better() ) - { - // set the new calculation ratios for N2, He and O2 - gas_set_ratios(); - - // prime the deco stop with the gas change time - update_deco_table(char_I_gas_change_time); - } + + // ----------------------------------------------------------------------- + // common end for straight Buhlmann and Buhlmann with GF extension + // ----------------------------------------------------------------------- + +done: + + // calculate absolute pressure at the depth found + sim_pres_respiration = char_depth_sim * METER_TO_BAR + pres_surface; return need_stop; } @@ -1133,83 +1481,70 @@ ////////////////////////////////////////////////////////////////////////////// // publish_deco_table // -// Buffer the stops, once computed, so we can continue to display them -// while computing the next set. +// Input: internal_deco_depth[] depth in internal stops table +// internal_deco_time[] times ... +// internal_deco_gas[] gases ... +// +// Output: char_O_deco_depth[] depth in the external stops table +// char_O_deco_time[] times ... +// char_O_deco_gas[] gases ... +// char_O_deco_time_for_log times in reverse order // static void publish_deco_table(void) { - overlay unsigned char x, y; - - - // Copy depth of the first (deepest) stop, because when reversing - // order, it will be hard to find... - char_O_first_deco_depth = internal_deco_depth[0]; - char_O_first_deco_time = internal_deco_time [0]; - - for( x = 0; x < NUM_STOPS; x++ ) + overlay unsigned char x = 0; + overlay unsigned char y; + + + // copy all entries from internal to external stops table + for( y = 0; y < NUM_STOPS; y++ ) { - char_O_deco_depth[x] = internal_deco_depth[x]; - char_O_deco_time [x] = internal_deco_time [x]; - char_O_deco_gas [x] = internal_deco_gas [x]; + // remember index of last entry with a non-null depth + if( internal_deco_depth[y] > 0 ) x = y; + + // copy depth, time and gas + char_O_deco_depth[y] = internal_deco_depth[y]; + char_O_deco_time [y] = internal_deco_time [y]; + char_O_deco_gas [y] = internal_deco_gas [y]; } - // Now fill the char_O_deco_time_for_log array - // ---- First: search the first non-null depth - for( x = (NUM_STOPS-1); x != 0; --x ) - if( internal_deco_depth[x] != 0 ) break; - - //---- Second: copy to output table (in reverse order) - for( y = 0; y < NUM_STOPS; y++, --x ) + + // copy times of shallowest stops to logging table + for( y = 0; y < NUM_STOPS_LOG; y++, --x ) { char_O_deco_time_for_log[y] = internal_deco_time [x]; - // Stop when the last transfer is done. + // stop when all entries are copied if( x == 0 ) break; } - //---- Third: fill table with null until end - for( y++; y < NUM_STOPS; y++ ) + // fill the remainder of the logging table with null + // if it is not completely filled already + for( y++; y < NUM_STOPS_LOG; y++ ) char_O_deco_time_for_log[y] = 0; } ////////////////////////////////////////////////////////////////////////////// -// temp_tissue_safety -// -// outsourced in v.102 -// -// Apply safety factors for both ZH-L16 models. -// -static void temp_tissue_safety(void) -{ - assert( 0.0 < float_desaturation_multiplier && float_desaturation_multiplier <= 1.0 ); - assert( 1.0 <= float_saturation_multiplier && float_saturation_multiplier <= 2.0 ); - - if( temp_tissue < 0.0 ) temp_tissue *= float_desaturation_multiplier; - else temp_tissue *= float_saturation_multiplier; -} - - -////////////////////////////////////////////////////////////////////////////// // Find current gas in the list (if any) and get its change depth // -// Input: char_I_current_gas : 1..5 or 6 -// -// Output: sim_gas_current : 1..6 or 0 for the manually configured gas/dil -// sim_gas_current_depth : change depth (MOD) of the gas/dil in meters +// Input: char_I_current_gas_num number of current gas (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_find_current(void) { - assert( 1 <= char_I_current_gas && char_I_current_gas <= 6 ); - - if( char_I_current_gas <= NUM_GAS ) // gas/diluent 1-5 + 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 { - sim_gas_current = char_I_current_gas; - sim_gas_current_depth = char_I_deco_gas_change[sim_gas_current-1]; + sim_gas_current_num = char_I_current_gas_num; + sim_gas_current_depth = char_I_deco_gas_change[sim_gas_current_num-1]; } else { - sim_gas_current = 0; + sim_gas_current_num = 0; sim_gas_current_depth = char_I_gas6_depth; } } @@ -1218,16 +1553,16 @@ ////////////////////////////////////////////////////////////////////////////// // Find the deco gas with the shallowest change depth below or at the current depth // -// INPUT sim_depth_limit : current depth in meters -// sim_gas_current : number of the currently used gas/dil -// sim_gas_current_depth : change depth 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 -// -// MODIFIED sim_gas_current : index of the gas (1..5) - only if return value is true -// sim_gas_current_depth : switch depth - only if return value is true -// -// RETURNS TRUE if a better gas is available +// Input: char_depth_sim simulated depth in meters +// sim_gas_current_num number of the currently used gas/dil +// sim_gas_current_depth change depth 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 +// +// Modified: sim_gas_current_num index of the gas (1..5) - only if return value is true +// sim_gas_current_depth switch depth - only if return value is true +// +// Return value is TRUE if a better gas is available // static unsigned char gas_find_better(void) { @@ -1235,27 +1570,21 @@ overlay unsigned char switch_gas = 0; overlay unsigned char j; - // no automatic gas changes in CCR mode - if( (char_O_deco_status & DECO_MODE_MASK) == DECO_MODE_CCR ) return 0; + // // no automatic gas changes in CCR mode + // if( (deco_status & MODE_MASK) == MODE_CCR ) return 0; // loop over all deco gases to find the shallowest one below or at current depth for( j = 0; j < NUM_GAS; ++j ) { // Is this gas not the one we are already breathing? - if( j+1 != sim_gas_current ) - - // Is this - an (active) deco gas, - // - or if in deco phase, any gas but disabled - // - or if in bailout, any gas but disabled, - // - or if in pSCR mode, any gas but disabled? - if( ( ( char_I_deco_gas_type[j] == 3 ) ) - || ( ( char_O_deco_info & DECO_FLAG ) && ( char_I_deco_gas_type[j] != 0 ) ) - || ( ( char_O_deco_status & DECO_BAILOUT_MODE ) && ( char_I_deco_gas_type[j] != 0 ) ) - || ( ( char_O_main_status & DECO_MODE_PSCR ) && ( char_I_deco_gas_type[j] != 0 ) ) ) + if( j+1 != sim_gas_current_num ) + + // Is this gas available? + if( char_I_deco_gas_type[j] > 0 ) // 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] >= sim_depth_limit ) + if( char_I_deco_gas_change[j] >= char_depth_sim ) // Is the change depth of this gas shallower than the // change depth of the gas we are currently on? @@ -1278,7 +1607,7 @@ if( switch_gas ) { // YES - set the better gas as the new gas - sim_gas_current = switch_gas; + sim_gas_current_num = switch_gas; // set its change depth as the last used change depth sim_gas_current_depth = switch_depth; @@ -1297,32 +1626,33 @@ ////////////////////////////////////////////////////////////////////////////// -// Set calc_N2/He/O2_ratios by sim_gas_current -// -// Input: sim_gas_current : index of gas to use -// real_O2_ratio, real_He_ratio : if gas = 0 (the manually set gas) -// char_I_deco_O2/He_ratio[] : if gas = 1..5 (the configured gases) -// -// Output: sim_N2_ratio, sim_He_ratio : ratios of the inert gases -// sim_pSCR_drop : ppO2 drop in pSCR loop +// Set calc_N2/He/O2_ratios by sim_gas_current_num +// +// Input: sim_gas_current_num index of gas to use +// real_O2_ratio, real_He_ratio if gas = 0 (the manually set gas) +// char_I_deco_O2/He_ratio[] if gas = 1..5 (the configured gases) +// +// Output: sim_N2_ratio, sim_He_ratio ratios of the inert gases +// sim_pSCR_drop ppO2 drop in pSCR loop // static void gas_set_ratios(void) { overlay float sim_IG_ratio; - assert( 0 <= sim_gas_current <= NUM_GAS ); - - + assert( 0 <= sim_gas_current_num <= NUM_GAS ); + + +#ifdef _helium // get gas ratios - if( sim_gas_current == 0 ) + if( sim_gas_current_num == 0 ) { sim_O2_ratio = real_O2_ratio; sim_He_ratio = real_He_ratio; } else { - sim_O2_ratio = 0.01 * char_I_deco_O2_ratio[sim_gas_current-1]; - sim_He_ratio = 0.01 * char_I_deco_He_ratio[sim_gas_current-1]; + sim_O2_ratio = 0.01 * char_I_deco_O2_ratio[sim_gas_current_num-1]; + sim_He_ratio = 0.01 * char_I_deco_He_ratio[sim_gas_current_num-1]; } // inert gas ratio (local helper variable) @@ -1330,9 +1660,24 @@ // N2 ratio sim_N2_ratio = sim_IG_ratio - sim_He_ratio; - +#else + // get O2 ratio + sim_O2_ratio = ( sim_gas_current_num == 0 ) ? real_O2_ratio : 0.01 * char_I_deco_O2_ratio[sim_gas_current_num-1]; + + // set H2 ratio to zero + sim_He_ratio = 0.0; + + // inert gas ratio (local helper variable) + sim_IG_ratio = 1.00 - sim_O2_ratio; + + // N2 ratio + sim_N2_ratio = sim_IG_ratio; +#endif + +#ifdef _ccr_pscr // ppO2 drop in pSCR loop sim_pSCR_drop = sim_IG_ratio * float_pSCR_factor; +#endif assert( 0.0 <= sim_N2_ratio && sim_N2_ratio <= 0.95 ); @@ -1345,8 +1690,8 @@ // Compute respired ppO2, ppN2 and ppHe // // Input: tissue_increment : selector for targeting simulated or real tissues -// char_O_main_status : breathing mode for real tissues -// char_O_deco_status : breathing mode for simulated tissues +// main_status : breathing mode for real tissues +// deco_status : breathing mode for simulated tissues // sim_/real_O2_ratio : (simulated) O2 ratio breathed // sim_/real_N2_ratio : (simulated) N2 ratio breathed // sim_/real_He_ratio : (simulated) He ratio breathed @@ -1359,52 +1704,72 @@ // // Output: ppN2 : respired N2 partial pressure // ppHe : respired He partial pressure -// char_ppO2 : breathed ppO2 in %, used in CNS calculation +// char_ppO2 : breathed ppO2 in %, used for CNS calculation // void calc_alveolar_pressures(void) { overlay float calc_pres_respiration; overlay float calc_O2_ratio; overlay float calc_N2_ratio; + +#ifdef _helium overlay float calc_He_ratio; +#endif + +#ifdef _ccr_pscr overlay float calc_pSCR_drop; +#endif overlay unsigned char status; - assert( 0.00 <= real_N2_ratio && real_N2_ratio <= 1.00 ); - assert( 0.00 <= real_He_ratio && real_He_ratio <= 1.00 ); - assert( (real_N2_ratio + real_He_ratio) <= 1.00 ); + assert( 0.00 <= real_N2_ratio && real_N2_ratio <= 1.00 ); + assert( 0.00 <= real_He_ratio && real_He_ratio <= 1.00 ); + assert( (real_N2_ratio + real_He_ratio) <= 1.00 ); assert( 0.800 < real_pres_respiration && real_pres_respiration < 14.0 ); - assert( 0.00 <= sim_N2_ratio && real_N2_ratio <= 1.00 ); - assert( 0.00 <= sim_He_ratio && real_He_ratio <= 1.00 ); - assert( (sim_N2_ratio + sim_He_ratio) <= 1.00 ); + assert( 0.00 <= sim_N2_ratio && real_N2_ratio <= 1.00 ); + assert( 0.00 <= sim_He_ratio && real_He_ratio <= 1.00 ); + assert( (sim_N2_ratio + sim_He_ratio) <= 1.00 ); assert( 0.800 < sim_pres_respiration && sim_pres_respiration < 14.0 ); // get input data according to context - if( tissue_increment & TISSUE_FLAG ) + if( tissue_increment & TISSUE_SELECTOR ) { //---- real tissues ----------------------------------------------------------- - status = char_O_main_status; calc_pres_respiration = real_pres_respiration; - calc_pSCR_drop = real_pSCR_drop; - + + status = main_status; calc_O2_ratio = real_O2_ratio; calc_N2_ratio = real_N2_ratio; + +#ifdef _helium calc_He_ratio = real_He_ratio; +#endif + +#ifdef _ccr_pscr + calc_pSCR_drop = real_pSCR_drop; +#endif } else { //---- simulated tissues ------------------------------------------------------ - status = char_O_deco_status; - calc_pres_respiration = sim_pres_respiration; - calc_pSCR_drop = sim_pSCR_drop; - + + // correct sim_pres_respiration if shallower than calculated stop depth + calc_pres_respiration = ( real_pres_respiration < sim_pres_respiration ) ? real_pres_respiration : sim_pres_respiration; + + status = deco_status; calc_O2_ratio = sim_O2_ratio; calc_N2_ratio = sim_N2_ratio; + +#ifdef _helium calc_He_ratio = sim_He_ratio; +#endif + +#ifdef _ccr_pscr + calc_pSCR_drop = sim_pSCR_drop; +#endif } //---- OC, CCR and Bailout Mode Gas Calculations ----------------------------------- @@ -1418,6 +1783,8 @@ // calculate ppO2 of the pure gas (OC, diluent) OC_ppO2 = O2_ppO2 * calc_O2_ratio; +#ifdef _ccr_pscr + // calculate pSCR ppO2 pSCR_ppO2 = OC_ppO2 - calc_pSCR_drop; @@ -1426,7 +1793,7 @@ //---- Loop modes : adjust ppN2 and ppHe for change in ppO2 due to setpoint (CCR) or drop (pSCR) --- - if( status & DECO_MODE_LOOP ) + if( status & MODE_LOOP ) { overlay float const_ppO2; overlay float max_ppO2; @@ -1437,21 +1804,21 @@ // Limit the setpoint to the maximum physically possible ppO2. This prevents for // example calculating with a setpoint of 1.3 bar in only 2 meters of depth. // Additionally, the ppO2 can be further reduced to account for exhaled inert gases - // accumulating in the loop by the user-adjustable setting char_I_cc_max_frac_o2. + // accumulating in the loop by the user-adjustable setting char_I_CC_max_frac_O2. // (ppWater is neglected here) - max_ppO2 = 0.01 * char_I_cc_max_frac_o2 * calc_pres_respiration; + max_ppO2 = 0.01 * char_I_CC_max_frac_O2 * calc_pres_respiration; if( const_ppO2 > max_ppO2 ) const_ppO2 = max_ppO2; // check which kind of loop we are on - if( status & DECO_MODE_PSCR ) + if( status & MODE_PSCR ) { //---- 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_FLAG)) ppO2 = const_ppO2; - else ppO2 = pSCR_ppO2; + if ( char_I_const_ppO2 && (tissue_increment & TISSUE_SELECTOR)) ppO2 = const_ppO2; + else ppO2 = pSCR_ppO2; } else { @@ -1464,7 +1831,7 @@ // adjust overall gas pressure for change in ppO2 due to setpoint (CCR) or drop (pSCR), // capture potential failure conditions first: if( ( calc_pres_respiration < ppO2 ) // sensor reading or selected setpoint is higher than ambient pressure - || ( calc_O2_ratio > 0.995 ) ) // diluent is pure O2, i.e. calc_N2_ratio + calc_He_ratio = 0 yielding a div/0 + || ( calc_O2_ratio > 0.995 ) ) // diluent is pure O2, i.e. calc_N2_ratio + calc_He_ratio = 0 would give a div/0 { // failure condition present, set predetermined result calc_pres_respiration = 0.0; @@ -1473,10 +1840,15 @@ { // no failure conditions present, equation can be executed calc_pres_respiration -= ppO2; +#ifdef _helium calc_pres_respiration /= calc_N2_ratio + calc_He_ratio; +#else + calc_pres_respiration /= calc_N2_ratio; +#endif } } else +#endif // _ccr_pscr { //---- OC mode --------------------------------------------------------------------------------- @@ -1495,7 +1867,7 @@ //---- calculate ppN2 and ppHe --------------------------------------------------------------------- // add deco safety distance when working on simulated tissues - if( !(tissue_increment & TISSUE_FLAG) ) calc_pres_respiration += float_deco_distance; + 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 ) @@ -1505,7 +1877,13 @@ // calculate partial pressures ppN2 = calc_N2_ratio * calc_pres_respiration; + +#ifdef _helium ppHe = calc_He_ratio * calc_pres_respiration; +#else + ppHe = 0.0; +#endif + } else { @@ -1517,109 +1895,291 @@ ////////////////////////////////////////////////////////////////////////////// +// init_output_vars +// +// Initializes all output variables to their default values +// +static void init_output_vars(void) +{ + // clear the internal stops table from remains lasting from the previous dive or deco calculator run + clear_deco_table(); + + // publish the cleared stops table to the display functions + publish_deco_table(); + + // clear the published gas needs in volume and pressure + for( i = 0; i < NUM_GAS; ++i ) + { + int_O_gas_need_vol[i] = 0; + int_O_gas_need_pres[i] = 0 + INT_FLAG_ZERO + INT_FLAG_INVALID; + } + + // values initially to be set to zero + int_O_ceiling = 0; // ceiling depth in mbar + char_O_deco_info = 0; // clear all deco information flags + char_O_deco_warnings = 0; // clear all deco warning flags + + // default desaturation time to 24 hours (it will not be computed during a dive) + int_O_desaturation_time = 1440; + + // initialize CNS values + int_O_CNS_norm = 0 + INT_FLAG_INVALID; + 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_TTS_norm = 0; + int_O_TTS_alt = 0 + INT_FLAG_INVALID + INT_FLAG_NOT_COMPUTED_YET; + +#ifdef _rx_functions + // clear TR values + int_O_SAC_measured = 0 + INT_FLAG_NOT_AVAIL; // SAC rate + int_O_pressure_need[0] = 0 + INT_FLAG_NOT_AVAIL; // pressure need to reading 1 + int_O_pressure_need[1] = 0 + INT_FLAG_NOT_AVAIL; // pressure need to reading 2 +#endif + +} + + +////////////////////////////////////////////////////////////////////////////// // clear_tissue // -// optimized in v.101 (var_N2_a) -// -// Reset all tissues to surface pressure equilibrium state. +// Reset all tissues to surface pressure equilibrium state +// +// Input: int_I_pres_surface current surface pressure in hPa (mbar) +// +// Output: real_pres_tissue_N2[] partial pressure of N2 in real tissues +// real_pres_tissue_He[] partial pressure of He in real tissues +// char_O_tissue_pres_N2[] partial pressure of N2 for tissue graphics +// char_O_tissue_pres_He[] partial pressure of He for tissue graphics +// char_O_tissue_pressure[] total pressure for tissue graphics +// CNS_fraction_real internal CNS value +// int_O_CNS_current current CNS value +// 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_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 // static void clear_tissue(void) { - // safety limit to prevent improper initialization values - if( int_I_pres_respiration < 500) int_I_pres_respiration = 500; // min. respiration pressure = 500 mbar - - real_pres_respiration = 0.001 * int_I_pres_respiration; - N2_equilibrium = 0.7902 * (real_pres_respiration - ppWater); - + // safeguard and convert the surface pressure (mbar -> bar) (*) + if( int_I_pres_surface < 500 ) pres_surface = 0.500; + else pres_surface = 0.001 * int_I_pres_surface; + + // calculate partial pressure of N2 in respired air at surface pressure + calc_N2_equilibrium(); + + // cycle through the 16 Buhlmann tissues for( ci = 0; ci < NUM_COMP; ci++ ) { - // cycle through the 16 Buhlmann N2 tissues - pres_tissue_N2[ci] = N2_equilibrium; // initialize data for "real" tissue - char_O_tissue_N2_saturation[ci] = 11; // initialize data for tissue graphics - - // cycle through the 16 Buhlmann He tissues - pres_tissue_He[ci] = 0.0; // initialize data for "real" tissue - char_O_tissue_He_saturation[ci] = 0; // initialize data for tissue graphics + // reset tissue pressures + real_pres_tissue_He[ci] = 0.0; // He + real_pres_tissue_N2[ci] = N2_equilibrium; // N2 + + // reset tissue pressures for scaled for tissue graphics + char_O_tissue_pres_He[ci] = 0; // He + char_O_tissue_pres_N2[ci] = 10; // N2 + char_O_tissue_pressure[ci] = 10; // combined } // reset CNS values - CNS_fraction = 0.0; - int_O_CNS_fraction = int_O_normal_CNS_fraction = int_O_alternate_CNS_fraction = 0; - - - // reset any warnings and status data - char_O_deco_warnings = 0; - char_O_deco_status = 0; + CNS_fraction_real = 0.0; + int_O_CNS_current = int_O_CNS_norm = int_O_CNS_alt = 0; // reset some more vars to their defaults - char_O_nullzeit = 240; - int_O_ascenttime = 0; - int_O_alternate_ascenttime = 0; - int_O_gradient_factor = 0; + 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; + + // reset all warning and info flags + char_O_deco_warnings = 0; + char_O_deco_info = 0; } ////////////////////////////////////////////////////////////////////////////// // calc_hauptroutine // -// this is the major code in dive mode calculates: -// the tissues, -// the bottom time, -// and simulates the ascend with all deco stops. +// This is the major code in dive mode, it calculates the tissue pressures, +// the bottom time, and it calculates the ascend with all deco stops, etc. +// +// Input: char_O_main_status deco engine control and real tissues mode +// char_O_deco_status deco engine control and simulated tissues mode +// char_I_sim_advance_time mailbox for bottom time incrementing +// +// 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_deco_distance safety distance between stop depth and calculated depth +// 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 +// int_I_pressure_drop[] pressure drop used to calculate SAC rate +// char_I_gas_avail_size[] size of the tanks in liters +// +// Output: int_O_O2_ppO2 partial pressure of pure O2 at current depth +// int_O_pure_ppO2 partial pressure of O2 in gas at current depth +// int_O_pSCR_ppO2 partial pressure of O2 in gas at current depth, corrected for pSCR mode +// int_O_breathed_ppO2 partial pressure of O2 currently breathed +// +// char_O_deco_status deco engine computations status +// 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_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_SAC_measured measured surface air consumption (SAC) rate in l/min +// +// Modified: int_IO_pressure_value[] warning flags added to pressure reading 1 & 2 +// int_IO_pressure_need[] pressure needs to pressure reading 1 & 2 // static void calc_hauptroutine(void) { - overlay unsigned int int_ppO2_min; - overlay unsigned int int_ppO2_max; - overlay unsigned int int_ppO2_max_dil; - overlay unsigned int int_ppO2_max_max; - overlay float EAD; - overlay float END; - - - //--- Set-up Part -------------------------------------------------------------------------------- - - // clear flags indicating a calculation has been completed - char_O_main_status &= ~( DECO_COMPLETED_NORM + DECO_COMPLETED_ALT ); - - // twosectimer: - // calc_hauptroutine is now invoked every second to speed up the deco planning. - // Because the tissue and CNS calculations are based on a two seconds period, a - // toggle-timer is used to skip every 2nd invocation. - twosectimer = (twosectimer) ? 0 : 1; - - // do initializations that need to be done only once at the beginning of a dive - if( (char_O_deco_status & DECO_STATUS_MASK) == DECO_STATUS_INIT ) + overlay unsigned short int_ppO2_min; + overlay unsigned short int_ppO2_max; + overlay unsigned short int_ppO2_max_dil; + overlay unsigned short int_ppO2_max_max; + overlay float EAD; + overlay float END; + + //============================================================================================= + + // + //--- Setup Part --------------------------------------------------------------------------------- + // + + // set time limit for preempting deco calculations, timer is 16 bit and increments every 1/32 ms + tmr5_value = 65535 - (32 * BUDGET_PER_SECOND / INVOKES_PER_SECOND); + + // load timer + load_tmr5(); + + // read command flags and set up what to do + switch( char_O_deco_status & COMMAND_MASK ) { - // compute a factor that will be used later on in pSCR calculations - float_pSCR_factor = 0.01 * char_I_PSCR_drop * char_I_PSCR_lungratio; + + case INITIALIZE: + case INITIALIZE_START_NORM: + case INITIALIZE_START_ALT: + + // copy master modes to shadow registers + main_status = char_O_main_status; + deco_status = char_O_deco_status; + + // clear all command flags on the master mode to signal that the command is read + char_O_deco_status &= ~COMMAND_MASK; + + // clear the initialization flag on the shadow copy + deco_status &= ~INITIALIZE; + + // initialize the sequence timer + sequence_timer = 0; + + // set the calculation phase to start with to doing the once-per-dive initialization + next_planning_phase = PHASE_10_DIVE_INIT; + + break; + + + case START_NORM: + case START_ALT: + + // copy master modes to shadow registers + main_status = char_O_main_status; + deco_status = char_O_deco_status; + + // clear all command flags on the master mode to signal that the command is read + char_O_deco_status &= ~COMMAND_MASK; + + // set the calculation phase to start with to doing the cyclic initialization + next_planning_phase = PHASE_11_CYCLIC_INIT; + + // continue in CALCULATING + + + case CALCULATING: + + // keep current calculation phase + + // step the sequence timer + sequence_timer = (sequence_timer < INVOKES_PER_SECOND * 2 - 1) ? sequence_timer + 1 : 0; + + break; } - //---- Calculations Part ---------------------------------------------------------------------- - - // acquire current environment data - calc_hauptroutine_data_input(); + // + //--- End of Setup Part ----------------------------------------------------------------------- + // + + //============================================================================================= + + // + //---- Calculations Part (real Tissues) ------------------------------------------------------- + // + // target the real tissues with 2 second increments by default - tissue_increment = TISSUE_FLAG | 0; - - // calculate ppO2, ppN2 and ppHe - calc_alveolar_pressures(); - - // All deco code is invoked every second. But as the tissue and CNS updates are based - // on 2 seconds periods, each update is done only on each 2nd second. In case a "fast - // forward" of the tissues is commanded, the 2-seconds rule is over-raided. - if( twosectimer || char_I_sim_advance_time ) + tissue_increment = TISSUE_SELECTOR | 0; + + + // Tasks every second, if more than 1 invocation per second: on the first section of the second. + // Requests for tissue "fast forward" are executed immediately. +#if (INVOKES_PER_SECOND > 1) + if( ( sequence_timer == 0 ) + || ( sequence_timer == INVOKES_PER_SECOND ) + || ( char_I_sim_advance_time > 0 ) + ) +#endif { + // acquire current environmental data + calc_hauptroutine_data_input(); + + // calculate ppO2, ppN2 and ppHe + calc_alveolar_pressures(); + + // add decent calculation here and include trigger in above if-statement + // TODO + + } // tasks every second, on the first section of the second + + + // Tasks every 2 seconds, on the first section of the respective second. + // Requests for tissue "fast forward" are executed immediately. + if( ( sequence_timer == 0 ) + || ( char_I_sim_advance_time > 0 ) + ) + { + // Tissue and CNS updates are based on 2 seconds periods! + // Set up normal tissue updating or "fast forward" updating for simulator // sim+5' function and deco calculator bottom time calculation. if( char_I_sim_advance_time > 0 ) { // configure "fast forward" tissue updating - tissue_increment = TISSUE_FLAG | char_I_sim_advance_time; - - // clear the "mailbox" + tissue_increment = TISSUE_SELECTOR | char_I_sim_advance_time; + + // clear the request char_I_sim_advance_time = 0; } @@ -1630,753 +2190,498 @@ 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 - - // convert ceiling from float to integer for export [mbar relative pressure] + if ( char_I_deco_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(); - // convert leading tissue supersaturation value from float to integer for export [%] - convert_GF_for_display(); - - // convert CNS value from float to integer for export - convert_CNS_for_display(); - } - - //---- 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 - if( (EAD < 0.0) || (EAD > 245.5) ) char_O_EAD = 0; - else char_O_EAD = (unsigned char)(EAD + 0.5); - - // export END - if( (END < 0.0) || (END > 245.5) ) char_O_END = 0; - else char_O_END = (unsigned char)(END + 0.5); - - - //---- Compute ppO2 Values in [cbar] --------------------------------------------------------- - - // pure oxygen ppO2 - if ( O2_ppO2 < 0.01 ) int_O_O2_ppO2 = 0; - else if ( O2_ppO2 >= 9.995 ) int_O_O2_ppO2 = 999; - else int_O_O2_ppO2 = (unsigned int)(100 * O2_ppO2 + 0.5); - - // pure gas ppO2 - if ( OC_ppO2 < 0.01 ) int_O_pure_ppO2 = 0; - else if ( OC_ppO2 >= 9.995 ) int_O_pure_ppO2 = 999; - else int_O_pure_ppO2 = (unsigned int)(100 * OC_ppO2 + 0.5); - - // calculated pSCR ppO2 - if ( pSCR_ppO2 < 0.01 ) int_O_pSCR_ppO2 = 0; - else if ( pSCR_ppO2 >= 9.995 ) int_O_pSCR_ppO2 = 999; - else int_O_pSCR_ppO2 = (unsigned int)(100 * pSCR_ppO2 + 0.5); - - // breathed ppO2 - if ( ppO2 < 0.01 ) int_O_breathed_ppO2 = 0; - else if ( ppO2 >= 9.995 ) int_O_breathed_ppO2 = 999; - else int_O_breathed_ppO2 = (unsigned int)(100 * ppO2 + 0.5); - - - //---- Set/Reset Deco Mode -------------------------------------------------------------------- - - // Set the deco mode flag if: - // - breathing an OC deco gas (gas type 3), or - // - breathing a gas or diluent that officially is disabled (type 0), or - // - if nearby or above the deepest deco stop (nearby means 1 meter below, the additional 0.9 serves rounding effects) - if ( ( char_I_current_gas_type == 3 ) - || ( char_I_current_gas_type == 0 ) - || ( (unsigned char)((real_pres_respiration - pres_surface) * BAR_TO_METER - 1.9) < char_O_first_deco_depth ) - ) - char_O_deco_info |= DECO_FLAG; - else - char_O_deco_info &= ~DECO_FLAG; - - - //---- Compute ppO2 Warnings ------------------------------------------------------------------ - - // compute conditional min/max values - int_ppO2_min = (char_O_main_status & DECO_MODE_LOOP) ? (unsigned int)char_I_ppO2_min_loop : (unsigned int)char_I_ppO2_min; - int_ppO2_max = (char_O_deco_info & DECO_FLAG ) ? (unsigned int)char_I_ppO2_max_deco : (unsigned int)char_I_ppO2_max; - - // get biggest of char_I_ppO2_max / char_I_ppO2_max_deco - int_ppO2_max_max = ( char_I_ppO2_max_deco > char_I_ppO2_max ) ? char_I_ppO2_max_deco : char_I_ppO2_max; - - // default value for the upper diluent ppO2 warning threshold is the normal upper warning threshold - int_ppO2_max_dil = int_ppO2_max; - - // when in CCR mode, the upper diluent warning threshold gets adjust according to the current setpoint - if( (char_O_main_status & DECO_MODE_MASK) == DECO_MODE_CCR ) + // convert the saturation value of the leading tissue to integer + convert_sat_for_display(); + + // convert the CNS value to integer + convert_cur_CNS_for_display(); + + } // tasks every 2 seconds + + + // Tasks every second, if more than 1 invocation per second: on the first section of the second. +#if (INVOKES_PER_SECOND > 1) + if( ( sequence_timer == 0 ) + || ( sequence_timer == INVOKES_PER_SECOND ) + ) +#endif { - overlay unsigned int max_dil; - - // The upper diluent ppO2 threshold is ppO2_GAP_TO_SETPOINT below the setpoint... - // (the condition protects from negative numbers which would cause a wrap-around in unsigned integers) - max_dil = (char_I_const_ppO2 > ppO2_GAP_TO_SETPOINT) ? (unsigned int)(char_I_const_ppO2 - ppO2_GAP_TO_SETPOINT) : 0; - - // ...but never above int_ppO2_max. - if( max_dil < int_ppO2_max ) int_ppO2_max_dil = max_dil; - - // We do not need to guard int_ppO2_max_dil against becoming lower than char_I_ppO2_min because the check - // against char_I_ppO2_min is done first and will then raise a low warning and inhibit further checks. - } - - // check for safe range of pure oxygen - if ( int_O_O2_ppO2 >= int_ppO2_max ) int_O_O2_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH; - - // check for safe range of breathed gas - if ( int_O_breathed_ppO2 <= int_ppO2_min ) int_O_breathed_ppO2 |= INT_FLAG_WARNING + INT_FLAG_LOW; - else if ( int_O_breathed_ppO2 >= int_ppO2_max_max ) int_O_breathed_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH; - else if ( char_O_deco_info & DECO_FLAG ) ; // no attention generated in deco mode - else if ( char_O_main_status & DECO_MODE_LOOP ) ; // no attention generated in loop modes - else if ( int_O_breathed_ppO2 >= (unsigned int)char_I_ppO2_max ) int_O_breathed_ppO2 |= INT_FLAG_ATTENTION; - - // check for safe range of pure diluent - if ( int_O_pure_ppO2 <= (unsigned int)char_I_ppO2_min ) int_O_pure_ppO2 |= INT_FLAG_WARNING + INT_FLAG_LOW; - else if ( int_O_pure_ppO2 >= int_ppO2_max ) int_O_pure_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH; - else if ( int_O_pure_ppO2 >= int_ppO2_max_dil ) int_O_pure_ppO2 |= INT_FLAG_ATTENTION; - - // check for safe range of calculated pSCR loop gas - if ( int_O_pSCR_ppO2 <= int_ppO2_min ) int_O_pSCR_ppO2 |= INT_FLAG_WARNING + INT_FLAG_LOW; - else if ( int_O_pSCR_ppO2 >= int_ppO2_max ) int_O_pSCR_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH; + //---- 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; + + + //---- 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 +#endif + + + //---- Set/Reset 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 and we are less deep than 1 meter below the deepest deco stop + if ( ( deco_info & DECO_FLAG ) == 0 ) + if ( ( char_I_current_gas_type == 3 ) + || ( char_I_current_gas_type == 0 ) + || ( ( char_O_deco_depth[0] > 0 ) && ( char_depth_real <= char_O_deco_depth[0] + 1 ) ) + ) + deco_info |= DECO_FLAG; + + // Clear the deco mode flag if: + // deco mode is set + // AND deeper than 7 meters below deepest deco stop (7 meters = 2 stop depth intervals plus 1 meter below stop) + if ( ( deco_info & DECO_FLAG ) > 0 ) + if ( ( char_depth_real > char_O_deco_depth[0] + 7 ) + ) + deco_info &= ~DECO_FLAG; + + + //---- Compute ppO2 Warnings ------------------------------------------------------------------ + + // compute conditional min values +#ifdef _ccr_pscr + int_ppO2_min = ( main_status & MODE_LOOP ) ? (unsigned short)char_I_ppO2_min_loop : (unsigned short)char_I_ppO2_min; +#else + int_ppO2_min = (unsigned short)char_I_ppO2_min; +#endif + + // compute conditional max values + int_ppO2_max = ( deco_info & DECO_FLAG ) ? (unsigned short)char_I_ppO2_max_deco : (unsigned short)char_I_ppO2_max_work; + + // add some margin on ppO2 max to compensate for surface pressures > 1.000 mbar + int_ppO2_max += ppO2_MARGIN_ON_MAX; + + // get biggest of char_I_ppO2_max_work / char_I_ppO2_max_deco + int_ppO2_max_max = ( char_I_ppO2_max_deco > char_I_ppO2_max_work ) ? char_I_ppO2_max_deco : char_I_ppO2_max_work; + +#ifdef _ccr_pscr + // default value for the upper diluent ppO2 warning threshold is the normal upper warning threshold + int_ppO2_max_dil = int_ppO2_max; + + // when in CCR mode, the upper diluent warning threshold gets adjust according to the current setpoint + if( (main_status & MODE_MASK) == MODE_CCR ) + { + overlay unsigned short max_dil; + + // The upper diluent ppO2 threshold is ppO2_GAP_TO_SETPOINT below the setpoint... + // (the condition protects from negative numbers which would cause a wrap-around in unsigned integers) + max_dil = (char_I_const_ppO2 > ppO2_GAP_TO_SETPOINT) ? (unsigned short)(char_I_const_ppO2 - ppO2_GAP_TO_SETPOINT) : 0; + + // ...but never above int_ppO2_max. + if( max_dil < int_ppO2_max ) int_ppO2_max_dil = max_dil; + + // We do not need to guard int_ppO2_max_dil against becoming lower than char_I_ppO2_min because the check + // against char_I_ppO2_min is done first and will then raise a low warning and inhibit further checks. + } +#endif + + // check for safe range of breathed gas + if ( int_O_breathed_ppO2 <= int_ppO2_min ) int_O_breathed_ppO2 |= INT_FLAG_WARNING + INT_FLAG_LOW; + else if ( int_O_breathed_ppO2 >= int_ppO2_max_max ) int_O_breathed_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH; + else if ( deco_info & DECO_FLAG ) ; // no attention generated in deco mode + else if ( main_status & MODE_LOOP ) ; // no attention generated in loop modes + else if ( int_O_breathed_ppO2 >= (unsigned short)char_I_ppO2_max_work ) int_O_breathed_ppO2 |= INT_FLAG_ATTENTION; + +#ifdef _ccr_pscr + // check for safe range of pure oxygen + if ( int_O_O2_ppO2 >= int_ppO2_max ) int_O_O2_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH; + + // check for safe range of pure diluent + if ( int_O_pure_ppO2 <= (unsigned short)char_I_ppO2_min ) int_O_pure_ppO2 |= INT_FLAG_WARNING + INT_FLAG_LOW; + else if ( int_O_pure_ppO2 >= int_ppO2_max ) int_O_pure_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH; + else if ( int_O_pure_ppO2 >= int_ppO2_max_dil ) int_O_pure_ppO2 |= INT_FLAG_ATTENTION; + + // check for safe range of calculated pSCR loop gas + if ( int_O_pSCR_ppO2 <= int_ppO2_min ) int_O_pSCR_ppO2 |= INT_FLAG_WARNING + INT_FLAG_LOW; + else if ( int_O_pSCR_ppO2 >= int_ppO2_max ) int_O_pSCR_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH; +#endif + + } // tasks every second / on the first section of the second #ifdef _rx_functions - //---- Process Pressure Readings (OSTC TR only) ----------------------------------------------- - - // only for OSTC TR model with TR functions enabled - if( char_O_main_status & DECO_TR_FUNCTIONS ) + // only when TR functions are enabled + if( main_status & TR_FUNCTIONS ) + + // Tasks every second, if more than 1 invocation per second: on the second section of the second. +#if (INVOKES_PER_SECOND > 1) + if( ( sequence_timer == 1 ) + || ( sequence_timer == INVOKES_PER_SECOND + 1 ) + ) +#endif { - // pressure warnings for reading 1, but only if enabled and pressure value available - if( (char_I_pressure_gas[0] > 0) && !(int_IO_pressure_value[0] & INT_FLAG_NOT_AVAIL) ) - { - overlay unsigned int pressure_value = int_IO_pressure_value[0] & ~INT_FLAG_OUTDATED; - - if( (char_I_pressure_gas[0] < 6 ) && !(int_O_pressure_need[0] & INT_FLAG_NOT_AVAIL) ) - { - // not a dil and need available: warning & attention by need - if( pressure_value <= int_O_pressure_need[0]) - int_IO_pressure_value[0] |= INT_FLAG_WARNING; - else if( pressure_value <= int_O_pressure_need[0] + int_O_pressure_need[0] / 2 ) - int_IO_pressure_value[0] |= INT_FLAG_ATTENTION; - } - else - { - // a dil or need not available: warning & attention by fixed thresholds - if ( pressure_value <= PRESSURE_LIMIT_WARNING ) int_IO_pressure_value[0] |= INT_FLAG_WARNING; - else if( pressure_value <= PRESSURE_LIMIT_ATTENTION ) int_IO_pressure_value[0] |= INT_FLAG_ATTENTION; - } - } - - // pressure warnings for reading 2, but only if enabled and pressure value available - if( (char_I_pressure_gas[1] > 0) && !(int_IO_pressure_value[1] & INT_FLAG_NOT_AVAIL) ) - { - overlay unsigned int pressure_value = int_IO_pressure_value[1] & ~INT_FLAG_OUTDATED; - - if( (char_I_pressure_gas[1] < 6 ) && !(int_O_pressure_need[1] & INT_FLAG_NOT_AVAIL) ) - { - // not a dil and need available: warning & attention by need - if( pressure_value <= int_O_pressure_need[1]) - int_IO_pressure_value[1] |= INT_FLAG_WARNING; - else if( pressure_value <= int_O_pressure_need[1] + int_O_pressure_need[1] / 2 ) - int_IO_pressure_value[1] |= INT_FLAG_ATTENTION; - } - else - { - // a dil or need not available: warning & attention by fixed thresholds - if( pressure_value <= PRESSURE_LIMIT_WARNING ) int_IO_pressure_value[1] |= INT_FLAG_WARNING; - else if( pressure_value <= PRESSURE_LIMIT_ATTENTION ) int_IO_pressure_value[1] |= INT_FLAG_ATTENTION; - } - } - - //--- SAC Calculation --------------------------------------------------------------------- - // - // char_I_SAC_mode =0: disabled - // =1: SAC from 1st reading - // =2: SAC from 2nd reading - // =3: SAC from higher one of both pressure drops (independent double mode) - // =4: SAC (O2 usage) from 2nd reading without real_pres_respiration term - - // set SAC rate to not available by default - int_O_sac_rate = 0 + INT_FLAG_NOT_AVAIL; - - // get a copy of the current absolute pressure - pres_respiration_sac = real_pres_respiration; - - // set threshold for SAC rate attention - max_sac_rate = (char_O_deco_info & DECO_FLAG) ? char_I_deco_usage : char_I_bottom_usage; - - // char_I_deco_usage / char_I_bottom_usage are in l/min, max_sac_rate is in 0.1 l/min - max_sac_rate *= 10; - - - // pre-process SAC mode 3 (independent double) - if( char_I_SAC_mode == 3 ) - { - overlay unsigned char reading1_gas; - overlay unsigned char reading2_gas; - overlay unsigned char reading1_tanksize; - overlay unsigned char reading2_tanksize; - overlay unsigned short reading1_press; - overlay unsigned short reading2_press; - overlay unsigned short reading1_drop; - overlay unsigned short reading2_drop; - - // get gas numbers (1-10) of both readings - reading1_gas = char_I_pressure_gas[0]; - reading2_gas = char_I_pressure_gas[1]; - - // default to no SAC calculation - char_I_SAC_mode = 0; - - // clear switch advice by default - char_O_deco_info &= ~IND_DOUBLE_SWITCH_FLAG; - - // check if both readings are configured and available - if( reading1_gas ) - if( reading2_gas ) - if( !(int_IO_pressure_value[0] & INT_FLAG_NOT_AVAIL) ) - if( !(int_IO_pressure_value[1] & INT_FLAG_NOT_AVAIL) ) - if( !(int_I_pressure_drop[0] & INT_FLAG_NOT_AVAIL) ) - if( !(int_I_pressure_drop[1] & INT_FLAG_NOT_AVAIL) ) - { - // get tank pressures, stripping flags - reading1_press = int_IO_pressure_value[0] & 0x0FFF; // in 0.1 bar - reading2_press = int_IO_pressure_value[1] & 0x0FFF; // in 0.1 bar - - // get pressure drops as integers, stripping flags and shifting right - // to avoid an overflow when multiplying with the tank size later on - reading1_drop = (int_I_pressure_drop[0] & 0x0FFF) >> 2; - reading2_drop = (int_I_pressure_drop[1] & 0x0FFF) >> 2; - - // get tank sizes - reading1_tanksize = char_I_tank_size[reading1_gas-1]; - reading2_tanksize = char_I_tank_size[reading2_gas-1]; - - // set mode to calculate SAC on the reading with the higher absolute drop - char_I_SAC_mode = (reading1_drop * reading1_tanksize > reading2_drop * reading2_tanksize) ? 1 : 2; - - // compute switch advice if pressure (in 0.1 bar) of tank breathed from is - // more than char_I_max_pres_diff (in bar) below pressure of the other tank. - if( char_I_SAC_mode == 1 ) - { - // breathing from reading 1, switch advice if pressure on reading 1 lower than on 2 - if( (reading1_press + 10*char_I_max_pres_diff) <= reading2_press ) - char_O_deco_info |= IND_DOUBLE_SWITCH_FLAG; - } - else - { - // breathing from reading 2, switch advice if pressure on reading 2 lower than on 1 - if( (reading2_press + 10*char_I_max_pres_diff) <= reading1_press ) - char_O_deco_info |= IND_DOUBLE_SWITCH_FLAG; - } - } - } - - - // pre-process SAC mode 4 (O2 usage by reading 2) - if( char_I_SAC_mode == 4 ) - { - // O2 usage on CCR is independent from absolute pressure - pres_respiration_sac = 1.0; - - // O2 pressure drop is measured via reading 2 - char_I_SAC_mode = 2; - - // reconfigure max SAC rate to O2 consumption attention threshold - max_sac_rate = O2_CONSUMPTION_LIMIT_ATTENTION; - } - - - // calculate SAC - modes 1 & 2 - if( (char_I_SAC_mode == 1) || (char_I_SAC_mode == 2) ) - { - overlay unsigned char reading_index; - overlay unsigned char reading_gas; - overlay unsigned char reading_tanksize; - overlay float reading_drop; - - // set index: char_I_SAC_mode = 1 -> reading one, index 0 - // = 2 -> two, 1 - reading_index = char_I_SAC_mode - 1; - - // get gas number (1-10) - reading_gas = char_I_pressure_gas[reading_index]; - - // check if reading is configured and available - if( reading_gas ) - if( !(int_I_pressure_drop[reading_index] & INT_FLAG_NOT_AVAIL) ) - { - // get tank size (in liter) - reading_tanksize = char_I_tank_size[reading_gas-1]; - - // get pressure drop as float, stripping flags (in 1/5120 bar/sec) - reading_drop = (float)(int_I_pressure_drop[reading_index] & 0x0FFF); - - // check if pressure drop is within range - if( !(int_I_pressure_drop[reading_index] & INT_FLAG_OUT_OF_RANGE) ) - { - // calculate SAC, 10 is factor to have result in 0.1 liter/min - // 60 is factor for 60 seconds per 1 minute, - // 5120 accounts for reading_drop being in 1/5120 bar/sec - // 10*60/5120 = 60/512 = 15/128 - float_sac = reading_drop * 15/128 * reading_tanksize / pres_respiration_sac; - - // limit result to 999 (99.9 liter/min) - if ( float_sac >= 998.5 ) - { - int_O_sac_rate = 999 + INT_FLAG_ATTENTION; - } - else - { - // convert float to integer - int_O_sac_rate = (unsigned short)(float_sac + 0.5); - - // set attention flag if exceeding SAC threshold, but only if pressure drop is not outdated - if( !(int_I_pressure_drop[reading_index] & INT_FLAG_OUTDATED) ) - if( int_O_sac_rate >= max_sac_rate ) - { - int_O_sac_rate |= INT_FLAG_ATTENTION; - } - } - } - else - { - // pressure drop is out of range, so SAC will be set out of range, too - int_O_sac_rate = 999 + INT_FLAG_ATTENTION; - } - - // copy outdated flag from int_I_pressure_drop to int_O_sac_rate - if( int_I_pressure_drop[reading_index] & INT_FLAG_OUTDATED ) - { - int_O_sac_rate |= INT_FLAG_OUTDATED; - } - } - } - } // TR functions - -#endif - - + calc_TR_functions(); + } + +#endif // _rx_functions + + + // //---- End of Computations for the real Tissues ----------------------------------------------- // + //============================================================================================= + // - //---- Begin of Computations for Ascent and Decompression ------------------------------------- - - // branch to the code for the current phase the deco calculations are in, i.e. - // toggle between calculating NDL (remaining bottom time), deco stops, and results - switch( char_O_deco_status & DECO_STATUS_MASK ) + //---- Begin of Computations for Ascent and Decompression (simulated Tissues) ----------------- + // + + // Dispatcher: select what to do based on the current calculation phase + do { - overlay unsigned char i; - - default: - - case DECO_STATUS_INIT: //---- At surface: Start a new dive --------------------- - - // clear the internal stops table from remains lasting from the previous dive or deco calculator run - clear_deco_table(); - - // publish the cleared stops table to the display functions - publish_deco_table(); - - // clear the gas needs table - for( i = 0; i < NUM_GAS; ++i ) + +#ifdef _profiling + profiling_phase = next_planning_phase; +#endif + + switch( next_planning_phase ) { - int_O_ascent_volumes[i] = 0; - int_O_ascent_pres_need[i] = 0 + INT_FLAG_ZERO; - } - - // safety limits to prevent eventual infinite looping (bricking the OSTC) - if( char_I_ascent_speed < 5 ) char_I_ascent_speed = 5; // min. 5 m/min - if( char_I_deco_distance > 20 ) char_I_deco_distance = 20; // max. 20 dm (= 2 m) - if( char_I_desaturation_multiplier < 50 ) char_I_desaturation_multiplier = 50; // min. 50 % - - // initialize values that are constant during the course of the dive - float_ascent_speed = 1.00 * char_I_ascent_speed; // in meter/minute - float_deco_distance = 0.01 * char_I_deco_distance; // in bar - float_desaturation_multiplier = 0.01 * char_I_desaturation_multiplier; // as factor, 1.0 = 100% - float_saturation_multiplier = 0.01 * char_I_saturation_multiplier; // as factor, 1.0 = 100% + + // + //---- once-per-dive Initialization of the Deco Engine ------------------------------------ + // + case PHASE_10_DIVE_INIT: + + // initialize all output variables to defaults + init_output_vars(); + + // safeguard input parameters that are constant during the course of the dive + if( char_I_deco_distance > 20 ) char_I_deco_distance = 20; + 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_deco_distance = 0.01 * char_I_deco_distance; + float_ascent_speed = 1.00 * char_I_ascent_speed; // initialize values that will be recalculated later on periodically - char_O_nullzeit = 0; // reset NDL time for the normal plan - char_O_alternate_nullzeit = 0; // reset NDL time for the alternative plan - int_O_ascenttime = 0; // reset ascent time for the normal plan - int_O_alternate_ascenttime = 0; // reset ascent time for the alternative plan - char_O_deco_warnings = 0; // reset all deco warnings - char_O_deco_info = 0; // reset all deco infos - deco_tissue_vector = 0; // reset tissue deco vector - IBCD_tissue_vector = 0; // reset tissue IBCD vector - NDL_lead_tissue_norm = 0; // reset first tissue to look at during NDL calculation - NDL_lead_tissue_alt = 0; // reset first tissue to look at during NDL calculation - - // tag desaturation time as invalid (it will not be computed during a dive) - int_O_desaturation_time = 65535; - - // initialize values for first stop depth and GF slope - low_depth_norm = 0.0; // reset depth of first stop in normal plan - locked_GF_step_norm = 0.0; // reset GF slope in normal plan - low_depth_alt = 0.0; // reset depth of first stop in alternative plan - locked_GF_step_alt = 0.0; // reset GF slope in alternative plan - - // initialize CNS values - int_O_normal_CNS_fraction = int_O_alternate_CNS_fraction = int_O_CNS_fraction; + 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 = 0; + GF_low_last = 0; + + +#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; + char_O_profiling_runs_norm = 0; + char_O_profiling_runs_alt = 0; +#endif + + + // the next calculation phase will do the cyclic initialization of the deco engine if a + // normal or alternative plan shall be calculated, else the calculation cycle is done. + if( deco_status & PLAN_MASK ) next_planning_phase = PHASE_11_CYCLIC_INIT; + else next_planning_phase = PHASE_00_DONE; + + break; + // - // --> code execution continues in state DECO_STATUS_START + //---- once-per-cycle Initialization of the Deco Engine------------------------------------ // - - case DECO_STATUS_START: //---- Start a new deco calculation cycle -------------- - - // clear the internal(!) stops table + case PHASE_11_CYCLIC_INIT: + + // target the simulated tissues (flag bit 7 = 0) + tissue_increment = 0; + + // clear the internal stops table clear_deco_table(); // initialize the simulated tissues with the current state of the real tissues for( i = 0; i < NUM_COMP; i++ ) { - sim_pres_tissue_N2[i] = pres_tissue_N2[i]; - sim_pres_tissue_He[i] = pres_tissue_He[i]; - } - - // initialize the simulated CNS value with the current CNS of the real tissues - sim_CNS_fraction = CNS_fraction; - - // initialize the simulated depth with the current depth (in absolute pressure) - sim_pres_respiration = real_pres_respiration; - - // Lookup the current gas and store it also as the first gas used. - // This gas will be used until gas_find_better() is invoked and finds - // a better gas to switch to. - gas_find_current(); - - // Setup the calculation ratio's for N2, He and O2 (sim_N2/He/_O2_ratio). - // These ratios will be used and remain valid to use until a gas switch - // is done. Thus, if a call to gas_find_better() has found a better gas, - // gas_set_ratios() needs to be called again. - gas_set_ratios(); - - // Calculate the effect of extended bottom time due to delayed ascent, - // if requested. - if( char_O_deco_status & DECO_ASCENT_DELAYED ) - { - // program interval on simulated tissues (flag bit 7 = 0) - tissue_increment = char_I_extra_time; - - // calculate ppO2, ppN2 and ppHe from sim_N2/real_He_ratio - calc_alveolar_pressures(); - - // update the tissues - calc_tissues(); - - // update the CNS value - calc_CNS(); - } - - // Calculate the remaining no decompression limit (NDL) time. calc_NDL_time() - // is very fast in detecting if being beyond NDL, so there is enough time left - // in this phase to do the initial ascent calculation if found to be outside NDL. - calc_NDL_time(); - - if( NDL_time == 0 ) - { - // calculate ascent to first stop using the set ascent rate, - // re-calculating the tissues and limits every minute along the ascent. - calc_ascent_to_first_stop(); - - // continue in next cycle(s) with calculating the initial ascent and stops - char_O_deco_status &= ~DECO_STATUS_MASK; - char_O_deco_status |= DECO_STATUS_STOPS; - } - else - { - // within NDL - continue in next cycle with gathering all results - char_O_deco_status &= ~DECO_STATUS_MASK; - char_O_deco_status |= DECO_STATUS_RESULTS; + sim_pres_tissue_N2[i] = real_pres_tissue_N2[i]; + sim_pres_tissue_He[i] = real_pres_tissue_He[i]; } - break; - - - case DECO_STATUS_STOPS: //---- Calculate Stops --------------------------------- - - // calculate the stops - calc_hauptroutine_calc_deco(); - - // calc_hauptroutine_calc_deco() iterates in this phase as long as it is - // calculating the stops. Once done, it will set the status to doing the - // results gathering. - - break; - - - case DECO_STATUS_RESULTS: //--- Gather Results --------------------------------- - - // if in normal plan, publish the stops table - if( !(char_O_deco_status & DECO_PLAN_ALTERNATE) ) + // initialize GF parameters if using GF model + if( char_I_deco_model != 0 ) { - // publish the stops table to the display functions - publish_deco_table(); - - // When entering deco and the ceiling depth becomes > 0 but the - // deco calculation reveals no distinct deco stop yet because - // 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_first_deco_depth == 0 ) // simulation reveals no stop required - if( int_O_ceiling > 0 ) // real status reveals a ceiling + // update GF parameters (GFs may have been switched between GF and aGF) + if( (char_I_GF_High_percentage != GF_high_last) || (char_I_GF_Low_percentage != GF_low_last) ) { - // set a pro forma stop at 3 meters - char_O_first_deco_depth = 3; - - // set a stop time of 0 minute, this will be displayed as "..'" - char_O_first_deco_time = 0; + // store new values in integer format + GF_high_last = char_I_GF_High_percentage; + GF_low_last = char_I_GF_Low_percentage; + + // store new values in float format + GF_high = 0.01 * char_I_GF_High_percentage; + GF_low = 0.01 * char_I_GF_Low_percentage; + + // 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; } - } - - // The current depth is needed by calc_ascenttime() and gas_volumes(). As we - // don't want it to be calculated multiple times, it's done here on stockpile. - char_bottom_depth = (unsigned char)((real_pres_respiration - pres_surface) * BAR_TO_METER + 0.5); - - // results to publish depend whether within NDL or in deco - if( NDL_time ) - { - //---- within NDL ---------------------------------------------- - - // check which plan we are on - if( char_O_deco_status & DECO_PLAN_ALTERNATE ) + + // retrieve GF parameters for current calculation cycle + if( deco_status & CALC_NORM ) { - //---- alternate dive plan --------------------------------- - - // output NDL time - char_O_alternate_nullzeit = NDL_time; - - // clear ascent time - int_O_alternate_ascenttime = 0; - - // As we are in no stop, CNS at end of dive is more or less - // the same CNS as we have right now. - int_O_alternate_CNS_fraction = int_O_CNS_fraction; + GF_low_depth = GF_low_depth_norm; + GF_slope = GF_slope_norm; } else { - //---- normal dive plan ------------------------------------ - - // output NDL time - char_O_nullzeit = NDL_time; - - // clear ascent time - int_O_ascenttime = 0; - - // As we are in no stop, CNS at end of dive is more or less - // the same CNS as we have right now. - int_O_normal_CNS_fraction = int_O_CNS_fraction; + GF_low_depth = GF_low_depth_alt; + GF_slope = GF_slope_alt; } - } // NDL - else - { - //---- in DECO ------------------------------------------------- - - // calculate the ascent time - calc_ascenttime(); - - // check which plan we are on - if( char_O_deco_status & DECO_PLAN_ALTERNATE ) - { - //---- alternative plan ---------------------------------------------------- - - // clear the NDL time - char_O_alternate_nullzeit = 0; - - // export the ascent time - int_O_alternate_ascenttime = ascent_time; - - // convert the CNS value to integer for export - convert_sim_CNS_for_display(); - - // export the integer CNS value - int_O_alternate_CNS_fraction = int_sim_CNS_fraction; - - } // alternative plan - else - { - //---- normal plan --------------------------------------------------------- - - // clear the NDL time - char_O_nullzeit = 0; - - // export the ascent time - int_O_ascenttime = ascent_time; - - // convert the CNS value to integer for export - convert_sim_CNS_for_display(); - - // export the integer CNS value - int_O_normal_CNS_fraction = int_sim_CNS_fraction; - - } // normal plan - } // NDL / DECO - - - // Check if deco obligation is steady or decreasing. This works only when an alternative plan is enabled and - // if it is not a bailout plan, thus DECO_BAILOUT_MODE must not be set while doing the DECO_PLAN_ALTERNATE. - if( (char_O_deco_status & DECO_PLAN_ALTERNATE) && !(char_O_deco_status & DECO_BAILOUT_MODE) ) - { - // Set DECO_DECREASING flag when fTTS < TTS and DECO_STEADY flag when fTTS = TTS. - if ( int_O_alternate_ascenttime < int_O_ascenttime ) char_O_deco_info |= DECO_DECREASING; - else if ( int_O_alternate_ascenttime == int_O_ascenttime ) char_O_deco_info |= DECO_STEADY; } - // Clear DECO_DECREASING flag when fTTS >= TTS and DECO_STEADY flag when fTTS > TTS. - // This works in any planning mode combination. - if ( int_O_alternate_ascenttime > int_O_ascenttime ) char_O_deco_info &= ~(DECO_DECREASING + DECO_STEADY); - else if ( int_O_alternate_ascenttime == int_O_ascenttime ) char_O_deco_info &= ~(DECO_DECREASING ); - - // If requested, calculate the required gas volumes and tank pressures at the end of the dive. - if( char_O_deco_status & DECO_VOLUME_CALCULATE ) - { - // When in bailout mode and within NDL, find the gas changes along the ascent and put - // them into the stops table for use by gas_volumes(). The stops table can be "polluted" - // by now because the table has already been published in "clean" state before. - if( (NDL_time) && ( char_O_deco_status & DECO_BAILOUT_MODE ) ) - { - // find the gas changes and put them into the stops table - find_NDL_gas_changes(); - } - - // calculate the required gas volumes and tank pressures - gas_volumes(); - } - - // set the computation cycle to finished - char_O_deco_status &= ~DECO_STATUS_MASK; - - // set flag indicating that deco calculation has been completed - if( char_O_deco_status & DECO_PLAN_ALTERNATE ) char_O_main_status |= DECO_COMPLETED_ALT; - else char_O_main_status |= DECO_COMPLETED_NORM; - + // 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 (in absolute pressure) + sim_pres_respiration = real_pres_respiration; + + // compute the depth in meters where we are now + float_depth_real = (sim_pres_respiration - pres_surface) * BAR_TO_METER; + + // convert to integer and round up to next full meter + char_depth_real = (unsigned char)(float_depth_real + 0.99); + + // initialize depth for deco ascent calculation + char_depth_sim = char_depth_real; + + // 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 + // gas_find_better() is invoked and finds a better gas to switch to. + gas_find_current(); + + // Setup the calculation ratio's for N2, He and O2 (sim_N2/He/_O2_ratio). These ratios + // can be kept until a gas switch is done. Thus, if a call to gas_find_better() has + // found a better gas and initiated a switch, gas_set_ratios() needs to be called again. + gas_set_ratios(); + + // compute ppO2, ppN2 and ppHe for current depth from sim_pres_respiration + calc_alveolar_pressures(); + + // initialize the no decompression limit (NDL) time to 240 minutes + NDL_time = 240; + + // 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; + + // start calculating NDL time with the tissue that had the shortest NDL last time + 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 calc_gas_needs_ascent() + gas_needs_next_phase = GAS_NEEDS_INIT; + + // initialization for convert_gas_needs_to_press() + gas_needs_gas_index = 0; + + +#ifdef _profiling + profiling_runs = 0; +#endif + + // The next calculation phase will + // - calculate the bottom segment if extended bottom time is configured (fTTS), + // - proceed with calculating the NDL time else. + if ( deco_status & DELAYED_ASCENT ) next_planning_phase = PHASE_20_EXTENDED_BOTTOM_TIME; + else next_planning_phase = PHASE_30_NDL_TIME; + + break; + + + // + //---- extended Bottom Time --------------------------------------------------------------- + // + case PHASE_20_EXTENDED_BOTTOM_TIME: + + // program interval on simulated tissues (flag bit 7 = 0) + tissue_increment = char_I_extra_time; + + // calculate ppO2, ppN2 and ppHe + calc_alveolar_pressures(); + + // update the tissues + calc_tissues(); + + // update the CNS value + calc_CNS(); + + // the next calculation phase will calculate the NDL time + next_planning_phase = PHASE_30_NDL_TIME; break; - } // switch -} - -////////////////////////////////////////////////////////////////////////////// -// calc_hauptroutine_data_input -// -// Set all C-code dive parameters from their ASM-code values. -// Detect gas change condition. -// -void calc_hauptroutine_data_input(void) -{ - overlay float IG_ratio; - - // safety limits to prevent eventual infinite looping (bricking the OSTC) - if( int_I_pres_surface < 500) int_I_pres_surface = 500; // min. surface pressure = 500 mbar - if( int_I_pres_respiration < 500) int_I_pres_respiration = 500; // min. respiration pressure = 500 mbar - - // safe-guard further parameters to protect the tissue-flag - if( char_I_sim_advance_time > 127 ) char_I_sim_advance_time = 127; - if( char_I_extra_time > 127 ) char_I_extra_time = 127; - if( char_I_gas_change_time > 99 ) char_I_gas_change_time = 99; - - // get the current pressures - pres_surface = 0.001 * int_I_pres_surface; - real_pres_respiration = 0.001 * int_I_pres_respiration; - - // N2 tissue pressure at surface equilibrium, used for tissue graphics scaling - N2_equilibrium = 0.7902 * (pres_surface - ppWater); - - // read the GF settings (they may have been switch between GF/aGF) - GF_high = 0.01 * char_I_GF_High_percentage; - GF_low = 0.01 * char_I_GF_Low_percentage; - GF_delta = GF_high - GF_low; - - // get the currently breathed gas mixture - real_O2_ratio = 0.01 * char_I_O2_ratio; - real_He_ratio = 0.01 * char_I_He_ratio; - - // inert gas ratio (local helper variable) - IG_ratio = 1.00 - real_O2_ratio; - - // N2 ratio - real_N2_ratio = IG_ratio - real_He_ratio; - - // compute values for ppO2 drop in pSCR loop - real_pSCR_drop = IG_ratio * float_pSCR_factor; -} - - -////////////////////////////////////////////////////////////////////////////// -// Compute stops -// -// Note: because this can be very long, break on 16 iterations, or after -// 512 ms, whichever comes first. Set state to DECO_STATUS_RESULTS -// when finished, or keep DECO_STATUS_STOPS when needing to continue. -// -void calc_hauptroutine_calc_deco(void) -{ - overlay unsigned char loop; - - for( loop = 0; loop < 16; ++loop ) - { - // limit execution time to 512 ms using timer 5 - if( tmr5() & (512*32) ) break; - - // calc_nextdecodepth() + + // + //---- NDL Time --------------------------------------------------------------------------- + // + case PHASE_30_NDL_TIME: + + // Calculate the remaining no decompression limit (NDL) time for the tissue NDL_tissue. + // NDL_time will be updated if the NDL time found is shorter than the current NDL_time. + // + // In the initialization phase of the calculation cycle: + // - NDL_time had been initialized to 240 (minutes), + // - NDL_tissue had been initialized to the tissue with + // the shortest NDL time in the last cycle. + // + calc_NDL_time_tissue(); + + // advance to next tissue, wrapping around after last tissue + NDL_tissue = (NDL_tissue + 1) & (NUM_COMP - 1); + + // did we run out of NDL time or did we have probed all tissues? + if( (NDL_time == 0) || (NDL_tissue == NDL_tissue_start) ) + { + // YES + + // set the tissue with the shortest NDL time found as + // the one to start with in the next calculation cycle + 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 gathering the results if within NDL time, or + // - proceed with the initial ascent if beyond NDL time. +#ifdef _cave_mode + if ( main_status & CAVE_MODE ) next_planning_phase = PHASE_40_CAVE_ASCENT; + else +#endif + if ( NDL_time ) next_planning_phase = PHASE_70_RESULTS; + else next_planning_phase = PHASE_60_DECO_ASCENT; + } + + break; + + +#ifdef _cave_mode + // + //---- Cave Mode Return/Ascent ------------------------------------------------------------ // - // INPUT sim_pres_respiration : current depth in absolute pressure - // OUTPUT sim_depth_limit : depth of next stop in meters (if RETURN = true) - // next depth without need of a stop (if RETURN = false) - // RETURN true if a stop is needed, else false + case PHASE_40_CAVE_ASCENT: + + // TODO + + // the next calculation phase will gather all results + next_planning_phase = PHASE_70_RESULTS; + + break; +#endif + + // - // The function manages gas changes by itself, including priming - // the deco stop with the configured gas change time. + //---- Open Water Ascent with Deco Stops -------------------------------------------------- // - if( calc_nextdecodepth() ) + case PHASE_60_DECO_ASCENT: + + // program 1 minute interval on simulated tissues + tissue_increment = 1; + + // ascent to the next stop depth or the depth that is reachable within one minute of ascent + // and decide if a stop is required (return value = 1/true) or not (return value = 0/false) + if( find_next_stop() ) { - // this check should not be needed as in this case the RETURN value will be false - if( sim_depth_limit == 0 ) goto Surface; - - //---- stop required at sim_depth_limit ---------------------- - - // convert stop depth in meters to absolute pressure - sim_pres_respiration = sim_depth_limit * METER_TO_BAR + pres_surface; - - // Add one minute to the current stop, or add a new stop, - // or abort deco calculation if the deco table is full. - if( !update_deco_table(1) ) goto Surface; + //---- stop required -------------------- + + // check if there is a better gas to switch to + if( gas_find_better() ) + { + // set the new calculation ratios for N2, He and O2 + gas_set_ratios(); + + // doing extended stops? + if( main_status & EXTENDED_STOPS ) + { + // YES - set char_depth_sim to the gas change depth + char_depth_sim = sim_gas_current_depth; + + // - adjust absolute pressure down to the change depth + sim_pres_respiration = char_depth_sim * METER_TO_BAR + pres_surface; + } + + // prime the deco stop with the gas change time + update_deco_table(char_I_gas_change_time); + } + + // add one minute to an existing stop or add a new stop at char_depth_sim, + // or abort stops calculation if the deco table is full + if( !update_deco_table(1) ) next_planning_phase = PHASE_70_RESULTS; } else { - //---- no stop required -------------------------------------- - - // convert next depth (without stop requirement) to absolute pressure - sim_pres_respiration = sim_depth_limit * METER_TO_BAR + pres_surface; - - // finish deco calculation if surface is reached - if( sim_pres_respiration <= pres_surface ) + //---- no stop required ----------------- + + // check if there is a better gas to switch to, but only: + // + // if extended stops are activated, + // OR if in bailout mode. + // + // Attention: do not use a && formula over both 'if' terms, the extended stops / bailout + // condition must be checked before a call to gas_find_better() is made! + // + if( (main_status & EXTENDED_STOPS) || (deco_status & BAILOUT_MODE) ) + if( gas_find_better() ) { -Surface: - // continue with gathering all results in the next calculation phase - char_O_deco_status &= ~DECO_STATUS_MASK; - char_O_deco_status |= DECO_STATUS_RESULTS; - - return; + // set the new calculation values for N2, He and O2 + gas_set_ratios(); + + // stop duration is the gas change time, a change time of 0 minutes + // will set a tissue calculation interval of 2 seconds + tissue_increment += char_I_gas_change_time; + + // set char_depth_sim to the gas change depth, but not deeper than + // the depth we came from. + // (char_depth_last holds the depth from before the ascent step) + char_depth_sim = (sim_gas_current_depth < char_depth_last) ? sim_gas_current_depth : char_depth_last; + + // adjust sim_pres_respiration to the adjusted value of char_depth_sim + sim_pres_respiration = char_depth_sim * METER_TO_BAR + pres_surface; + + // create a stop for the gas change in the stops table + update_deco_table(char_I_gas_change_time); } } - //---- as one minute as passed now, update the tissues ----------- - - // program 1 minute interval on simulated tissues - tissue_increment = 1; + //---- one minute has passed by now, update the tissues ---------------- // compute current ppO2, ppN2 and ppHe calc_alveolar_pressures(); @@ -2386,23 +2691,360 @@ // update the CNS value calc_CNS(); + + // finish stops calculation if the surface is reached + if( char_depth_sim == 0 ) next_planning_phase = PHASE_70_RESULTS; + + break; + + + /// + //--- Results - Initialization ------------------------------------------------------------ + // + case PHASE_70_RESULTS: + + // The current depth is needed by calc_ascenttime(), find_NDL_gas_changes() and + // calc_gas_needs_ascent(). As we don't want it to be calculated multiple times, + // it is done here on stockpile. + char_depth_bottom = (unsigned char)((real_pres_respiration - pres_surface) * BAR_TO_METER + 0.5); + + // The next calculation phase will + // - publish the stops table if in normal plan mode, + // - proceed with remaining results dependent on if within NDL, or + // - in deco + if ( deco_status & CALC_NORM ) next_planning_phase = PHASE_71_RESULTS_STOPS_TABLE; + else if ( NDL_time ) next_planning_phase = PHASE_72_RESULTS_NDL; + else next_planning_phase = PHASE_73_RESULTS_DECO; + + break; + + + /// + //--- Publish Stops Table ----------------------------------------------------------------- + // + case PHASE_71_RESULTS_STOPS_TABLE: + + // publish the stops table to the display functions + publish_deco_table(); + + // When entering deco and the ceiling depth becomes > 0 but the + // deco calculation reveals no distinct deco stop yet because + // 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 + { + // set a pro forma stop at the configured last stop depth + char_O_deco_depth[0] = char_I_depth_last_deco; + + // 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. + if ( NDL_time ) next_planning_phase = PHASE_72_RESULTS_NDL; + else next_planning_phase = PHASE_73_RESULTS_DECO; + + break; + + + /// + //--- Results - within NDL ---------------------------------------------------------------- + // + case PHASE_72_RESULTS_NDL: + + // results to publish depend on 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; + + // as we are in no stop, CNS at end of dive is more or less the same CNS as we have right now + int_O_CNS_norm = int_O_CNS_current; + } + else + { + // output the NDL time + char_O_NDL_alt = NDL_time; + + // clear the alternative ascent time + int_O_TTS_alt = 0; + + // as we are in no stop, CNS at end of dive is more or less the same CNS as we have right now + int_O_CNS_alt = int_O_CNS_current; + } + + // The next calculation phase will + // - finish the calculation cycle if no gas needs calculation configured, else + // - find gas switches when in bailout mode (we are in NDL), or + // - calculate the gas needs along the ascent + if ( !(main_status & CALC_VOLUME ) ) next_planning_phase = PHASE_90_FINISH; + else if ( (deco_status & BAILOUT_MODE) ) next_planning_phase = PHASE_80_GAS_NEEDS_SWITCHES; + else next_planning_phase = PHASE_81_GAS_NEEDS_ASCENT; + + break; + + + /// + //--- Results - in Deco ------------------------------------------------------------------- + // + case PHASE_73_RESULTS_DECO: + + // calculate the ascent time + calc_ascenttime(); + + // convert the CNS value to integer + convert_sim_CNS_for_display(); + + // results to publish depend on 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; + + // export the integer CNS value + int_O_CNS_norm = int_sim_CNS_fraction; + } + else + { + // clear the alternative NDL time + char_O_NDL_alt = 0; + + // export the ascent time + int_O_TTS_alt = ascent_time; + + // export the integer CNS value + int_O_CNS_alt = int_sim_CNS_fraction; + } + + // 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_81_GAS_NEEDS_ASCENT; + + break; + + + // + //--- Gas Needs - Switches ---------------------------------------------------------------- + // + case PHASE_80_GAS_NEEDS_SWITCHES: + + // When in bailout mode and within NDL, find the gas switches along the ascent and put + // them into the stops table. The stops table can be "polluted" by now because the table + // has already been published in "clean" state before. + find_NDL_gas_changes(); + + // the next calculation phase will calculate the gas needs along the ascent + next_planning_phase = PHASE_81_GAS_NEEDS_ASCENT; + + break; + + + // + //--- Gas Needs - calculate Ascent Needs using Data from Stop Table ----------------------- + // + case PHASE_81_GAS_NEEDS_ASCENT: + + // calculate the gas needs along the ascent + calc_gas_needs_ascent(); + + // if calculation has finished, advance to next calculation phase + if( gas_needs_next_phase == GAS_NEEDS_DONE ) next_planning_phase = PHASE_82_GAS_NEEDS_PRESSURES; + + break; + + + // + //--- Gas Needs - convert Volumes to Pressures -------------------------------------------- + // + case PHASE_82_GAS_NEEDS_PRESSURES: + + // 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(); + + // increment index to address next gas + gas_needs_gas_index++; + + // if all gases have been converted, advance to next calculation phase + if( gas_needs_gas_index == NUM_GAS ) next_planning_phase = PHASE_90_FINISH; + + break; + + + // + //--- 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) ) + { + if ( int_O_TTS_alt <= int_O_TTS_norm ) deco_info |= DECO_ZONE; + else deco_info &= ~DECO_ZONE; + } + + // export updated deco infos and warnings + char_O_deco_info = deco_info; + char_O_deco_warnings = deco_warnings; + + // restore command flag to indicate that deco calculation cycle has finished + char_O_deco_status = deco_status; + + // signal end of deco calculation + next_planning_phase = PHASE_00_DONE; + + break; + + } // switch + + // read timer 5, result will be stored in tmr5_value (in 1/32 ms) and tmr5_overflow + read_tmr5(); + + } // sequence calculation phases while not timed out and calculation cycle is not finished + while( (tmr5_overflow == 0) && ( next_planning_phase != PHASE_00_DONE ) ); + + // report where we are in terms of depth reached, used in deco calculator to show deco calculation progress + char_O_depth_sim = char_depth_sim; + + +#ifdef _profiling + + //---- Performance Measurement ------------------------------------------- + + // convert timer 5 readout into ms + profiling_runtime = tmr5_value / 32; + + // actual runtime longer than target runtime? + if( tmr5_overflow ) + { + // YES - report excess + int_O_profiling_overrun = profiling_runtime; + + // - excess > max we had so far? + if( int_O_profiling_overrun > int_O_profiling_overrun_max ) + { + // YES - update max + int_O_profiling_overrun_max = int_O_profiling_overrun; + + // - store the causing phase + char_O_profiling_overrun_phase = profiling_phase; + } } + else + { + // NO - calculate unused budget and flag it to be under-run time + int_O_profiling_overrun = (2048 - profiling_runtime) | 0x8000; + } + + // increment number of runs in current cycle + profiling_runs += 1; + + // planning cycle completed? + if( next_planning_phase == PHASE_00_DONE ) + { + // YES - export number of runs it took + if( deco_status & COMPLETED_NORM ) char_O_profiling_runs_norm = profiling_runs; + else char_O_profiling_runs_alt = profiling_runs; + } + +#endif + +} + + +////////////////////////////////////////////////////////////////////////////// +// calc_hauptroutine_data_input +// +// Set all C-code dive parameters from their ASM-code values. +// +void calc_hauptroutine_data_input(void) +{ + overlay float IG_ratio; + + // safeguard and convert the surface pressure (mbar -> bar) (*) + if( int_I_pres_surface < 500 ) pres_surface = 0.500; + else pres_surface = 0.001 * int_I_pres_surface; + + // safeguard and convert the current real pressure + if( int_I_pres_respiration < 500 ) real_pres_respiration = 0.500; + else real_pres_respiration = 0.001 * int_I_pres_respiration; + + // safeguard further parameters to protect the tissue-flag and the stop table + if( char_I_sim_advance_time > 127 ) char_I_sim_advance_time = 127; + if( char_I_extra_time > 127 ) char_I_extra_time = 127; + if( char_I_gas_change_time > 99 ) char_I_gas_change_time = 99; + + // calculate partial pressure of N2 in respired air at surface pressure + calc_N2_equilibrium(); + + // get, safeguard and convert the saturation and desaturation factors + get_saturation_factors(); + +#ifdef _ccr_pscr + // compute a factor that will be used later on for pSCR ppO2 drop calculation (*) + float_pSCR_factor = 0.01 * char_I_PSCR_drop * char_I_PSCR_lungratio; +#endif + +#ifdef _helium + // get the currently breathed gas ratios + real_O2_ratio = 0.01 * char_I_O2_ratio; + real_He_ratio = 0.01 * char_I_He_ratio; + + // calculate the inert gas ratio (local helper variable) + IG_ratio = 1.00 - real_O2_ratio; + + // calculate the N2 ratio + real_N2_ratio = IG_ratio - real_He_ratio; +#else + // get the currently breathed O2 ratio + real_O2_ratio = 0.01 * char_I_O2_ratio; + + // set the He ratio to zero + real_He_ratio = 0.0; + + // calculate the N2 / inert gas ratio + real_N2_ratio = IG_ratio = 1.00 - real_O2_ratio; +#endif // _helium + +#ifdef _ccr_pscr + // calculate ppO2 drop in pSCR loop for real tissues + real_pSCR_drop = IG_ratio * float_pSCR_factor; +#endif + } ////////////////////////////////////////////////////////////////////////////// // Find gas changes on an NDL ascent // -// This function is a variant of calc_ascent_to_first_stop() to be used solely -// for finding the gas changes in an OC bailout ascent that is within NDL. -// -// Input : char_bottom_depth : depth at which the ascent starts, in meters -// -// Output : gas change stops put into stops table -// -// Destroyed: sim_depth_limit -// sim_gas_current -// sim_gas_current_depth +// This function is used for finding the gas changes in an OC bailout ascent +// that is within NDL. +// +// Input: char_depth_bottom depth at which the ascent starts, in meters +// +// Output: gas change stops put into stops table +// +// Destroyed: char_depth_sim +// sim_gas_current_num number of current gas +// sim_gas_current_depth change depth of current gas // void find_NDL_gas_changes(void) { @@ -2412,20 +3054,20 @@ gas_find_current(); // loop in ascending until reaching a depth of 3 meters, no gas switches considered thereafter - for( sim_depth_limit = char_bottom_depth; sim_depth_limit >= 3; ) + for( char_depth_sim = char_depth_bottom; char_depth_sim >= 3; ) { // memorize the depth we came from - old_depth_limit = sim_depth_limit; + old_depth_limit = char_depth_sim; // ascent - initially in steps of 10 m, then slowing down to 1 m steps to not miss a O2 gas - if( sim_depth_limit > 10 ) sim_depth_limit -= 10; - else sim_depth_limit -= 1; + if ( char_depth_sim > 10 ) char_depth_sim -= 10; + else char_depth_sim -= 1; // check if there is a better gas to switch to if( gas_find_better() ) { - // adjust sim_depth_limit to the gas change depth, but not deeper than the depth we came from - sim_depth_limit = (sim_gas_current_depth < old_depth_limit) ? sim_gas_current_depth : old_depth_limit; + // adjust char_depth_sim to the gas change depth, but not deeper than the depth we came from + char_depth_sim = (sim_gas_current_depth < old_depth_limit) ? sim_gas_current_depth : old_depth_limit; // create a stop for the gas change in the stops table update_deco_table(char_I_gas_change_time); @@ -2435,139 +3077,29 @@ ////////////////////////////////////////////////////////////////////////////// -// Calculate ascent to first deco stop -// -// Modified : sim_pres_respiration : current depth in ascent and deco simulation, in bar absolute pressure -// -// Output : sim_depth_limit : depth in meters of the 1st stop, if a stop is found -// -// Destroyed: tissue_increment : tissue and update period selector -// -void calc_ascent_to_first_stop(void) -{ - overlay float old_pres_respiration; - overlay unsigned char fast = 1; // 0: 2 seconds step, 1: 1 minute step - - // target the simulated tissues - tissue_increment = 0; - - // loop until first deco stop or the surface is reached - for(;;) - { - // memorize depth in absolute pressure we came from - old_pres_respiration = sim_pres_respiration; - - // try ascending 1 full minute (fast) or 2 seconds (!fast) - if( fast ) sim_pres_respiration -= float_ascent_speed * METER_TO_BAR; // 1 min at float_ascent_speed ( 5 .. 10 m) - else sim_pres_respiration -= 0.0333 * float_ascent_speed * METER_TO_BAR; // 2 sec at float_ascent_speed (17 .. 33 cm) - - // but don't go over surface - if( sim_pres_respiration < pres_surface ) sim_pres_respiration = pres_surface; - - // compute ceiling of the simulated tissues - if( char_I_deco_model != 0 ) calc_limit(GF_low); - else calc_limit(1.0); - - // did we overshoot the ceiling? - if( sim_pres_respiration < (ceiling + pres_surface) ) - { - // YES - back to memorized depth - sim_pres_respiration = old_pres_respiration; - - // switch to 2 seconds ascent if not yet in, else done - if( fast ) - { - fast = 0; // ascent with 2 seconds ascent steps - continue; - } - else - { - break; // done, stop required - } - } - - // if code execution passes along here, we did not overshoot the ceiling - - // did we reach the surface? If yes, deco has vanished, no stop required, done. - if( sim_pres_respiration == pres_surface ) break; - - // depth in meters where we are now (no round-up) - sim_depth_limit = (unsigned char)((sim_pres_respiration - pres_surface) * BAR_TO_METER); - - // program interval on simulated tissues: - // fast = 1 -> 1 minute, - // fast = 0 -> 2 seconds - tissue_increment = fast; - - // Check if there is a better gas to switch to, but only if bailout mode is enabled. - // If yes, introduce a stop for the gas change. - if( char_O_deco_status & DECO_BAILOUT_MODE ) - if( gas_find_better() ) - { - overlay unsigned char old_depth_limit; - - // set the new calculation values for N2, He and O2 - gas_set_ratios(); - - // add gas change time: a gas change time of - // 0 minutes will keep the 1 minute / 2 seconds interval selection, - // >= 1 minute will add the the 1 minute interval but overrule a 2 seconds interval. - tissue_increment += char_I_gas_change_time; - - // depth in meters we came from - old_depth_limit = (unsigned char)((old_pres_respiration - pres_surface) * BAR_TO_METER); - - // adjust sim_depth_limit to the gas change depth, but not deeper than the depth we came from - sim_depth_limit = (sim_gas_current_depth < old_depth_limit) ? sim_gas_current_depth : old_depth_limit; - - // Adjust the depth for the tissue update to the current depth. In case of fast mode, - // this imposes that the ascent from the 'old_pres_respiration' depth to this depth - // took one minute although we might have only ascended one or two meters... - sim_pres_respiration = sim_depth_limit * METER_TO_BAR + pres_surface; - - // create a stop for the gas change in the stops table - update_deco_table(char_I_gas_change_time); - } - - // omit the 2 seconds interval updates (do only updates for >= 1 minute) - // It's a trade-off between computational effort and accuracy... - if( tissue_increment ) - { - // compute ppO2, ppN2 and ppHe for current depth from sim_pres_respiration - calc_alveolar_pressures(); - - // update the tissues - calc_tissues(); - - // update the CNS value - calc_CNS(); - } - - } // for() -} - - -////////////////////////////////////////////////////////////////////////////// // calc_tissues // -// INPUT: ppN2 : partial pressure of inspired N2 -// ppHe : partial pressure of inspired He -// tissue_increment : integration time and tissue selector (real or simulated) -// -// MODIFIED: pres_tissue_N2[] : tissue N2 pressures (in real tissues context) -// pres_tissue_He[] : tissue He pressures (in real tissues context) -// sim_pres_tissue_N2[] : tissue N2 pressures (in simulated tissues context) -// sim_pres_tissue_He[] : tissue He pressures (in simulated tissues context) -// -// OUTPUT: char_O_tissue_N2_saturation[] : tissue N2 pressures scaled for display purpose (in real tissues context) -// char_O_tissue_He_saturation[] : tissue He pressures scaled for display purpose (in real tissues context) +// INPUT: ppN2 partial pressure of inspired N2 +// ppHe partial pressure of inspired He +// tissue_increment integration time and tissue selector (real or simulated) +// +// MODIFIED: real_pres_tissue_N2[] tissue N2 pressures (in real tissues context) +// real_pres_tissue_He[] tissue He pressures (in real tissues context) +// sim_pres_tissue_N2[] tissue N2 pressures (in simulated tissues context) +// sim_pres_tissue_He[] tissue He pressures (in simulated tissues context) +// +// OUTPUT: char_O_tissue_pres_N2[] tissue N2 pressures scaled for display purpose (in real tissues context) +// char_O_tissue_pres_He[] tissue He pressures scaled for display purpose (in real tissues context) +// char_O_tissue_pressure[] combined tissue pressures scaled for display purpose (in real tissue context) // static void calc_tissues() { + overlay unsigned char period; overlay float temp_tissue_N2; + +#ifdef _helium overlay float temp_tissue_He; - overlay unsigned char period; - overlay unsigned char i; +#endif assert( 0.00 <= ppN2 && ppN2 < 11.2 ); // 80% N2 at 130m @@ -2581,59 +3113,62 @@ if( i == 0 ) // check if we shall do one 2-seconds period { - read_Buhlmann_times(0); // YES, program coefficients for a 2 seconds period - period = 1; // set period length (in cycles) - i = 1; // and one cycle to do + read_Buhlmann_times(0); // YES - program coefficients for a 2 seconds period + period = 1; // - set period length (in cycles) + i = 1; // - and one cycle to do } else if( i > 9 ) // check if we can start with 10 minutes periods { - read_Buhlmann_times(2); // YES, program coefficients for 10 minutes periods - period = 10; // set period length (in cycles) to ten + read_Buhlmann_times(2); // YES - program coefficients for 10 minutes periods + period = 10; // set period length (in cycles) to ten } - else // we shall do 1 to 9 minutes + else // last but not lease, do 1 to 9 minutes { - read_Buhlmann_times(1); // program coefficients for 1 minute periods - period = 1; // set period length (in cycles) to one + read_Buhlmann_times(1); // NO - program coefficients for 1 minute periods + period = 1; // - set period length (in cycles) to one } do { - //---- N2 ------------------------------------------------------------------------------- - - temp_tissue = (tissue_increment & TISSUE_FLAG) ? pres_tissue_N2[ci] : sim_pres_tissue_N2[ci]; + //---- N2 -------------------------------------------------------- + + temp_tissue = (tissue_increment & TISSUE_SELECTOR) ? real_pres_tissue_N2[ci] : sim_pres_tissue_N2[ci]; temp_tissue = (ppN2 - temp_tissue) * var_N2_e; - temp_tissue_safety(); - - if( tissue_increment & TISSUE_FLAG ) + apply_saturation_factors(); + + if( tissue_increment & TISSUE_SELECTOR ) { - temp_tissue_N2 = temp_tissue; - pres_tissue_N2[ci] += temp_tissue; + temp_tissue_N2 = temp_tissue; + real_pres_tissue_N2[ci] += temp_tissue; } else { - sim_pres_tissue_N2[ci] += temp_tissue; + sim_pres_tissue_N2[ci] += temp_tissue; } - - //---- He ------------------------------------------------------------------------------- - - temp_tissue = (tissue_increment & TISSUE_FLAG) ? pres_tissue_He[ci] : sim_pres_tissue_He[ci]; +#ifdef _helium + //---- He -------------------------------------------------------- + + temp_tissue = (tissue_increment & TISSUE_SELECTOR) ? real_pres_tissue_He[ci] : sim_pres_tissue_He[ci]; temp_tissue = (ppHe - temp_tissue) * var_He_e; - temp_tissue_safety(); - - if( tissue_increment & TISSUE_FLAG ) + apply_saturation_factors(); + + if( tissue_increment & TISSUE_SELECTOR ) { - temp_tissue_He = temp_tissue; - pres_tissue_He[ci] += temp_tissue; + temp_tissue_He = temp_tissue; + real_pres_tissue_He[ci] += temp_tissue; } else { - sim_pres_tissue_He[ci] += temp_tissue; + sim_pres_tissue_He[ci] += temp_tissue; } +#endif + + //---- decrement loop counter and adjust step size --------------- // decrement loop counter i -= period; @@ -2649,116 +3184,169 @@ // have the computations been done for the "real" tissues? - if( tissue_increment & TISSUE_FLAG ) + if( tissue_increment & TISSUE_SELECTOR ) { + +#ifdef _helium + // net tissue balance temp_tissue = temp_tissue_N2 + temp_tissue_He; + // check tissue on-/off-gassing and IBCD with applying a threshold of +/-HYST // if ( temp_tissue < -HYST ) // check if the tissue is off-gassing { - deco_tissue_vector |= (1 << ci); // tag tissue as being in decompression - IBCD_tissue_vector &= ~(1 << ci); // tag tissue as not experiencing mentionable IBCD + // tag tissue as not experiencing mentionable IBCD + IBCD_tissue_vector &= ~(1 << ci); } else if ( temp_tissue > +HYST ) // check if the tissue in on-gassing { - deco_tissue_vector &= ~(1 << ci); // tag tissue as not being in decompression - - if( ((temp_tissue_N2 > 0.0) && (temp_tissue_He < 0.0)) // check for counter diffusion + // check for counter diffusion + if( ((temp_tissue_N2 > 0.0) && (temp_tissue_He < 0.0)) || ((temp_tissue_N2 < 0.0) && (temp_tissue_He > 0.0)) ) { - IBCD_tissue_vector |= (1 << ci); // tag tissue as experiencing mentionable IBCD + // tag tissue as experiencing mentionable IBCD + IBCD_tissue_vector |= (1 << ci); } } - - // keep the saturating / desaturating flags from last invocation - char_O_tissue_N2_saturation[ci] &= 128; - char_O_tissue_He_saturation[ci] &= 128; - - // flip the flags applying a hysteresis of HYST (actual value: see #define of HYST) - if( temp_tissue_N2 > +HYST ) char_O_tissue_N2_saturation[ci] = 128; // set flag for tissue pressure is increasing - else if( temp_tissue_N2 < -HYST ) char_O_tissue_N2_saturation[ci] = 0; // clear flag (-> tissue pressure is decreasing) - - if( temp_tissue_He > +HYST ) char_O_tissue_He_saturation[ci] = 128; // set flag for tissue pressure is increasing - else if( temp_tissue_He < -HYST ) char_O_tissue_He_saturation[ci] = 0; // clear flag (-> tissue pressure is decreasing) - - - // For N2 tissue display purpose: - // Scale tissue press so that saturation in 70m on AIR gives a value of approx. 80. - // The surface steady-state tissue loading of [0.7902 * (real_pres_respiration - ppWater)] bar - // gives then a 10. If N2 is completely washed out of the tissue, result will be 0. - // This scaling is adapted to the capabilities of the tissue graphics in the custom views. - temp_tissue = (pres_tissue_N2[ci] / N2_equilibrium) * 10; - - // limit to 127 to leave space for sat/desat flag +#endif + + // For N2 tissue pressure display purpose: + + // basically keep the on-gassing / off-gassing flag from last invocation, but flip + // it in case the rate exceeds a set hysteresis (actual value: see #define of HYST) + char_O_tissue_pres_N2[ci] &= 128; + if ( temp_tissue_N2 > +HYST ) char_O_tissue_pres_N2[ci] = 128; // set flag for tissue pressure is increasing + else if ( temp_tissue_N2 < -HYST ) char_O_tissue_pres_N2[ci] = 0; // clear flag (-> tissue pressure is decreasing) + + // scale N2 tissue pressure such that the surface steady-state tissue loading + // of [0.7902 * (1013 hPa - ppWater)] bar will give a 8, which aligns with + // the 2nd scale line. + temp_tissue_N2 = (8 / (0.7902 * (1.013 - ppWater))) * real_pres_tissue_N2[ci]; + + // limit to 127 to protect the uppermost bit which holds the sat/desat flag + if (temp_tissue_N2 > 127) temp_tissue_N2 = 127; + + // convert to integer and combine with sat/desat flag + char_O_tissue_pres_N2[ci] += (unsigned char)temp_tissue_N2; + +#ifdef _helium + + // For He tissue pressure display purpose: + + // basically keep the on-gassing / off-gassing flag from last invocation, but flip + // it in case the rate exceeds a set hysteresis (actual value: see #define of HYST) + char_O_tissue_pres_He[ci] &= 128; + if ( temp_tissue_He > +HYST ) char_O_tissue_pres_He[ci] = 128; // set flag for tissue pressure is increasing + else if ( temp_tissue_He < -HYST ) char_O_tissue_pres_He[ci] = 0; // clear flag (-> tissue pressure is decreasing) + + // scale He tissue pressure alike it is done for N2. + // With no He in a tissue, the result will be 0. + temp_tissue_He = (8 / (0.7902 * (1.013 - ppWater))) * real_pres_tissue_He[ci]; + + // limit to 127 to protect the uppermost bit which holds the sat/desat flag + if (temp_tissue_He > 127) temp_tissue_He = 127; + + // convert to integer and combine with sat/desat flag + char_O_tissue_pres_He[ci] += (unsigned char)temp_tissue_He; + + + // For combined tissue pressure display purpose: + + // basically keep the on-gassing / off-gassing flag from last invocation, but flip + // it in case the rate exceeds a set hysteresis (actual value: see #define of HYST) + char_O_tissue_pressure[ci] &= 128; + if ( temp_tissue > +HYST ) char_O_tissue_pressure[ci] = 128; // set flag for tissue pressure is increasing + else if ( temp_tissue < -HYST ) char_O_tissue_pressure[ci] = 0; // clear flag (-> tissue pressure is decreasing) + + // add the two scaled pressures. + temp_tissue = temp_tissue_N2 + temp_tissue_He; + + // limit to 127 to protect the uppermost bit which holds the sat/desat flag if (temp_tissue > 127) temp_tissue = 127; - // export as integer - char_O_tissue_N2_saturation[ci] += (unsigned char)temp_tissue; - - - // For H2 tissue display purpose: - // Scale tissue press so that saturation in 120m on TMX 10/70 gives a value of approx. 70. - // With no He in a tissue, result will be 0. - // This scaling is adapted to the capabilities of the tissue graphics in the custom views. - temp_tissue = pres_tissue_He[ci] * 7.7; - - // limit to 127 to leave space for sat/desat flag - if (temp_tissue > 127) temp_tissue = 127; - - // export as integer - char_O_tissue_He_saturation[ci] += (unsigned char)temp_tissue; + // convert to integer and combine with sat/desat flag + char_O_tissue_pressure[ci] += (unsigned char)temp_tissue; + +#else + + // He tissue pressure is zero + char_O_tissue_pres_He[ci] = 0; + + // combined tissue pressure equals N2 tissue pressure + char_O_tissue_pressure[ci] = char_O_tissue_pres_N2[ci]; + +#endif + } //if } // for } + ////////////////////////////////////////////////////////////////////////////// // calc_limit // -// Input: GF_parameter gradient factor to be used, negative values activate surface mode -// tissue_increment selector for context: real or simulated tissues -// sim_pres_tissue_N2/_He tissue pressures (used in simulated tissues context) -// pres_tissue_N2/_He tissue pressures (used in real tissues context) -// -// Output: lead_supersat highest supersaturation found among all tissues, 1.0 = 100% -// lead_tissue number of the leading tissue (0-15) -// ceiling ceiling in bar relative pressure -// -// Modified: -// char_O_deco_warnings for IBCD, microbubbles and outside warning (only in real tissues context) +// Input: GF_parameter gradient factor to be used, negative values activate surface mode +// tissue_increment selector for context: real or simulated tissues +// sim_pres_tissue_N2/_He tissue pressures (used in simulated tissues context) +// real_pres_tissue_N2/_He tissue pressures (used in real tissues context) +// +// Output: lead_supersat highest supersaturation found among all tissues, 1.0 = 100% +// lead_tissue number of the leading tissue (0-15) +// ceiling ceiling in bar relative pressure +// +// Modified: deco_warnings for IBCD, micro bubbles and outside warning (only in real tissues context) // static void calc_limit(PARAMETER float GF_parameter) { - overlay float lead_tissue_limit = 0.0; - - - // set leading tissue number to not yet computed - lead_number = 0; + overlay float pres_respiration_min_total = 0.0; + overlay unsigned char surface_mode = 0; // 0: off, 1: on + + + // check mode + if( GF_parameter < 0 ) + { + // activate surface mode + surface_mode = 1; + + // normalize parameter + GF_parameter = -GF_parameter; + } + + // set leading tissue number to tissue 1 (it has the index 0) + lead_tissue = 0; // initialize leading tissue supersaturation value to null - lead_supersat = 0.0; - - // check context - if( tissue_increment & TISSUE_FLAG ) + lead_supersat = 0.0; + + // next code section is relevant only when invoked on the real tissues + if( tissue_increment & TISSUE_SELECTOR ) { - // clear IBCD, micro bubbles and outside warning flags (locked warnings will be preserved) - char_O_deco_warnings &= ~(DECO_WARNING_IBCD + DECO_WARNING_MBUBBLES + DECO_WARNING_OUTSIDE + DECO_ATTENTION_OUTSIDE ); + // clear IBCD, micro-bubbles and outside warning flags (locked warnings will be preserved) + deco_warnings &= ~( DECO_WARNING_IBCD + DECO_WARNING_MBUBBLES + DECO_WARNING_OUTSIDE + DECO_ATTENTION_OUTSIDE ); } // loop over all tissues for( ci = 0; ci < NUM_COMP; ci++ ) { - overlay float pres_min; + overlay float pres_respiration_min_tissue; + + + // get the coefficients for tissue ci + read_Buhlmann_coefficients(); + +#ifdef _helium // get the tissue pressures - if( tissue_increment & TISSUE_FLAG ) + // adopt_Buhlmann_coefficients needs calc_pres_tissue_N2/He when compiled for helium + if( tissue_increment & TISSUE_SELECTOR ) { // context is real tissues - calc_pres_tissue_N2 = pres_tissue_N2[ci]; - calc_pres_tissue_He = pres_tissue_He[ci]; + calc_pres_tissue_N2 = real_pres_tissue_N2[ci]; + calc_pres_tissue_He = real_pres_tissue_He[ci]; } else { @@ -2770,29 +3358,38 @@ // overall tissue pressure pres_tissue = calc_pres_tissue_N2 + calc_pres_tissue_He; - // get the coefficients for tissue ci - read_Buhlmann_coefficients(); +#else + + // get the tissue pressure + pres_tissue = ( tissue_increment & TISSUE_SELECTOR ) ? real_pres_tissue_N2[ci] : sim_pres_tissue_N2[ci]; + +#endif // adopt a and b coefficients to current N2/He ratio inside the tissue adopt_Buhlmann_coefficients(); // next calculations are only relevant when invoked on the real tissues - if( tissue_increment & TISSUE_FLAG ) + if( tissue_increment & TISSUE_SELECTOR ) { overlay float pres_tissue_max; overlay float supersat; overlay float baseline_threshold; + // check if tissue is in supersaturation if( pres_tissue > real_pres_respiration ) { // calculate maximum allowed tissue pressure at current ambient pressure - pres_tissue_max = real_pres_respiration / var_N2_b + var_N2_a; + pres_tissue_max = real_pres_respiration / var_b + var_a; // calculate current supersaturation value (1.0 = 100%) of this tissue according to straight Buhlmann supersat = ( pres_tissue - real_pres_respiration ) / ( pres_tissue_max - real_pres_respiration ); + // calculate supersaturation value for display purpose: 1.35 = 135% = 86 pixel + if( supersat <= 1.35 ) char_O_tissue_saturation[ci] = (unsigned char)(supersat * 64); + else char_O_tissue_saturation[ci] = 86; + // memorize highest supersaturation found if( supersat > lead_supersat ) lead_supersat = supersat; @@ -2801,218 +3398,300 @@ // micro bubbles warning: supersaturation > baseline threshold if( supersat > baseline_threshold ) - char_O_deco_warnings |= (DECO_WARNING_MBUBBLES + DECO_WARNING_MBUBBLES_lock); + deco_warnings |= (DECO_WARNING_MBUBBLES + DECO_WARNING_MBUBBLES_lock); // outside warning: supersaturation > baseline threshold + additional 5% margin - if( supersat > baseline_threshold + 0.05 ) - char_O_deco_warnings |= (DECO_WARNING_OUTSIDE + DECO_WARNING_OUTSIDE_lock ); + if( supersat > (baseline_threshold + 0.05) ) + deco_warnings |= (DECO_WARNING_OUTSIDE + DECO_WARNING_OUTSIDE_lock ); + } + else + { + // supersaturation is defined as zero while tissue pressure <= ambient pressure + supersat = 0.0; + char_O_tissue_saturation[ci] = 0; } - } + + // next only when in surface mode + if( surface_mode ) + { + // tag tissue whether it is beyond the M-line limit or not + if( supersat > 1.0 ) + { + char_O_tissue_pres_N2[ci] |= 128; +#ifdef _helium + char_O_tissue_pres_He[ci] |= 128; +#endif + char_O_tissue_pressure[ci] |= 128; + } + else + { + char_O_tissue_pres_N2[ci] &= ~128; +#ifdef _helium + char_O_tissue_pres_He[ci] &= ~128; +#endif + char_O_tissue_pressure[ci] &= ~128; + } + } + } // real tissues // calculate the minimum ambient pressure that the tissue can withstand if( char_I_deco_model == 0 ) { // straight Buhlmann - pres_min = (pres_tissue - var_N2_a) * var_N2_b; + pres_respiration_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_min = ( pres_tissue - (var_N2_a * GF_parameter) ) - / ( 1.0 - GF_parameter + (GF_parameter / var_N2_b ) ); + pres_respiration_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_min > lead_tissue_limit ) + if( pres_respiration_min_tissue > pres_respiration_min_total ) { - lead_tissue_limit = pres_min; - lead_number = ci; + pres_respiration_min_total = pres_respiration_min_tissue; + lead_tissue = ci; } } // for - // compute ceiling for the real tissues in bar relative pressure - ceiling = lead_tissue_limit - pres_surface; - - - // next in real tissue context only - if( tissue_increment & TISSUE_FLAG ) + ceiling = pres_respiration_min_total - pres_surface; + +#ifdef _helium + // IBCD is checked for real tissues only + if( tissue_increment & TISSUE_SELECTOR ) { // check if the leading tissue is in IBCD condition - if( (IBCD_tissue_vector & (1 << lead_number)) - && ((pres_tissue_N2[lead_number] + pres_tissue_He[lead_number]) > real_pres_respiration) ) + if( (IBCD_tissue_vector & (1 << lead_tissue)) + && ((real_pres_tissue_N2[lead_tissue] + real_pres_tissue_He[lead_tissue]) > real_pres_respiration) ) { - // leading tissue is in IBCD condition and in super-saturation, so issue a warning - char_O_deco_warnings |= (DECO_WARNING_IBCD + DECO_WARNING_IBCD_lock); + // leading tissue is in IBCD condition and in super-saturation, so issue a warning. + deco_warnings |= (DECO_WARNING_IBCD + DECO_WARNING_IBCD_lock); } } +#endif + } + + ////////////////////////////////////////////////////////////////////////////// -// calc_NDL_time -// -// calculation of the remaining bottom time (NDL: no decompression limit) +// calc_NDL_time_tissue +// +// calculation of the remaining no decompression limit (NDL) time for a tissue // // NOTE: Erik Baker's closed formula works for Nitrox. Trimix adds a second // exponential term to the M-value equation, making it impossible to -// invert. So we have to solve the problem with an iterative approach. -// -// Input: ppN2 -// ppHe -// -// Output: NDL_time -// -static void calc_NDL_time(void) +// invert. So we have to solve the problem with a search approach. +// +// Input: NDL_tissue tissue for which to calculate remaining NDL time +// GF_high gradient factor used when GF factors are enabled +// ppN2, ppHe partial pressures of N2 and He breathed +// +// Modified: NDL_time shortest NDL time found so far +// NDL_tissue_lead leading tissue, i.e. tissue with the shortest NDL +// +static void calc_NDL_time_tissue(void) { - overlay unsigned char new_NDL_lead_tissue = 0; - overlay unsigned char i; - - - // initialize NDL_time to 240 minutes - NDL_time = 240; - - for( i = 0; i < NUM_COMP; i++ ) + overlay unsigned char NDL_time_tissue = 0; // NDL time of this tissue, starting with 0 minutes + overlay unsigned char step_size = 10; // step size in searching, starting with 10 minutes + overlay float pres_limit; // max. tissue pressure allowed + +#ifdef _helium + overlay float last_pres_tissue_N2; // last tissue pressure for N2 + overlay float last_pres_tissue_He; // last tissue pressure for He +#else + overlay float last_pres_tissue; // last tissue pressure +#endif + + + // set the compartment index ci for reading the Buhlmann increments and coefficients + ci = NDL_tissue; + + // read the tissue increments for a step size of 10 minutes + read_Buhlmann_times(2); + + // read Buhlmann a and b coefficients for tissue ci + read_Buhlmann_coefficients(); + +#ifdef _helium + + // get the current simulated tissue pressures + calc_pres_tissue_N2 = last_pres_tissue_N2 = sim_pres_tissue_N2[ci]; + calc_pres_tissue_He = last_pres_tissue_He = sim_pres_tissue_He[ci]; + +#else + + // get the current simulated tissue pressure + pres_tissue = last_pres_tissue = sim_pres_tissue_N2[ci]; + + // set the a and b coefficients + adopt_Buhlmann_coefficients(); + +#endif + + // simulate an increasing bottom time and check when the NDL is hit + for(;;) { - overlay unsigned char period = 10; // start with iterations of 10 minutes - overlay unsigned char NDL_tissue; // loop variable - overlay float GF_factor; // gradient factor to be applied - overlay float next_pres_tissue; // auxiliary variable to cache a calculation result - - - // select gradient factor to use - GF_factor = (char_I_deco_model != 0) ? GF_high : 1.0; - - // the fastest way to find out if already being beyond NDL is to start with - // the tissue that was the leading one during the last NDL computation... - ci = (char_O_deco_status & DECO_PLAN_ALTERNATE) ? (NDL_lead_tissue_alt + i) : (NDL_lead_tissue_norm + i); - - // wrap around after the 16th tissue - if( ci >= NUM_COMP ) ci -= NUM_COMP; - - // read the loading factors for 10 minute iterations - read_Buhlmann_times(2); - - // get the tissue pressures for N2 and He - calc_pres_tissue_N2 = sim_pres_tissue_N2[ci]; - calc_pres_tissue_He = sim_pres_tissue_He[ci]; - - // calculate the total pressure tissue + +#ifdef _helium + + // calculate the total tissue pressure pres_tissue = calc_pres_tissue_N2 + calc_pres_tissue_He; - // Simulate an increasing bottom time and check when we hit the NDL. - // It is not needed to simulate for longer than the already found NDL. - for( NDL_tissue = 0; NDL_tissue < NDL_time; ) + // adopt a and b coefficients to current N2/He ratio inside the tissue + adopt_Buhlmann_coefficients(); + +#endif + + // compute the maximum tissue pressure allowed to be exposed to an + // ambient pressure equaling the surface pressure + if( char_I_deco_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; + } + else { - overlay float pres_limit; - overlay float delta_pres_tissue_N2; - overlay float delta_pres_tissue_He; - - - // read Buhlmann a and b coefficients for tissue ci, they need to be re-read on each - // iteration because adopt_Buhlmann_coefficients() twiddles with the N2 coefficients - read_Buhlmann_coefficients(); - - // adopt a and b coefficients to current N2/He ratio inside the tissue - adopt_Buhlmann_coefficients(); - - // compute the maximum tissue pressure allowed to be exposed to an ambient pressure equaling - // the surface pressure (this equation [2] is the inverse of equation [1]) - pres_limit = (1.0 - GF_factor + GF_factor / var_N2_b) * pres_surface + GF_factor * var_N2_a; - - // check if this tissue is already beyond the NDL - if( pres_tissue > pres_limit) + // straight Buhlmann + pres_limit = pres_surface / var_b + var_a; + } + + // is the tissue pressure higher than the maximum tissue pressure allowed? + if( pres_tissue > pres_limit) + { + // YES - tissue is outside NDL + + // was the tissue outside NDL right from the start? + if( NDL_time_tissue == 0 ) { - // beyond NDL - finish the outer loop, ... - i = NUM_COMP; - - // ... and finish the inner loop + // YES - search can be aborted + + // at least one tissue is outside NDL, so overall NDL time is zero + NDL_time = 0; + + // store the number of this tissue as being the leading one + NDL_tissue_lead = NDL_tissue; + + // done break; } - // compute tissue pressure deltas for 10 or 1 minute of time ahead - delta_pres_tissue_N2 = (ppN2 - calc_pres_tissue_N2) * var_N2_e; - delta_pres_tissue_He = (ppHe - calc_pres_tissue_He) * var_He_e; - - // apply safety factors to the pressure deltas - // NDL can be computed while ascending, so we have to check if the tissues is saturating or desaturating - if( delta_pres_tissue_N2 > 0.0 ) delta_pres_tissue_N2 *= float_saturation_multiplier; - else delta_pres_tissue_N2 *= float_desaturation_multiplier; - - if( delta_pres_tissue_He > 0.0 ) delta_pres_tissue_He *= float_saturation_multiplier; - else delta_pres_tissue_He *= float_saturation_multiplier; - - // simulate off-gassing while going to surface - well, maybe some day we'll do that... - // delta_pres_tissue_N2 -= exp( ... ascent time ... ppN2...) - // delta_pres_tissue_He -= exp( ... ascent time ... ppHe...) - - // calculate tissue pressure for given time ahead - next_pres_tissue = pres_tissue + delta_pres_tissue_N2 + delta_pres_tissue_He; - - // within NDL now, but still within NDL in 10 or 1 minute from now? - if( next_pres_tissue <= pres_limit ) + // when code execution passes here, the tissue has become + // being outside NDL after doing one or more search steps + + // still searching with a step size of 10 minutes? + if( step_size == 10 ) { - // YES - apply the pressure deltas to the tissues - calc_pres_tissue_N2 += delta_pres_tissue_N2; - calc_pres_tissue_He += delta_pres_tissue_He; - - // update the overall tissue pressure - pres_tissue = next_pres_tissue; - - // increment the NDL - NDL_tissue += period; - - // do next iteration + // YES - retry with smaller step size + + // go back to last NDL time + NDL_time_tissue -= 10; + +#ifdef _helium + + // go back to last pressures + calc_pres_tissue_N2 = last_pres_tissue_N2; + calc_pres_tissue_He = last_pres_tissue_He; + +#else + + // go back to last pressure + pres_tissue = last_pres_tissue; + +#endif + + // reduce step size to 1 minute + step_size = 1; + + // read the tissue increments for a step size of 1 minute + read_Buhlmann_times(1); + + // redo search from last pressure & time within NDL with smaller step size continue; } - - // NO - if delta pressures were for 10 minutes of time ahead, continue with trying for 1 minute ahead - if( period == 10 ) + else { - // reduce period to 1 minute - period = 1; - - // read the loading factors for 1 minute periods - read_Buhlmann_times(1); - - // do next iteration - continue; + // NO - already tried with a step size of 1 minute + + // go back to last NDL time that was within NDL + NDL_time_tissue -= 1; + + // is the NDL time of this tissue shorter than the overall NDL time found so far? + if( NDL_time_tissue < NDL_time ) + { + // YES - set this tissue's NDL time as the new overall NDL time + NDL_time = NDL_time_tissue; + + // - store the number of this tissue as being the leading one + NDL_tissue_lead = NDL_tissue; + } + + // done + break; } - - // less than a full minute of NDL time left, so finish the inner loop - break; - - } // inner for-loop simulating increasing bottom time - - // is the current NDL shorter than the shortest so far? - if ( NDL_tissue < NDL_time ) + } + else { - // keep the current's tissue NDL as the new shortest NDL - NDL_time = NDL_tissue; - - // store the causing tissue - new_NDL_lead_tissue = ci; + // NO - tissue is still within NDL + + // The search can be terminated when the NDL time of this tissue + // exceeds the overall NDL time, thus when a shorter NDL time has + // already been found with another tissue. + if( NDL_time_tissue >= NDL_time ) break; + +#ifdef _helium + + // back-up current tissue pressures + last_pres_tissue_N2 = calc_pres_tissue_N2; + last_pres_tissue_He = calc_pres_tissue_He; + +#else + + // back-up current tissue pressure + last_pres_tissue = pres_tissue; + +#endif + + // step forward NDL time of current tissue + NDL_time_tissue += step_size; + +#ifdef _helium + + // step forward tissue pressure - N2 + temp_tissue = (ppN2 - calc_pres_tissue_N2) * var_N2_e; // pressure delta breathed - tissue + apply_saturation_factors(); // apply safety factor + calc_pres_tissue_N2 += temp_tissue; // add pressure delta to tissue + + // step forward tissue pressure - He + temp_tissue = (ppHe - calc_pres_tissue_He) * var_He_e; // pressure delta breathed - tissue + apply_saturation_factors(); // apply safety factor + calc_pres_tissue_He += temp_tissue; // add pressure delta to tissue + +#else + + // step forward tissue pressure + temp_tissue = (ppN2 - pres_tissue ) * var_N2_e; // pressure delta breathed - tissue + apply_saturation_factors(); // apply safety factor + pres_tissue += temp_tissue; // add pressure delta to tissue + +#endif + } - - // If NDL is > 0 the outer loop will continues with the next tissue. - // If NDL found to be overrun, outer loop will be terminated by means of the i = NUM_COMP statement. - - } // outer for-loop iterating over all tissues - - // store the NDL dominating tissue for to start with in the next NDL calculation - if( char_O_deco_status & DECO_PLAN_ALTERNATE ) NDL_lead_tissue_alt = new_NDL_lead_tissue; - else NDL_lead_tissue_norm = new_NDL_lead_tissue; + } } ////////////////////////////////////////////////////////////////////////////// // calc_ascenttime // -// Sum up ascent from bottom to surface at float_ascent_speed, slowing down to -// 1 minute per meter for the final ascent when in deco, and all stop times. +// Sum up ascent from bottom to surface at char_I_ascent_speed, slowing down +// to 1 minute per meter for the final ascent when in deco, and all stop times. // // Input: char_I_depth_last_deco // char_I_ascent_speed -// char_bottom_depth +// char_depth_bottom // internal_deco_depth[] // internal_deco_time[] // @@ -3020,100 +3699,87 @@ // static void calc_ascenttime(void) { - overlay unsigned char x; // loop counter - overlay unsigned char ascent; // meters to go from bottom to last stop - overlay unsigned char final; // meters to go from last stop to surface - - // check if there are stops if( internal_deco_depth[0] ) { - // stops / in deco + // YES - stops / in deco // check if already at last stop depth or shallower - if( char_bottom_depth <= char_I_depth_last_deco) + if( char_depth_bottom <= char_I_depth_last_deco) { - // YES - ascent = 0; - final = char_bottom_depth; + // YES - final ascent part only + ascent_time = char_depth_bottom; } else { - // NO - ascent = char_bottom_depth - char_I_depth_last_deco; - final = char_I_depth_last_deco; + // NO - ascent part from bottom to last stop + ascent_time = (char_depth_bottom - char_I_depth_last_deco) / char_I_ascent_speed + 1; + + // - ascent part from last stop to surface at 1 meter per minute + ascent_time += char_I_depth_last_deco; } + + // add all stop times + for( i=0; i < NUM_STOPS && internal_deco_depth[i]; i++ ) + ascent_time += internal_deco_time[i]; + + // limit result to display max. + if( ascent_time > 999) ascent_time = 999; + + // tag result as invalid if there is an overflow in the stops table + if( deco_warnings & DECO_WARNING_STOPTABLE_OVERFLOW ) ascent_time |= INT_FLAG_INVALID; } else { - // no stops / within NDL - ascent = char_bottom_depth; - final = 0; + // NO - no stops / within NDL + ascent_time = char_depth_bottom / char_I_ascent_speed + 1; } - - - // initialize ascent time - ascent_time = 0; - - // time for the ascent part (bottom to last stop), if existing - if( ascent ) ascent_time += ascent / char_I_ascent_speed + 1; - - // add time for the final ascent (last stop to surface) at 1 min/m - ascent_time += final; - - // add all stop times - for( x=0; x < NUM_STOPS && internal_deco_depth[x]; x++ ) - ascent_time += internal_deco_time[x]; - - // limit result to display max. - if( ascent_time > 999) ascent_time = 999; - - // tag result as invalid if there is an overflow in the stops table - if( char_O_deco_warnings & DECO_WARNING_STOPTABLE_OVERFLOW ) ascent_time |= INT_FLAG_INVALID; } ////////////////////////////////////////////////////////////////////////////// // clear_deco_table // +// Modified: internal_deco_time[] stop durations +// internal_deco_depth[] stop depths +// internal_deco_gas[] gases used at stops // static void clear_deco_table(void) { - overlay unsigned char x; - - for( x = 0; x < NUM_STOPS; ++x ) + for( i = 0; i < NUM_STOPS; ++i ) { - internal_deco_time [x] = 0; - internal_deco_depth[x] = 0; - internal_deco_gas[x] = 0; + internal_deco_time [i] = 0; + internal_deco_depth[i] = 0; + internal_deco_gas[i] = 0; } // clear stop table overflow warning - char_O_deco_warnings &= ~DECO_WARNING_STOPTABLE_OVERFLOW; + deco_warnings &= ~DECO_WARNING_STOPTABLE_OVERFLOW; } + ////////////////////////////////////////////////////////////////////////////// // update_deco_table // -// Add time to a stop at sim_depth_limit +// Add time to a stop at char_depth_sim // // It is possible to create stops with a duration of 0 minutes, e.g. to // note a gas change "on the fly" while ascending. Therefore the criteria // to have reached the end of the list is depth == 0. // -// Input: sim_depth_limit : stop's depth, in meters -// sim_gas_current : gas used at stop, as index 1..5 or 0 for gas 6 -// time_increment : number of minutes to add to the stop -// -// Updated: internal_deco_depth[] : depth (in meters) of each stop -// internal_deco_time [] : time (in minutes) of each stop -// internal_deco_gas [] : gas used (index 1-5) at each stop +// Input: char_depth_sim stop's depth, in meters +// sim_gas_current_num gas used at stop, as index 1..5 or 0 for gas 6 +// time_increment number of minutes to add to the stop +// +// Updated: internal_deco_depth[] depth (in meters) of each stop +// 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) { overlay unsigned char x; - assert( sim_depth_limit > 0 ); // no stop at surface + assert( char_depth_sim > 0 ); // no stop at surface // loop through internal deco table for( x = 0; x < NUM_STOPS; ++x ) @@ -3124,11 +3790,11 @@ // real stop's depth), relocate the deco stop to the depth of the last gas change. // The resulting combined stop's duration will be the sum of the configured gas // change time plus the duration of the deco stop itself. - if( internal_deco_depth[x] && (sim_depth_limit > internal_deco_depth[x]) ) - sim_depth_limit = internal_deco_depth[x]; + if( internal_deco_depth[x] && (char_depth_sim > internal_deco_depth[x]) ) + char_depth_sim = internal_deco_depth[x]; // Is there already a stop entry for our current depth? - if( internal_deco_depth[x] == sim_depth_limit ) + if( internal_deco_depth[x] == char_depth_sim ) { // Yes - increment stop time if possible // Stop time entries are limited to 99 minutes because of display constraints. @@ -3145,8 +3811,8 @@ if( internal_deco_depth[x] == 0 ) { internal_deco_time[x] = time_increment; // initialize entry with first stop's time, - internal_deco_depth[x] = sim_depth_limit; // ... depth, and - internal_deco_gas[x] = sim_gas_current; // ... gas + internal_deco_depth[x] = char_depth_sim; // ... depth, and + internal_deco_gas[x] = sim_gas_current_num; // ... gas return 1; // return with status 'success' } } @@ -3154,7 +3820,7 @@ // If program flow passes here, all deco table entries are used up. // set overflow warning - char_O_deco_warnings |= DECO_WARNING_STOPTABLE_OVERFLOW; + deco_warnings |= DECO_WARNING_STOPTABLE_OVERFLOW; // return with status 'failed'. return 0; @@ -3162,16 +3828,27 @@ ////////////////////////////////////////////////////////////////////////////// -// calc_desaturation_time +// calc_desaturation_time_helper // // Helper function // +// Input: pres_actual current tissue pressure +// pres_target target tissue pressure +// var_ht half-time of the tissue +// desat_factor desaturation factor +// +// Output: int_time time needed by tissue to reach target pressure +// static void calc_desaturation_time_helper(void) { - if( pres_actual > pres_target ) // check if actual pressure is higher then target pressure - { // YES - compute remaining time + // check if actual pressure is higher then target pressure + if( pres_actual > pres_target ) + { + // YES - compute remaining time + overlay float pres_ratio; + // compute pressure ratio to archive pres_ratio = pres_actual / pres_target; // Compute desaturation time with result rounded up to multiples of 10 minutes. @@ -3179,72 +3856,131 @@ // one minute steps any more but get constantly re-computed according to current // ambient pressure and may therefor make steps of several minutes forwards and // backwards as ambient pressure rises/falls and N2/He ratio is being adjusted. - int_time = (unsigned int)( (var_ht * log(pres_ratio) / desat_factor) + 0.9 ); + int_time = (unsigned short)( (var_ht * log(pres_ratio) / desat_factor) + 0.9 ); } else - { // NO - desaturation state reached, no remaining time + { + // NO - desaturation state reached, no remaining time int_time = 0; } } + ///////////////////////////////////////////////////////////////////////////// // calc_desaturation_time // -// Inputs: int_I_pres_surface, ppWater, char_I_desaturation_multiplier -// Outputs: int_O_desaturation_time, int_O_nofly_time -// -// Calculate the time needed for the tissues to equilibrate with surface pressure +// Calculates the time needed for the tissues to equilibrate with +// surface pressure and the no-fly / no-altitude time. +// +// Input: int_I_pres_surface +// char_I_desaturation_multiplier +// +// Output: int_O_desaturation_time +// int_O_nofly_time // void calc_desaturation_time(void) { + overlay float P_ambient_altitude; + assert( 800 < int_I_pres_surface && int_I_pres_surface < 1100 ); assert( 0 < char_I_desaturation_multiplier && char_I_desaturation_multiplier <= 100 ); - // safety limit to prevent eventual infinite looping (bricking the OSTC) - if( int_I_pres_surface < 500) int_I_pres_surface = 500; - - // fraction of inert gases in respired air - real_N2_ratio = 0.7902; - real_He_ratio = 0.0; - - // surface pressure in bar - pres_surface = 0.001 * int_I_pres_surface; - - // partial pressure of N2 in respired air - N2_equilibrium = real_N2_ratio * (pres_surface - ppWater); - - // pre-computed term for later use: 10 [Min] * 0.01 [%] * 0.6931 [=log(2)] * ... - desat_factor = 0.06931 * char_I_desaturation_multiplier * SURFACE_DESAT_FACTOR; + // safeguard and convert surface pressure + if( int_I_pres_surface < 500) pres_surface = 0.5; + else pres_surface = 0.001 * int_I_pres_surface; + + // calculate partial pressure of N2 in respired air at surface pressure + calc_N2_equilibrium(); + + // get, safeguard and convert the saturation and desaturation factors + get_saturation_factors(); + + // pre-computed term for later use: 10 [Min] * 0.6931 [=log(2)] * 1 [Desat Factor] * ... + desat_factor = (6.931 * SURFACE_DESAT_FACTOR) * float_desaturation_multiplier; // initialize vars int_O_desaturation_time = 0; int_O_nofly_time = 0; - + // get selected target altitude + switch( char_I_altitude_wait ) + { + case 1: P_ambient_altitude = P_ambient_1000m; break; + case 2: P_ambient_altitude = P_ambient_2000m; break; + case 3: P_ambient_altitude = P_ambient_3000m; break; + default: P_ambient_altitude = P_ambient_fly; break; + } + + // loop over all compartments in order slowest to fastest for( ci = NUM_COMP; ci > 0; ) { - overlay float pres_tissue_max; - overlay float P_ambient_altitude; - overlay signed char search_direction; - overlay unsigned int nofly_N2 = 0; - overlay unsigned int nofly_He = 0; - overlay unsigned int nofly_last = ~0; - - + overlay float pres_tissue_max; + overlay unsigned short nofly_last = ~0; + overlay unsigned short nofly_N2 = 0; + +#ifdef _helium + overlay signed char search_direction; + overlay unsigned short nofly_He = 0; +#endif + + + // decrement compartment index ci -= 1; + // get the Buhlmann halftimes and coefficients read_Buhlmann_ht(); read_Buhlmann_coefficients(); - // get selected target altitude - switch( char_I_altitude_wait ) - { - case 1: P_ambient_altitude = P_ambient_1000m; break; - case 2: P_ambient_altitude = P_ambient_2000m; break; - case 3: P_ambient_altitude = P_ambient_3000m; break; - default: P_ambient_altitude = P_ambient_fly; break; - } + + // + // Desaturation time + // + + // calculate desaturation time for N2 in tissue, + // desaturated state is defined as residual tissue pressure <= 1.05 x ppN2 respired + + // current tissue pressure above equilibrium pressure + pres_actual = real_pres_tissue_N2[ci] - N2_equilibrium; + + // target pressure above equilibrium pressure + pres_target = 0.05 * N2_equilibrium; + + // half-time of the current tissue + var_ht = var_N2_ht; + + // calculate desaturation time + calc_desaturation_time_helper(); + + // store desaturation time if it is longer than longest found so far + if( int_time > int_O_desaturation_time) int_O_desaturation_time = int_time; + + +#ifdef _helium + + // calculate desaturation time for He in the tissue, + // desaturated state is defined as residual tissue pressure <= 0.05 x ppN2 respired + + // actual tissue pressure above equilibrium: equilibrium for He is 0 bar + pres_actual = real_pres_tissue_He[ci]; + + // target pressure above equilibrium pressure: use same target pressure as for N2 + pres_target = 0.05 * N2_equilibrium; + + // half-time of the current tissue + var_ht = var_He_ht; + + // calculate desaturation time + calc_desaturation_time_helper(); + + // store desaturation time if it is longer than longest found so far + if( int_time > int_O_desaturation_time) int_O_desaturation_time = int_time; + +#endif + + // + // no-fly time + // // Target pressure for the tissue is the Buhlmann limit. We use the Buhlmann // coefficients for N2 also for He because it is easier to calculate and the @@ -3252,53 +3988,18 @@ // safe side, too. pres_tissue_max = (P_ambient_altitude/var_N2_b + var_N2_a); - // Adjust target pressure in case the GF model is in use by GF-high - if( char_I_deco_model != 0 ) - pres_tissue_max = P_ambient_altitude + - 0.01 * char_I_GF_High_percentage * (pres_tissue_max - P_ambient_altitude); - - - // - // Desaturation time - // - - // N2: actual amount of tissue pressure above equilibrium. - pres_actual = pres_tissue_N2[ci] - N2_equilibrium; - - // N2: half-time of the current tissue - var_ht = var_N2_ht; - - // Calculate desaturation time for N2 in tissue. - // Desaturated state is defined as residual tissue pressure <= 1.05 x ppN2 respired - - pres_target = 0.05 * N2_equilibrium; - - calc_desaturation_time_helper(); - - if( int_time > int_O_desaturation_time) int_O_desaturation_time = int_time; - - - // He: actual amount of tissue pressure above equilibrium: equilibrium for He is 0 bar - pres_actual = pres_tissue_He[ci]; - - // He: half-time of the current tissue - var_ht = var_He_ht; - - // Calculate desaturation time for He in the tissue. - // Desaturated state is defined as residual tissue pressure <= 0.05 x ppN2 respired - - pres_target = 0.05 * N2_equilibrium; - - calc_desaturation_time_helper(); - - if( int_time > int_O_desaturation_time) int_O_desaturation_time = int_time; - - - // - // no-fly time - // - - // initialize split_N2_He in case there was a hard reboot / memory clear. + // 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 ) + pres_tissue_max = P_ambient_altitude + + 0.01 * char_I_GF_High_percentage * (pres_tissue_max - P_ambient_altitude); + + +#ifdef _helium + + //---- Variant with Helium ------------------------------------------- + + // initialize split_N2_He in case there was a hard reboot / memory clear if( split_N2_He[ci] == 0 ) split_N2_He[ci] = 90; // initialize search direction @@ -3306,46 +4007,56 @@ for(;;) { - // N2: actual amount of tissue pressure above equilibrium. - pres_actual = pres_tissue_N2[ci] - N2_equilibrium; - - // N2: half-time of the current tissue - var_ht = var_N2_ht; - // Calculate no-fly time for N2 in the tissue. // Flying is permitted when the N2 pressure fits into the assigned fraction above equilibrium. + // current tissue pressure above equilibrium + pres_actual = real_pres_tissue_N2[ci] - N2_equilibrium; + + // target pressure above equilibrium pressure, weighted by N2/He split pres_target = (split_N2_He[ci] * 0.01) * (pres_tissue_max - N2_equilibrium); - if( pres_target < 0.0 ) // check if desaturation to fly target is possible + // half-time of the current tissue + var_ht = var_N2_ht; + + // check if desaturation to target pressure is possible at all + if( pres_target < 0.0 ) { - int_O_nofly_time = 288; // NO - set no-fly time to 288 * 10 min = 48 h - break; // done for this compartment + // NO - set no-fly time to 288 * 10 min = 48 h + int_O_nofly_time = 288; + break; } else { + // YES - calculate desaturation time calc_desaturation_time_helper(); + + // store time found nofly_N2 = int_time; } - // He: actual amount of tissue pressure above equilibrium - equilibrium for He is 0 bar. - pres_actual = pres_tissue_He[ci]; - - // He: half-time of the current tissue + // calculate no-fly time for He in the tissue, + // flying is permitted when the He pressure fits into the assigned fraction + + // current tissue pressure above equilibrium: equilibrium for He is 0 bar + pres_actual = real_pres_tissue_He[ci]; + + // target pressure above equilibrium pressure, weighted by N2/He split + pres_target = ((100 - split_N2_He[ci]) * 0.01) * (pres_tissue_max - N2_equilibrium); + + // half-time of the current tissue var_ht = var_He_ht; - // Calculate no-fly time for He in the tissue. - // Flying is permitted when the He pressure fits into the assigned fraction. - - pres_target = (0.01 * (100 - split_N2_He[ci])) * (pres_tissue_max - N2_equilibrium); - + // calculate desaturation time calc_desaturation_time_helper(); + + // store time found nofly_He = int_time; // Because the sum of N2 and He tissue pressures needs to fit into the Buhlmann limit for // no-fly time calculation, each gas gets assigned a fraction of the available total pressure - // limit. The optimum split between the two gases can not be computed by a single formular, + // limit. The optimum split between the two gases can not be computed by a single formula, // because this would require the inversion of a function with two exponential terms, which is // not possible. We do not want to do a computational complex simulation here like it is done // in the deco calculation code (although we tackle the same base problem here), so we just let @@ -3362,17 +4073,21 @@ // optimum now, or if we are at the upper stop limit of split_N2_He if( (search_direction < 0) || (split_N2_He[ci] == 99) ) { - // Either the just completed iteration was more close to the optimum or the one before - // was, so we take the best (i.e. shortest) time of both as the final no-fly time. + // either the just completed iteration was more close to the optimum or the one before + // was, so we take the best (i.e. shortest) time of both as the final no-fly time int_O_nofly_time = (nofly_N2 < nofly_last) ? nofly_N2 : nofly_last; + + // done break; } // store the no-fly time found in this iteration nofly_last = nofly_N2; - // increase the N2 fraction of the split and set search direction towards more N2 + // increase the N2 fraction of the split split_N2_He[ci] += 1; + + // set search direction towards more N2 search_direction = +1; } else @@ -3381,51 +4096,86 @@ // optimum now, or if we are at the lower stop limit of split_N2_He if( (search_direction > 0) || (split_N2_He[ci] == 1) ) { - // Either the just completed iteration was more close to the optimum or the one before - // was, so we take the best (i.e. shortest) time of both as the final no-fly time. + // either the just completed iteration was more close to the optimum or the one before + // was, so we take the best (i.e. shortest) time of both as the final no-fly time int_O_nofly_time = (nofly_He < nofly_last) ? nofly_He : nofly_last; + + // done break; } // store the no-fly time found in this iteration nofly_last = nofly_He; - // decrease the N2 fraction of the split and set search direction towards less N2 + // decrease the N2 fraction of the split split_N2_He[ci] -= 1; + + // set search direction towards less N2 search_direction = -1; } } // for(;;) +#else + + //---- Variant without Helium ---------------------------------------- + + // current tissue pressure above equilibrium + pres_actual = real_pres_tissue_N2[ci] - N2_equilibrium; + + // target pressure above equilibrium pressure + pres_target = pres_tissue_max - N2_equilibrium; + + // half-time of the current tissue + var_ht = var_N2_ht; + + // check if desaturation to target pressure is possible at all + if( pres_target < 0.0 ) + { + // NO - set no-fly time to 288 * 10 min = 48 h + int_O_nofly_time = 288; + } + else + { + // YES - calculate desaturation time + calc_desaturation_time_helper(); + + // - extend desaturation time if this tissue needs + // more time than already found to be needed + if( int_time > int_O_nofly_time ) int_O_nofly_time = int_time; + } + +#endif + } // for(compartments) - // Rescale int_O_desaturation_time and int_O_nofly_time to full minutes for display purpose + // rescale int_O_desaturation_time and int_O_nofly_time to full minutes for display purpose int_O_desaturation_time *= 10; int_O_nofly_time *= 10; - // Limit int_O_desaturation_time and int_O_nofly_time to 5999 = 99 hours + 59 minutes - // because of display space constraints and rounding done above. + // limit int_O_desaturation_time and int_O_nofly_time to 5999 = 99 hours + 59 minutes + // because of display space constraints and rounding done above if( int_O_desaturation_time > 5999 ) int_O_desaturation_time = 5999; if( int_O_nofly_time > 5999 ) int_O_nofly_time = 5999; - // Clear the microbubbles warning when the current gradient factor is < 100%. + // Clear the micro bubbles warning when the current gradient factor is < 100%. // The current gradient factor is calculated by calc_interval() while not in diving mode. // As the locked warning will stay set, this will cause the warning be be displayed in // attention color instead of warning color. - if( int_O_gradient_factor < 100 ) - char_O_deco_warnings &= ~DECO_WARNING_MBUBBLES; + if( int_O_lead_supersat < 100 ) + deco_warnings &= ~DECO_WARNING_MBUBBLES; // clear some warnings when the desaturation time has become zero if( int_O_desaturation_time == 0 ) - char_O_deco_warnings &= ~( DECO_WARNING_IBCD + DECO_WARNING_IBCD_lock - + DECO_WARNING_MBUBBLES + DECO_WARNING_MBUBBLES_lock - + DECO_WARNING_OUTSIDE + DECO_WARNING_OUTSIDE_lock - + DECO_ATTENTION_OUTSIDE ); - + deco_warnings &= ~( DECO_WARNING_IBCD + DECO_WARNING_IBCD_lock + + DECO_WARNING_MBUBBLES + DECO_WARNING_MBUBBLES_lock + + DECO_WARNING_OUTSIDE + DECO_WARNING_OUTSIDE_lock + + DECO_ATTENTION_OUTSIDE ); } + ////////////////////////////////////////////////////////////////////////////// // Calculate desaturation of the real tissues for a given time interval // @@ -3433,14 +4183,14 @@ // If in doubt, use this function only inside a context surrounded with // push_tissues_to_vault() / pull_tissues_from_vault() ! // -// Input: int_I_pres_surface : surface pressure in mbar -// time_interval : time interval in minutes, must be limited to 254 at max -// -// Modified: tissue pressures : N2 and He pressures of the tissues -// CNS_fraction : current CNS value -// ceiling : minimum allowed depth in mbar relative pressure -// lead_supersat : supersaturation of the leading tissue -// int_O_gradient_factor : current GF factor +// Input: int_I_pres_surface surface pressure in mbar +// time_interval time interval in minutes, must be limited to 254 at max +// +// 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 +// lead_supersat supersaturation of the leading tissue (float) +// int_O_lead_supersat supersaturation of the leading tissue (integer) // static void calc_interval(PARAMETER unsigned char time_interval) { @@ -3451,20 +4201,25 @@ assert( 0 < char_I_desaturation_multiplier && char_I_desaturation_multiplier <= 100 ); - // safety limit to prevent eventual infinite looping (bricking the OSTC) - if( int_I_pres_surface < 500) int_I_pres_surface = 500; // min. surface pressure = 500 mbar - - // setup input data for deco routines - real_pres_respiration = pres_surface = 0.001 * int_I_pres_surface; - - real_N2_ratio = 0.7902; // according to Buhlmann - N2_equilibrium = real_N2_ratio * (pres_surface - ppWater); // used for N2 tissue graphics scaling - ppN2 = real_N2_ratio * (real_pres_respiration - ppWater); - ppHe = 0.0; - - float_desaturation_multiplier = 0.01 * char_I_desaturation_multiplier * SURFACE_DESAT_FACTOR; - float_saturation_multiplier = 0.01 * char_I_saturation_multiplier; - + // safeguard and convert surface pressure + if( int_I_pres_surface < 500) pres_surface = 0.500; + else pres_surface = 0.001 * int_I_pres_surface; + + // set breathed pressure to surface pressure + real_pres_respiration = pres_surface; + + // calculate partial pressure of N2 in respired air at surface pressure + calc_N2_equilibrium(); + + // calculate partial pressures (0.7902 is fraction of N2 in atmosphere as of Buhlmann) + ppN2 = N2_equilibrium; + ppHe = 0.0; + + // get, safeguard and convert the saturation and desaturation factors + get_saturation_factors(); + + // adjust desaturation factor to surface mode + float_desaturation_multiplier *= SURFACE_DESAT_FACTOR; // Calculate the tissues: // Because calc_tissues() can calculate for 127 minutes at max, @@ -3476,15 +4231,15 @@ if( time > 127) { // do a full 127 minutes on the real tissues - tissue_increment = TISSUE_FLAG | 127; + tissue_increment = TISSUE_SELECTOR | 127; calc_tissues(); - // determine the remaining part + // determine the remaining time time -= 127; } - // program the remaining part (or full part if not exceeding 127 minutes) - tissue_increment = TISSUE_FLAG | time; + // program the remaining time (or full time if not exceeding 127 minutes) on the real tissues + tissue_increment = TISSUE_SELECTOR | time; // update the N2 and He pressures in the tissues calc_tissues(); @@ -3500,549 +4255,831 @@ { if( time > 9 ) { - CNS_fraction *= 0.925874712; // Half-time = 90min -> 10 min: (1/2)^(1/9) - time -= 10; // fast speed looping + CNS_fraction_real *= 0.925874712; // half-time = 90 min -> 10 min: (1/2)^(1/9) + time -= 10; // fast speed looping } else { - CNS_fraction *= 0.992327946; // Half-time = 90min -> 1 min: (1/2)^(1/90) - time -= 1; // slow speed looping + CNS_fraction_real *= 0.992327946; // half-time = 90 min -> 1 min: (1/2)^(1/90) + time -= 1; // slow speed looping } } - // compute integer copy of CNS value - convert_CNS_for_display(); - - // calculate GF value (for a GF high of 100%) - calc_limit(1.0); - - // compute integer copy of GF value - convert_GF_for_display(); + // convert the CNS value to integer + convert_cur_CNS_for_display(); + + // calculate the supersaturation of the leading tissue, the + // negative argument puts calc_limit() into surface mode + // Attention: do not pass char_I_GF_High_percentage as an argument + // here because it is not configured outside dive mode + calc_limit(-1.0); + + // convert the saturation value of the leading tissue to integer + convert_sat_for_display(); } ////////////////////////////////////////////////////////////////////////////// // calc_CNS // -// Input: char_ppO2 : current ppO2 [decibars] -// tissue_increment : time increment and tissue selector -// -// Modified: CNS_fraction accumulated CNS (real tissue context) -// sim_CNS_fraction : accumulated CNS (simulated tissue context) +// Input: char_ppO2 current ppO2 [in 0.1 bars] +// tissue_increment time increment and tissue selector +// +// Modified: CNS_fraction_real accumulated CNS (real tissue context) +// CNS_fraction_sim accumulated CNS (simulated tissue context) // static void calc_CNS(void) { overlay float CNS_fraction_inc; // increment of CNS load, 0.01 = 1% - overlay float time_factor; // factor for time increment - - assert( char_ppO2 > 15 ); - - // adjust time factor to 2 seconds (factor = 1.0) or minute-based interval (factor = N * 30.0) - if( tissue_increment & TIME_MASK ) time_factor = (float)(tissue_increment & TIME_MASK) * 30.0; - else time_factor = 1.0; - - //------------------------------------------------------------------------ - // No CNS increase below 0.5 bar ppO2 - if (char_ppO2 < 50) CNS_fraction_inc = 0.0; - //------------------------------------------------------------------------ - // Below (and including) 1.60 bar - else if (char_ppO2 < 61) CNS_fraction_inc = time_factor/(-533.07 * char_ppO2 + 54000.0); - else if (char_ppO2 < 71) CNS_fraction_inc = time_factor/(-444.22 * char_ppO2 + 48600.0); - else if (char_ppO2 < 81) CNS_fraction_inc = time_factor/(-355.38 * char_ppO2 + 42300.0); - else if (char_ppO2 < 91) CNS_fraction_inc = time_factor/(-266.53 * char_ppO2 + 35100.0); - else if (char_ppO2 < 111) CNS_fraction_inc = time_factor/(-177.69 * char_ppO2 + 27000.0); - else if (char_ppO2 < 152) CNS_fraction_inc = time_factor/( -88.84 * char_ppO2 + 17100.0); - else if (char_ppO2 < 167) CNS_fraction_inc = time_factor/(-222.11 * char_ppO2 + 37350.0); - //------------------------------------------------------------------------ - // Arieli et all.(2002): Modeling pulmonary and CNS O2 toxicity: - // J Appl Physiol 92: 248--256, 2002, doi:10.1152/japplphysiol.00434.2001 - // Formula (A1) based on value for 1.55 and c=20 - // example calculation: Sqrt((1.7/1.55)^20)*0.000404 - else if (char_ppO2 < 172) CNS_fraction_inc = time_factor * 0.00102; - else if (char_ppO2 < 177) CNS_fraction_inc = time_factor * 0.00136; - else if (char_ppO2 < 182) CNS_fraction_inc = time_factor * 0.00180; - else if (char_ppO2 < 187) CNS_fraction_inc = time_factor * 0.00237; - else if (char_ppO2 < 192) CNS_fraction_inc = time_factor * 0.00310; - else if (char_ppO2 < 198) CNS_fraction_inc = time_factor * 0.00401; - else if (char_ppO2 < 203) CNS_fraction_inc = time_factor * 0.00517; - else if (char_ppO2 < 233) CNS_fraction_inc = time_factor * 0.02090; - else CNS_fraction_inc = time_factor * 0.04820; // value for 2.5 bar, used for 2.33 bar and above + + + // calculate CNS increment for 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 + + // read coefficient (increment) + 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; + } + 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 + + // read coefficients + read_CNS_ab_coefficient(); + + // calculate the CNS increment + CNS_fraction_inc = 1.0 / (var_cns_a * char_ppO2 + var_cns_b ); + } + else + { // no increment up to 0.5 bar ppO2 + 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; + } // update the CNS accumulator - if( tissue_increment & TISSUE_FLAG ) CNS_fraction += CNS_fraction_inc; // real tissues - else sim_CNS_fraction += CNS_fraction_inc; // simulated tissues + if ( tissue_increment & TISSUE_SELECTOR ) CNS_fraction_real += CNS_fraction_inc; // real tissues + else CNS_fraction_sim += CNS_fraction_inc; // simulated tissues +} + + +////////////////////////////////////////////////////////////////////////////// +// calc_due_by_depth_time_sac (Helper Function saving Code Space) +// +// 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_float_depth depth in meters +// gas_needs_float_time time in minutes +// gas_needs_stop_usage 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) +{ + gas_needs_volume_due = (gas_needs_float_depth * METER_TO_BAR + 1.0) * gas_needs_float_time * gas_needs_stop_usage; +} + + +////////////////////////////////////////////////////////////////////////////// +// calc_gas_needs_ascent +// +// calculates the gas needs along the ascent +// +// Input: char_depth_bottom depth of the bottom segment +// char_I_bottom_time duration of the bottom segment +// char_I_extra_time extra bottom time for fTTS / delayed ascent +// float_ascent_speed ascent speed, in meters/minute +// internal_deco_depth[] depth of the stops +// internal_deco_time[] duration of the stops +// internal_deco_gas[] gas breathed at the stops +// NDL_time remaining NDL time, used to adjust speed of final ascent +// char_I_SAC_work gas consumption during bottom part and initial ascent, in liters/minute +// char_I_SAC_deco gas consumption during stops and following ascents, in liters/minute +// char_I_gas_avail_size[] size of the tanks for gas 1-5, in liters +// char_I_gas_avail_pres[] fill pressure of the tanks +// +// Output: gas_volume_need[] amount of gas needed, in liters +// +static void calc_gas_needs_ascent(void) +{ + switch (gas_needs_next_phase) + { + //--------------------------------------------------------------------- + + case GAS_NEEDS_INIT: + + // set index to the first stop table entry + gas_needs_stop_index = 0; + + // clear the gas volume needs + for( i = 0; i < NUM_GAS; ++i ) gas_volume_need[i] = 0.0; + +#ifdef _rx_functions + // only for OSTC TR model with TR functions enabled + if( main_status & TR_FUNCTIONS ) + { + // invalidate pressure needs to pressure readings + int_O_pressure_need[0] = 0 + INT_FLAG_NOT_AVAIL; + int_O_pressure_need[1] = 0 + INT_FLAG_NOT_AVAIL; + } +#endif + + // terminate if in loop mode (CCR, pSCR) as there are no gas needs to calculate, + // else continue with the gas needs of the bottom segment + if ( deco_status & MODE_LOOP ) gas_needs_next_phase = GAS_NEEDS_DONE; + else gas_needs_next_phase = GAS_NEEDS_BOTTOM_SEGMENT; + + break; + + //--------------------------------------------------------------------- + + case GAS_NEEDS_BOTTOM_SEGMENT: + + // sim_gas_current_num gas used during bottom segment (0, 1-5) + // char_depth_bottom depth of the bottom segment + + // get the gas used during bottom segment + gas_find_current(); + + // initialize variables + gas_needs_stop_gas_last = gas_needs_stop_gas = sim_gas_current_num; + + // set the usage (SAC rate) to bottom usage rate for bottom part and initial ascent + gas_needs_stop_usage = char_I_SAC_work; + + // volumes are only calculated for gases 1-5, but not the manually configured one + if( gas_needs_stop_gas ) + { + // set the bottom depth + gas_needs_float_depth = (float)char_depth_bottom; + + // calculate either whole bottom time or just the fTTS/bailout extra time + gas_needs_float_time = ( main_status & CALCULATE_BOTTOM ) ? (float)char_I_bottom_time : (float)char_I_extra_time; + + // calculate gas demand + calc_due_by_depth_time_sac(); + + // take result + gas_volume_need[gas_needs_stop_gas-1] = gas_needs_volume_due; + } + + // continue with initial ascent demand + gas_needs_next_phase = GAS_NEEDS_INITIAL_ASCENT; + + break; + + + //--------------------------------------------------------------------- + + case GAS_NEEDS_INITIAL_ASCENT: + + // gas_needs_stop_gas : gas from bottom segment + // char_depth_bottom : depth of the bottom segment + // internal_deco_depth[0]: depth of the first stop, may be 0 if no stop exists + + // get the data of the first stop + gas_needs_stop_depth = internal_deco_depth[0]; + gas_needs_stop_time = internal_deco_time[0]; + + // volumes are only calculated for gases 1-5, but not the manually configured one + if( gas_needs_stop_gas ) + { + // compute distance between bottom and first stop + gas_needs_float_depth = (float)char_depth_bottom - (float)gas_needs_stop_depth; + + // initial ascent exists only if ascent distance is > 0 + if( gas_needs_float_depth > 0.0 ) + { + // compute ascent time + gas_needs_float_time = gas_needs_float_depth / float_ascent_speed; + + // compute average depth between bottom and first stop + gas_needs_float_depth = (float)char_depth_bottom - gas_needs_float_depth * 0.5; + + // calculate gas demand + calc_due_by_depth_time_sac(); + + // add to overall demand + gas_volume_need[gas_needs_stop_gas-1] += gas_needs_volume_due; + } + } + + // switch the usage (SAC rate) to deco usage rate + // for stops, intermediate and final ascent + gas_needs_stop_usage = char_I_SAC_deco; + + // is there a (first) stop? + if( gas_needs_stop_depth ) + { + // YES - continue with stop demand + gas_needs_next_phase = GAS_NEEDS_STOP; + + break; + } + else + { + // NO - add demand of a 3 minutes safety stop at 5 meters, at least for contingency... + gas_needs_float_time = 3.0; + gas_needs_float_depth = 5.0; + + // calculate gas demand + calc_due_by_depth_time_sac(); + + // add to overall demand + gas_volume_need[gas_needs_stop_gas-1] += gas_needs_volume_due; + + // calculation finished + gas_needs_next_phase = GAS_NEEDS_DONE; + + break; + } + + + //--------------------------------------------------------------------- + + case GAS_NEEDS_STOP: + + // correct stop depth if shallower than calculated stop depth and convert to float + gas_needs_float_depth = ( char_depth_bottom < gas_needs_stop_depth ) ? (float)char_depth_bottom : (float)gas_needs_stop_depth; + + // get the gas on this stop + gas_needs_stop_gas = internal_deco_gas[gas_needs_stop_index]; + + // do we have a gas change? + if( gas_needs_stop_gas_last && (gas_needs_stop_gas != gas_needs_stop_gas_last) ) + { + // YES - spend an additional char_I_gas_change_time on the old gas + gas_needs_float_time = (float)char_I_gas_change_time; + + // calculate gas demand + calc_due_by_depth_time_sac(); + + // add to overall demand + gas_volume_need[gas_needs_stop_gas_last-1] += gas_needs_volume_due; + } + + // calculate demand of (new) gas for the full stop duration + if( gas_needs_stop_gas ) + { + // get the duration of the stop + gas_needs_float_time = (float)gas_needs_stop_time; + + // calculate gas demand + calc_due_by_depth_time_sac(); + + // add to overall demand + gas_volume_need[gas_needs_stop_gas-1] += gas_needs_volume_due; + } + + // Continue with the demand of the intermediate ascent to the next stop. + // If there is no further stop, it will divert by itself to final ascent. + gas_needs_next_phase = GAS_NEEDS_INTERMEDIATE_ASCENT; + + break; + + + //--------------------------------------------------------------------- + + case GAS_NEEDS_INTERMEDIATE_ASCENT: + + // store last stop depth and last gas + gas_needs_stop_depth_last = gas_needs_stop_depth; + gas_needs_stop_gas_last = gas_needs_stop_gas; + + // check if end of stop table is reached + if( gas_needs_stop_index < NUM_STOPS-1 ) + { + // NO - check if there is another stop entry + if( internal_deco_depth[gas_needs_stop_index+1] == 0 ) + { + // NO - continue with final ascent demand + gas_needs_next_phase = GAS_NEEDS_FINAL_ASCENT; + + break; + } + else + { + // YES - goto next stop entry + gas_needs_stop_index++; + + // get the depth of the next stop entry + gas_needs_stop_depth = internal_deco_depth[gas_needs_stop_index]; + + // get the duration of the next stop + gas_needs_stop_time = internal_deco_time[gas_needs_stop_index]; + } + } + else + { + // YES - end of stop table reached + // We are stranded at some stop depth and do not know how many more + // stops there may be in front of us and how long they may be. So as + // as last resort to calculate at least something, we assume that the + // rest of the ascent will be done in deco final ascent pace, i.e. at + // 1 meter per minute. Because of the stop table overflow, the result + // will be flagged as being invalid later on. + // + gas_needs_next_phase = GAS_NEEDS_FINAL_ASCENT; + + break; + } + + // volumes are only calculated for gases 1-5, but not the manually configured one + if( gas_needs_stop_gas_last ) + { + // compute distance between the two stops + gas_needs_float_depth = (float)(gas_needs_stop_depth_last - gas_needs_stop_depth); + + // compute ascent time + gas_needs_float_time = gas_needs_float_depth / float_ascent_speed; + + // compute average depth between the two stops + gas_needs_float_depth = (float)gas_needs_stop_depth_last - gas_needs_float_depth * 0.5; + + // calculate gas demand + calc_due_by_depth_time_sac(); + + // add to overall demand + gas_volume_need[gas_needs_stop_gas_last-1] += gas_needs_volume_due; + } + + // continue with calculation stop demand + gas_needs_next_phase = GAS_NEEDS_STOP; + + break; + + + //--------------------------------------------------------------------- + + case GAS_NEEDS_FINAL_ASCENT: + + // gas_needs_float_depth: still holds depth of the last stop + // gas_needs_stop_gas : still holds gas from last stop (0 or 1-5) + + // volumes are only calculated for gases 1-5, but not the manually configured one + if( gas_needs_stop_gas ) + { + // set ascent time dependent on deco status + if( NDL_time ) + { + // within NDL - ascent with float_ascent_speed + // + // Remark: When calculating a bailout ascent, there may be stops + // for gas changes although the dive is still within NDL + // and final ascent thus does not need to be slowed down. + gas_needs_float_time = gas_needs_float_depth / float_ascent_speed; + } + else + { + // in deco - reduce ascent speed to 1 meter per minute + gas_needs_float_time = gas_needs_float_depth; + } + + // set half-way depth + gas_needs_float_depth *= 0.5; + + // calculate gas demand + calc_due_by_depth_time_sac(); + + // add to overall demand + gas_volume_need[gas_needs_stop_gas-1] += gas_needs_volume_due; + } + + // calculation finished + gas_needs_next_phase = GAS_NEEDS_DONE; + + break; + + } // switch } ////////////////////////////////////////////////////////////////////////////// -// gas_volumes -// -// calculates volumes and required tank fill pressures for each gas. -// -// Input: char_bottom_depth depth of the bottom segment -// char_I_bottom_time duration of the bottom segment -// char_I_extra_time extra bottom time for fTTS / delayed ascent -// float_ascent_speed ascent speed, in meters/minute -// internal_deco_depth[] depth of the stops -// internal_deco_time[] duration of the stops -// internal_deco_gas[] gas breathed at the stops -// NDL_time remaining NDL time, used to adjust speed of final ascent -// char_I_bottom_usage gas consumption during bottom part and initial ascent, in liters/minute -// char_I_deco_usage gas consumption during stops and following ascents, in liters/minute -// char_I_tank_size[] size of the tanks for gas 1-5, in liters -// char_I_tank_pres_fill[] fill pressure of the tanks -// -// Output: int_O_ascent_volumes[] amount of gas needed, in liters -// int_O_ascent_pres_need[] in bar, + flags for fast evaluation by dive mode warnings: -// 2^15: pres_need >= pres_fill -// 2^14: pres_need >= press_fill * GAS_NEEDS_ATTENTION_THRESHOLD -// 2^11: pres_need == 0 -// 2^10: pres_need is invalid -// -static void gas_volumes_helper_1(void) +// calc_TR_functions +// +// Process Pressure Readings (OSTC TR only) +// +// Input: todo +// +// Output: todo +// +#ifdef _rx_functions +static void calc_TR_functions(void) { - // Calculate the gas volume needed at a given depth, time and usage (SAC rate). - // We use 1.0 for the surface pressure to have stable results when used through - // the deco calculator (simulation mode). - volume = (float_depth * METER_TO_BAR + 1.0) * float_time * char_usage; - - return; -} - -static void gas_volume_helper_2(void) -{ - // Convert a gas volume in liters given as a float into an integer number - // and computes the equivalent tank pressure in bar, including all flags. - - if( volume >= 65534.5 ) - { - int_volume = 65535; - int_pres_need = 999 + INT_FLAG_WARNING; // 999 bar + warning flag for > pres_fill - } - else + // pressure warnings for reading 1, but only if enabled and pressure value available + if( (char_I_pressure_gas[0] > 0) && !(int_IO_pressure_value[0] & INT_FLAG_NOT_AVAIL) ) { - overlay unsigned short tank_pres_fill = 10.0 * (unsigned short)char_I_tank_pres_fill[gas_num]; - - // No distinct rounding done here because volumes are not accurate to the single liter anyhow - - // convert gas volumes to integers - int_volume = (unsigned short)volume; - - // compute how much pressure in the tank will be needed [in bar] (integer-division) - int_pres_need = (unsigned short)(int_volume / char_I_tank_size[gas_num]); - - // limit to 999 bar because of display constraints - if( int_pres_need > 999 ) int_pres_need = 999; - - // set flags for fast evaluation by divemode check_for_warnings - if ( int_pres_need == 0 ) int_pres_need |= INT_FLAG_ZERO; - else if( int_pres_need >= tank_pres_fill ) int_pres_need |= INT_FLAG_WARNING; - else if( int_pres_need >= GAS_NEEDS_ATTENTION_THRESHOLD * tank_pres_fill ) int_pres_need |= INT_FLAG_ATTENTION; + overlay unsigned short pressure_value = int_IO_pressure_value[0] & ~INT_FLAG_OUTDATED; + + if( (char_I_pressure_gas[0] < 6 ) && !(int_O_pressure_need[0] & INT_FLAG_NOT_AVAIL) ) + { + // not a diluent and need available: warning & attention by need + if ( pressure_value <= int_O_pressure_need[0]) + int_IO_pressure_value[0] |= INT_FLAG_WARNING; + else if( pressure_value <= int_O_pressure_need[0] + int_O_pressure_need[0] / 2 ) + int_IO_pressure_value[0] |= INT_FLAG_ATTENTION; + } + else + { + // a diluent or need not available: warning & attention by fixed thresholds + if ( pressure_value <= PRESSURE_LIMIT_WARNING ) int_IO_pressure_value[0] |= INT_FLAG_WARNING; + else if ( pressure_value <= PRESSURE_LIMIT_ATTENTION ) int_IO_pressure_value[0] |= INT_FLAG_ATTENTION; + } } - return; -} - -static void gas_volumes(void) -{ - overlay float volumes[NUM_GAS]; - - overlay unsigned char stop_gas; - overlay unsigned char stop_gas_last; - overlay unsigned char stop_time; - overlay unsigned char stop_depth; - overlay unsigned char stop_depth_last; - overlay unsigned char i; - - //---- initialization ---------------------------------------------------- - - // null the volume accumulators - for( gas_num = 0; gas_num < NUM_GAS; ++gas_num ) volumes[gas_num] = 0.0; - - // quit for CCR and pSCR mode - if( char_O_deco_status & DECO_MODE_LOOP ) goto done; - - - //---- bottom demand ----------------------------------------------------- - - // sim_gas_current : gas used during bottom segment (0, 1-5) - // char_bottom_depth: depth of the bottom segment - - // get the gas used during bottom segment - gas_find_current(); - - // initialize variables - stop_gas_last = stop_gas = sim_gas_current; - - // set the usage (SAC rate) to bottom usage rate for bottom part and initial ascent - char_usage = char_I_bottom_usage; - - // volumes are only calculated for gases 1-5, but not the manually configured one - if( stop_gas ) + // pressure warnings for reading 2, but only if enabled and pressure value available + if( (char_I_pressure_gas[1] > 0) && !(int_IO_pressure_value[1] & INT_FLAG_NOT_AVAIL) ) { - // set the bottom depth - float_depth = (float)char_bottom_depth; - - // calculate either bottom segment or just the fTTS/bailout delayed part - if( char_O_main_status & DECO_BOTTOM_CALCULATE ) + overlay unsigned short pressure_value = int_IO_pressure_value[1] & ~INT_FLAG_OUTDATED; + + if( (char_I_pressure_gas[1] < 6 ) && !(int_O_pressure_need[1] & INT_FLAG_NOT_AVAIL) ) { - // duration of bottom segment - float_time = (float)char_I_bottom_time; + // not a diluent and need available: warning & attention by need + if ( pressure_value <= int_O_pressure_need[1]) + int_IO_pressure_value[1] |= INT_FLAG_WARNING; + else if ( pressure_value <= int_O_pressure_need[1] + int_O_pressure_need[1] / 2 ) + int_IO_pressure_value[1] |= INT_FLAG_ATTENTION; } else { - // duration of delayed ascent - float_time = (float)char_I_extra_time; + // a diluent or need not available: warning & attention by fixed thresholds + if ( pressure_value <= PRESSURE_LIMIT_WARNING ) int_IO_pressure_value[1] |= INT_FLAG_WARNING; + else if ( pressure_value <= PRESSURE_LIMIT_ATTENTION ) int_IO_pressure_value[1] |= INT_FLAG_ATTENTION; } - - // calculate gas demand - gas_volumes_helper_1(); - - // take result - volumes[stop_gas-1] = volume; } - // initialize stop index with first stop - i = 0; - - //---- initial ascent demand --------------------------------------------- - - // stop_gas : gas from bottom segment - // char_bottom_depth : depth of the bottom segment - // internal_deco_depth[i=0]: depth of the first stop, may be 0 if no stop exists - - // get the data of the first stop - stop_depth = internal_deco_depth[i]; - stop_time = internal_deco_time[i]; - - // volumes are only calculated for gases 1-5, but not the manually configured one - if( stop_gas ) + //--- SAC Calculation --------------------------------------------------------------------- + // + // char_I_SAC_mode =0: disabled + // =1: SAC from 1st reading + // =2: SAC from 2nd reading + // =3: SAC from higher one of both pressure drops (independent double mode) + // =4: SAC (O2 usage) from 2nd reading without real_pres_respiration term + + // set SAC rate to not available by default + int_O_SAC_measured = 0 + INT_FLAG_NOT_AVAIL; + + // get a copy of the current absolute pressure + pres_respiration_sac = real_pres_respiration; + + // set threshold for SAC rate attention + max_sac_rate = (deco_info & DECO_FLAG) ? char_I_SAC_deco : char_I_SAC_work; + + // char_I_SAC_deco / char_I_SAC_work are in l/min, max_sac_rate is in 0.1 l/min + max_sac_rate *= 10; + + + // pre-process SAC mode 3 (independent double) + if( char_I_SAC_mode == 3 ) { - // compute distance between bottom and first stop - float_depth = (float)char_bottom_depth - (float)stop_depth; - - // initial ascent exists only if ascent distance is > 0 - if( float_depth > 0.0 ) + overlay unsigned char reading1_gas; + overlay unsigned char reading2_gas; + overlay unsigned char reading1_tanksize; + overlay unsigned char reading2_tanksize; + overlay unsigned short reading1_press; + overlay unsigned short reading2_press; + overlay unsigned short reading1_drop; + overlay unsigned short reading2_drop; + + // get gas numbers (1-10) of both readings + reading1_gas = char_I_pressure_gas[0]; + reading2_gas = char_I_pressure_gas[1]; + + // default to no SAC calculation + char_I_SAC_mode = 0; + + // clear switch advice by default + deco_info &= ~IND_DOUBLE_SWITCH_FLAG; + + // check if both readings are configured and available + if( reading1_gas ) + if( reading2_gas ) + if( !(int_IO_pressure_value[0] & INT_FLAG_NOT_AVAIL) ) + if( !(int_IO_pressure_value[1] & INT_FLAG_NOT_AVAIL) ) + if( !(int_I_pressure_drop[0] & INT_FLAG_NOT_AVAIL) ) + if( !(int_I_pressure_drop[1] & INT_FLAG_NOT_AVAIL) ) { - // compute ascent time - float_time = float_depth / float_ascent_speed; - - // compute average depth between bottom and first stop - float_depth = (float)char_bottom_depth - float_depth * 0.5; - - // calculate gas demand - gas_volumes_helper_1(); - - // add result - volumes[stop_gas-1] += volume; + // get tank pressures, stripping flags + reading1_press = int_IO_pressure_value[0] & 0x0FFF; // in 0.1 bar + reading2_press = int_IO_pressure_value[1] & 0x0FFF; // in 0.1 bar + + // get pressure drops as integers, stripping flags and shifting right + // to avoid an overflow when multiplying with the tank size later on + reading1_drop = (int_I_pressure_drop[0] & 0x0FFF) >> 2; + reading2_drop = (int_I_pressure_drop[1] & 0x0FFF) >> 2; + + // get tank sizes + reading1_tanksize = char_I_gas_avail_size[reading1_gas-1]; + reading2_tanksize = char_I_gas_avail_size[reading2_gas-1]; + + // set mode to calculate SAC on the reading with the higher absolute drop + char_I_SAC_mode = (reading1_drop * reading1_tanksize > reading2_drop * reading2_tanksize) ? 1 : 2; + + // compute switch advice if pressure (in 0.1 bar) of tank breathed from is + // more than char_I_max_pres_diff (in bar) below pressure of the other tank. + if( char_I_SAC_mode == 1 ) + { + // breathing from reading 1, switch advice if pressure on reading 1 lower than on 2 + if( (reading1_press + 10*char_I_max_pres_diff) <= reading2_press ) + deco_info |= IND_DOUBLE_SWITCH_FLAG; + } + else + { + // breathing from reading 2, switch advice if pressure on reading 2 lower than on 1 + if( (reading2_press + 10*char_I_max_pres_diff) <= reading1_press ) + deco_info |= IND_DOUBLE_SWITCH_FLAG; + } } } - // switch the usage (SAC rate) to deco usage rate - // for stops, intermediate and final ascent - char_usage = char_I_deco_usage; - - // is there a (first) stop? if yes, goto stops processing - if( stop_depth ) goto stops; - - // add demand of a 3 minutes safety stop at 5 meters, at least for contingency... - float_time = 3.0; - float_depth = 5.0; - - // calculate gas demand - gas_volumes_helper_1(); - - // add result - volumes[stop_gas-1] += volume; - - // proceed to volume conversion and pressure calculations - goto done; - - - //---- intermediate ascent demand --------------------------------------- -inter_ascents: - - // store last stop depth and gas - stop_depth_last = stop_depth; - stop_gas_last = stop_gas; - - // check if we are at the end of the stops table - if( i < NUM_STOPS-1 ) + + // pre-process SAC mode 4 (O2 usage by reading 2) + if( char_I_SAC_mode == 4 ) + { + // O2 usage on CCR is independent from absolute pressure + pres_respiration_sac = 1.0; + + // O2 pressure drop is measured via reading 2 + char_I_SAC_mode = 2; + + // reconfigure max SAC rate to O2 consumption attention threshold + max_sac_rate = O2_CONSUMPTION_LIMIT_ATTENTION; + } + + + // calculate SAC - modes 1 & 2 + if( (char_I_SAC_mode == 1) || (char_I_SAC_mode == 2) ) { - // there are more entries - get the next stop data - i++; - - // get the next stop depth - stop_depth = internal_deco_depth[i]; - - // check if there is indeed another stop, - // if not (depth = 0) treat as end of table - if( stop_depth == 0 ) goto end_of_table; - - // get the next stop duration - stop_time = internal_deco_time[i]; + overlay unsigned char reading_index; + overlay unsigned char reading_gas; + overlay unsigned char reading_tanksize; + overlay float reading_drop; + + // set index: char_I_SAC_mode = 1 -> reading one, index 0 + // = 2 -> two, 1 + reading_index = char_I_SAC_mode - 1; + + // get gas number (1-10) + reading_gas = char_I_pressure_gas[reading_index]; + + // check if reading is configured and available + if( reading_gas ) + if( !(int_I_pressure_drop[reading_index] & INT_FLAG_NOT_AVAIL) ) + { + // get tank size (in liter) + reading_tanksize = char_I_gas_avail_size[reading_gas-1]; + + // get pressure drop as float, stripping flags (in 1/5120 bar/sec) + reading_drop = (float)(int_I_pressure_drop[reading_index] & 0x0FFF); + + // check if pressure drop is within range + if( !(int_I_pressure_drop[reading_index] & INT_FLAG_OUT_OF_RANGE) ) + { + // calculate SAC, 10 is factor to have result in 0.1 liter/min + // 60 is factor for 60 seconds per 1 minute, + // 5120 accounts for reading_drop being in 1/5120 bar/sec + // 10*60/5120 = 60/512 = 15/128 + float_sac = reading_drop * 15/128 * reading_tanksize / pres_respiration_sac; + + // limit result to 999 (99.9 liter/min) + if ( float_sac >= 998.5 ) + { + int_O_SAC_measured = 999 + INT_FLAG_ATTENTION; + } + else + { + // convert float to integer + int_O_SAC_measured = (unsigned short)(float_sac + 0.5); + + // set attention flag if exceeding SAC threshold, but only if pressure drop is not outdated + if( !(int_I_pressure_drop[reading_index] & INT_FLAG_OUTDATED) ) + if( int_O_SAC_measured >= max_sac_rate ) + { + int_O_SAC_measured |= INT_FLAG_ATTENTION; + } + } + } + else + { + // pressure drop is out of range, so SAC will be set out of range, too + int_O_SAC_measured = 999 + INT_FLAG_ATTENTION; + } + + // copy outdated flag from int_I_pressure_drop to int_O_SAC_measured + if( int_I_pressure_drop[reading_index] & INT_FLAG_OUTDATED ) + { + int_O_SAC_measured |= INT_FLAG_OUTDATED; + } + } + } +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +// convert_gas_needs_to_press +// +// Converts gas volumes into pressures and sets respective flags +// +// Input: gas_needs_gas_index index of the gas to convert (0-4) +// gas_volume_need[] needed gas volume in liters +// char_I_gas_avail_pres[] available gas volume in bar +// 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 +// int_O_pressure_need[] required gas amount for reading 1/2 (TR only) +// +static void convert_gas_needs_to_press(void) +{ + + // just to make the code more readable... + i = gas_needs_gas_index; + + if( gas_volume_need[i] >= 65534.5 ) + { + int_O_gas_need_vol[i] = 65535; // clip at 65535 liters + int_O_gas_need_pres[i] = 999 | INT_FLAG_WARNING | INT_FLAG_HIGH; // 999 bar + warning flag + >999 flag } else { -end_of_table: - - // End of the stops table reached or no more stops: Split the remaining - // ascent into an intermediate ascent and a final ascent by creating a - // dummy stop at the usual last deco stop depth. Stop gas doesn't change. - stop_time = 0; - stop_depth = char_I_depth_last_deco; - } - - // volumes are only calculated for gases 1-5, but not the manually configured one - if( stop_gas_last ) - { - // compute distance between the two stops: - // last stop will always be deeper than current stop - float_depth = (float)(stop_depth_last - stop_depth); - - // compute ascent time - float_time = float_depth / float_ascent_speed; - - // compute average depth between the two stops - float_depth = (float)stop_depth_last - float_depth * 0.5; - - // calculate gas demand - gas_volumes_helper_1(); - - // add result - volumes[stop_gas_last-1] += volume; + 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_ATTENTION_THRESHOLD * 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]; + + // 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 ); + + // 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; } - - //---- next stop demand ------------------------------------------------- -stops: - - // convert depth of the stop - float_depth = (float)stop_depth; - - // get the next gas - stop_gas = internal_deco_gas[i]; - - // in case of end-of-table, keep the last gas - if( !stop_gas ) stop_gas = stop_gas_last; - - // do we have a gas change? - if( stop_gas_last && (stop_gas != stop_gas_last) ) - { - // yes - spend an additional char_I_gas_change_time on the old gas - float_time = (float)char_I_gas_change_time; - - // calculate gas demand - gas_volumes_helper_1(); - - // add result - volumes[stop_gas_last-1] += volume; - } - - // calculate and add demand on new gas for the full stop duration - if( stop_gas ) - { - // get the duration of the stop - float_time = (float)stop_time; - - // calculate gas demand - gas_volumes_helper_1(); - - // add result to last gas - volumes[stop_gas-1] += volume; - } - - // continue with the next intermediate ascent if this was not the last stop - if( stop_depth > char_I_depth_last_deco ) goto inter_ascents; - - - //---- final ascent demand ----------------------------------------------- -final_ascent: - - // float_depth: depth of last stop - // stop_gas : gas from last stop (0 or 1-5) - - // volumes are only calculated for gases 1-5, but not the manually configured one - if( stop_gas ) - { - // set ascent time dependent on deco status - if( NDL_time ) - { - // within NDL - ascent with float_ascent_speed - float_time = float_depth / float_ascent_speed; - } - else - { - // in deco - reduce ascent speed to 1 meter per minute - float_time = float_depth; - } - - // set half-way depth - float_depth *= 0.5; - - // calculate gas demand - gas_volumes_helper_1(); - - // add result - volumes[stop_gas-1] += volume; - } - - - //---- convert results for the assembler interface ----------------------------- -done: + // set invalid flag if there is an overflow in the stops table + if( deco_warnings & DECO_WARNING_STOPTABLE_OVERFLOW ) int_O_gas_need_pres[i] |= INT_FLAG_INVALID; #ifdef _rx_functions // only for OSTC TR model with TR functions enabled - if( char_O_main_status & DECO_TR_FUNCTIONS ) - { - // invalidate pressure needs to pressure readings - int_O_pressure_need[0] = 0 + INT_FLAG_NOT_AVAIL; - int_O_pressure_need[1] = 0 + INT_FLAG_NOT_AVAIL; - } -#endif - - for( gas_num = 0; gas_num < NUM_GAS; ++gas_num ) + if( main_status & TR_FUNCTIONS ) { - volume = volumes[gas_num]; - - // compute int_volume and int_pres_need from volume and gas_num - gas_volume_helper_2(); - - // set invalid flag if there is an overflow in the stops table - if( char_O_deco_warnings & DECO_WARNING_STOPTABLE_OVERFLOW ) - int_pres_need |= INT_FLAG_INVALID; - - // copy result data to ASM interface - int_O_ascent_volumes[gas_num] = int_volume; - int_O_ascent_pres_need[gas_num] = int_pres_need; - -#ifdef _rx_functions - // only for OSTC TR model with TR functions enabled - if( char_O_main_status & DECO_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; + + // 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) ) { - // char_I_pressure_gas[] uses gas numbers 1-10, gas_num runs from 0 to 4 - overlay unsigned char gas = gas_num + 1; - - // check if the current gas is configured on pressure reading 1 or 2 - if( (gas == char_I_pressure_gas[0]) || (gas == char_I_pressure_gas[1]) ) - { - // strip all flags from int_pres_need - int_pres_need &= 1023; - - // limit to 400 bar and multiply by 10 to get result 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 - if( char_O_deco_warnings & DECO_WARNING_STOPTABLE_OVERFLOW ) - int_pres_need |= INT_FLAG_NOT_AVAIL; - - // copy to result vars (in both readings the same gas could be configured) - if( gas == char_I_pressure_gas[0] ) int_O_pressure_need[0] = int_pres_need; - if( gas == char_I_pressure_gas[1] ) int_O_pressure_need[1] = int_pres_need; - } - } // TR functions + // get a copy of the required pressure in full bar + overlay unsigned short int_pres_need = int_O_gas_need_pres[i]; + + // strip all flags + int_pres_need &= 1023; + + // 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 + if( deco_warnings & DECO_WARNING_STOPTABLE_OVERFLOW ) int_pres_need |= INT_FLAG_NOT_AVAIL; + + // copy to reading data (in both readings the same gas could be configured) + if( char_I_pressure_gas[0] == j ) int_O_pressure_need[0] = int_pres_need; + if( char_I_pressure_gas[1] == j ) int_O_pressure_need[1] = int_pres_need; + } + } // TR functions #endif - - } // for } + +////////////////////////////////////////////////////////////////////////////// +// convert the real CNS value to integer +// +// Input CNS_fraction_real current CNS value as float +// +// Output: int_O_CNS_current current CNS value as integer including flags +// +static void convert_cur_CNS_for_display(void) +{ + // convert to integer + float_value = CNS_fraction_real; convert_float_to_int(); int_O_CNS_current = int_value; + + // set warning & attention flags + if ( int_O_CNS_current >= CNS_WARNING_THRESHOLD ) int_O_CNS_current |= INT_FLAG_WARNING; + else if ( int_O_CNS_current >= CNS_ATTENTION_THRESHOLD ) int_O_CNS_current |= INT_FLAG_ATTENTION; +} + + ////////////////////////////////////////////////////////////////////////////// - -static void convert_CNS_for_display(void) -{ - if( CNS_fraction < 0.010 ) int_O_CNS_fraction = 0; - else if( CNS_fraction >= 9.985 ) int_O_CNS_fraction = 999 + INT_FLAG_WARNING; - else - { - // convert float to integer - int_O_CNS_fraction = (unsigned short)(100 * CNS_fraction + 0.5); - - // set warning & attention flags - if( int_O_CNS_fraction >= CNS_WARNING_THRESHOLD ) int_O_CNS_fraction |= INT_FLAG_WARNING; - else if( int_O_CNS_fraction >= CNS_ATTENTION_THRESHOLD ) int_O_CNS_fraction |= INT_FLAG_ATTENTION; - } -} - -////////////////////////////////////////////////////////////////////////////// - +// convert the simulated CNS value to integer +// +// Input: CNS_fraction_sim CNS value after predicted ascent in float +// +// Output: int_sim_CNS_fraction CNS value after predicted ascent in integer +// including flags, will be routed to +// int_O_{normal,alternative}_CNS_fraction +// static void convert_sim_CNS_for_display(void) { - if( sim_CNS_fraction < 0.010 ) int_sim_CNS_fraction = 0; - else if( sim_CNS_fraction >= 9.985 ) int_sim_CNS_fraction = 999 + INT_FLAG_WARNING; - else - { - // convert float to integer - int_sim_CNS_fraction = (unsigned short)(100 * sim_CNS_fraction + 0.5); - - // set warning & attention flags - if ( int_sim_CNS_fraction >= CNS_WARNING_THRESHOLD ) int_sim_CNS_fraction |= INT_FLAG_WARNING; - else if ( int_sim_CNS_fraction >= CNS_ATTENTION_THRESHOLD ) int_sim_CNS_fraction |= INT_FLAG_ATTENTION; - } + // convert to integer + float_value = CNS_fraction_sim; convert_float_to_int(); int_sim_CNS_fraction = int_value; + + // set warning & attention flags + if ( int_sim_CNS_fraction >= CNS_WARNING_THRESHOLD ) int_sim_CNS_fraction |= INT_FLAG_WARNING; + else if ( int_sim_CNS_fraction >= CNS_ATTENTION_THRESHOLD ) int_sim_CNS_fraction |= INT_FLAG_ATTENTION; // set invalid flag if there is an overflow in the stops table - if( char_O_deco_warnings & DECO_WARNING_STOPTABLE_OVERFLOW ) int_sim_CNS_fraction |= INT_FLAG_INVALID; + if ( deco_warnings & DECO_WARNING_STOPTABLE_OVERFLOW ) int_sim_CNS_fraction |= INT_FLAG_INVALID; } + ////////////////////////////////////////////////////////////////////////////// - -static void convert_GF_for_display(void) +// convert the saturation value of the leading tissue to integer +// +// Input lead_supersat saturation of the leading tissue +// lead_tissue number of the leading tissue +// char_I_GF_High_percentage GF high factor +// +// Output: int_O_lead_supersat saturation of the leading tissue +// char_O_lead_tissue number of the leading tissue +// +// Modified: deco_warnings deco engine warnings vector +// +static void convert_sat_for_display(void) { - // convert supersaturation of the leading tissue to int_O_gradient_factor in % (1.0 = 100%) + // convert supersaturation of the leading tissue to int_O_lead_supersat in % (1.0 = 100%) // limit to 255 because of constraints in ghostwriter code - if( lead_supersat <= 0.000 ) int_O_gradient_factor = 0; - else if( lead_supersat > 2.545 ) int_O_gradient_factor = 255 + INT_FLAG_WARNING; - else + if ( lead_supersat <= 0.000 ) int_O_lead_supersat = 0; + else if ( lead_supersat > 2.545 ) int_O_lead_supersat = 255; + else int_O_lead_supersat = (unsigned short)(100 * lead_supersat + 0.5); + + // set warning & attention flags + if( int_O_lead_supersat > 100 ) { - int_O_gradient_factor = (unsigned int)(100 * lead_supersat + 0.5); - - if( char_I_deco_model != 0 ) - { - // GF factors enabled - if ( int_O_gradient_factor > 99 ) - { - int_O_gradient_factor |= INT_FLAG_WARNING; // make GF factor shown in red - } - else if( int_O_gradient_factor > char_I_GF_High_percentage ) - { - int_O_gradient_factor |= INT_FLAG_ATTENTION; // make GF factor shown in yellow - char_O_deco_warnings |= DECO_ATTENTION_OUTSIDE; // make depth blink in yellow - } - } - else - { - // straight Buhlmann - if ( int_O_gradient_factor > 100 ) - int_O_gradient_factor |= INT_FLAG_WARNING; // make GF factor shown in red - - else if ( int_O_gradient_factor > 99 ) - { - int_O_gradient_factor |= INT_FLAG_ATTENTION; // make GF factor shown in yellow - char_O_deco_warnings |= DECO_ATTENTION_OUTSIDE; // make depth blink in yellow - } - } + 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 ) ) + { + int_O_lead_supersat |= INT_FLAG_ATTENTION; // make GF factor shown in yellow + deco_warnings |= DECO_ATTENTION_OUTSIDE; // make depth shown in yellow } // export also the number of the leading tissue - char_O_lead_number = lead_number; + char_O_lead_tissue = lead_tissue; } + ////////////////////////////////////////////////////////////////////////////// - +// convert the ceiling value to integer +// +// Input: ceiling minimum depth permitted in float +// +// Output: int_O_ceiling minimum depth permitted in mbar +// +// Modified: deco_info deco engine information vector +// static void convert_ceiling_for_display(void) { // Convert ceiling to int_O_ceiling in mbar relative pressure. // Round up to next 10 cm so that the ceiling disappears only // when the ceiling limit is really zero. This will coincident // with TTS switching back to NDL time. - if( ceiling <= 0.0 ) int_O_ceiling = 0; - else if( ceiling > 16.0 ) int_O_ceiling = 16000; - else int_O_ceiling = (short)(ceiling * 1000 + 9); + if ( ceiling <= 0.0 ) int_O_ceiling = 0; + else if ( ceiling > 16.0 ) int_O_ceiling = 16000; + else int_O_ceiling = (unsigned short)(ceiling * 1000 + 9); // set/reset ceiling flag - if( int_O_ceiling ) char_O_deco_info |= DECO_CEILING; - else char_O_deco_info &= ~DECO_CEILING; + if ( int_O_ceiling ) deco_info |= DECO_CEILING; + else deco_info &= ~DECO_CEILING; } + ////////////////////////////////////////////////////////////////////////////// // push_tissues_to_vault & pull_tissues_from_vault // @@ -4050,37 +5087,55 @@ // The vault is exclusively reserved to back-up and restore the real // tissues and related data when entering / leaving simulation mode! // - +// Input/Output: CNS_fraction_real current real CNS value +// char_O_deco_warnings deco engine warnings vector +// real_pres_tissue_N2[] partial pressure of N2 in real tissues +// real_pres_tissue_He[] partial pressure of He in real tissues +// +// Output: int_O_CNS_current current CNS value as integer including flags +// static void push_tissues_to_vault(void) { - overlay unsigned char x; - - cns_vault_float = CNS_fraction; - deco_warnings_vault = char_O_deco_warnings; - - for( x = 0; x < NUM_COMP; x++ ) + // store the current CNS value and deco warnings + vault_CNS_fraction_real = CNS_fraction_real; + vault_deco_warnings = char_O_deco_warnings; + vault_deco_info = char_O_deco_info; + + // store the tissue pressures + for( i = 0; i < NUM_COMP; i++ ) { - pres_tissue_N2_vault[x] = pres_tissue_N2[x]; - pres_tissue_He_vault[x] = pres_tissue_He[x]; + vault_pres_tissue_N2[i] = real_pres_tissue_N2[i]; +#ifdef _helium + vault_pres_tissue_He[i] = real_pres_tissue_He[i]; +#else + vault_pres_tissue_He[i] = 0; +#endif } } static void pull_tissues_from_vault(void) { - overlay unsigned char x; - - CNS_fraction = cns_vault_float; - char_O_deco_warnings = deco_warnings_vault; - - convert_CNS_for_display(); - - for( x = 0; x < NUM_COMP; x++ ) + // restore the CNS value and deco warnings + CNS_fraction_real = vault_CNS_fraction_real; + char_O_deco_warnings = vault_deco_warnings; + char_O_deco_info = vault_deco_info; + + // convert the CNS value to integer + convert_cur_CNS_for_display(); + + // restore the tissue pressures + for( i = 0; i < NUM_COMP; i++ ) { - pres_tissue_N2[x] = pres_tissue_N2_vault[x]; - pres_tissue_He[x] = pres_tissue_He_vault[x]; + real_pres_tissue_N2[i] = vault_pres_tissue_N2[i]; +#ifdef _helium + real_pres_tissue_He[i] = vault_pres_tissue_He[i]; +#else + real_pres_tissue_He[i] = 0; +#endif } } + ////////////////////////////////////////////////////////////////////////////// // #ifndef CROSS_COMPILE