Mercurial > public > hwos_code
diff src/p2_deco.c @ 604:ca4556fb60b9
bump to 2.99beta, work on 3.00 stable
author | heinrichsweikamp |
---|---|
date | Thu, 22 Nov 2018 19:47:26 +0100 |
parents | ab88a7e3de94 |
children | d866684249bd |
line wrap: on
line diff
--- a/src/p2_deco.c Thu Oct 11 21:06:29 2018 +0200 +++ b/src/p2_deco.c Thu Nov 22 19:47:26 2018 +0100 @@ -1,5 +1,5 @@ // *************************************************************************** -// p2_deco.c REFACTORED VERSION V2.97 SP1 +// p2_deco.c REFACTORED VERSION V2.99e // // Created on: 12.05.2009 // Author: heinrichs weikamp, contributions by Ralph Lembcke and others @@ -8,7 +8,7 @@ ////////////////////////////////////////////////////////////////////////////// // OSTC - diving computer code -// Copyright (C) 2011 HeinrichsWeikamp GbR +// Copyright (C) 2018 HeinrichsWeikamp GmbH // // 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 @@ -74,28 +74,36 @@ // // Literature: // Buhlmann, Albert: Tauchmedizin; 4. Auflage [2002]; -// Schr"oder, Kai & Reith, Steffen; 2000; S"attigungsvorg"ange beim Tauchen, das Modell ZH-L16, Funktionsweise von Tauchcomputern; http://www.achim-und-kai.de/kai/tausim/saett_faq +// Schroeder, Kai & Reith, Steffen; 2000; Saettigungsvorgaenge beim Tauchen, das Modell ZH-L16, Funktionsweise von Tauchcomputern; http://www.achim-und-kai.de/kai/tausim/saett_faq // Morrison, Stuart; 2000; DIY DECOMPRESSION; http://www.lizardland.co.uk/DIYDeco.html // Balthasar, Steffen; Dekompressionstheorie I: Neo Haldane Modelle; http://www.txfreak.de/dekompressionstheorie_1.pdf // Baker, Erik C.; Clearing Up The Confusion About "Deep Stops" // Baker, Erik C.; Understanding M-values; http://www.txfreak.de/understanding_m-values.pdf -// + + +// ********************************************************************************************************************************* // - -// ********************* -// ** I N C L U D E S ** -// ********************* -#include <math.h> - -// *********************************************** -// ** V A R I A B L E S D E F I N I T I O N S ** -// *********************************************** - +// I N C L U D E S +// +// ********************************************************************************************************************************* + +#include <math.h> #include "p2_definitions.h" #define TEST_MAIN #include "shared_definitions.h" +// ********************************************************************************************************************************* +// +// C O N S T A N T S D E F I N I T I O N S +// +// ********************************************************************************************************************************* + +// 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! + // 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 #define P_ambient_2000m 0.782 // [bar] @@ -108,99 +116,160 @@ // 0.135 bar safety margin // constants and factors -#define ppWater 0.0627 // water vapor partial pressure in the lungs +#define ppWater 0.06270 // water vapor partial pressure in the lungs #define METER_TO_BAR 0.09985 // conversion factor #define BAR_TO_METER 10.0150 // conversion factor (1.0/METER_TO_BAR) -#define SURFACE_DESAT_FACTOR 0.7042 // surface desaturation safety factor +#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 GF_WARNING_THRESHOLD 100 // threshold for GF warning (attention threshold is current GF_high) #define CNS_WARNING_THRESHOLD 100 // threshold for CNS warning #define CNS_ATTENTION_THRESHOLD 70 // threshold for CNS attention -#define ppO2_ATTENTION_THRESHOLD 130 // threshold for ppO2 attention (thresholds for warnings come by options_table.asm) #define ppO2_GAP_TO_SETPOINT 10 // gap between setpoint and max. ppO2 of the pure diluent [cbar] -#define GAS_NEEDS_ATTENTION_THRESHOLD 0.70 // threshold for gas needs attention - -// deco engine states and modes - char_O_deco_status -#define DECO_STATUS_MASK 0x03 -#define DECO_STATUS_START 0x00 -#define DECO_STATUS_FINISHED 0x00 -#define DECO_STATUS_STOPS 0x01 -#define DECO_STATUS_RESULTS 0x02 -#define DECO_STATUS_INIT 0x03 - -#define DECO_MODE_MASK 0x0C -#define DECO_MODE_LOOP 0x04 +#define GAS_NEEDS_ATTENTION_THRESHOLD 0.70 // threshold for gas needs attention [1.00 = 100%] +#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 - -#define DECO_PLAN_ALTERNATE 0x10 -#define DECO_CNS_CALCULATE 0x20 -#define DECO_VOLUME_CALCULATE 0x40 -#define DECO_ASCENT_DELAYED 0x80 - -// deco engine states and modes - char_O_main_status -//#define DECO_MODE_MASK 0x0C -//#define DECO_MODE_LOOP 0x04 -//#define DECO_MODE_CCR 0x04 // to be used with == operator in combination with DECO_MODE_MASK only! -//#define DECO_MODE_PSCR 0x08 -#define DECO_GASCHANGE_OVRD 0x10 -#define DECO_BOTTOM_CALCULATE 0x40 - -// deco engine states and modes - tissue_increment +#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 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 +#define DECO_WARNING_MBUBBLES_lock 0x08 // ditto, but sometime during the dive +#define DECO_WARNING_OUTSIDE 0x10 // tissue pressures outside the Buhlmann model now +#define DECO_WARNING_OUTSIDE_lock 0x20 // tissue pressures outside the model sometime during the dive +#define DECO_ATTENTION_OUTSIDE 0x40 // tissue pressures are very close to the Buhlmann limit +#define DECO_WARNING_STOPTABLE_OVERFLOW 0x80 // internal error: no more space in the deco stops table + +// 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) +#define DECO_CEILING 0x10 // =1: ceiling depth > 0 +#define GAS_NEEDS_CAVE 0x20 // =1: indicated gas needs are calculated in cave mode + +// deco engine control - tissue_increment #define TIME_MASK 0x7F // (127 decimal, bits 0-6 set) #define TISSUE_FLAG 0x80 // (128 decimal, bit 7 set) -// deco engine warnings -#define DECO_WARNING_IBCD 0x01 -#define DECO_WARNING_IBCD_lock 0x02 -#define DECO_WARNING_MBUBBLES 0x04 -#define DECO_WARNING_MBUBBLES_lock 0x08 -#define DECO_WARNING_OUTSIDE 0x10 -#define DECO_WARNING_OUTSIDE_lock 0x20 -#define DECO_WARNING_STOPTABLE_OVERFLOW 0x40 -#define DECO_FLAG 0x80 // flags used with integer numbers -#define INT_FLAG_INVALID 0x0400 -#define INT_FLAG_ZERO 0x0800 -#define INT_FLAG_LOW 0x1000 -#define INT_FLAG_HIGH 0x2000 -#define INT_FLAG_ATTENTION 0x4000 -#define INT_FLAG_WARNING 0x8000 - - - -// ************************* -// ** P R O T O T Y P E S ** -// ************************* - -static void calc_hauptroutine(void); -static void calc_hauptroutine_data_input(void); -static void calc_hauptroutine_calc_deco(void); -static void calc_alveolar_pressures(void); -static void calc_tissues(void); -static void calc_NDL_time(void); -static void calc_ascenttime(void); -static void calc_CNS_increment(void); -static void calc_desaturation_time(void); -static void calc_ascent_to_first_stop(void); -static void calc_limit(PARAMETER float GF_current); -static void calc_interval(PARAMETER unsigned char time_increment); - -static void gas_find_current(void); -static void gas_set_ratios(void); -static void convert_CNS_for_display(void); -static void convert_sim_CNS_for_display(void); -static void publish_deco_table(void); -static void clear_deco_table(void); -static void clear_tissue(void); - -static unsigned char calc_nextdecodepth(void); -static unsigned char gas_find_better(void); +#define INT_FLAG_INVALID 0x0400 // =1: value not valid +#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) +#define INT_FLAG_HIGH 0x2000 // =1: value is above an upper warning threshold +#define INT_FLAG_OUTDATED 0x2000 // =1: value has not been updated for too long +#define INT_FLAG_ATTENTION 0x4000 // =1: value exceeds the attention threshold +#define INT_FLAG_WARNING 0x8000 // =1: value exceeds the warning threshold +#define INT_FLAG_OUT_OF_RANGE 0x8000 // =1: value exceeds presentable range + + + +// ********************************************************************************************************************************* +// +// ** P R O T O T Y P E S ** +// +// The Functions are listed in sequence of intended usage / application. +// +// ********************************************************************************************************************************* + +// 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 calc_hauptroutine(void); // Sequences all calculations for the real tissues and the deco calculation. + +// 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 +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(). +static void calc_tissues(void); // Updates the tissues dependent on the partial pressures of N2 and He. +static void calc_CNS(void); // Updates the CNS value dependent on the partial pressure of the O2. +static void calc_limit(PARAMETER float GF_current); + // Calculates ceiling, current GF (supersaturation) and some more data. + +// 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 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 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_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_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. +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. + + + +// ********************************************************************************************************************************* +// +// V A R I A B L E S D E F I N I T I O N S +// +// ********************************************************************************************************************************* //---- Bank 5 parameters ----------------------------------------------------- #ifndef UNIX @@ -209,45 +278,56 @@ // general deco parameters -static float GF_low; // initialized from deco parameters, constant during all computations -static float GF_high; // initialized from deco parameters, constant during all computations -static float GF_delta; // initialized from deco parameters, constant during all computations +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 (1.0 .. 10.0 m/min) +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 ceiling; // minimum tolerated relative pressure (i.e. without surface pressue) static float CNS_fraction; // current CNS (1.00 = 100%) -static unsigned short deco_tissue_vector; // 16 bit vector to memories all tissues that are in decompression -static unsigned short IBCD_tissue_vector; // 16 bit vector to memories all tissues that experience IBCD +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_ceiling; // minimum tolerated relative pressure (i.e. without surface pressue) -static float sim_CNS_fraction; // CNS increase during predicted ascent, 0.01 = 1% - -static unsigned int int_sim_CNS_fraction; // CNS increase during predicted ascent, in % +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 split_N2_He[NUM_COMP]; // used for calculating the desaturation time -static unsigned char NDL_lead_tissue; // used to cache tissue to start with calculating NDL - +static unsigned char NDL_lead_tissue; // 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]; // depth of the stop -static unsigned char internal_deco_time[NUM_STOPS]; // duration of the stop -static unsigned char internal_deco_gas[NUM_STOPS]; // gas used at the stop +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() @@ -259,12 +339,15 @@ 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() +// 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 char usage; // gas usage in l/min +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 @@ -272,9 +355,11 @@ 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 - - -// 35 byte free space left in this bank (4 bytes per float, 2 bytes per int/short, 1 byte per char) +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 + +// 11 byte free space left in this bank (4 bytes per float, 2 bytes per int/short, 1 byte per char) //---- Bank 6 parameters ----------------------------------------------------- @@ -293,13 +378,13 @@ static float pres_surface; // absolute pressure at the surface -static unsigned char bottom_depth; // bottom depth in meters, used by CNS and gas needs calculation - -static float pres_respiration; // current depth in absolute pressure -static float O2_ratio; // real breathed gas oxygen ratio -static float N2_ratio; // real breathed gas nitrogen ratio -static float He_ratio; // real breathed gas helium ratio -static float pSCR_drop; // real ppO2 drop in pSCR loop +static unsigned char char_bottom_depth; // bottom depth in meters, used by ascent time and gas needs calculation + +static float real_pres_respiration; // current real depth in absolute pressure +static float real_O2_ratio; // real breathed gas oxygen ratio +static float real_N2_ratio; // real breathed gas nitrogen ratio +static float real_He_ratio; // real breathed gas helium ratio +static float real_pSCR_drop; // real ppO2 drop in pSCR loop static float sim_pres_respiration; // simulated current depth in abs.pressure, used for deco calculations static float sim_O2_ratio; // simulated breathed gas oxygen ratio @@ -307,19 +392,17 @@ static float sim_He_ratio; // simulated breathed gas helium ratio static float sim_pSCR_drop; // simulated ppO2 drop in pSCR loop + +// result values from calculation functions + static float O2_ppO2; // ppO2 - calculated for pure oxygen at current depth -static float OC_ppO2; // ppO2 - calculated for breathed in OC mode -static float pSCR_ppO2; // ppO2 - calculated for breathed from pSCR loop +static float OC_ppO2; // ppO2 - calculated for breathing in OC mode +static float pSCR_ppO2; // ppO2 - calculated for breathing in pSCR mode static float ppO2; // partial pressure of breathed oxygen static float ppN2; // partial pressure of breathed nitrogen static float ppHe; // partial pressure of breathed helium - -// Result values from calculation functions - -static float CNS_fraction_inc; // increment of CNS load, 0.01 = 1% - 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 @@ -333,18 +416,17 @@ static float var_He_b; // Buhlmann b, for current He tissue 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 N2 tissue - - -// Gas switch history - -static unsigned char sim_gas_first_used; // Number of first used gas, for bottom segment -static unsigned char sim_gas_last_used; // number of last used gas -static unsigned char sim_gas_last_depth; // change depth of last used gas - - -// Vault to back-up & restore tissue data +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 @@ -375,7 +457,7 @@ #ifndef UNIX # pragma udata overlay bank8=0x800 -static char md_pi_subst[256]; // Overlay C-code data stack here, too. +static char md_pi_subst[256]; // overlay C-code data stack here, too # define C_STACK md_pi_subst #endif @@ -388,14 +470,14 @@ #endif -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -//////////////// THE LOOKUP TABLES //////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// +// ********************************************************************************************************************************* +// +// L O O K - U P T A B L E S +// +// ********************************************************************************************************************************* #ifndef UNIX -# pragma romdata Buhlmann_tables = 0x1DD00 // Needs to be in UPPER bank. +# pragma romdata Buhlmann_tables = 0x1DD00 // needs to be in the UPPER bank #endif rom const float Buhlmann_ab[4*16] = { @@ -508,23 +590,19 @@ //------------------------------------- }; -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -//////////////// THE SUBROUTINES //////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// + +// ********************************************************************************************************************************* // -// all new in v.102 +// H E L P E R F U N C T I O N S +// +// ********************************************************************************************************************************* + + // moved from 0x0D000 to 0x0C000 in v.108 #ifndef UNIX # pragma code p2_deco = 0x0C000 #endif -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -//////////////// U T I L I T I E S //////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Bump to blue-screen when an assert is wrong @@ -534,6 +612,7 @@ } #endif + ////////////////////////////////////////////////////////////////////////////// // When calling C code from ASM context, the data stack pointer and // frames should be reset. Bank8 is used by stack @@ -546,60 +625,61 @@ void fillDataStack(void) { _asm - LFSR 1,C_STACK - MOVLW 0xCC - loop: MOVWF POSTINC1,0 - TSTFSZ FSR1L,0 - BRA loop - - LFSR 1,C_STACK - LFSR 2,C_STACK + LFSR 1,C_STACK + MOVLW 0xCC + loop: MOVWF POSTINC1,0 + TSTFSZ FSR1L,0 + BRA loop + + LFSR 1,C_STACK + LFSR 2,C_STACK _endasm } # else # define RESET_C_STACK \ _asm \ - LFSR 1, C_STACK \ - LFSR 2, C_STACK \ + LFSR 1,C_STACK \ + LFSR 2,C_STACK \ _endasm # endif #endif + ////////////////////////////////////////////////////////////////////////////// -// Fast subroutine to read timer 5. -// Note: result is in 1/32 of milliseconds (30,51757813 us/bit to be precise) +// 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) { #ifndef CROSS_COMPILE _asm movff 0xf7c,PRODL // TMR5L movff 0xf7d,PRODH // TMR5H - _endasm // result in PRODH:PRODL. + _endasm // result in PRODH:PRODL #else return 0; #endif } + ////////////////////////////////////////////////////////////////////////////// -// read Buhlmann tables A and B for compartment ci +// read Buhlmann coefficients a and b for compartment ci // static void read_Buhlmann_coefficients(void) { #ifndef CROSS_COMPILE - // Note: we don't use far rom pointer, because the - // 24 bits is too complex, hence we have to set - // the UPPER page ourself... - // --> Set zero if tables are moved to lower pages ! + // Note: We don't use far ROM pointer, because handling + // 24 bit is too 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 + movlw 1 + movwf TBLPTRU,0 _endasm #endif assert( ci < NUM_COMP ); - // Use an interleaved array (AoS) to access coefficients with a - // single addressing. + // use an interleaved array (AoS) to access coefficients with a single addressing { overlay rom const float* ptr = &Buhlmann_ab[4*ci]; var_N2_a = *ptr++; @@ -609,21 +689,22 @@ } } + ////////////////////////////////////////////////////////////////////////////// -// read Buhlmann tables for compartment ci -// If period == 0 : 2sec interval -// 1 : 1 min interval -// 2 : 10 min interval. +// read Buhlmann increments for compartment ci +// If period == 0 : 2 sec interval +// 1 : 1 min interval +// 2 : 10 min interval static void read_Buhlmann_times(PARAMETER char period) { #ifndef CROSS_COMPILE - // Note: we don't use far rom pointer, because the - // 24 bits is to complex, hence we have to set - // the UPPER page ourself... - // --> Set zero if tables are moved to lower pages! + // 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 + movlw 1 + movwf TBLPTRU,0 _endasm #endif @@ -661,20 +742,21 @@ } } + ////////////////////////////////////////////////////////////////////////////// -// read Buhlmann tables for compartment ci +// read Buhlmann half-times for compartment ci // static void read_Buhlmann_ht(void) { #ifndef CROSS_COMPILE - // Note: we don't use far rom pointer, because the - // 24 bits is to complex, hence we have to set - // the UPPER page ourself... - // --> Set zero if tables are moved to lower pages ! + // 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 + movlw 1 + movwf TBLPTRU,0 _endasm #endif @@ -685,15 +767,28 @@ var_He_ht = *ptr++; } - assert( 4.0 <= var_N2_ht && var_N2_ht <= 635.0 ); + assert( 4.0 <= var_N2_ht && var_N2_ht <= 635.0 ); assert( 1.5099 <= var_He_ht && var_He_ht <= 240.03 ); } + ////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -//////////////// THE JUMP-IN CODE for the asm code //////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// +// compute adopted Buhlmann coefficients +// +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; +} + + +// ********************************************************************************************************************************* +// +// J U M P I N F U N C T I O N S +// +// ********************************************************************************************************************************* + ////////////////////////////////////////////////////////////////////////////// // deco_calc_hauptroutine @@ -735,7 +830,7 @@ // // called from: simulator.asm // -// Updates tissues and CNS value for char_I_dive_interval minutes on Air +// Updates tissues and CNS value for char_I_dive_interval minutes on air // at ambient pressure and calculates resulting GF factor and ceiling for // a GF-high of 100% (ceiling and GF factor not used by simulator.asm) // @@ -754,7 +849,7 @@ // menu_tree.asm // ghostwriter.asm // -// Updates tissues and CNS value for 1 minute on Air at ambient pressure and +// Updates tissues and CNS value for 1 minute on air at ambient pressure and // calculates resulting GF factor and ceiling for a GF-high of 100% (ceiling // is not used by *.asm files). // @@ -766,11 +861,11 @@ ////////////////////////////////////////////////////////////////////////////// -// deco_calc_dive_interval_1min +// deco_calc_dive_interval_10min // // called from: sleepmode.asm // -// Updates tissues and CNS value for 10 minutes on Air at ambient pressure and +// Updates tissues and CNS value for 10 minutes on air at ambient pressure and // calculates resulting GF factor and ceiling for a GF-high of 100% (ceiling // is not used by sleepmode.asm). // @@ -824,37 +919,36 @@ pull_tissues_from_vault(); } -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -//////////////// THE FUNCTIONS //////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// + +// ********************************************************************************************************************************* +// +// M A I N F U N C T I O N S +// +// ********************************************************************************************************************************* ////////////////////////////////////////////////////////////////////////////// // calc_nextdecodepth // -// new in v.102 -// // INPUT, changing during dive: -// sim_pres_respiration : current depth in absolute pressure +// sim_pres_respiration : current depth in absolute pressure // // INPUT, fixed during dive: -// pres_surface -// GF_delta -// GF_high -// GF_low -// char_I_depth_last_deco +// 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 +// locked_GF_step_norm/_alt : used for GF model +// low_depth_norm/_alt : used for GF model // // OUTPUT -// sim_depth_limit : depth of next stop in meters (if RETURN == true ) -// depth we can ascent to without stop (if RETURN == false) +// 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. +// RETURN TRUE if a stop is needed, else false // static unsigned char calc_nextdecodepth(void) { @@ -867,9 +961,8 @@ overlay float min_depth = (depth > float_ascent_speed) ? (depth - float_ascent_speed) : 0.0; - // allow for 200mbar of weather dependent surface pressure change - assert( depth >= -0.2 ); - + // target the simulated tissues + tissue_increment = 0; //---- check if a stop is needed for deco reasons ---------------------------- @@ -889,18 +982,18 @@ calc_limit(GF_low); // check if we can surface directly - if( sim_ceiling <= 0.0 ) + if( ceiling <= 0.0 ) { min_depth = 0.0; // set minimum depth to 0 meters = surface - goto no_deco_stop; // done. + goto no_deco_stop; // done } // convert minimum depth we can ascent to from relative pressure to depth in meters - limit_depth = sim_ceiling * BAR_TO_METER; + 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 @@ -925,7 +1018,7 @@ } else { - // recall locked_GF_step dependent on current plan + // 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; } @@ -933,10 +1026,10 @@ 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 - first_stop = 3 * (unsigned char)(0.9995 + limit_depth * 0.333333); + //---- 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 ); @@ -947,9 +1040,9 @@ // 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) + while( first_stop > 0 ) { - // Next depth + // next depth overlay unsigned char next_stop; // invalidate this stop if we can ascent one more minute without going above minimum required deco depth @@ -965,7 +1058,8 @@ else calc_limit(GF_high - next_stop * locked_GF_step); // check if ascent to the next stop candidate is possible - if( sim_ceiling * BAR_TO_METER >= next_stop ) goto deco_stop_found; // no - ascent to next_stop forbidden + 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; @@ -994,13 +1088,13 @@ calc_limit(1.0); // check if we can surface directly - if (sim_ceiling >= 0) + 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 = sim_ceiling * BAR_TO_METER / 3; + 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); @@ -1018,22 +1112,23 @@ } } - // After the first deco stop, gas changes are only done at deco stops now! + // ---- 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( 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); + update_deco_table(char_I_gas_change_time); } return need_stop; } + ////////////////////////////////////////////////////////////////////////////// // publish_deco_table // @@ -1050,32 +1145,33 @@ 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++) + for( x = 0; x < NUM_STOPS; x++ ) { 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]; } - //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) + // 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) + for( y = 0; y < NUM_STOPS; y++, --x ) { char_O_deco_time_for_log[y] = internal_deco_time [x]; - // Stop only once the last transfer is done. + // Stop when the last transfer is done. if( x == 0 ) break; } //---- Third: fill table with null until end - for(y++; y<NUM_STOPS; y++) + for( y++; y < NUM_STOPS; y++ ) char_O_deco_time_for_log[y] = 0; } + ////////////////////////////////////////////////////////////////////////////// // temp_tissue_safety // @@ -1093,140 +1189,149 @@ } - ////////////////////////////////////////////////////////////////////////////// // Find current gas in the list (if any) and get its change depth // -// Input: char_I_current_gas : 1..5 or 6 +// Input: char_I_current_gas : 1..5 or 6 // -// Output: sim_gas_last_used : 1..6 or 0 if it is the gas set as FIRST -// sim_gas_last_depth : change depth in meters or 0 if it is the gas set as FIRST +// 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 // 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 1-5 + if( char_I_current_gas <= NUM_GAS ) // gas/diluent 1-5 { - sim_gas_last_used = sim_gas_first_used = char_I_current_gas; - sim_gas_last_depth = char_I_deco_gas_change[sim_gas_last_used-1]; // > 0 for OC deco gases, - // > 0 for first & normal diluents, - // = 0 else + sim_gas_current = char_I_current_gas; + sim_gas_current_depth = char_I_deco_gas_change[sim_gas_current-1]; } else { - sim_gas_last_used = sim_gas_first_used = 0; // Gas 6 (the manually set one) has number 0 here - sim_gas_last_depth = 0; // handle it as a travel/normal gas + sim_gas_current = 0; + sim_gas_current_depth = char_I_gas6_depth; } } ////////////////////////////////////////////////////////////////////////////// -// Find the deco gas with the shallowest change depth beyond current depth +// Find the deco gas with the shallowest change depth below or at the current depth // -// INPUT sim_depth_limit : current depth in meters -// char_I_deco_gas_change[] : change depths of the deco gases -// sim_gas_last_depth : change depth of the currently used gas, 0 if on the gas set as FIRST +// 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 // -// OUTPUT sim_gas_last_depth : switch depth - only if return value is true -// sim_gas_last_used : index of the gas (1..5) - only if return value is true +// 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 // static unsigned char gas_find_better(void) { overlay unsigned char switch_depth = 255; - overlay unsigned char switch_gas = 0; + overlay unsigned char switch_gas = 0; overlay unsigned char j; - - // no automatic gas changes in CCR mode and - as of now - in pSCR mode - if( char_O_deco_status & DECO_MODE_LOOP ) return 0; - - // Loop over all deco gases to find the shallowest one below or at current depth. - for(j=0; j<NUM_GAS; ++j) + // no automatic gas changes in CCR mode + if( (char_O_deco_status & DECO_MODE_MASK) == DECO_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 the gas we are already breathing? - // If yes, skip this gas. - if( j+1 == sim_gas_last_used ) continue; - - // Is the change depth of the gas shallower than the current depth? - // If yes, skip this gas as it is not to be used yet. - // Remark: this check will also skip all disabled gases and the gas set - // as 'first' because these have their change depth set to 0. - if( sim_depth_limit > char_I_deco_gas_change[j] ) continue; - - // Is the change depth of the gas deeper or equal than the change depth of the - // gas we are currently one? - // If yes, skip this gas as it is not better than the current one. - // Remark: if there is more than one gas with the same change depth, - // the last one from the list will be taken. - if( sim_gas_last_depth && (char_I_deco_gas_change[j] >= sim_gas_last_depth) ) continue; - - // Is the change depth of the gas shallower or equal to the change depth - // of the best gas found so far, or is it the first better gas found? - // If yes, we have a better gas + // 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 ) ) ) + + // 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 ) + + // Is the change depth of this gas shallower than the + // change depth of the gas we are currently on? + if( char_I_deco_gas_change[j] < sim_gas_current_depth ) + + // Is the change depth of this gas shallower than the change + // depth of the best gas found so far, or is it the first + // better gas found? if( char_I_deco_gas_change[j] < switch_depth ) + + // If there is a yes to all these questions, we have a better gas! { switch_gas = j+1; // remember this gas (1..5) switch_depth = char_I_deco_gas_change[j]; // remember its change depth } + } // continue looping through all gases to eventually find an even better gas // has a better gas been found? if( switch_gas ) { - // yes - sim_gas_last_used = switch_gas; // report the index of the better - sim_gas_last_depth = switch_depth; // report its change depth - - assert( sim_gas_last_depth < switch_depth ); - - return 1; // signal a better gas was found + // YES - set the better gas as the new gas + sim_gas_current = switch_gas; + + // set its change depth as the last used change depth + sim_gas_current_depth = switch_depth; + + assert( sim_gas_current_depth < switch_depth ); + + // signal a better gas was found + return 1; } else { - return 0; // signal no better gas was found + // NO - signal no better gas was found + return 0; } } + ////////////////////////////////////////////////////////////////////////////// -// Set calc_N2/He/O2_ratios by sim_gas_last_used +// Set calc_N2/He/O2_ratios by sim_gas_current // -// Input: sim_gas_last_used : index of gas to use -// N2_ratio, He_ratio : if gas = 0 (the manually set gas) -// char_I_deco_O2/He_ratio[] : if gas = 1..5 (the configured gases) +// 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 +// 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_last_used <= NUM_GAS ); + assert( 0 <= sim_gas_current <= NUM_GAS ); // get gas ratios - if( sim_gas_last_used == 0 ) + if( sim_gas_current == 0 ) { - sim_O2_ratio = O2_ratio; - sim_He_ratio = He_ratio; + 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_last_used-1]; - sim_He_ratio = 0.01 * char_I_deco_He_ratio[sim_gas_last_used-1]; + 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]; } // inert gas ratio (local helper variable) - sim_IG_ratio = 1.00 - sim_O2_ratio; + sim_IG_ratio = 1.00 - sim_O2_ratio; // N2 ratio - sim_N2_ratio = sim_IG_ratio - sim_He_ratio; + sim_N2_ratio = sim_IG_ratio - sim_He_ratio; // ppO2 drop in pSCR loop - sim_pSCR_drop = sim_IG_ratio * float_pSCR_factor; + sim_pSCR_drop = sim_IG_ratio * float_pSCR_factor; assert( 0.0 <= sim_N2_ratio && sim_N2_ratio <= 0.95 ); @@ -1234,30 +1339,30 @@ assert( (sim_N2_ratio + sim_He_ratio) <= 0.95 ); } + ////////////////////////////////////////////////////////////////////////////// -// Compute respired ppN2 and ppHe +// 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 -// (sim_)O2_ratio : (simulated) O2 ratio breathed -// (sim_)N2_ratio : (simulated) N2 ratio breathed -// (sim_)He_ratio : (simulated) He ratio breathed -// (sim_)pres_respiration : (simulated) respiration pressure -// char_I_const_ppO2 : ppO2 reported from sensors or setpoint -// char_I_PSCR_drop : pSCR parameter -// char_I_PSCR_lungratio : pSCR parameter -// pres_surface : surface pressure -// float_deco_distance : safety factor -// ppWater : water-vapor pressure inside respiratory tract +// 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 +// sim_/real_O2_ratio : (simulated) O2 ratio breathed +// sim_/real_N2_ratio : (simulated) N2 ratio breathed +// sim_/real_He_ratio : (simulated) He ratio breathed +// sim_/real_pres_respiration : (simulated) respiration pressure [bar] +// sim_/real_pSCR_drop : (simulated) pSCR O2 drop +// pres_surface : surface pressure [bar] +// char_I_const_ppO2 : ppO2 reported from sensors or setpoint [cbar] +// float_deco_distance : safety factor, additional depth below stop depth [bar] +// ppWater : water-vapor pressure inside respiratory tract [bar] // -// Output: ppN2 : respired N2 partial pressure -// ppHe : respired He partial pressure -// char_ppO2 : breathed ppO2 in %, to be used for CNS calculation +// Output: ppN2 : respired N2 partial pressure +// ppHe : respired He partial pressure +// char_ppO2 : breathed ppO2 in %, used in CNS calculation // void calc_alveolar_pressures(void) { - overlay float pres_diluent; + overlay float calc_pres_respiration; overlay float calc_O2_ratio; overlay float calc_N2_ratio; overlay float calc_He_ratio; @@ -1266,14 +1371,14 @@ overlay unsigned char status; - assert( 0.00 <= N2_ratio && N2_ratio <= 1.00 ); - assert( 0.00 <= He_ratio && He_ratio <= 1.00 ); - assert( (N2_ratio + He_ratio) <= 1.00 ); - assert( 0.800 < pres_respiration && pres_respiration < 14.0 ); - - assert( 0.00 <= sim_N2_ratio && N2_ratio <= 1.00 ); - assert( 0.00 <= sim_He_ratio && He_ratio <= 1.00 ); - assert( (sim_N2_ratio + sim_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.800 < sim_pres_respiration && sim_pres_respiration < 14.0 ); @@ -1281,35 +1386,32 @@ if( tissue_increment & TISSUE_FLAG ) { //---- real tissues ----------------------------------------------------------- - status = char_O_main_status; - pres_diluent = pres_respiration; - calc_pSCR_drop = pSCR_drop; - - calc_O2_ratio = O2_ratio; - calc_N2_ratio = N2_ratio; - calc_He_ratio = He_ratio; + status = char_O_main_status; + calc_pres_respiration = real_pres_respiration; + calc_pSCR_drop = real_pSCR_drop; + + calc_O2_ratio = real_O2_ratio; + calc_N2_ratio = real_N2_ratio; + calc_He_ratio = real_He_ratio; } else { //---- simulated tissues ------------------------------------------------------ - status = char_O_deco_status; - pres_diluent = sim_pres_respiration; - calc_pSCR_drop = sim_pSCR_drop; - - calc_O2_ratio = sim_O2_ratio; - calc_N2_ratio = sim_N2_ratio; - calc_He_ratio = sim_He_ratio; - - // take deco offset into account, but not at surface - if( pres_diluent > pres_surface ) pres_diluent += float_deco_distance; + status = char_O_deco_status; + calc_pres_respiration = sim_pres_respiration; + calc_pSCR_drop = sim_pSCR_drop; + + calc_O2_ratio = sim_O2_ratio; + calc_N2_ratio = sim_N2_ratio; + calc_He_ratio = sim_He_ratio; } //---- OC, CCR and Bailout Mode Gas Calculations ----------------------------------- // calculate ppO2 of pure oxygen - O2_ppO2 = (pres_diluent - ppWater); - - // capture failure condition in case pres_respiration is < ppWater (should never happen...) + O2_ppO2 = calc_pres_respiration - ppWater; + + // capture failure condition in case real_pres_respiration is < ppWater (should never happen...) if( O2_ppO2 < 0.0 ) O2_ppO2 = 0.0; // calculate ppO2 of the pure gas (OC, diluent) @@ -1326,19 +1428,19 @@ if( status & DECO_MODE_LOOP ) { overlay float const_ppO2; + overlay float max_ppO2; // get the current sensor reading (CCR / pSCR if fitted) or the fixed setpoint (CCR) / a zero (pSCR) const_ppO2 = 0.01 * char_I_const_ppO2; // 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, if limiting occurs, the ppO2 can be further reduced to account - // for residual inert gases by the user-adjustable setting char_I_cc_max_frac_o2. - - if( const_ppO2 > pres_diluent ) // no ppWater subtracted here to give some margin for - { // sensors delivering data a little bit over target - const_ppO2 = 0.01 * char_I_cc_max_frac_o2 * (pres_diluent - ppWater); - } + // 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. + // (ppWater is neglected here) + 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 ) @@ -1354,33 +1456,36 @@ { //---- CCR Mode --------------------------------------------------------------------------- - // derive breathed ppO2 from (char_I_)const_ppO2, - // which holds sensor reading or fixed setpoint + // derive breathed ppO2 from (char_I_)const_ppO2, which holds sensor reading or selected setpoint ppO2 = const_ppO2; } - // adjust diluent pressure (ppN2 + ppHe) for change - // in ppO2 due to setpoint (CCR) or drop (pSCR) - pres_diluent -= ppO2; - pres_diluent /= calc_N2_ratio + calc_He_ratio; - - // capture all failure conditions, including div/0 - // in case diluent is pure O2 - if( (pres_diluent < 0.0) || (calc_O2_ratio > 99.5) ) + // 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 { - pres_diluent = 0.0; - ppO2 = OC_ppO2; + // failure condition present, set predetermined result + calc_pres_respiration = 0.0; + } + else + { + // no failure conditions present, equation can be executed + calc_pres_respiration -= ppO2; + calc_pres_respiration /= calc_N2_ratio + calc_He_ratio; } } else - { //---- OC mode --------------------------------------------------------------------------------- + { + //---- OC mode --------------------------------------------------------------------------------- // breathed ppO2 is ppO2 of pure gas ppO2 = OC_ppO2; } - // derive char_ppO2 in [cbar], used for calculating CNS% + //---- derive char_ppO2 in [cbar], used for calculating CNS% --------------------------------------- + if ( ppO2 < 0.01 ) char_ppO2 = 0; else if ( ppO2 >= 2.545 ) char_ppO2 = 255; else char_ppO2 = (unsigned char)(100 * ppO2 + 0.5); @@ -1388,31 +1493,44 @@ //---- calculate ppN2 and ppHe --------------------------------------------------------------------- - if( pres_diluent > ppWater ) + // add deco safety distance when working on simulated tissues + if( !(tissue_increment & TISSUE_FLAG) ) calc_pres_respiration += float_deco_distance; + + // compute ppN2 and ppHe, capture potential failure conditions first: + if( calc_pres_respiration > ppWater ) { - ppN2 = calc_N2_ratio * (pres_diluent - ppWater); - ppHe = calc_He_ratio * (pres_diluent - ppWater); + // subtract water vapor pressure + calc_pres_respiration -= ppWater; + + // calculate partial pressures + ppN2 = calc_N2_ratio * calc_pres_respiration; + ppHe = calc_He_ratio * calc_pres_respiration; } else { + // calculated respired pressure is < water vapor pressure, thus set ppN2 and ppHe to 0 ppN2 = 0.0; ppHe = 0.0; } } + ////////////////////////////////////////////////////////////////////////////// // clear_tissue // // optimized in v.101 (var_N2_a) // -// preload tissues with standard pressure for the given ambient pressure. +// Reset all tissues to surface pressure equilibrium state. // static void clear_tissue(void) { - pres_respiration = 0.001 * int_I_pres_respiration; - N2_equilibrium = 0.7902 * (pres_respiration - ppWater); - - for(ci=0; ci<NUM_COMP; ci++) + // 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); + + 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 @@ -1428,8 +1546,9 @@ int_O_CNS_fraction = int_O_normal_CNS_fraction = int_O_alternate_CNS_fraction = 0; - // reset any warnings + // reset any warnings and status data char_O_deco_warnings = 0; + char_O_deco_status = 0; // reset some more vars to their defaults char_O_nullzeit = 240; @@ -1452,34 +1571,27 @@ 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 by the respective routines to skip every 2nd invocation. - twosectimer = (twosectimer) ? 0 : 1; // toggle the toggle-timer - - - // 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 ) + // 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 ) { - // configure char_I_sim_advance_time minutes of tissue updating - tissue_increment = char_I_sim_advance_time // given number of minutes, limited to 127 - | TISSUE_FLAG; // set flag for updating the "real" tissues & CNS - - char_I_sim_advance_time = 0; // clear "mailbox" - } - else - { - // configure 2 seconds of tissue updating - tissue_increment = 0 // encoding for 2 seconds update - | TISSUE_FLAG; // set flag for updating the "real" tissues & CNS + // 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; } @@ -1488,35 +1600,48 @@ // acquire current environment data calc_hauptroutine_data_input(); - // calculate ppN2 and ppHe + // 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. - // To distribute computational load, updating of tissues and CNS is done in alternation. - if( twosectimer || (tissue_increment & TIME_MASK) ) + // 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 ) { + // 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" + char_I_sim_advance_time = 0; + } + // calculate the real tissues calc_tissues(); - // calculate ceiling (at GF_high) and current GF - calc_limit(GF_high); - } - - if( !twosectimer || (tissue_increment & TIME_MASK) ) - { - // calculate CNS value increment for the real tissues - calc_CNS_increment(); - - // increment CNS value of the real tissues - CNS_fraction += CNS_fraction_inc; - - // compute integer copy of CNS value for display purpose + // update the CNS value for the real tissues + 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] + 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 @@ -1525,7 +1650,7 @@ // 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 = (pres_respiration - ppHe - pres_surface) * BAR_TO_METER; + END = (real_pres_respiration - ppHe - pres_surface) * BAR_TO_METER; // export EAD if( (EAD < 0.0) || (EAD > 245.5) ) char_O_EAD = 0; @@ -1539,31 +1664,49 @@ //---- Compute ppO2 Values in [cbar] --------------------------------------------------------- // pure oxygen ppO2 - if ( O2_ppO2 < 0.01 ) int_O_O2_ppO2 = 0; + 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; + 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; + 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; + 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_warnings & DECO_FLAG ) ? (unsigned int)char_I_ppO2_max_deco : (unsigned int)char_I_ppO2_max; + 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; @@ -1574,7 +1717,7 @@ 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) + // (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. @@ -1585,160 +1728,374 @@ } // 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; + 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 ) int_O_breathed_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH; - else if ( char_O_main_status & DECO_MODE_LOOP ) ; // no attention generated in loop modes - else if ( int_O_breathed_ppO2 >= ppO2_ATTENTION_THRESHOLD ) int_O_breathed_ppO2 |= INT_FLAG_ATTENTION; + 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; + 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; - - - // done with the real tissues - - - - //---- Toggle between Calculation for NDL (bottom time), ------------------------------------- - //---- Deco Stops, more Deco Stops and Results Gathering ------------------------------------- - - - // all following operations target the simulated tissues, so clear flag in bit 7 - tissue_increment = 0; - - // branch to the code for the current phase the deco calculations are in + 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; + + +#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 ) + { + // 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 + + + //---- 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 ) { 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 last 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) + for( i = 0; i < NUM_GAS; ++i ) { - int_O_gas_volumes[i] = 0; - int_O_tank_pres_need[i] = 0 + INT_FLAG_ZERO; + int_O_ascent_volumes[i] = 0; + int_O_ascent_pres_need[i] = 0 + INT_FLAG_ZERO; } - // initialize the balancing between N2 and He for later no-fly time calculation - for(i=0; i<NUM_COMP; ++i) - { - split_N2_He[i] = 90; // assumes 90% of total tissue pressure will be needed for N2 - } - - // ** UNDER CONSTRUCTION - temporary code only ** - char_I_gas_change_time = 1; // TODO: validate proper operation before enabling this options-table parameter - char_I_ascent_speed = 10; // TODO: validate proper operation before enabling this options-table parameter, - // caution: values < 10 may have an impact on the deco calculation run-times! + // 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; - float_desaturation_multiplier = 0.01 * char_I_desaturation_multiplier; - float_saturation_multiplier = 0.01 * char_I_saturation_multiplier; - float_deco_distance = 0.01 * char_I_deco_distance; + 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% // 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 - deco_tissue_vector = 0; // reset tissue deco vector - IBCD_tissue_vector = 0; // reset tissue IBCD vector - NDL_lead_tissue = 0; // reset first tissue to look at during NDL calculation + 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 = 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; - - // Values that should be reset just once for the full real dive. - // This is used to record the lowest stop for the whole dive, - // including ACCROSS all simulated ascents. - low_depth_norm = low_depth_alt = 0.0; - locked_GF_step_norm = locked_GF_step_alt = 0.0; + int_O_normal_CNS_fraction = int_O_alternate_CNS_fraction = int_O_CNS_fraction; // // --> code execution continues in state DECO_STATUS_START // - case DECO_STATUS_START: //---- Bottom Time & initial Ascent -------------------- - default: + case DECO_STATUS_START: //---- Start a new deco calculation cycle -------------- // 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++) + 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 for the bottom segment of the dive and for - // the period of delayed ascent when calculating fTTS or bailout. + // 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) + // 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(); - // initialize depth in absolute pressure, it is needed by - // - calc_alveolar_pressures(), - // - calc_ascent_to_first_stop(), and - // - calc_hauptroutine_calc_deco() - sim_pres_respiration = pres_respiration; - - // calculate ppN2 and ppHe from sim_N2/He_ratio (<- tissue_increment has been set to 0) - calc_alveolar_pressures(); - - // calculate the effect of extended bottom time due to delayed ascent + // 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 if we are within no decompression limit (NDL) + // 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(); - // Calculate the initial ascent if in deco. calc_NDL_time() is very fast - // in detecting being beyond NDL, so there is enough time left in this - // phase to do the initial ascent calculation. if( NDL_time == 0 ) { - //--- in deco -------------------------------------------------------- - - // calculate ascent to first stop + // 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 with calculating the stops - char_O_deco_status &= ~DECO_STATUS_MASK; // clear status bits and set status bits for - char_O_deco_status |= DECO_STATUS_STOPS; // calculation of stops on next invocation + // 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 with gathering all results + // within NDL - continue in next cycle with gathering all results char_O_deco_status &= ~DECO_STATUS_MASK; char_O_deco_status |= DECO_STATUS_RESULTS; } @@ -1751,138 +2108,159 @@ // calculate the stops calc_hauptroutine_calc_deco(); - // calc_hauptroutine_calc_deco iterates in this phase as long as it is + // 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: //--- Gathering of all Results ----------------------- - - // if in normal plan, publish the stops table to the display functions - if( !(char_O_deco_status & DECO_PLAN_ALTERNATE) ) publish_deco_table(); - - // The current depth is needed by calc_CNS_planning() and gas_volumes(). - // As it may be needed in different code blocks below but we don't want - // it to be in the code multiple times, it's done here on stockpile. - bottom_depth = (unsigned char)((pres_respiration - pres_surface) * BAR_TO_METER); - - // Calculate the ascent time. - // When within NDL, potential gas switches will be treated as done "on the fly". - calc_ascenttime(); + case DECO_STATUS_RESULTS: //--- Gather Results --------------------------------- + + // if in normal plan, publish the stops table + if( !(char_O_deco_status & DECO_PLAN_ALTERNATE) ) + { + // 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 + { + // 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; + } + } + + // 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 ---------------------------------------------- - // Calculate the initial ascent (not yet done when within NDL) - - // just to get potential gas switches into the stops table for use - // by gas_volumes(). The stops table can be polluted by now because - // the clean table has already been published to the display - // functions before. - calc_ascent_to_first_stop(); - // check which plan we are on if( char_O_deco_status & DECO_PLAN_ALTERNATE ) { //---- alternate dive plan --------------------------------- - // As we are in no stop, CNS at end of dive is more or less - // the same CNS as we have right now. It's so simple that we - // don't check if it requested to be computed or not... - int_O_alternate_CNS_fraction = int_O_CNS_fraction; - // 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; } else { //---- normal dive plan ------------------------------------ - // As we are in no stop, CNS at end of dive is more or less - // the same CNS as we have right now. It's so simple that we - // don't check if it requested to be computed or not... - int_O_normal_CNS_fraction = int_O_CNS_fraction; - // 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; } } // 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 NDL time - char_O_alternate_nullzeit = 0; - - // output ascent time + // clear the NDL time + char_O_alternate_nullzeit = 0; + + // export the ascent time int_O_alternate_ascenttime = ascent_time; - // shall the CNS at the end of the dive be calculated? - if( char_O_deco_status & DECO_CNS_CALCULATE ) - { - // calculate the CNS for the predicted ascent, result in sim_CNS_fraction - calc_CNS_planning(); - - // add current CNS value - sim_CNS_fraction += CNS_fraction; - - // convert to integer value - convert_sim_CNS_for_display(); - - // export result - int_O_alternate_CNS_fraction = int_sim_CNS_fraction; - } + // 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 NDL time + // clear the NDL time char_O_nullzeit = 0; - // output ascent time + // export the ascent time int_O_ascenttime = ascent_time; - // shall the CNS at the end of the dive be calculated? - if( char_O_deco_status & DECO_CNS_CALCULATE ) - { - // calculate the CNS for the predicted ascent, result in sim_CNS_fraction - calc_CNS_planning(); - - // add current CNS value - sim_CNS_fraction += CNS_fraction; - - // convert to integer value - convert_sim_CNS_for_display(); - - // export result - int_O_normal_CNS_fraction = int_sim_CNS_fraction; - } + // 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 - } // DECO - - // if requested, calculate the required gas volumes and tank pressures at the end of the dive - if( char_O_deco_status & DECO_VOLUME_CALCULATE ) gas_volumes(); - - // signal that the computation cycle is finished + } // 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; + + break; } // switch @@ -1898,9 +2276,18 @@ { 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; - pres_respiration = 0.001 * int_I_pres_respiration; + 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); @@ -1911,60 +2298,57 @@ GF_delta = GF_high - GF_low; // get the currently breathed gas mixture - O2_ratio = 0.01 * char_I_O2_ratio; - He_ratio = 0.01 * char_I_He_ratio; + 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 - O2_ratio; + IG_ratio = 1.00 - real_O2_ratio; // N2 ratio - N2_ratio = IG_ratio - He_ratio; - - // precomputed values for ppO2 drop in pSCR loop - float_pSCR_factor = 0.01 * char_I_PSCR_drop * char_I_PSCR_lungratio; - pSCR_drop = IG_ratio * float_pSCR_factor; + 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. +// Compute stops // -// Note: because this can be very long, break on 16 iterations, and set state -// to DECO_STATUS_FINISHED when finished, or to DECO_STATUS_STOPS when -// needing to continue. -// Note: because each iteration might be very long too (~ 66 ms in 1.84beta), -// break the loop when elapsed time exceeds 512 milliseconds. +// 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) + for( loop = 0; loop < 16; ++loop ) { - // limit loops to 512ms, using timer 5 + // limit execution time to 512 ms using timer 5 if( tmr5() & (512*32) ) break; // calc_nextdecodepth() // // INPUT sim_pres_respiration : current depth in absolute pressure - // OUTPUT sim_depth_limit : depth of next stop in meters - // RETURN true if a stop is needed + // 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 // // The function manages gas changes by itself, including priming // the deco stop with the configured gas change time. // if( calc_nextdecodepth() ) { - if( sim_depth_limit == 0 ) goto Surface; // this check should not bee needed as in - // this case the RETURN value will be false - - //---- stop required at sim_depth_limit ------------------------------------- + // 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, + // 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; } @@ -1987,128 +2371,177 @@ } } - - //---- as one minute as passed now, update the tissues ---------------------- - - // program 1 minute interval on simulated tissues (Flagbit 7 = 0) + //---- as one minute as passed now, update the tissues ----------- + + // program 1 minute interval on simulated tissues tissue_increment = 1; - // compute current ppN2 and ppHe + // compute current ppO2, ppN2 and ppHe calc_alveolar_pressures(); // update the tissues calc_tissues(); + + // update the CNS value + calc_CNS(); } } ////////////////////////////////////////////////////////////////////////////// -// Calculate ascent to first deco stop. +// 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 // +void find_NDL_gas_changes(void) +{ + overlay unsigned char old_depth_limit; + + // set gas to start with + 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; ) + { + // memorize the depth we came from + old_depth_limit = sim_depth_limit; + + // 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; + + // 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; + + // create a stop for the gas change in the stops table + update_deco_table(char_I_gas_change_time); + } + } // for() +} + + +////////////////////////////////////////////////////////////////////////////// +// Calculate ascent to first deco stop // -// Modified: sim_pres_respiration : current depth in ascent and deco simulation, in bar absolute pressure +// 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 unsigned char fast = 1; // 1 = 1 minute steps, 0 = 2 seconds steps - overlay unsigned char gaschange = 0; // 1 = do a gas change, 0 = no better gas available - - - //---- Loop until first deco stop or surface is reached ---------- + 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(;;) { - // depth in absolute pressure we came from - overlay float old_deco = sim_pres_respiration; + // 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/min) - else sim_pres_respiration -= (float_ascent_speed/30.0) * METER_TO_BAR; // 2 sec at float_ascent_speed (17 .. 33 cm/min) + 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 current ceiling of the simulated tissues - if ( char_I_deco_model != 0 ) calc_limit(GF_low); - else calc_limit(1.0); - - // did we overshoot the first deco stop? - if( sim_pres_respiration < (sim_ceiling + 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 last depth below first stop - sim_pres_respiration = old_deco; + // 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; // retry with 2 seconds ascent steps + fast = 0; // ascent with 2 seconds ascent steps continue; } else { - break; // done... + break; // done, stop required } } - // If code execution passes along here, we did not overshoot the first stop. - - // did we reach the surface? if yes, done! + // 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); - // Check if there is a better gas to switch to, but only in alternative plan mode - // or if override is set. If yes, introduce a stop for the gas change. - if( (char_O_deco_status & DECO_PLAN_ALTERNATE) || (char_O_main_status & DECO_GASCHANGE_OVRD) ) + // 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() ) { - // depth in meters we came from - overlay unsigned char old_depth_limit = (unsigned char)((old_deco - 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_last_depth < old_depth_limit) ? sim_gas_last_depth : old_depth_limit; - - // create a stop for the gas change - update_deco_table(char_I_gas_change_time); + overlay unsigned char old_depth_limit; // set the new calculation values for N2, He and O2 gas_set_ratios(); - // signal to create a stop for the gas change and update the tissues - gaschange = char_I_gas_change_time; - - // Adjust the depth for the tissue update to the stop depth. In case of fast mode, this - // imposes that the ascent from the 'old_deco' depth to this stop took 1 minute although - // we might have only ascended one or two meters... + // 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); } - // Did one minute pass by and/or do we have a gas change? - // Remark: The 2 seconds ascent iterations towards the first deco stop in !fast speed may take - // up to 28 seconds in total - for this rough half of a minute no tissue updates will be computed. - // Well, it could be done by setting tissue_increment = 0 in !fast condition and making calls to - // calc_alveolar_pressures() and calc_tissues() - see code commented out below. - if( fast || gaschange ) + // 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 ) { - // program interval on simulated tissues (flag bit 7 = 0) - tissue_increment = fast + gaschange; - - // clear gas change signal - gaschange = 0; - // } - // else - // { - // // program 2 seconds interval on simulated tissues (flag bit 7 = 0) - // tissue_increment = 0; - // } - // { - // compute ppN2/ppHe for current depth from sim_pres_respiration + // 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() } @@ -2139,7 +2572,7 @@ assert( 0.00 <= ppHe && ppHe < 12.6 ); // 90% He at 130m - for (ci=0;ci<NUM_COMP;ci++) // iterate through all compartments + for( ci=0; ci < NUM_COMP; ci++ ) // iterate through all compartments { i = tissue_increment & TIME_MASK; // extract number of minutes to do (if i > 0) // or if one 2 second period is to do (if i = 0) @@ -2230,8 +2663,8 @@ { 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 - || ((temp_tissue_N2 < 0.0) && (temp_tissue_He > 0.0)) ) + if( ((temp_tissue_N2 > 0.0) && (temp_tissue_He < 0.0)) // check for counter diffusion + || ((temp_tissue_N2 < 0.0) && (temp_tissue_He > 0.0)) ) { IBCD_tissue_vector |= (1 << ci); // tag tissue as experiencing mentionable IBCD } @@ -2240,7 +2673,7 @@ // keep the saturating / desaturating flags from last invocation char_O_tissue_N2_saturation[ci] &= 128; - char_O_tissue_He_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 @@ -2252,7 +2685,7 @@ // 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 * (pres_respiration - ppWater)] bar + // 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; @@ -2271,19 +2704,13 @@ 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; + if (temp_tissue > 127) temp_tissue = 127; // export as integer char_O_tissue_He_saturation[ci] += (unsigned char)temp_tissue; } //if } // for - - - // set deco flag if we are in deco and at least one of the real tissues is off-gassing - // clear deco flag if all of the real tissues are on-gassing - if ( (char_O_nullzeit == 0) && deco_tissue_vector ) char_O_deco_warnings |= DECO_FLAG; - else if ( !deco_tissue_vector ) char_O_deco_warnings &= ~DECO_FLAG; } ////////////////////////////////////////////////////////////////////////////// @@ -2295,36 +2722,33 @@ // pres_tissue_N2/_He : tissue pressures (used in real tissues context) // // Output: -// sim_ceiling : ceiling in bar relative pressure (only in simulated tissues context) -// ceiling : ceiling in bar relative pressure (only in real tissues context) -// int_O_ceiling : ceiling in mbar relative pressure (only in real tissues context) -// int_O_gradient_factor : gradient factor in % (only in real tissues context) +// lead_supersat : supersaturation of the leading tissue, 1.0 = 100% +// ceiling : ceiling in bar relative pressure // // Modified: // char_O_deco_warnings : for IBCD, microbubbles and outside warning (only in real tissues context) // static void calc_limit(PARAMETER float GF_parameter) { - overlay float lead_tissue_limit = 0.0; - overlay float lead_supersat = 0.0; - - overlay unsigned char lead_tissue_no = 0; - + overlay float lead_tissue_limit = 0.0; + + + // set leading tissue number to not yet computed + lead_number = 0; + + // initialize leading tissue supersaturation value to null + lead_supersat = 0.0; // check context if( tissue_increment & TISSUE_FLAG ) { // clear IBCD, microbubbles and outside warning flags (locked warnings will be preserved) - char_O_deco_warnings &= ~(DECO_WARNING_IBCD + DECO_WARNING_MBUBBLES + DECO_WARNING_OUTSIDE); + char_O_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++) + for( ci = 0; ci < NUM_COMP; ci++ ) { - overlay float calc_pres_tissue_N2; - overlay float calc_pres_tissue_He; - overlay float pres_tissue; overlay float pres_min; // get the tissue pressures @@ -2347,9 +2771,8 @@ // get the coefficients for tissue ci read_Buhlmann_coefficients(); - // adapt the coefficients according to the N2/He ratio in 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; + // adopt a and b coefficients to current N2/He ratio inside the tissue + adopt_Buhlmann_coefficients(); // calculate minimum ambient pressure that the tissue can withstand according to straight Buhlmann pres_min = (pres_tissue - var_N2_a) * var_N2_b; @@ -2358,10 +2781,10 @@ if( tissue_increment & TISSUE_FLAG ) { overlay float supersat; - overlay float threshold; - - // calculate current supersaturation value (1.0 = 100%) of this tissue - supersat = (pres_tissue - pres_respiration) / (pres_tissue - pres_min); + overlay float limit_warning; + + // calculate current supersaturation value (1.0 = 100%) of this tissue according to straight Buhlmann + supersat = (pres_tissue - real_pres_respiration) / (pres_tissue - pres_min); // check if tissue is in supersaturation if( supersat > 0.0 ) @@ -2369,42 +2792,22 @@ // memorize highest supersaturation found if( supersat > lead_supersat ) lead_supersat = supersat; - // set a threshold value for the microbubbles and outside warnings - // ToDo: finalize the definition of the threshold - threshold = 0.02 * ci + 0.9; - - // check if this tissue is likely to develop microbubbles - // and/or if this tissue is outside of the Buhlmann model - if( ci <= 5 ) - { - if( supersat >= threshold ) - { - char_O_deco_warnings |= (DECO_WARNING_MBUBBLES + DECO_WARNING_MBUBBLES_lock); - - if( supersat >= 1.0 ) - { - char_O_deco_warnings |= (DECO_WARNING_OUTSIDE + DECO_WARNING_OUTSIDE_lock); - } - } - } - else // ci > 5 - { - if( supersat >= 1.0 ) - { - char_O_deco_warnings |= (DECO_WARNING_MBUBBLES + DECO_WARNING_MBUBBLES_lock); - - if( supersat >= threshold ) - { - char_O_deco_warnings |= (DECO_WARNING_OUTSIDE + DECO_WARNING_OUTSIDE_lock); - } - } - } + // limit value for micro bubbles and outside warnings + limit_warning = 0.02 * ci + 0.9; + + // micro bubbles warning: supersaturation >= limit_warning OR >= 1.0 + if( (supersat >= limit_warning) || (supersat >= 1.0) ) + char_O_deco_warnings |= (DECO_WARNING_MBUBBLES + DECO_WARNING_MBUBBLES_lock); + + // outside warning: supersaturation >= limit_warning AND >= 1.0 + if( (supersat >= limit_warning) && (supersat >= 1.0) ) + char_O_deco_warnings |= (DECO_WARNING_OUTSIDE + DECO_WARNING_OUTSIDE_lock ); } } // Apply the Eric Baker's varying gradient factor correction if the GF-Model is selected. - // Note: the correction factor depends both on GF and b, so that can change who is the - // leading gas... + // Note: the correction factor depends both on GF and b, so that can change which is the + // leading tissue... (this equation [1] is the inverse of equation [2]) if( char_I_deco_model != 0 ) pres_min = ( pres_tissue - (var_N2_a * GF_parameter) ) / ( 1.0 - GF_parameter + (GF_parameter / var_N2_b ) ); @@ -2412,71 +2815,36 @@ if( pres_min > lead_tissue_limit ) { lead_tissue_limit = pres_min; - lead_tissue_no = ci; + lead_number = ci; } } // for - // compile outputs + // 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 ) { - //--- real tissues ----------------------------------------------------- - - // check if leading tissue is in IBCD condition - if( (IBCD_tissue_vector & (1 << lead_tissue_no)) - && ((pres_tissue_N2[lead_tissue_no] + pres_tissue_He[lead_tissue_no]) > pres_respiration) ) + // 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) ) { - // leading tissue is in IBCD condition and in super-saturation, so issue a warning. + // 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); } - - - // compute ceiling in bar relative pressure - ceiling = lead_tissue_limit - pres_surface; - - // convert ceiling to int_O_ceiling in mbar - if ( ceiling <= 0 ) int_O_ceiling = 0; - else if ( ceiling > 16 ) int_O_ceiling = 16000; - // Compatibility version - // else int_O_ceiling = (short)(ceiling * 1000); - - // New version: Rounds up to next 10 cm so that the ceiling disappears on the display only when the - // ceiling limit is really zero. This will coincident then with TTS switching back to NDL time. - else int_O_ceiling = (short)(ceiling * 1000 + 9); - - - // convert highest supersaturation found to int_O_gradient_factor in % (1.0 = 100%) - // limit to 255 because of constraints in ghostwriter code - if ( lead_supersat <= 0.0 ) int_O_gradient_factor = 0; - else if( lead_supersat > 2.545 ) int_O_gradient_factor = 255 + INT_FLAG_WARNING; - else - { - int_O_gradient_factor = (unsigned int)(100 * lead_supersat + 0.5); - - if ( int_O_gradient_factor >= GF_WARNING_THRESHOLD ) - int_O_gradient_factor |= INT_FLAG_WARNING; - - else if ( int_O_gradient_factor >= char_I_GF_High_percentage ) - int_O_gradient_factor |= INT_FLAG_ATTENTION; - } - } - else - { - //--- simulated tissues ------------------------------------------------ - - // compute ceiling for the simulated tissues in bar relative pressure - sim_ceiling = lead_tissue_limit - pres_surface; } } ////////////////////////////////////////////////////////////////////////////// // calc_NDL_time // -// calculates the remaining bottom time +// calculation of the remaining bottom time (NDL: no decompression limit) // -// NOTE: Erik Baker's closed formula works for Nitroxes. Trimix adds a second +// 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 make a fast-simu until we find a better way. +// invert. So we have to solve the problem with an iterative approach. // // Input: ppN2 // ppHe @@ -2492,28 +2860,27 @@ // initialize NDL_time to 240 minutes NDL_time = 240; - for(i=0; i<NUM_COMP; i++) + for( i = 0; i < NUM_COMP; i++ ) { - overlay float calc_pres_tissue_N2; - overlay float calc_pres_tissue_He; - overlay float pres_tissue; - - overlay unsigned char NDL_tissue; - overlay unsigned char period = 10; // start with 10 minute periods - - - // check lead tissue from last NDL computation first + 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 = i + NDL_lead_tissue; // wrap around after the 16th tissue if( ci >= NUM_COMP ) ci -= NUM_COMP; - // read Buhlmann a and b coefficients for tissue ci - read_Buhlmann_coefficients(); - - // read the loading factors for 10 minute periods + // 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]; @@ -2521,72 +2888,73 @@ // calculate the total pressure tissue pres_tissue = calc_pres_tissue_N2 + calc_pres_tissue_He; - - // simulate an increasing bottom time and check when we hit the NDL ------------------------ - for( NDL_tissue = 0; NDL_tissue < NDL_time; ) // not needed to simulate for longer than the already found NDL + // 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; ) { - overlay float var_a; - overlay float var_b; 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 - 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; - - // compute pressure limit for tissues under surface pressure conditions - pres_limit = (var_a + pres_surface / var_b); - - // adopt pressure limit when using the GF extension - if (char_I_deco_model != 0 ) pres_limit = GF_high * (pres_limit - pres_surface) + pres_surface; - - //---- Check if this tissue is already beyond the NDL + 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) { - // NO - finish the outer loop, + // beyond NDL - finish the outer loop, ... i = NUM_COMP; - // and finish the inner loop + // ... and finish the inner loop break; } - // compute delta to tissue pressures in 10 or 1 minutes of time ahead + // 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 we are saturating or desaturating + // 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 - // TODO ! + // 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...) - // within NDL now, but still within in 10 or 1 minutes from now? - if( pres_tissue + delta_pres_tissue_N2 + delta_pres_tissue_He <= pres_limit ) + // 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 ) { - // YES - apply the pressure deltas to tissues + // 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 = calc_pres_tissue_N2 + calc_pres_tissue_He; + pres_tissue = next_pres_tissue; // increment the NDL NDL_tissue += period; - // do next loop + // do next iteration continue; } - // NO - if delta pressures were for 10 minutes of time ahead, try with 1 minute ahead + // NO - if delta pressures were for 10 minutes of time ahead, continue with trying for 1 minute ahead if( period == 10 ) { // reduce period to 1 minute @@ -2595,21 +2963,16 @@ // read the loading factors for 1 minute periods read_Buhlmann_times(1); - // do next loop + // do next iteration continue; } - // NO - not even within NDL in just one more minute, so make a linear approx for the last minute - // (make a meaningful rounding of NDL, but ONLY if positive: negative casted to unsigned is bad) - if( pres_limit > pres_tissue ) - NDL_tissue += (unsigned char)(0.5 + (pres_limit - pres_tissue ) - / (delta_pres_tissue_N2 + delta_pres_tissue_He) ); - - // finish the inner loop + // less than a full minute of NDL time left, so finish the inner loop break; - } - - // is the current NDL short than the shortest so far? + + } // inner for-loop simulating increasing bottom time + + // is the current NDL shorter than the shortest so far? if ( NDL_tissue < NDL_time ) { // keep the current's tissue NDL as the new shortest NDL @@ -2619,64 +2982,76 @@ new_NDL_lead_tissue = ci; } - // if NDL is > 0 the outer loop will continues with the next tissue - // if NDL found to be overrun, outer loop will be terminated through i = NUM_COMP statement - } + // 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 NDL_lead_tissue = new_NDL_lead_tissue; } + ////////////////////////////////////////////////////////////////////////////// // calc_ascenttime // -// Sum up ascent from bottom to surface at float_ascent_speed, -// but 1 minute per meter for the final ascent, and all stops. +// 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. // // Input: char_I_depth_last_deco -// pres_respiration -// pres_surface -// float_ascent_speed +// char_I_ascent_speed +// char_bottom_depth // internal_deco_depth[] +// internal_deco_time[] // // Output: ascent_time // static void calc_ascenttime(void) { - overlay unsigned char x; - - - // preset final ascent - overlay float final = (float)char_I_depth_last_deco; - - // calculate depth - overlay float ascent = (pres_respiration - pres_surface) * BAR_TO_METER; - - // check if we are already in final ascent - if (ascent <= final) + 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] ) { - // yes - all ascent is final ascent - final = ascent; - ascent = 0.0; + // stops / in deco + + // check if already at last stop depth or shallower + if( char_bottom_depth <= char_I_depth_last_deco) + { + // YES + ascent = 0; + final = char_bottom_depth; + } + else + { + // NO + ascent = char_bottom_depth - char_I_depth_last_deco; + final = char_I_depth_last_deco; + } } else { - // no - subtract final ascent part from overall ascent - ascent -= final; - - // compute time for ascent part without final ascent - ascent /= float_ascent_speed; + // no stops / within NDL + ascent = char_bottom_depth; + final = 0; } - // add 1 minute for each meter of final ascent - ascent += final; - - // convert to integer - ascent_time = (unsigned short)(ascent + 0.5); + + // 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 += (unsigned short)internal_deco_time[x]; + 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; @@ -2694,11 +3069,11 @@ { overlay unsigned char x; - for(x=0; x<NUM_STOPS; ++x) + for( x = 0; x < NUM_STOPS; ++x ) { internal_deco_time [x] = 0; internal_deco_depth[x] = 0; - internal_deco_gas[x] = 0; + internal_deco_gas[x] = 0; } // clear stop table overflow warning @@ -2712,11 +3087,11 @@ // // 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 needs always to be depth == 0. +// to have reached the end of the list is depth == 0. // -// Input: sim_depth_limit : stop's depth, in meters. -// sim_gas_last_used : gas used at stop, as index 1..5 or 0 for gas 6 -// PARAMETER time_increment : number of minutes to add to the stop +// 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 @@ -2726,26 +3101,25 @@ { overlay unsigned char x; - assert( sim_depth_limit > 0 ); // No stop at surface... + assert( sim_depth_limit > 0 ); // no stop at surface // loop through internal deco table - for(x=0; x<NUM_STOPS; ++x) + for( x = 0; x < NUM_STOPS; ++x ) { - // In case the first deco stop is to be placed deeper than previously recorded - // stops for gas changes during the initial ascent (this may happen because the - // deco stops are placed at the next deeper multiple of 3 meters instead of the - // 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]; - + // In case the first deco stop is to be placed deeper than previously recorded + // stops for gas changes during the initial ascent (this may happen because the + // deco stops are placed at the next deeper multiple of 3 meters instead of the + // 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]; + // Is there already a stop entry for our current depth? if( internal_deco_depth[x] == sim_depth_limit ) { // Yes - increment stop time if possible // Stop time entries are limited to 99 minutes because of display constraints. - // Else a limit of 254 would account because of constrains in calc_CNS_planning(). if( internal_deco_time[x] < (100 - time_increment) ) { internal_deco_time[x] += time_increment; // increment stop time @@ -2760,7 +3134,7 @@ { 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_last_used; // ... gas + internal_deco_gas[x] = sim_gas_current; // ... gas return 1; // return with status 'success' } } @@ -2778,16 +3152,13 @@ ////////////////////////////////////////////////////////////////////////////// // calc_desaturation_time // -// Inputs: int_I_pres_surface, ppWater, char_I_desaturation_multiplier -// Outputs: int_O_desaturation_time, int_O_nofly_time -// // Helper function // -void calc_desaturation_time_helper(void) +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 - overlay float pres_ratio; + overlay float pres_ratio; pres_ratio = pres_actual / pres_target; @@ -2795,7 +3166,7 @@ // Main purpose is to avoid confusion, because the times do not clock down in // 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 and falls. + // 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 ); } else @@ -2805,22 +3176,31 @@ } ///////////////////////////////////////////////////////////////////////////// -// Main function +// 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 // void calc_desaturation_time(void) { - assert( 800 < int_I_pres_surface && int_I_pres_surface < 1100 ); - assert( 0 < char_I_desaturation_multiplier && char_I_desaturation_multiplier <= 100 ); + 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 - N2_ratio = 0.7902; - He_ratio = 0.0; + 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 = N2_ratio * (pres_surface - ppWater); + 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; @@ -2830,7 +3210,7 @@ int_O_nofly_time = 0; - for(ci=NUM_COMP; ci>0;) + for( ci = NUM_COMP; ci > 0; ) { overlay float pres_tissue_max; overlay float P_ambient_altitude; @@ -2872,9 +3252,9 @@ // 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; + 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 @@ -2906,6 +3286,9 @@ // no-fly time // + // 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 search_direction = 0; @@ -2915,7 +3298,7 @@ pres_actual = pres_tissue_N2[ci] - N2_equilibrium; // N2: half-time of the current tissue - var_ht = var_N2_ht; + 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. @@ -2927,7 +3310,7 @@ int_O_nofly_time = 288; // NO - set no-fly time to 288 * 10 min = 48 h break; // done for this compartment } - else + else { calc_desaturation_time_helper(); nofly_N2 = int_time; @@ -2966,7 +3349,7 @@ // check if the search direction has changed, which means we are beyond the // 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. int_O_nofly_time = (nofly_N2 < nofly_last) ? nofly_N2 : nofly_last; @@ -2997,7 +3380,7 @@ // decrease the N2 fraction of the split and set search direction towards less N2 split_N2_He[ci] -= 1; - search_direction = -1; + search_direction = -1; } } // for(;;) @@ -3015,17 +3398,19 @@ if( int_O_nofly_time > 5999 ) int_O_nofly_time = 5999; - // Clear the microbubbles warning when the current gradient factor is < GF_WARNING_THRESHOLD. - // 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 < GF_WARNING_THRESHOLD ) + // Clear the microbubbles 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; // 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 ); + 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 ); } @@ -3039,26 +3424,30 @@ // 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 -// CNS value -// ceiling and current GF +// 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 // static void calc_interval(PARAMETER unsigned char time_interval) { overlay unsigned char time; - assert( 800 < int_I_pres_surface && int_I_pres_surface < 1100 ); - assert( 100 <= char_I_saturation_multiplier && char_I_saturation_multiplier < 200 ); - assert( 0 < char_I_desaturation_multiplier && char_I_desaturation_multiplier <= 100 ); - + assert( 100 <= char_I_saturation_multiplier && char_I_saturation_multiplier < 200 ); + 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 - pres_respiration = pres_surface = 0.001 * int_I_pres_surface ; - - N2_ratio = 0.7902; // according to Buhlmann - N2_equilibrium = N2_ratio * (pres_surface - ppWater); // used for N2 tissue graphics scaling - ppN2 = N2_ratio * (pres_respiration - ppWater); + 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; @@ -3075,7 +3464,7 @@ if( time > 127) { // do a full 127 minutes on the real tissues - tissue_increment = 127 | TISSUE_FLAG; + tissue_increment = TISSUE_FLAG | 127; calc_tissues(); // determine the remaining part @@ -3083,9 +3472,9 @@ } // program the remaining part (or full part if not exceeding 127 minutes) - tissue_increment = time | TISSUE_FLAG; - - // update the N2 and He pressures in the tissues for the remaining part of the time interval + tissue_increment = TISSUE_FLAG | time; + + // update the N2 and He pressures in the tissues calc_tissues(); @@ -3095,7 +3484,7 @@ time = time_interval; - while ( time ) + while( time ) { if( time > 9 ) { @@ -3112,32 +3501,37 @@ // compute integer copy of CNS value convert_CNS_for_display(); - - // calculate ceiling (for a GF high of 100%) and gradient factor + // calculate GF value (for a GF high of 100%) calc_limit(1.0); + + // compute integer copy of GF value + convert_GF_for_display(); } ////////////////////////////////////////////////////////////////////////////// -// calc_CNS_increment +// calc_CNS // -// Input: char_ppO2 : current ppO2 [decibars] -// tissue_increment : time increment and tissue selector +// Input: char_ppO2 : current ppO2 [decibars] +// tissue_increment : time increment and tissue selector // -// Output: CNS_fraction_inc : increment of the CNS value +// Modified: CNS_fraction accumulated CNS (real tissue context) +// sim_CNS_fraction : accumulated CNS (simulated tissue context) // -void calc_CNS_increment(void) +static void calc_CNS(void) { - overlay float time_factor = 1.0; // default is 2sec + 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 if minute-based stepping is commanded, mask out flag bit - if( tissue_increment & TIME_MASK ) time_factor = 30.0 * (float)(tissue_increment & TIME_MASK); + // 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; //------------------------------------------------------------------------ - // Don't increase CNS below 0.5 bar, but keep it steady. - if (char_ppO2 < 50) CNS_fraction_inc = 0.0; // no CNS increase below 0.5 bar ppO2 + // 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); @@ -3152,178 +3546,19 @@ // 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.0209; - else CNS_fraction_inc = time_factor*0.0482; // value for 2.5 bar, used for 2.33 bar and above -} - -////////////////////////////////////////////////////////////////////////////// -// calc_CNS_planning -// -// Compute CNS increase during predicted ascent -// -// Input: internal_deco_time[], internal_deco_depth[], internal_deco_gas[] -// Output: sim_CNS_fraction -// -void calc_CNS_planning(void) -{ - // null sim_CNS_fraction - sim_CNS_fraction = 0.0; - - //---- CCR mode : do the full TTS at once --------------------------------- - - if( ((char_O_deco_status & DECO_MODE_MASK) == DECO_MODE_CCR) ) - { - overlay unsigned short t; // needs 16 bits here ! - - // get current ppO2 from sensors or setpoint - char_ppO2 = char_I_const_ppO2; - - // calculate CNS% for the period of additional staying at bottom depth (fTTS / delayed ascent) - if( char_O_deco_status & DECO_ASCENT_DELAYED) - { - tissue_increment = char_I_extra_time; // must be limited to 127, is limited by range of char_I_extra_time - calc_CNS_increment(); // calculate the CNS increment - sim_CNS_fraction += CNS_fraction_inc; // sum up - } - - // get the ascent time dependent on the current plan +++ - t = (char_O_deco_status & DECO_PLAN_ALTERNATE) ? int_O_alternate_ascenttime : int_O_ascenttime; - - // start simulating CNS% in chunks of 127 minutes - tissue_increment = 127; - - while( t > 127 ) - { - t -= 127; // tissue_increment is limited to 127 minutes because of flag in bit 7 - calc_CNS_increment(); // calculate CNS in chunks of full 127 minutes - sim_CNS_fraction += CNS_fraction_inc; // sum up - } - - tissue_increment = (char)t; // get the remaining minutes <= 127 - calc_CNS_increment(); // calculate CNS for the remaining minutes - sim_CNS_fraction += CNS_fraction_inc; // sum up - } - else //---- OC mode and pSCR without sensors: have to follow all gas switches... ----- - { - overlay float float_actual_ppO2; - overlay float abs_pres; - - overlay unsigned char stop_depth; - overlay unsigned char last_gas; - overlay unsigned char i; // stop table index - - - // retrieve bottom gas: 1-5 for the configured gases or 0 for the manually set gas - last_gas = sim_gas_last_used = sim_gas_first_used; - - // get the calc_N2/He/O2_ratios of the bottom gas - gas_set_ratios(); - - // calculate absolute pressure - abs_pres = pres_surface + bottom_depth * METER_TO_BAR; - - // calculate OC ppO2 (ppWater omitted here on purpose) - float_actual_ppO2 = abs_pres * sim_O2_ratio; - - // correct ppO2 in case of pSCR mode by drop - if( char_O_deco_status & DECO_MODE_PSCR ) float_actual_ppO2 -= sim_pSCR_drop; - - // convert ppO2 from float to char - if ( float_actual_ppO2 < 0.0 ) char_ppO2 = 0; - else if ( float_actual_ppO2 > 2.545 ) char_ppO2 = 255; - else char_ppO2 = (unsigned char)(100 * float_actual_ppO2 + 0.5); - - - // simulate extended bottom time (fTTS) / delay before ascent (bailout) if configured - if( char_O_deco_status & DECO_ASCENT_DELAYED ) - { - tissue_increment = char_I_extra_time; // must be limited to 127, is limited by range of char_I_extra_time - calc_CNS_increment(); // calculate the CNS increment - sim_CNS_fraction += CNS_fraction_inc; // sum up - } - - - // For simplicity reason (non-linearity of the relation between ppO2 and CNS increments), the - // whole ascent is calculated with bottom ppO2. This errs, but it does so to the safe side. - - // calculate ascent time (integer division and generous round-up) - tissue_increment = bottom_depth / char_I_ascent_speed + 1; - - // ** commented out - not needed when char_I_ascent_speed is limited to a minimum - // ** of 2.something, it is indeed limited to a minimum of 5. - // - // // limit tissue_increment to 127 minutes - // if( tissue_increment > 127 ) tissue_increment = 127; - - // simulate the CNS increase - calc_CNS_increment(); // calculate the CNS increment - sim_CNS_fraction += CNS_fraction_inc; // sum up - - - //---- Stops --------------------------------------------------------- - - for(i=0; i<NUM_STOPS; ++i) - { - // get the depth of the stop - stop_depth = internal_deco_depth[i]; - - // did we reach the last entry (depth = 0)? if yes, done - if (stop_depth == 0) break; - - // get the duration of the stop and the gas breathed - tissue_increment = internal_deco_time[i]; - sim_gas_last_used = internal_deco_gas[i]; - - // do we have a gas switch? - if( sim_gas_last_used != last_gas ) - { - // yes - get new calculation ratios - gas_set_ratios(); - - // remember new gas as last gas - last_gas = sim_gas_last_used; - } - - // calculate absolute pressure at stop depth - abs_pres = pres_surface + stop_depth * METER_TO_BAR; - - // calculate OC ppO2 (ppWater omitted here on purpose) - float_actual_ppO2 = abs_pres * sim_O2_ratio; - - // correct ppO2 in case of pSCR mode by drop - if( char_O_deco_status & DECO_MODE_PSCR ) float_actual_ppO2 -= sim_pSCR_drop; - - // convert ppO2 from float to char - if ( float_actual_ppO2 < 0.0 ) char_ppO2 = 0; - else if ( float_actual_ppO2 > 2.545 ) char_ppO2 = 255; - else char_ppO2 = (unsigned char)(100 * float_actual_ppO2 + 0.5); - - // ** Currently, stop times per stop entry are limited to 99 minutes in update_deco_table(), - // ** so the following code block is not needed at times. - // - // // tissue_increment is limited to 127 when fed to calc_CNS_increment(), - // // so if the stop is longer than 127 minutes (but not longer than 254 minutes!) - // // we need to calculate the CNS in two chunks. - // if( tissue_increment > 127) - // { - // tissue_increment -= 127; // subtract full 127 minutes and do the "remaining" minutes first - // calc_CNS_increment(); // calculate the CNS increment - // sim_CNS_fraction += CNS_fraction_inc; // sum up - // tissue_increment = 127; // catch up with the previously subtracted full 127 minutes - // } - - // calculate CNS% for the stop - calc_CNS_increment(); // calculate the CNS increment - sim_CNS_fraction += CNS_fraction_inc; // sum up - } - } + 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 + + // 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 } @@ -3332,37 +3567,71 @@ // // calculates volumes and required tank fill pressures for each gas. // -// Input: bottom_depth depth of the bottom segment +// 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 -// sim_gas_first_used the bottom gas (1-5 for configured gases, 0 for the manual gas) // 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_gas_volumes[] amount of gas needed, in liters -// int_O_tank_pres_need[] in bar, + flags for fast evaluation by dive mode warnings: +// 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 invalid +// 2^10: pres_need is invalid // -void gas_volumes_helper(void) +static void gas_volumes_helper_1(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 * usage; + volume = (float_depth * METER_TO_BAR + 1.0) * float_time * char_usage; return; } -void gas_volumes(void) +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 + { + 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; + } + + return; +} + +static void gas_volumes(void) { overlay float volumes[NUM_GAS]; @@ -3373,11 +3642,10 @@ overlay unsigned char stop_depth_last; overlay unsigned char i; - //---- initialization ---------------------------------------------------- // null the volume accumulators - for(i=0; i<NUM_GAS; ++i) volumes[i] = 0.0; + 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; @@ -3385,22 +3653,23 @@ //---- bottom demand ----------------------------------------------------- - // sim_gas_first_used : gas used during bottom segment (0, 1-5) - // bottom_depth: depth of the bottom segment - - assert(0 <= sim_gas_first_used && sim_gas_first_used <= NUM_GAS); + // 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 - stop_gas_last = stop_gas = sim_gas_first_used; + 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 - usage = char_I_bottom_usage; + char_usage = char_I_bottom_usage; // volumes are only calculated for gases 1-5, but not the manually configured one if( stop_gas ) { // set the bottom depth - float_depth = (float)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 ) @@ -3415,7 +3684,7 @@ } // calculate gas demand - gas_volumes_helper(); + gas_volumes_helper_1(); // take result volumes[stop_gas-1] = volume; @@ -3427,10 +3696,10 @@ //---- initial ascent demand --------------------------------------------- // stop_gas : gas from bottom segment - // bottom_depth : depth of the 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 + // get the data of the first stop stop_depth = internal_deco_depth[i]; stop_time = internal_deco_time[i]; @@ -3438,7 +3707,7 @@ if( stop_gas ) { // compute distance between bottom and first stop - float_depth = (float)bottom_depth - (float)stop_depth; + float_depth = (float)(char_bottom_depth - stop_depth); // initial ascent exists only if ascent distance is > 0 if( float_depth > 0.0 ) @@ -3447,10 +3716,10 @@ float_time = float_depth / float_ascent_speed; // compute average depth between bottom and first stop - float_depth = (float)bottom_depth - float_depth * 0.5; + float_depth = (float)char_bottom_depth - float_depth * 0.5; // calculate gas demand - gas_volumes_helper(); + gas_volumes_helper_1(); // add result volumes[stop_gas-1] += volume; @@ -3459,7 +3728,7 @@ // switch the usage (SAC rate) to deco usage rate // for stops, intermediate and final ascent - usage = char_I_deco_usage; + char_usage = char_I_deco_usage; // is there a (first) stop? if yes, goto stops processing if( stop_depth ) goto stops; @@ -3469,7 +3738,7 @@ float_depth = 5.0; // calculate gas demand - gas_volumes_helper(); + gas_volumes_helper_1(); // add result volumes[stop_gas-1] += volume; @@ -3526,7 +3795,7 @@ float_depth = (float)stop_depth_last - float_depth * 0.5; // calculate gas demand - gas_volumes_helper(); + gas_volumes_helper_1(); // add result volumes[stop_gas_last-1] += volume; @@ -3541,18 +3810,18 @@ // 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 we have a gas change? + + // 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(); + gas_volumes_helper_1(); // add result volumes[stop_gas_last-1] += volume; @@ -3565,7 +3834,7 @@ float_time = (float)stop_time; // calculate gas demand - gas_volumes_helper(); + gas_volumes_helper_1(); // add result to last gas volumes[stop_gas-1] += volume; @@ -3581,17 +3850,26 @@ // 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 + // volumes are only calculated for gases 1-5, but not the manually configured one if( stop_gas ) { - // set ascent time according to an ascent speed of 1 meter per minute - float_time = float_depth; + // 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(); + gas_volumes_helper_1(); // add result volumes[stop_gas-1] += volume; @@ -3601,89 +3879,155 @@ //---- convert results for the assembler interface ----------------------------- done: - for(i=0; i<NUM_GAS; ++i) +#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( volumes[i] >= 65534.5 ) - { - int_O_gas_volumes[i] = 65535; - int_O_tank_pres_need[i] = 999 + INT_FLAG_WARNING; // 999 bar + warning flag for > pres_fill - } - else + 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 ) { - overlay unsigned short tank_pres_fill = 10.0 * (unsigned short)char_I_tank_pres_fill[i]; - - // No distinct rounding done here because volumes are not accurate to the single liter anyhow - - // convert gas volumes to integers - int_O_gas_volumes[i] = (unsigned short)volumes[i]; - - // compute how much pressure in the tank will be needed [in bar] (integer-division) - int_O_tank_pres_need[i] = (unsigned short)(int_O_gas_volumes[i] / char_I_tank_size[i]); - - // limit to 999 bar because of display constraints - if( int_O_tank_pres_need[i] > 999 ) int_O_tank_pres_need[i] = 999; - - // set flags for fast evaluation by divemode check for warnings - if ( int_O_tank_pres_need[i] == 0 ) + // 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]) ) { - // set flag for 0 bar - int_O_tank_pres_need[i] |= INT_FLAG_ZERO; - } - else if( int_O_tank_pres_need[i] >= tank_pres_fill ) - { - // set warning flag - int_O_tank_pres_need[i] |= INT_FLAG_WARNING; - + // 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; } - else if( int_O_tank_pres_need[i] >= tank_pres_fill * GAS_NEEDS_ATTENTION_THRESHOLD ) - { - // set pre-warning flag - int_O_tank_pres_need[i] |= 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_O_tank_pres_need[i] |= INT_FLAG_INVALID; - - } // if( volumes[i] ) + } // TR functions +#endif + } // for } ////////////////////////////////////////////////////////////////////////////// -void convert_CNS_for_display(void) +static void convert_CNS_for_display(void) { - if ( CNS_fraction < 0.01 ) int_O_CNS_fraction = 0; - else if ( CNS_fraction >= 9.985 ) int_O_CNS_fraction = 999 + INT_FLAG_WARNING; + 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 warnings - 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; + // 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; } } ////////////////////////////////////////////////////////////////////////////// -void convert_sim_CNS_for_display(void) +static void convert_sim_CNS_for_display(void) { - if ( sim_CNS_fraction < 0.01 ) int_sim_CNS_fraction = 0; - else if ( sim_CNS_fraction >= 9.985 ) int_sim_CNS_fraction = 999 + INT_FLAG_WARNING; + 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 flag if CNS is >= 100% + // 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; } + + // 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; +} + +////////////////////////////////////////////////////////////////////////////// + +static void convert_GF_for_display(void) +{ + // convert supersaturation of the leading tissue to int_O_gradient_factor 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 + { + 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 >= 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 + } + + if( int_O_gradient_factor >= 100 ) + int_O_gradient_factor |= INT_FLAG_WARNING; // make GF factor shown in red + } + 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 >= 90 ) + { + 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 + } + } + } + + // export also the number of the leading tissue + char_O_lead_number = lead_number; +} + +////////////////////////////////////////////////////////////////////////////// + +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); + + // set/reset ceiling flag + if( int_O_ceiling ) char_O_deco_info |= DECO_CEILING; + else char_O_deco_info &= ~DECO_CEILING; } ////////////////////////////////////////////////////////////////////////////// @@ -3694,21 +4038,21 @@ // tissues and related data when entering / leaving simulation mode! // -void push_tissues_to_vault(void) +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++) + for( x = 0; x < NUM_COMP; x++ ) { pres_tissue_N2_vault[x] = pres_tissue_N2[x]; pres_tissue_He_vault[x] = pres_tissue_He[x]; } } -void pull_tissues_from_vault(void) +static void pull_tissues_from_vault(void) { overlay unsigned char x; @@ -3717,10 +4061,7 @@ convert_CNS_for_display(); - locked_GF_step_norm = GF_delta / low_depth_norm; - locked_GF_step_alt = GF_delta / low_depth_alt; - - for (x=0; x<NUM_COMP; x++) + for( x = 0; x < NUM_COMP; x++ ) { pres_tissue_N2[x] = pres_tissue_N2_vault[x]; pres_tissue_He[x] = pres_tissue_He_vault[x];