comparison src/p2_deco.c @ 560:b7eb98dbd800

bump to 2.96beta (REFACTORED VERSION)
author heinrichsweikamp
date Wed, 31 Jan 2018 19:39:37 +0100
parents a5d2e6083b1d
children 3febf1cd1bf4
comparison
equal deleted inserted replaced
559:9cb967d844f0 560:b7eb98dbd800
1 // ************************************************************** 1 // **************************************************************
2 // p2_deco.c 2 // p2_deco.c REFACTORED VERSION V2.95a2
3 // 3 //
4 // Created on: 12.05.2009 4 // Created on: 12.05.2009
5 // Author: chsw 5 // Author: heinrichs weikamp, contributions by Ralph Lembcke and others
6 // 6 //
7 // ************************************************************** 7 // **************************************************************
8 8
9 ////////////////////////////////////////////////////////////////////////////// 9 //////////////////////////////////////////////////////////////////////////////
10 // OSTC - diving computer code 10 // OSTC - diving computer code
23 // You should have received a copy of the GNU General Public License 23 // You should have received a copy of the GNU General Public License
24 // along with this program. If not, see <http://www.gnu.org/licenses/>. 24 // along with this program. If not, see <http://www.gnu.org/licenses/>.
25 // 25 //
26 ////////////////////////////////////////////////////////////////////////////// 26 //////////////////////////////////////////////////////////////////////////////
27 27
28 // *****************************
29 // ** I N T R O D U C T I O N **
30 // *****************************
31 //
32 // OSTC
33 //
34 // code:
35 // p2_deco_main_c_v101.c
36 // part2 of the OSTC code
37 // code with constant O2 partial pressure routines
38 // under construction !!
39 //
40 // summary:
41 // decompression routines
42 // for the OSTC experimental project
43 // written by Christian Weikamp
44 // last revision __________
45 // comments added _________
46 //
47 // additional files:
48 // p2_tables_v100.romdata (other files)
49 // 18f4685_ostc_v100.lkr (linker script)
50 //
51 // history: 28 // history:
52 // 01/03/08 v100: first release candidate 29 // 01/03/08 v100: first release candidate
53 // 03/13/08 v101: start of programming ppO2 code 30 // 03/13/08 v101: start of programming ppO2 code
54 // 03/13/25 v101a: backup of interrim version with ppO2 calculation 31 // 03/13/25 v101a: backup of interim version with ppO2 calculation
55 // 03/13/25 v101: open circuit gas change during deco 32 // 03/13/25 v101: open circuit gas change during deco
56 // 03/13/25 v101: CNS_fraction calculation 33 // 03/13/25 v101: CNS_fraction calculation
57 // 03/13/26 v101: optimization of tissue calc routines 34 // 03/13/26 v101: optimization of tissue calc routines
58 // 07/xx/08 v102a: debug of bottom time routine 35 // 07/xx/08 v102a: debug of bottom time routine
59 // 09/xx/08 v102d: Gradient Factor Model implemenation 36 // 09/xx/08 v102d: Gradient Factor Model implementation
60 // 10/10/08 v104: renamed to build v103 for v118 stable 37 // 10/10/08 v104: renamed to build v103 for v118 stable
61 // 10/14/08 v104: integration of char_I_depth_last_deco for Gradient Model 38 // 10/14/08 v104: integration of char_I_depth_last_deco for Gradient Model
62 // 03/31/09 v107: integration of FONT Incon24 39 // 03/31/09 v107: integration of FONT Incon24
63 // 05/23/10 v109: 5 gas changes & 1 min timer 40 // 05/23/10 v109: 5 gas changes & 1 min timer
64 // 07/13/10 v110: cns vault added 41 // 07/13/10 v110: cns vault added
69 // 2011/01/25: [jDG] Use CF(54) to reverse deco order. 46 // 2011/01/25: [jDG] Use CF(54) to reverse deco order.
70 // 2011/02/11: [jDG] Reworked gradient-factor implementation. 47 // 2011/02/11: [jDG] Reworked gradient-factor implementation.
71 // 2011/02/15: [jDG] Fixed inconsistencies introduced by gas switch delays. 48 // 2011/02/15: [jDG] Fixed inconsistencies introduced by gas switch delays.
72 // 2011/03/21: [jDG] Added gas consumption (CF56 & CF57) evaluation for OCR mode. 49 // 2011/03/21: [jDG] Added gas consumption (CF56 & CF57) evaluation for OCR mode.
73 // 2011/04/15: [jDG] Store low_depth in 32bits (w/o rounding), for a better stability. 50 // 2011/04/15: [jDG] Store low_depth in 32bits (w/o rounding), for a better stability.
74 // 2011/04/25: [jDG] Added 1mn mode for CNS calculation, to allow it for decoplanning. 51 // 2011/04/25: [jDG] Added 1mn mode for CNS calculation, to allow it for deco planning.
75 // 2011/04/27: [jDG] Fixed char_O_gradient_factor calculation when model uses gradient-factor. 52 // 2011/04/27: [jDG] Fixed char_O_gradient_factor calculation when model uses gradient-factor.
76 // 2011/05/02: [jDG] Added "Future TTS" function (CF58). 53 // 2011/05/02: [jDG] Added "Future TTS" function (CF58).
77 // 2011/05/17: [jDG] Various cleanups. 54 // 2011/05/17: [jDG] Various cleanups.
78 // 2011/08/08: [jDG] Computes CNS during deco planning ascent. 55 // 2011/08/08: [jDG] Computes CNS during deco planning ascent.
79 // 2011/11/24: [jDG] Slightly faster and better NDL computation. 56 // 2011/11/24: [jDG] Slightly faster and better NDL computation.
80 // 2011/12/17: [mH] Remove of the useless debug stuff 57 // 2011/12/17: [mH] Remove of the useless debug stuff
81 // 2012/02/24: [jDG] Remove missed stop bug. 58 // 2012/02/24: [jDG] Remove missed stop bug.
82 // 2012/02/25: [jDG] Looking for a more stable LOW grad factor reference. 59 // 2012/02/25: [jDG] Looking for a more stable LOW grad factor reference.
83 // 2012/09/10: [mH] Fill char_O_deco_time_for_log for logbook write 60 // 2012/09/10: [mH] Fill char_O_deco_time_for_log for logbook write
84 // 2012/10/05: [jDG] Better deco_gas_volumes accuracy (average depth, switch between stop). 61 // 2012/10/05: [jDG] Better gas_volumes accuracy (average depth, switch between stop).
85 // 2013/03/05: [jDG] Should vault low_depth too. 62 // 2013/03/05: [jDG] Should vault low_depth too.
86 // 2013/03/05: [jDG] Wrobell remark: ascent_to_first_stop works better with finer steps (2sec). 63 // 2013/03/05: [jDG] Wrobell remark: ascent_to_first_stop works better with finer steps (2sec).
87 // 2013/05/08: [jDG] A. Salm remark: NOAA tables for CNS are in ATA, not bar. 64 // 2013/05/08: [jDG] A. Salm remark: NOAA tables for CNS are in ATA, not bar.
88 // 2013/12/21: [jDG] Fix CNS calculation in decoplan w/o marked gas switch 65 // 2013/12/21: [jDG] Fix CNS calculation in deco plan w/o marked gas switch
89 // 2014/06/16: [jDG] Fix Helium diluant. Fix volumes with many travel mix. 66 // 2014/06/16: [jDG] Fix Helium diluent. Fix volumes with many travel mix.
90 // 2014/06/29: [mH] Compute int_O_ceiling 67 // 2014/06/29: [mH] Compute int_O_ceiling
91 // 2015/06/12: [jDG] Fix NDL prediction while desaturating with the Buhlmann model. 68 // 2015/06/12: [jDG] Fix NDL prediction while desaturating with the Buhlmann model.
92 // 2017/08/04: [mH] Switch to absolute GF everywhere and apply safety margin parameters to both models (GF and non-GF), fixes from Ralph Lembcke 69 // 2017/08/04: [mH] Switch to absolute GF everywhere and apply safety margin parameters to both models (GF and non-GF), fixes from Ralph Lembcke
93 // 70 // 2017/10/31: [rl] enhancements for pSCR mode and introduction of 2nd deco plan computation
94 // TODO: 71 // 2017/12/31: [rl] completion of 2nd deco plan computation and various up-fixes
72 //
95 // 73 //
96 // Literature: 74 // Literature:
97 // Buhlmann, Albert: Tauchmedizin; 4. Auflage [2002]; 75 // Buhlmann, Albert: Tauchmedizin; 4. Auflage [2002];
98 // 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 76 // 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
99 // Morrison, Stuart; 2000; DIY DECOMPRESSION; http://www.lizardland.co.uk/DIYDeco.html 77 // Morrison, Stuart; 2000; DIY DECOMPRESSION; http://www.lizardland.co.uk/DIYDeco.html
111 // *********************************************** 89 // ***********************************************
112 // ** V A R I A B L E S D E F I N I T I O N S ** 90 // ** V A R I A B L E S D E F I N I T I O N S **
113 // *********************************************** 91 // ***********************************************
114 92
115 #include "p2_definitions.h" 93 #include "p2_definitions.h"
116 #define TEST_MAIN 94 #define TEST_MAIN
117 #include "shared_definitions.h" 95 #include "shared_definitions.h"
118 96
119 // Water vapour partial pressure in the lungs 97
120 #define ppWater 0.0627 98 // ambient pressure at different mountain heights
121 #define METER_TO_BAR 0.09985 99 #define P_ambient_1000m 0.880 // [bar] based on 990 hPa and 20°C at sea level, 15°C at altitude
122 #define BAR_TO_METER 10.0150 // (1.0/METER_TO_BAR) 100 #define P_ambient_2000m 0.782 // [bar]
123 101 #define P_ambient_3000m 0.695 // [bar]
124 // Surface security factor 102
125 #define SURFACE_DESAT_FACTOR 0.7042 103 // ambient pressure in aircraft cabin during flying - worst case according to Buhlmann
104 #define P_ambient_fly 0.600 // [bar], 0.600 bar is the value used by Buhlmann for his flying-after-diving calculations
105 // 0.735 bar is a typical cabin pressure for nowadays commercial jet aircrafts
106 // -----
107 // 0.135 bar safety margin
108
109 // constants and factors
110 #define ppWater 0.0627 // water vapor partial pressure in the lungs
111 #define METER_TO_BAR 0.09985 // conversion factor
112 #define BAR_TO_METER 10.0150 // conversion factor (1.0/METER_TO_BAR)
113 #define SURFACE_DESAT_FACTOR 0.7042 // surface desaturation safety factor
114 #define HYST 1.0E-06 // threshold for tissue graphics on-gassing / off-gassing visualization
115
116 // thresholds
117 #define GF_warning_threshold 100 // threshold for GF warning (attention threshold is current GF_high)
118 #define CNS_warning_threshold 100 // threshold for CNS warning
119 #define CNS_prewarning_threshold 70 // threshold for CNS attention
120 #define ppO2_prewarn_threshold 120 // threshold for ppO2 attention (master warnings come through options_table.asm)
121 #define GAS_NEEDS_ATTENTION_THRESHOLD 0.70 // threshold for gas needs attention
122
123 // deco engine states and modes - char_O_deco_status
124 #define DECO_STATUS_MASK 0x03
125 #define DECO_STATUS_START 0x00
126 #define DECO_STATUS_FINISHED 0x00
127 #define DECO_STATUS_STOPS 0x01
128 #define DECO_STATUS_ASCENT 0x02
129 #define DECO_STATUS_INIT 0x03
130
131 #define DECO_MODE_MASK 0x0C
132 #define DECO_MODE_LOOP 0x04
133 #define DECO_MODE_CCR 0x04 // to be used with == operator in combination with DECO_MODE_MASK only!
134 #define DECO_MODE_PSCR 0x08
135
136 #define DECO_PLAN_ALTERNATE 0x10
137 #define DECO_CNS_CALCULATE 0x20
138 #define DECO_VOLUME_CALCULATE 0x40
139 #define DECO_ASCENT_DELAYED 0x80
140
141 // deco engine states and modes - char_O_main_status
142 //#define DECO_MODE_MASK 0x0C
143 //#define DECO_MODE_LOOP 0x04
144 //#define DECO_MODE_CCR 0x04 // to be used with == operator in combination with DECO_MODE_MASK only!
145 //#define DECO_MODE_PSCR 0x08
146 #define DECO_GASCHANGE_OVRD 0x10
147 #define DECO_BOTTOM_CALCULATE 0x40
148
149
150 // deco engine warnings
151 #define DECO_WARNING_IBCD 0x01
152 #define DECO_WARNING_IBCD_lock 0x02
153 #define DECO_WARNING_MBUBBLES 0x04
154 #define DECO_WARNING_MBUBBLES_lock 0x08
155 #define DECO_WARNING_OUTSIDE 0x10
156 #define DECO_WARNING_OUTSIDE_lock 0x20
157 #define DECO_WARNING_STOPTABLE_OVERFLOW 0x40
158 #define DECO_FLAG 0x80
159
160 // flags used with integer numbers
161 #define INT_FLAG_INVALID 0x0400
162 #define INT_FLAG_ZERO 0x0800
163 #define INT_FLAG_LOW 0x1000
164 #define INT_FLAG_HIGH 0x2000
165 #define INT_FLAG_PREWARNING 0x4000
166 #define INT_FLAG_WARNING 0x8000
167
168
126 169
127 // ************************* 170 // *************************
128 // ** P R O T O T Y P E S ** 171 // ** P R O T O T Y P E S **
129 // ************************* 172 // *************************
130 173
131 static void calc_hauptroutine(void); 174 static void calc_hauptroutine(void);
132 static void calc_nullzeit(void);
133
134 static void calc_tissue(PARAMETER unsigned char period);
135 static void calc_limit(void);
136
137 static void clear_tissue(void);
138 static void calc_ascenttime(void);
139 static void update_startvalues(void);
140 static void clear_deco_table(void);
141 static unsigned char update_deco_table(void);
142
143 static void sim_tissue(PARAMETER unsigned char period);
144 static void sim_limit(PARAMETER float GF_current);
145 static void sim_extra_time(void);
146 static void calc_dive_interval(void);
147
148 static void calc_gradient_factor(void);
149 static void calc_wo_deco_step_1_min(void);
150
151 static void calc_hauptroutine_data_input(void); 175 static void calc_hauptroutine_data_input(void);
152 static void calc_hauptroutine_update_tissues(void); 176 static void calc_hauptroutine_update_tissues(void);
153 static void calc_hauptroutine_calc_deco(void); 177 static void calc_hauptroutine_calc_deco(void);
178 static void calc_tissue(void);
179 static void calc_limit(void);
180 static void calc_nullzeit(void);
181 static void calc_ascenttime(void);
182 static void calc_dive_interval(void);
183 static void calc_gradient_factor(void);
184 static void calc_wo_deco_step_1_min(void);
185 static void calc_desaturation_time(void);
186
187 static void sim_extra_time(void);
154 static void sim_ascent_to_first_stop(void); 188 static void sim_ascent_to_first_stop(void);
155 189 static void sim_limit(PARAMETER float GF_current);
156 static unsigned char gas_switch_deepest(void); 190
191 static void update_startvalues(void);
157 static void gas_switch_set(void); 192 static void gas_switch_set(void);
158 193 static void compute_CNS_for_display(void);
194
195 static void clear_deco_table(void);
196 static void clear_tissue(void);
197
198 static unsigned char gas_find_better(void);
159 static unsigned char calc_nextdecodepth(void); 199 static unsigned char calc_nextdecodepth(void);
200 static unsigned char update_deco_table(PARAMETER unsigned char time_increment);
201
160 202
161 //---- Bank 5 parameters ----------------------------------------------------- 203 //---- Bank 5 parameters -----------------------------------------------------
162 #ifndef UNIX 204 #ifndef UNIX
163 # pragma udata bank5=0x500 205 # pragma udata bank5=0x500
164 #endif 206 #endif
165 207
166 static float GF_low; 208 // general deco parameters
167 static float GF_high; 209
168 static float GF_delta; 210 static float GF_low; // initialized from deco parameters, constant during all computations
169 static float locked_GF_step; // GF_delta / low_depth 211 static float GF_high; // initialized from deco parameters, constant during all computations
170 212 static float GF_delta; // initialized from deco parameters, constant during all computations
171 static unsigned char temp_depth_limit; 213 static float locked_GF_step_norm; // GF_delta / low_depth_norm in normal plan
172 float low_depth; // Depth of deepest stop 214 static float locked_GF_step_alt; // GF_delta / low_depth_alt in alternative plan
173 215
174 // Simulation context: used to predict ascent. 216 static float low_depth_norm; // Depth of deepest stop in normal plan
175 static unsigned char sim_lead_tissue_no; // Leading compatiment number. 217 static float low_depth_alt; // Depth of deepest stop in alternative plan
176 static float sim_lead_tissue_limit; // Buhlmann tolerated pressure. 218
177 219 static float float_ascent_speed; // ascent speed from options_table (1.0 .. 10.0 m/min)
178 // Real context: what we are doing now. 220 static float float_saturation_multiplier; // safety factor for on-gassing rates
179 static float calc_lead_tissue_limit; // 221 static float float_desaturation_multiplier; // safety factor for off-gassing rates
180 222 static float float_deco_distance; // additional depth below stop depth for tissue, CNS and gas volume calculation
181 static unsigned char internal_deco_time[NUM_STOPS]; 223
182 static unsigned char internal_deco_depth[NUM_STOPS]; 224
183 225 // real context: what we are doing now.
184 static float cns_vault; 226
185 static float low_depth_vault; 227 static float calc_lead_tissue_limit; // minimum tolerated ambient pressure by Buhlmann model
186 static float pres_tissue_N2_vault[NUM_COMP]; 228 static float CNS_fraction; // current CNS (1.00 = 100%)
187 static float pres_tissue_He_vault[NUM_COMP]; 229
230 static unsigned short deco_tissue_vector; // 32 bit vector to memories all tissues that are in decompression
231 static unsigned short IBCD_tissue_vector; // 32 bit vector to memories all tissues that experience IBCD
232
233 // simulation context: used to predict ascent.
234
235 static float sim_lead_tissue_limit; // minimum tolerated ambient pressure by Buhlmann model
236 static float CNS_sim_norm_fraction; // CNS at end of dive in normal plan
237 static float CNS_sim_alt_fraction; // CNS at end of dive in alternative plan
238
239 static unsigned char temp_depth_limit; // depth of next stop in meters, used in deco calculations
240 static unsigned char sim_lead_tissue_no; // Leading compartment number
241 static unsigned char split_N2_He[NUM_COMP]; // used for calculating the desaturation time
242
243
244 // stops table
245
246 static unsigned char internal_deco_depth[NUM_STOPS]; // depth of the stop
247 static unsigned char internal_deco_time[NUM_STOPS]; // duration of the stop
248 static unsigned char internal_deco_gas[NUM_STOPS]; // gas used at the stop
249
250
251 // transfer variables between calc_desaturation_time() and calc_desaturation_time_helper()
252
253 static float desat_factor; // used to cache a pre-computed factor
254 static float var_ht; // buffer for a half-time factor
255 static float pres_target; // target pressure for a compartment
256 static float pres_actual; // current pressure of the compartment
257 static unsigned short short_time; // time it takes for the compartment to reach the target pressure
258
259 // transfer variables between gas_volumes() and gas_volumes_helper()
260 static float float_depth; // depth of the stop or half-way point
261 static float float_time; // duration of the stop or ascent phase
262 static float volume; // computed volume of gas
263 static unsigned char usage; // gas usage in l/min
264
265
266 // 44 byte free space left in this bank
267
188 268
189 //---- Bank 6 parameters ----------------------------------------------------- 269 //---- Bank 6 parameters -----------------------------------------------------
190 #ifndef UNIX 270 #ifndef UNIX
191 # pragma udata bank6=0x600 271 # pragma udata bank6=0x600
192 #endif 272 #endif
193 273
194 static unsigned char ci; 274 // indexing and sequencing
195 static float pres_respiration; 275
196 static float pres_surface; 276 static unsigned char ci; // used as index to the Buhlmann tables
197 static float temp_deco; 277 static unsigned char twosectimer = 0; // used for timing the tissue updating
198 static float ppN2; 278 static unsigned char tissue_increment; // Selector for real/simulated tissues and time increment
199 static float ppHe; 279
200 static float temp_tissue; 280
201 static float N2_ratio; // Breathed gas nitrogen ratio. 281 // environmental and gas data
202 static float He_ratio; // Breathed gas helium ratio. 282
203 static float var_N2_a; // Buhlmann a, for current N2 tissue. 283 static float pres_respiration; // current depth in absolute pressure
204 static float var_N2_b; // Buhlmann b, for current N2 tissue. 284 static float pres_surface; // absolute pressure at the surface
205 static float var_He_a; // Buhlmann a, for current He tissue. 285 static float temp_deco; // simulated current depth in abs.pressure, used for deco calculations
206 static float var_He_b; // Buhlmann b, for current He tissue. 286
207 static float var_N2_e; // Exposition, for current N2 tissue. 287 static unsigned char bottom_depth; // bottom depth in meters, used by CNS and gas needs calculation
208 static float var_He_e; // Exposition, for current He tissue. 288
209 static float var_N2_ht; // Half-time for current N2 tissue. 289 static float O2_ratio; // real breathed gas oxygen ratio
210 static float var_He_ht; // Half-time for current N2 tissue. 290 static float N2_ratio; // real breathed gas nitrogen ratio
211 291 static float He_ratio; // real breathed gas helium ratio
212 static float pres_diluent; // new in v.101 292
213 static float const_ppO2; // new in v.101 293 static float calc_O2_ratio; // simulated breathed gas oxygen ratio
214 294 static float calc_N2_ratio; // simulated breathed gas nitrogen ratio
215 static unsigned char sim_gas_last_depth; // Depth of last used gas, to detected a gas switch. 295 static float calc_He_ratio; // simulated breathed gas helium ratio
216 static unsigned char sim_gas_last_used; // Number of last used gas, to detected a gas switch. 296
217 static unsigned short sim_dive_mins; // Simulated dive time. 297 static float O2_ppO2; // ppO2 - calculated for pure oxygen at current depth
218 static float calc_N2_ratio; // Simulated (switched) nitrogen ratio. 298 static float pSCR_ppO2; // ppO2 - calculated for breathed from pSCR loop
219 static float calc_He_ratio; // Simulated (switched) helium ratio. 299 static float pure_ppO2; // ppO2 - calculated for breathed in OC mode
220 static float CNS_fraction; // new in v.101 300
221 static float float_saturation_multiplier; // new in v.101 301 static unsigned char char_actual_ppO2; // ppO2 - assumed to be breathed, as integer 100 = 1.00 bar
222 static float float_desaturation_multiplier; // new in v.101 302
223 static float float_deco_distance; // new in v.101 303 static float breathed_ppO2; // partial pressure of breathed oxygen
224 304 static float ppN2; // partial pressure of breathed nitrogen
225 static unsigned char deco_gas_change[NUM_GAS]; // new in v.109 305 static float ppHe; // partial pressure of breathed helium
226 static unsigned char internal_deco_gas [NUM_STOPS]; 306
307
308 // Buhlmann model parameters
309
310 static float var_N2_a; // Buhlmann a, for current N2 tissue
311 static float var_N2_b; // Buhlmann b, for current N2 tissue
312 static float var_He_a; // Buhlmann a, for current He tissue
313 static float var_He_b; // Buhlmann b, for current He tissue
314 static float var_N2_e; // exposition, for current N2 tissue
315 static float var_He_e; // exposition, for current He tissue
316 static float var_N2_ht; // half-time for current N2 tissue
317 static float var_He_ht; // half-time for current N2 tissue
318
319
320 // gas switch history
321
322 static unsigned char sim_gas_first_used; // Number of first used gas, for bottom segment
323 static unsigned char sim_gas_last_used; // number of last used gas
324 static unsigned char sim_gas_last_depth; // change depth of last used gas
325
326
327 // vault to back-up & restore tissue data
328
329 static float pres_tissue_N2_vault[NUM_COMP]; // stores the nitrogen tissue pressures
330 static float pres_tissue_He_vault[NUM_COMP]; // stores the helium tissue pressures
331 static float low_depth_norm_vault; // stores a parameter of the GF model for normal plan
332 static float low_depth_alt_vault; // stores a parameter of the GF model for alternative plan
333 static float cns_vault_float; // stores current CNS (float representation)
334
335 static unsigned int cns_vault_int; // stores current CNS (integer representation)
336 static unsigned char deco_warnings_vault; // stores warnings status
337
338
339 // auxiliary variables for local data buffering
340
341 static float N2_equilibrium; // used for N2 tissue graphics scaling
342 static float temp_tissue; // auxiliary variable to buffer tissue pressures
343
344
345 // 6 byte free space left in this bank
346
227 347
228 //---- Bank 7 parameters ----------------------------------------------------- 348 //---- Bank 7 parameters -----------------------------------------------------
229 #ifndef UNIX 349 #ifndef UNIX
230 # pragma udata bank7=0x700 350 # pragma udata bank7=0x700
231 #endif 351 #endif
232 // Keep order of 0x700 variables 352
233 float pres_tissue_N2[NUM_COMP]; 353 // Keep order and position of the variables in bank 7 as they are backed-up to & restored from EEPROM
234 float pres_tissue_He[NUM_COMP]; 354
235 float sim_pres_tissue_N2[NUM_COMP]; // 16 floats = 64 bytes. 355 float pres_tissue_N2[NUM_COMP]; // 16 floats = 64 bytes
236 float sim_pres_tissue_He[NUM_COMP]; // 16 floats = 64 bytes. 356 float pres_tissue_He[NUM_COMP]; // 16 floats = 64 bytes
357 float sim_pres_tissue_N2[NUM_COMP]; // 16 floats = 64 bytes
358 float sim_pres_tissue_He[NUM_COMP]; // 16 floats = 64 bytes
359
237 360
238 //---- Bank 8 parameters ----------------------------------------------------- 361 //---- Bank 8 parameters -----------------------------------------------------
239 #ifndef UNIX 362 #ifndef UNIX
240 # pragma udata overlay bank8=0x800 363 # pragma udata overlay bank8=0x800
241 static char md_pi_subst[256]; 364
242 # define C_STACK md_pi_subst // Overlay C-code data stack here, too. 365 static char md_pi_subst[256]; // Overlay C-code data stack here, too.
366
367 # define C_STACK md_pi_subst
243 #endif 368 #endif
244 369
245 // Back to bank6 for further tmp data 370 // Back to bank6 for further tmp data
246 #ifndef UNIX 371 #ifndef UNIX
247 # pragma udata bank6 372 # pragma udata bank6
254 ////////////////////////////////////////////////////////////////////////////// 379 //////////////////////////////////////////////////////////////////////////////
255 // 380 //
256 // End of PROM code is 17F00, So push tables on PROM top... 381 // End of PROM code is 17F00, So push tables on PROM top...
257 // 382 //
258 #ifndef UNIX 383 #ifndef UNIX
259 # pragma romdata buhlmann_tables = 0x1DD00 // Needs to be in UPPER bank. 384 # pragma romdata Buhlmann_tables = 0x1DD00 // Needs to be in UPPER bank.
260 #endif 385 #endif
261 386
262 rom const float buhlmann_ab[4*16] = { 387 rom const float Buhlmann_ab[4*16] = {
263 // Data ZH-L16C, from Bühlmann Tauchmedizin 2002, option 1a (4mn) 388 // Data ZH-L16C, from Bühlmann Tauchmedizin 2002, option 1a (4mn)
264 // a for N2 b for N2 a of He b for He 389 // a for N2 b for N2 a of He b for He
265 1.2599, 0.5050, 1.7424, 0.4245, 390 1.2599, 0.5050, 1.7424, 0.4245,
266 1.0000, 0.6514, 1.3830, 0.5747, 391 1.0000, 0.6514, 1.3830, 0.5747,
267 0.8618, 0.7222, 1.1919, 0.6527, 392 0.8618, 0.7222, 1.1919, 0.6527,
278 0.2610, 0.9544, 0.5176, 0.9171, 403 0.2610, 0.9544, 0.5176, 0.9171,
279 0.2480, 0.9602, 0.5172, 0.9217, 404 0.2480, 0.9602, 0.5172, 0.9217,
280 0.2327, 0.9653, 0.5119, 0.9267 405 0.2327, 0.9653, 0.5119, 0.9267
281 }; 406 };
282 407
283 rom const float buhlmann_ht[2*16] = { 408 rom const float Buhlmann_ht[2*16] = {
284 // Compartiment half-life, in minute 409 // Compartment half-life, in minute
285 //-- N2 ---- He --------------------------------------------------------------------- 410 //--- N2 ---- He ----------------------
286 4.0, 1.51, 411 4.0, 1.51,
287 8.0, 3.02, 412 8.0, 3.02,
288 12.5, 4.72, 413 12.5, 4.72,
289 18.5, 6.99, 414 18.5, 6.99,
290 27.0, 10.21, 415 27.0, 10.21,
300 498.0, 188.24, 425 498.0, 188.24,
301 635.0, 240.03 426 635.0, 240.03
302 }; 427 };
303 428
304 rom const float e2secs[2*16] = { 429 rom const float e2secs[2*16] = {
305 // result of 1 - 2^(-1/(30sec*HT)) 430 // result of 1 - 2^(-1/(2sec*HT))
306 //---- N2 ------------- He ------------ 431 //---- N2 ------------- He ------------
307 5.75958E-03, 1.51848E-02, 432 5.75958E-03, 1.51848E-02,
308 2.88395E-03, 7.62144E-03, 433 2.88395E-03, 7.62144E-03,
309 1.84669E-03, 4.88315E-03, 434 1.84669E-03, 4.88315E-03,
310 1.24813E-03, 3.29997E-03, 435 1.24813E-03, 3.29997E-03,
426 # endif 551 # endif
427 #endif 552 #endif
428 553
429 ////////////////////////////////////////////////////////////////////////////// 554 //////////////////////////////////////////////////////////////////////////////
430 // Fast subroutine to read timer 5. 555 // Fast subroutine to read timer 5.
431 // Note: result is in 1/32 of msecs (30,51757813 us/bit to be precise) 556 // Note: result is in 1/32 of milliseconds (30,51757813 us/bit to be precise)
432 static unsigned short tmr5(void) 557 static unsigned short tmr5(void)
433 { 558 {
434 #ifndef CROSS_COMPILE 559 #ifndef CROSS_COMPILE
435 _asm 560 _asm
436 movff 0xf7c,PRODL // TMR5L 561 movff 0xf7c,PRODL // TMR5L
439 #else 564 #else
440 return 0; 565 return 0;
441 #endif 566 #endif
442 } 567 }
443 568
444 569 //////////////////////////////////////////////////////////////////////////////
445 ////////////////////////////////////////////////////////////////////////////// 570 // read Buhlmann tables A and B for compartment ci
446 // read buhlmann tables A and B for compatriment ci 571 //
447 // 572 static void read_Buhlmann_coefficients(void)
448 static void read_buhlmann_coefficients(void)
449 { 573 {
450 #ifndef CROSS_COMPILE 574 #ifndef CROSS_COMPILE
451 // Note: we don't use far rom pointer, because the 575 // Note: we don't use far rom pointer, because the
452 // 24 bits is too complex, hence we have to set 576 // 24 bits is too complex, hence we have to set
453 // the UPPER page ourself... 577 // the UPPER page ourself...
461 assert( ci < NUM_COMP ); 585 assert( ci < NUM_COMP );
462 586
463 // Use an interleaved array (AoS) to access coefficients with a 587 // Use an interleaved array (AoS) to access coefficients with a
464 // single addressing. 588 // single addressing.
465 { 589 {
466 overlay rom const float* ptr = &buhlmann_ab[4*ci]; 590 overlay rom const float* ptr = &Buhlmann_ab[4*ci];
467 var_N2_a = *ptr++; 591 var_N2_a = *ptr++;
468 var_N2_b = *ptr++; 592 var_N2_b = *ptr++;
469 var_He_a = *ptr++; 593 var_He_a = *ptr++;
470 var_He_b = *ptr++; 594 var_He_b = *ptr++;
471 } 595 }
472 } 596 }
473 597
474 ////////////////////////////////////////////////////////////////////////////// 598 //////////////////////////////////////////////////////////////////////////////
475 // read buhlmann tables for compatriment ci 599 // read Buhlmann tables for compartment ci
476 // If period == 0 : 2sec interval 600 // If period == 0 : 2sec interval
477 // 1 : 1 min interval 601 // 1 : 1 min interval
478 // 2 : 10 min interval. 602 // 2 : 10 min interval.
479 static void read_buhlmann_times(PARAMETER char period) 603 static void read_Buhlmann_times(PARAMETER char period)
480 { 604 {
481 #ifndef CROSS_COMPILE 605 #ifndef CROSS_COMPILE
482 // Note: we don't use far rom pointer, because the 606 // Note: we don't use far rom pointer, because the
483 // 24 bits is to complex, hence we have to set 607 // 24 bits is to complex, hence we have to set
484 // the UPPER page ourself... 608 // the UPPER page ourself...
522 assert(0); // Never go there... 646 assert(0); // Never go there...
523 } 647 }
524 } 648 }
525 649
526 ////////////////////////////////////////////////////////////////////////////// 650 //////////////////////////////////////////////////////////////////////////////
527 // read buhlmann tables for compatriment ci 651 // read Buhlmann tables for compartment ci
528 // 652 //
529 static void read_buhlmann_ht(void) 653 static void read_Buhlmann_ht(void)
530 { 654 {
531 655
532 #ifndef CROSS_COMPILE 656 #ifndef CROSS_COMPILE
533 // Note: we don't use far rom pointer, because the 657 // Note: we don't use far rom pointer, because the
534 // 24 bits is to complex, hence we have to set 658 // 24 bits is to complex, hence we have to set
540 _endasm 664 _endasm
541 #endif 665 #endif
542 666
543 assert( ci < NUM_COMP ); 667 assert( ci < NUM_COMP );
544 { 668 {
545 overlay rom const float* ptr = &buhlmann_ht[2*ci]; 669 overlay rom const float* ptr = &Buhlmann_ht[2*ci];
546 var_N2_ht = *ptr++; 670 var_N2_ht = *ptr++;
547 var_He_ht = *ptr++; 671 var_He_ht = *ptr++;
548 } 672 }
549 673
550 assert( 4.0 <= var_N2_ht && var_N2_ht <= 635.0 ); 674 assert( 4.0 <= var_N2_ht && var_N2_ht <= 635.0 );
555 // calc_nextdecodepth 679 // calc_nextdecodepth
556 // 680 //
557 // new in v.102 681 // new in v.102
558 // 682 //
559 // INPUT, changing during dive: 683 // INPUT, changing during dive:
560 // temp_deco 684 // temp_deco : current depth in absolute pressure
561 // low_depth
562 // 685 //
563 // INPUT, fixed during dive: 686 // INPUT, fixed during dive:
564 // pres_surface 687 // pres_surface
565 // GF_delta 688 // GF_delta
566 // GF_high 689 // GF_high
567 // GF_low 690 // GF_low
568 // char_I_depth_last_deco 691 // char_I_depth_last_deco
569 // float_deco_distance 692 //
570 // 693 // MODIFIED
571 // RETURN TRUE iff a stop is needed. 694 // locked_GF_step_norm/_alt : used for GF model
695 // low_depth_norm/_alt : used for GF model
572 // 696 //
573 // OUTPUT 697 // OUTPUT
574 // locked_GF_step 698 // temp_depth_limit : depth of next stop in meters (if RETURN == true )
575 // temp_depth_limt 699 // depth we can ascent to without stop (if RETURN == false)
576 // low_depth 700 //
701 // RETURN TRUE if a stop is needed.
577 // 702 //
578 static unsigned char calc_nextdecodepth(void) 703 static unsigned char calc_nextdecodepth(void)
579 { 704 {
580 //--- Max ascent speed --------------------------------------------------- 705 overlay unsigned char need_stop;
581 // Recompute leading gas limit, at current depth: 706
707 // compute current depth in meters
582 overlay float depth = (temp_deco - pres_surface) * BAR_TO_METER; 708 overlay float depth = (temp_deco - pres_surface) * BAR_TO_METER;
583 709
584 // At most, ascent 1 minute, at 10m/min == 10.0 m. 710 // compute depth in meters after 1 minute of ascent at float_ascent_speed (5..10 m/min)
585 overlay float min_depth = (depth > 10.0) ? (depth - 10.0) : 0.0; 711 overlay float min_depth = (depth > float_ascent_speed) ? (depth - float_ascent_speed) : 0.0;
586 712
587 // Do we need to stop at current depth ? 713
588 overlay unsigned char need_stop = 0; 714 // allow for 200mbar of weather dependent surface pressure change
589 715 assert( depth >= -0.2 );
590 assert( depth >= -0.2 ); // Allow for 200mbar of weather change. 716
591 717
592 //---- ZH-L16 + GRADIENT FACTOR model ------------------------------------ 718 //---- check if a stop is needed for deco reasons ----------------------------
719
720 // switch on deco model
593 if( char_I_deco_model != 0 ) 721 if( char_I_deco_model != 0 )
594 { 722 {
723 //---- ZH-L16 + GRADIENT FACTOR Model ------------------------------------
724
725 overlay float locked_GF_step;
726 overlay float low_depth;
727 overlay float pres_gradient;
728
595 overlay unsigned char first_stop = 0; 729 overlay unsigned char first_stop = 0;
596 overlay float p; 730
597 731
732 // calculate minimum depth we can ascent to in absolute pressure
598 sim_limit( GF_low ); 733 sim_limit( GF_low );
599 p = sim_lead_tissue_limit - pres_surface; 734
600 if( p <= 0.0f ) 735 // ...and convert the depth into relative pressure
601 goto no_deco_stop; // We can surface directly... 736 pres_gradient = sim_lead_tissue_limit - pres_surface;
602 737
603 p *= BAR_TO_METER; 738 // check if we can surface directly
604 739 if( pres_gradient <= 0.0 )
740 {
741 min_depth = 0.0; // set minimum depth to 0 meters = surface
742 goto no_deco_stop; // done.
743 }
744
745 // convert minimum depth we can ascent to from relative pressure to depth in meters
746 pres_gradient *= BAR_TO_METER;
747
748 // recall low_depth dependent on current plan
749 low_depth = (char_O_deco_status & DECO_PLAN_ALTERNATE) ? low_depth_alt : low_depth_norm;
750
605 // Store the deepest point needing a deco stop as the LOW reference for GF. 751 // Store the deepest point needing a deco stop as the LOW reference for GF.
606 // NOTE: following stops will be validated using this LOW-HIGH gf scale, 752 // NOTE: following stops will be validated using this LOW-HIGH GF scale,
607 // so if we want to keep coherency, we should not validate this stop 753 // so if we want to keep coherency, we should not validate this stop
608 // yet, but apply the search to it, as for all the following stops afterward. 754 // yet, but apply the search to it, as for all the following stops afterward.
609 if( p > low_depth ) 755 if( pres_gradient > low_depth )
610 { 756 {
611 low_depth = p; 757 // update GF parameters
758 low_depth = pres_gradient;
612 locked_GF_step = GF_delta / low_depth; 759 locked_GF_step = GF_delta / low_depth;
760
761 // store updated GF parameters dependent on current plan
762 if( char_O_deco_status & DECO_PLAN_ALTERNATE )
763 {
764 low_depth_alt = low_depth;
765 locked_GF_step_alt = locked_GF_step;
766 }
767 else
768 {
769 low_depth_norm = low_depth;
770 locked_GF_step_norm = locked_GF_step;
771 }
613 } 772 }
614 773 else
615 if( p < min_depth ) 774 {
616 goto no_deco_stop; // First stop is higher than 1' ascent. 775 // recall locked_GF_step dependent on current plan
617 776 locked_GF_step = (char_O_deco_status & DECO_PLAN_ALTERNATE) ? locked_GF_step_alt : locked_GF_step_norm;
618 // Round to multiple of 3m. 777 }
619 first_stop = 3 * (short)(0.9995f + p*0.333333f); 778
779 // invalidate this stop if we can ascent for 1 minute without going above minimum required deco depth
780 if( pres_gradient < min_depth ) goto no_deco_stop;
781
782
783 // if program execution passes here, we need a deco stop
784
785 // Round to multiple of 3 meters
786 first_stop = 3 * (unsigned char)(0.9995 + pres_gradient * 0.333333);
787
788 // check a constraint
620 assert( first_stop < 128 ); 789 assert( first_stop < 128 );
621 790
622 // Apply correction for the shallowest stop. 791 // apply correction for the shallowest stop, use char_I_depth_last_deco (3..6 m) instead
623 if( first_stop == 3 ) // new in v104 792 if( first_stop == 3 ) first_stop = char_I_depth_last_deco;
624 first_stop = char_I_depth_last_deco; // Use last 3m..6m instead.
625 793
626 // We have a stop candidate. 794 // We have a stop candidate.
627 // But maybe ascending to the next stop will diminish the constraint, 795 // But maybe ascending to the next stop will diminish the constraint,
628 // because the GF might decrease more than the preassure gradient... 796 // because the GF might decrease more than the pressure gradient...
629 while(first_stop > 0) 797 while(first_stop > 0)
630 { 798 {
631 overlay unsigned char next_stop; // Next depth (0..90m) 799 // Next depth
632 800 overlay unsigned char next_stop;
633 // Check max speed, or reaching surface. 801
634 if( first_stop <= min_depth ) 802 // invalidate this stop if we can ascent one more minute without going above minimum required deco depth
635 goto no_deco_stop; 803 if( first_stop <= (unsigned char)min_depth ) goto no_deco_stop;
636 804
637 if( first_stop <= char_I_depth_last_deco ) // new in v104 805 // compute depth of next stop
638 next_stop = 0; 806 if ( first_stop <= char_I_depth_last_deco ) next_stop = 0;
639 else if( first_stop == 6 ) 807 else if ( first_stop == 6 ) next_stop = char_I_depth_last_deco;
640 next_stop = char_I_depth_last_deco; 808 else next_stop = first_stop - 3;
641 else 809
642 next_stop = first_stop - 3; // Index of next (upper) stop. 810 // compute total pressure at the new stop candidate
643 811 pres_gradient = next_stop * METER_TO_BAR + pres_surface;
644 // Total preassure at the new stop candidate: 812
645 p = next_stop * METER_TO_BAR 813 // compute limit for the new stop candidate
646 + pres_surface; 814 if( (low_depth == 0.0) || (next_stop > low_depth) ) sim_limit( GF_low );
647 815 else sim_limit( GF_high - next_stop * locked_GF_step );
648 // Recompute limit for this new stop: 816
649 if( !low_depth || next_stop > low_depth ) 817 // check if ascent to the next stop candidate is possible
650 sim_limit( GF_low ); 818 if( sim_lead_tissue_limit >= pres_gradient ) goto deco_stop_found; // no - ascent to next_stop forbidden
651 else 819
652 sim_limit( GF_high - next_stop * locked_GF_step ); 820 // else, validate that stop and loop...
653
654 // Check upper limit (lowest ambiant pressure tolerated):
655 if( sim_lead_tissue_limit >= p )
656 goto deco_stop_found; // Ascent to next_stop forbiden.
657
658 // Else, validate that stop and loop...
659 first_stop = next_stop; 821 first_stop = next_stop;
660 } 822 }
661 823
662 no_deco_stop: 824 no_deco_stop:
663 temp_depth_limit = min_depth; 825 need_stop = 0; // set flag for stop needed to 'no'
664 goto done; 826 temp_depth_limit = (unsigned char)min_depth; // report depth we can ascent to without stop
827 goto done;
665 828
666 deco_stop_found: 829 deco_stop_found:
667 // next stop is the last validated depth found, aka first_stop 830 need_stop = 1; // set flag for stop needed to 'yes'
668 need_stop = 1; // Hit. 831 temp_depth_limit = (unsigned char)first_stop; // stop depth, in meters
669 temp_depth_limit = first_stop; // Stop depth, in meter.
670 832
671 done: 833 done:
672 ; 834 ;
673 } 835 }
674 else //---- ZH-L16 model ------------------------------------------------- 836 else
675 { 837 {
838 //---- ZH-L16 model -------------------------------------------------
839
676 overlay float pres_gradient; 840 overlay float pres_gradient;
677 841
678 // Original model 842
679 // optimized in v.101 843 // calculate minimum depth we can ascent to in absolute pressure
680 // char_I_depth_last_deco included in v.101
681
682 // Compute sim_lead_tissue_limit too, but just once.
683 sim_limit(1.0); 844 sim_limit(1.0);
684 845
846 // ...and convert the depth into relative pressure
685 pres_gradient = sim_lead_tissue_limit - pres_surface; 847 pres_gradient = sim_lead_tissue_limit - pres_surface;
848
849 // check if we can surface directly
686 if (pres_gradient >= 0) 850 if (pres_gradient >= 0)
687 { 851 {
688 pres_gradient *= BAR_TO_METER/3; // bar --> stop number; 852 // no - set flag for stop needed to 'yes'
689 temp_depth_limit = 3 * (short) (pres_gradient + 0.99); // --> metre : depth for deco 853 need_stop = 1;
690 need_stop = 1; // Hit. 854
691 855 // convert stop depth in relative pressure to stop index
692 // Implement last stop at 4m/5m/6m... 856 pres_gradient *= BAR_TO_METER / 3;
693 if( temp_depth_limit == 3 ) 857
694 temp_depth_limit = char_I_depth_last_deco; 858 // convert stop index to depth in meters, rounded to multiple of 3 meters
859 temp_depth_limit = 3 * (short) (pres_gradient + 0.99);
860
861 // correct last stop to 4m/5m/6m
862 if( temp_depth_limit == 3 ) temp_depth_limit = char_I_depth_last_deco;
695 } 863 }
696 else 864 else
865 {
866 // yes - set flag for stop needed to 'no'
867 need_stop = 0;
868
869 // set depth we can ascent to as 0 = surface
697 temp_depth_limit = 0; 870 temp_depth_limit = 0;
871 }
698 } 872 }
699 873
700 //---- Check gas change -------------------------------------------------- 874
701 need_stop |= gas_switch_deepest(); // Update temp_depth_limit if there is a change, 875 // After the first deco stop, gas changes are only done at deco stops now!
876
877 // check if a stop is found and there is a better gas to switch to
878 if( need_stop && gas_find_better() )
879 {
880 // set the new calculation ratios for N2, He and O2
881 gas_switch_set();
882
883 // prime the deco stop with the gas change time
884 update_deco_table(char_I_gas_change_time);
885 }
702 886
703 return need_stop; 887 return need_stop;
704 } 888 }
705 889
706 ////////////////////////////////////////////////////////////////////////////// 890 //////////////////////////////////////////////////////////////////////////////
741 } 925 }
742 926
743 //---- Third: fill table end with null 927 //---- Third: fill table end with null
744 for(y++; y<NUM_STOPS; y++) 928 for(y++; y<NUM_STOPS; y++)
745 { 929 {
746 char_O_deco_time_for_log [y] = 0; 930 char_O_deco_time_for_log[y] = 0;
747 } 931 }
748 } 932 }
749 } 933 }
750 934
751 ////////////////////////////////////////////////////////////////////////////// 935 //////////////////////////////////////////////////////////////////////////////
752 // temp_tissue_safety // 936 // temp_tissue_safety
753 // 937 //
754 // outsourced in v.102 938 // outsourced in v.102
755 // 939 //
756 // Apply safety factors for both ZH-L16 models. 940 // Apply safety factors for both ZH-L16 models.
757 // 941 //
758 static void temp_tissue_safety(void) 942 static void temp_tissue_safety(void)
759 { 943 {
760 assert( 0.0 < float_desaturation_multiplier && float_desaturation_multiplier <= 1.0 ); 944 assert( 0.0 < float_desaturation_multiplier && float_desaturation_multiplier <= 1.0 );
761 assert( 1.0 <= float_saturation_multiplier && float_saturation_multiplier <= 2.0 ); 945 assert( 1.0 <= float_saturation_multiplier && float_saturation_multiplier <= 2.0 );
762 946
763 if( temp_tissue < 0.0 ) 947 if( temp_tissue < 0.0 ) temp_tissue *= float_desaturation_multiplier;
764 temp_tissue *= float_desaturation_multiplier; 948 else temp_tissue *= float_saturation_multiplier;
765 else
766 temp_tissue *= float_saturation_multiplier;
767 } 949 }
768 950
769 ////////////////////////////////////////////////////////////////////////////// 951 //////////////////////////////////////////////////////////////////////////////
770 ////////////////////////////////////////////////////////////////////////////// 952 //////////////////////////////////////////////////////////////////////////////
771 // ** THE JUMP-IN CODE ** 953 // ** THE JUMP-IN CODE **
772 // ** for the asm code ** 954 // ** for the asm code **
773 ////////////////////////////////////////////////////////////////////////////// 955 //////////////////////////////////////////////////////////////////////////////
774 ////////////////////////////////////////////////////////////////////////////// 956 //////////////////////////////////////////////////////////////////////////////
775 957
776 ////////////////////////////////////////////////////////////////////////////// 958 //////////////////////////////////////////////////////////////////////////////
777 // Called every 2 seconds during diving. 959 // Called every second during diving.
778 // update tissues every time. 960 // updates tissues every second invocation.
779 // 961 //
780 // Every 6 seconds (or slower when TTS > 16): 962 // Every few seconds (or slower when TTS > 16):
781 // - update deco table (char_O_deco_time/depth) with new values. 963 // - updates deco table (char_O_deco_time/depth) with new values.
782 // - update ascent time, 964 // - updates ascent time,
783 // - set status to zero (so we can check there is new results). 965 // - sets status to zero (so we can check there is new results).
784 // 966 //
785 void deco_calc_hauptroutine(void) 967 void deco_calc_hauptroutine(void)
786 { 968 {
787 RESET_C_STACK 969 RESET_C_STACK
788 calc_hauptroutine(); 970 calc_hauptroutine();
789 int_O_desaturation_time = 65535;
790 } 971 }
791 972
792 ////////////////////////////////////////////////////////////////////////////// 973 //////////////////////////////////////////////////////////////////////////////
793 // Reset decompression model: 974 // Reset decompression model:
794 // + Set all tissues to equilibrium with Air at ambient pressure. 975 // + Set all tissues to equilibrium with Air at ambient pressure.
799 RESET_C_STACK 980 RESET_C_STACK
800 clear_tissue(); 981 clear_tissue();
801 } 982 }
802 983
803 ////////////////////////////////////////////////////////////////////////////// 984 //////////////////////////////////////////////////////////////////////////////
804 // Called every 1 min during decoplanning.
805 // Update tissues for 1 min.
806 //
807 void deco_calc_tissue(void)
808 {
809 RESET_C_STACK
810 calc_hauptroutine_update_tissues();
811 }
812
813 //////////////////////////////////////////////////////////////////////////////
814 985
815 void deco_calc_wo_deco_step_1_min(void) 986 void deco_calc_wo_deco_step_1_min(void)
816 { 987 {
817 RESET_C_STACK 988 RESET_C_STACK
818 calc_wo_deco_step_1_min(); 989 calc_wo_deco_step_1_min();
819 deco_calc_desaturation_time(); 990 }
991
992 //////////////////////////////////////////////////////////////////////////////
993
994 void deco_calc_desaturation_time(void)
995 {
996 RESET_C_STACK
997 calc_desaturation_time();
820 } 998 }
821 999
822 ////////////////////////////////////////////////////////////////////////////// 1000 //////////////////////////////////////////////////////////////////////////////
823 1001
824 void deco_calc_dive_interval(void) 1002 void deco_calc_dive_interval(void)
826 RESET_C_STACK 1004 RESET_C_STACK
827 calc_dive_interval(); 1005 calc_dive_interval();
828 } 1006 }
829 1007
830 ////////////////////////////////////////////////////////////////////////////// 1008 //////////////////////////////////////////////////////////////////////////////
831 // Find current gas in the list (if any). 1009 // deco_calc_CNS_decrease_15min
832 // 1010 //
833 // Input: char_I_current_gas = 1..6 1011 // new in v.101
834 // 1012 //
835 // Output: sim_gas_last_depth = 0..5, temp_depth_limit. 1013 // calculates the half time of 90 minutes in 6 steps of 15 min
836 // 1014 // (Used in sleep mode, for low battery mode).
837 static void gas_switch_find_current(void) 1015 //
838 { 1016 // Output: int_O_CNS_fraction
839 assert( 0 < char_I_current_gas && char_I_current_gas <= (2*NUM_GAS) ); 1017 // Uses and Updates: CNS_fraction
840 1018 //
841 if( char_I_current_gas <= NUM_GAS ) // Gas1..Gas5 1019 void deco_calc_CNS_decrease_15min(void)
1020 {
1021 RESET_C_STACK
1022
1023 // clock down CNS
1024 CNS_fraction = 0.890899 * CNS_fraction;
1025
1026 // compute integer copy of CNS value
1027 compute_CNS_for_display();
1028 }
1029
1030
1031 //////////////////////////////////////////////////////////////////////////////
1032 // Find current gas in the list (if any) and get its change depth
1033 //
1034 // Input: char_I_current_gas : 1..5 or 6
1035 //
1036 // Output: sim_gas_last_used : 1..6 or 0 if it is the gas set as FIRST
1037 // sim_gas_last_depth : change depth in meters or 0 if it is the gas set as FIRST
1038 //
1039 static void gas_find_current(void)
1040 {
1041 assert( 1 <= char_I_current_gas && char_I_current_gas <= 6 );
1042
1043 if( char_I_current_gas <= NUM_GAS ) // Gas 1-5
842 { 1044 {
843 sim_gas_last_used = char_I_current_gas; 1045 sim_gas_last_used = sim_gas_first_used = char_I_current_gas;
844 1046
845 // Note: if current is first gas, we must find it, but not set 1047 // If current gas is a deco gas get it's change depth.
846 // last depth change to surface. 1048 // Set change depth to 0 if the current gas is the first gas or
847 if( char_I_deco_gas_change[sim_gas_last_used-1] ) 1049 // a travel/normal gas, i.e. if it can be breathed at "any" depth.
848 sim_gas_last_depth = char_I_deco_gas_change[sim_gas_last_used-1]; 1050 if( char_I_deco_gas_change[sim_gas_last_used-1] ) sim_gas_last_depth = char_I_deco_gas_change[sim_gas_last_used-1];
1051 else sim_gas_last_depth = 0;
849 } 1052 }
850 else 1053 else
851 sim_gas_last_used = 0; // Gas 6 = manual set 1054 {
852 } 1055 sim_gas_last_used = sim_gas_first_used = 0; // Gas 6 (the manually set one) has number 0 here
853 1056 sim_gas_last_depth = 0; // handle it as a travel/normal gas
854 ////////////////////////////////////////////////////////////////////////////// 1057 }
855 // Find deepest available gas. 1058 }
856 // 1059
857 // Input: temp_depth_limit, 1060
858 // deco_gas_change[] 1061 //////////////////////////////////////////////////////////////////////////////
859 // sim_gas_depth_used, sim_dive_mins. 1062 // Find the deco gas with the shallowest change depth beyond current depth
860 // 1063 //
861 // RETURNS TRUE if a stop is needed for gas switch. 1064 // INPUT temp_depth_limit : current depth in meters
862 // 1065 // char_I_deco_gas_change[] : change depths of the deco gases
863 // Output: temp_depth_limit, sim_gas_depth_used IFF the is a switch. 1066 // sim_gas_last_depth : change depth of the currently used gas, 0 if on the gas set as FIRST
864 // 1067 //
865 // NOTE: might be called from bottom (when sim_gas_delay and sim_gas_depth_used 1068 // OUTPUT sim_gas_last_depth : switch depth - only if return value is true
866 // are null), or during the ascent to make sure we are not passing a 1069 // sim_gas_last_used : index of the gas (1..5) - only if return value is true
867 // stop (in which case both can be already set). 1070 //
868 // 1071 // RETURNS TRUE if a better gas is available
869 static unsigned char gas_switch_deepest(void) 1072 //
870 { 1073 static unsigned char gas_find_better(void)
871 overlay unsigned char switch_deco = 0, switch_last = 0; 1074 {
872 1075 overlay unsigned char switch_depth = 255;
873 if (char_I_const_ppO2 == 0) 1076 overlay unsigned char switch_gas = 0;
1077 overlay unsigned char j;
1078
1079
1080 // no automatic gas changes in CCR mode and - as of now - in pSCR mode
1081 if( char_O_deco_status & DECO_MODE_LOOP ) return 0;
1082
1083 // Loop over all deco gases to find the shallowest one below or at current depth.
1084 for(j=0; j<NUM_GAS; ++j)
1085 {
1086 // Is this the gas we are already breathing?
1087 // If yes, skip this gas.
1088 if( j+1 == sim_gas_last_used ) continue;
1089
1090 // Is the change depth of the gas shallower than the current depth?
1091 // If yes, skip this gas as it is not to be used yet.
1092 // Remark: this check will also skip all disabled gases and the gas set
1093 // as 'first' because these have their change depth set to 0.
1094 if( temp_depth_limit > char_I_deco_gas_change[j] ) continue;
1095
1096 // Is the change depth of the gas deeper than the change depth of the
1097 // gas we are currently one?
1098 // If yes, skip this gas as it is not better than the current one.
1099 // Remark: if there is more than one gas with the same change depth,
1100 // the last one from the list will be taken.
1101 if( sim_gas_last_depth && (char_I_deco_gas_change[j] > sim_gas_last_depth) ) continue;
1102
1103 // Is the change depth of the gas shallower or equal to the change depth
1104 // of the best gas found so far, or is it the first better gas found?
1105 // If yes, we have a better gas
1106 if( char_I_deco_gas_change[j] <= switch_depth )
1107 {
1108 switch_gas = j+1; // remember this gas (1..5)
1109 switch_depth = char_I_deco_gas_change[j]; // remember its change depth
1110 }
1111 } // continue looping through all gases to eventually find an even better gas
1112
1113 // has a better gas been found?
1114 if( switch_gas )
1115 {
1116 // yes
1117 sim_gas_last_used = switch_gas; // report the index of the better
1118 sim_gas_last_depth = switch_depth; // report its change depth
1119
1120 assert( sim_gas_last_depth < switch_depth );
1121
1122 return 1; // signal a better gas was found
1123 }
1124 else
1125 {
1126 return 0; // signal no better gas was found
1127 }
1128 }
1129
1130 //////////////////////////////////////////////////////////////////////////////
1131 // Set calc_N2/He/O2_ratios by sim_gas_last_used
1132 //
1133 // Input: sim_gas_last_used : index of gas to use
1134 // N2_ratio, He_ratio : if gas 0 = the manually set gas is in use
1135 //
1136 // Output: calc_N2_ratio, calc_He_ratio, calc_O2ratio
1137 //
1138 static void gas_switch_set(void)
1139 {
1140 assert( 0 <= sim_gas_last_used <= NUM_GAS );
1141
1142 if( sim_gas_last_used == 0 ) // Gas6 = manually set gas.
1143 {
1144 calc_O2_ratio = O2_ratio;
1145 calc_He_ratio = He_ratio;
1146 }
1147 else
1148 {
1149 calc_O2_ratio = char_I_deco_O2_ratio[sim_gas_last_used-1] * 0.01;
1150 calc_He_ratio = char_I_deco_He_ratio[sim_gas_last_used-1] * 0.01;
1151 }
1152
1153 calc_N2_ratio = 1.0 - calc_O2_ratio - calc_He_ratio;
1154
1155 assert( 0.0 <= calc_N2_ratio && calc_N2_ratio <= 0.95 );
1156 assert( 0.0 <= calc_He_ratio && calc_He_ratio <= 1.00 );
1157 assert( (calc_N2_ratio + calc_He_ratio) <= 1.00 );
1158 }
1159
1160 //////////////////////////////////////////////////////////////////////////////
1161 // Compute ppN2 and ppHe
1162 //
1163 // Input: calc_N2_ratio, calc_He_ratio : simulated gas mix.
1164 // temp_deco : simulated respiration pressure
1165 // float_deco_distance : safety factor
1166 // ppWater : water-vapor pressure inside respiratory tract
1167 //
1168 // Output: ppN2, ppHe.
1169 //
1170 static void sim_alveolar_presures(void)
1171 {
1172 overlay float deco_diluent = temp_deco;
1173
1174 // read ppO2 reported from sensors or by setpoint // TODO: can be deleted
1175 // char_actual_ppO2 = char_I_const_ppO2;
1176
1177
1178 // Take deco offset into account, but not at surface.
1179 // Note: this should be done on ambient pressure, hence before
1180 // computing the diluent partial pressure...
1181 if( deco_diluent > pres_surface ) deco_diluent += float_deco_distance;
1182
1183 if( char_O_deco_status & DECO_MODE_LOOP )
874 { 1184 {
875 overlay unsigned char j; 1185 //---- Loop mode : adjust ppN2 and ppHe for change in ppO2 due to setpoint (CCR) or drop (pSCR)-------
876 1186
877 // Loop over all enabled gas, to find the deepest one, 1187 // get current setpoint (CCR) or sensor value (CCR, for pSCR see text below) as default
878 // above last used gas, but below temp_depth_limit. 1188 overlay float const_ppO2 = char_I_const_ppO2 * 0.01;
879 for(j=0; j<NUM_GAS; ++j) 1189
880 { 1190 if( char_O_deco_status & DECO_MODE_PSCR )
881 // Gas not (yet) allowed ? Skip ! 1191 {
882 if( temp_depth_limit > deco_gas_change[j] ) 1192 //---- PSCR mode : compute loop gas ----------------------------------------
883 continue; 1193 //
884 1194 // As the ppO2 in the loop changes with water depth, we can not use the current
885 // Gas deeper (or equal) than the current one ? Skip ! 1195 // sensor value as with CCR mode, but need to compute the ppO2 for the given depth.
886 if( sim_gas_last_depth && deco_gas_change[j] >= sim_gas_last_depth ) 1196 // Then we continue with the CCR mode code which calculates the increases of ppN2
887 continue; 1197 // and ppH2 due to the reduction of the ppO2 in the loop. Essentially, diving a
888 1198 // PSCR is like diving a CCR with a setpoint lower than the ambient pressure x the
889 // First, or deeper ? 1199 // O2 fraction of the diluent would yield...
890 if( switch_deco < deco_gas_change[j] ) 1200 //
891 { 1201
892 switch_deco = deco_gas_change[j]; 1202 // deco_diluent is 0.0 ... in bar
893 switch_last = j+1; // 1..5 1203 // calc_O2_ratio is 0.0 ... 1 as factor
894 } 1204 // char_I_PSCR_drop is 0 ... 15 as %
895 } 1205 // char_I_PSCR_lungratio is 5 ... 20 as %
1206 // const_ppO2 is 0.0 ... in bar
1207
1208 const_ppO2 = (deco_diluent * calc_O2_ratio) - (1 - calc_O2_ratio) * 0.01 * char_I_PSCR_drop * char_I_PSCR_lungratio;
1209
1210 // capture failure condition
1211 if( const_ppO2 < 0.0 ) const_ppO2 = 0.0;
1212 }
1213 else
1214 {
1215
1216 //---- CCR mode ------------------------------------------------------------
1217
1218 // Limit the setpoint to the maximum physically possible ppO2. This prevents for
1219 // example calculating with a setpoint of 1.3 bar in only 2 meters of depth.
1220 // Additionally, if limiting occurs, the ppO2 can be further reduced to account
1221 // for residual inert gases by the user-adjustable setting char_I_cc_max_frac_o2.
1222
1223 if( const_ppO2 > deco_diluent ) // no ppWater subtracted here to give some margin for
1224 { // sensors delivering data a little bit over target
1225
1226 const_ppO2 = 0.01 * char_I_cc_max_frac_o2 * (deco_diluent - ppWater);
1227 }
1228 }
1229
1230 if ( const_ppO2 == 0.0 ) char_actual_ppO2 = 0;
1231 else if ( const_ppO2 > 2.545 ) char_actual_ppO2 = 255;
1232 else char_actual_ppO2 = (unsigned char)(const_ppO2*100 + 0.5);
1233
1234 // Note: ppO2 and ratios are known outside the lungs, so there is no ppWater in the equations below:
1235 deco_diluent -= const_ppO2;
1236 deco_diluent /= calc_N2_ratio + calc_He_ratio;
1237
1238 // capture all failure conditions, including div/0 in case diluent is pure O2
1239 if( (deco_diluent < 0.0) || (calc_O2_ratio > 99.5) )
1240 {
1241 deco_diluent = 0.0;
1242
1243 char_actual_ppO2 = (unsigned char)(temp_deco*100 + 0.5); // without float_deco_distance here as this situation
1244 // is likely to occur only at 6 meters or shallower
1245 }
896 } 1246 }
897 1247 else
898 // If there is a better gas available 1248 {
899 if( switch_deco ) 1249 //---- OC mode: char_actual_ppO2 will be needed for CNS calculation later --------------------------------
900 { 1250
901 assert( !sim_gas_last_depth || sim_gas_last_depth > switch_deco ); 1251 overlay float ppO2 = pres_respiration * calc_O2_ratio;
902 1252
903 sim_gas_last_depth = switch_deco; 1253 if ( ppO2 > 2.545 ) char_actual_ppO2 = 255;
904 sim_gas_last_used = switch_last; 1254 else char_actual_ppO2 = (unsigned char)(ppO2*100 + 0.5);
905 } 1255 }
906 return 0; 1256
907 } 1257
908
909 //////////////////////////////////////////////////////////////////////////////
910 // Calculate gas switches
911 //
912 //
913 // Input: N2_ratio, He_ratio.
914 // sim_gas_last_used
915 //
916 // Output: calc_N2_ratio, calc_He_ratio
917 //
918 static void gas_switch_set(void)
919 {
920 assert( sim_gas_last_used <= NUM_GAS );
921
922 if( sim_gas_last_used == 0 ) // Gas6 = manualy set gas.
923 {
924 calc_N2_ratio = N2_ratio;
925 calc_He_ratio = He_ratio;
926 }
927 else
928 {
929 calc_N2_ratio = char_I_deco_N2_ratio[sim_gas_last_used-1] * 0.01;
930 calc_He_ratio = char_I_deco_He_ratio[sim_gas_last_used-1] * 0.01;
931 }
932
933 assert( 0.0 <= calc_N2_ratio && calc_N2_ratio <= 0.95 );
934 assert( 0.0 <= calc_He_ratio && calc_He_ratio <= 1.00 );
935 assert( (calc_N2_ratio + calc_He_ratio) <= 1.00 );
936 }
937
938 //////////////////////////////////////////////////////////////////////////////
939 //
940 // Input: calc_N2_ratio, calc_He_ratio : simulated gas mix.
941 // temp_deco : simulated respiration pressure
942 // float_deco_distance : security factor.
943 // Water-vapor pressure inside limbs (ppWater).
944 //
945 // Output: ppN2, ppHe.
946 //
947 static void sim_alveolar_presures(void)
948 {
949 overlay float deco_diluent = temp_deco; // new in v.101
950
951 // Take deco offset into account, but not at surface.
952 // Note: this should be done on ambiant pressure, hence before
953 // computing the diluant partial pressure...
954 if( deco_diluent > pres_surface )
955 deco_diluent += float_deco_distance;
956
957 //---- CCR mode : deco gas switch ? --------------------------------------
958 if( char_I_const_ppO2 != 0 )
959 {
960 // In CCR mode, use calc_XX_ratio instead of XX_ratio.
961 // Note: PPO2 and ratios are known outside the lumbs, so there is no
962 // ppWater in the equations below:
963 deco_diluent -= const_ppO2;
964 deco_diluent /= calc_N2_ratio + calc_He_ratio;// potential DIV/0 issue when O2 is used as diluent!
965
966 if(calc_N2_ratio==0&calc_He_ratio==0) deco_diluent = 0.0; // workaround for potential DIV/0 issue
967 }
968
969 if( deco_diluent > ppWater ) 1258 if( deco_diluent > ppWater )
970 { 1259 {
971 ppN2 = calc_N2_ratio * (deco_diluent - ppWater); 1260 ppN2 = calc_N2_ratio * (deco_diluent - ppWater);
972 ppHe = calc_He_ratio * (deco_diluent - ppWater); 1261 ppHe = calc_He_ratio * (deco_diluent - ppWater);
973 } 1262 }
974 else 1263 else
975 { 1264 {
976 ppN2 = 0.0; 1265 ppN2 = 0.0;
977 ppHe = 0.0; 1266 ppHe = 0.0;
978 } 1267 }
1268
979 assert( 0.0 <= ppN2 && ppN2 < 14.0 ); 1269 assert( 0.0 <= ppN2 && ppN2 < 14.0 );
980 assert( 0.0 <= ppHe && ppHe < 14.0 ); 1270 assert( 0.0 <= ppHe && ppHe < 14.0 );
981 } 1271 }
982 1272
983 ////////////////////////////////////////////////////////////////////////////// 1273 //////////////////////////////////////////////////////////////////////////////
988 // preload tissues with standard pressure for the given ambient pressure. 1278 // preload tissues with standard pressure for the given ambient pressure.
989 // Note: fixed N2_ratio for standard air. 1279 // Note: fixed N2_ratio for standard air.
990 // 1280 //
991 static void clear_tissue(void) 1281 static void clear_tissue(void)
992 { 1282 {
993 overlay float p; 1283 pres_respiration = 0.001 * int_I_pres_respiration;
994 1284 N2_equilibrium = 0.7902 * (pres_respiration - ppWater);
995 // Kludge: the 0.0002 of 0.7902 are missing with standard air. 1285
996 N2_ratio = 0.7902;
997 pres_respiration = int_I_pres_respiration * 0.001;
998
999 p = N2_ratio * (pres_respiration - ppWater);
1000 for(ci=0; ci<NUM_COMP; ci++) 1286 for(ci=0; ci<NUM_COMP; ci++)
1001 { 1287 {
1002 // cycle through the 16 Buhlmann N2 tissues 1288 // cycle through the 16 Buhlmann N2 tissues
1003 pres_tissue_N2[ci] = p; 1289 pres_tissue_N2[ci] = N2_equilibrium; // initialize data for "real" tissue
1004 1290 char_O_tissue_N2_saturation[ci] = 11; // initialize data for tissue graphics
1005 // cycle through the 16 Buhlmann tissues for Helium 1291
1006 pres_tissue_He[ci] = 0.0; 1292
1293 // cycle through the 16 Buhlmann He tissues
1294 pres_tissue_He[ci] = 0.0; // initialize data for "real" tissue
1295 char_O_tissue_He_saturation[ci] = 0; // initialize data for tissue graphics
1007 } 1296 }
1008 1297
1298 clear_CNS_fraction();
1299
1009 clear_deco_table(); 1300 clear_deco_table();
1010 char_O_deco_status = 0; 1301
1011 char_O_nullzeit = 0; 1302 char_O_main_status = 0;
1012 int_O_ascenttime = 0; 1303 char_O_deco_status = 0;
1013 char_O_gradient_factor = 0; 1304 char_O_nullzeit = 0;
1014 1305 char_O_gtissue_no = 0;
1015 calc_lead_tissue_limit = 0.0; 1306 char_O_deco_warnings = 0;
1016 char_O_gtissue_no = 0; 1307
1308 int_O_ascenttime = 0;
1309 int_O_gradient_factor = 0;
1310
1311 calc_lead_tissue_limit = 0.0;
1017 } 1312 }
1018 1313
1019 ////////////////////////////////////////////////////////////////////////////// 1314 //////////////////////////////////////////////////////////////////////////////
1020 // calc_hauptroutine 1315 // calc_hauptroutine
1021 // 1316 //
1022 // this is the major code in dive mode calculates: 1317 // this is the major code in dive mode calculates:
1023 // the tissues, 1318 // the tissues,
1024 // the bottom time, 1319 // the bottom time,
1025 // and simulates the ascend with all deco stops. 1320 // and simulates the ascend with all deco stops.
1026 // 1321 //
1027 // The deco_state sequence is :
1028 // 3 (at surface)
1029 // +---> 0 : calc nullzeit
1030 // | 2 : simulate ascent to first stop (at 10m/min, less that 16x 1min simu)
1031 // | +-> 1 : simulate up to 16min of stops.
1032 // | +------< not finished
1033 // +--------< finish
1034 //
1035 // Added steps 6,5 for @+5 calculation:
1036 // 6 = ascent to first stop (same as 2), except continue to 7
1037 // 7 = same as 1, except loop to 7.
1038 // 1322 //
1039 static void calc_hauptroutine(void) 1323 static void calc_hauptroutine(void)
1040 { 1324 {
1041 static unsigned char backup_gas_used = 0; 1325 unsigned int int_ppO2_min;
1042 static unsigned char backup_gas_depth = 0; 1326 unsigned int int_ppO2_max;
1043 1327
1044 calc_hauptroutine_data_input(); 1328
1045 1329 //--- set-up part --------------------------------------------------------------------------------
1046 calc_hauptroutine_update_tissues(); 1330
1047 calc_gradient_factor(); 1331 // twosectimer:
1048 1332 // calc_hauptroutine is now invoked every second to speed up the deco planning.
1049 // toggle between calculation for nullzeit (bottom time), 1333 // Because the tissue and CNS calculations are based on a 2 seconds period, the
1050 // deco stops 1334 // the following toggle-timer will be used by the respective routines to skip
1051 // and more deco stops (continue) 1335 // every 2nd invocation.
1052 switch( char_O_deco_status ) 1336 twosectimer = (twosectimer) ? 0 : 1; // toggle the toggle-timer
1337
1338
1339 // set up normal tissue updating or "fast forward" updating for simulator sim+5' function
1340 // and deco calculator bottom time calculation
1341 if( char_I_sim_advance_time > 0 )
1342 {
1343 // configure char_I_sim_advance_time minutes of tissue updating
1344 tissue_increment = char_I_sim_advance_time // given number of minutes, limited to 127
1345 | 128; // set flag for updating the "real" tissues & CNS
1346
1347 char_I_sim_advance_time = 0; // clear "mailbox"
1348 }
1349 else
1350 {
1351 // configure 2 seconds of tissue updating
1352 tissue_increment = 0 // encoding for 2 seconds update
1353 | 128; // set flag for updating the "real" tissues & CNS
1354 }
1355
1356 //---- calculate the real tissue's data -----------------------------------------------------------------
1357
1358 calc_hauptroutine_data_input(); // acquire current environment data
1359
1360 calc_hauptroutine_update_tissues(); // update tissue pressures, also sets char_actual_ppO2
1361
1362 calc_CNS_fraction(); // calculate CNS% for the real tissues
1363
1364 compute_CNS_for_display(); // compute integer copy of CNS value for display purpose
1365
1366 calc_gradient_factor(); // compute current GF
1367
1368
1369 //---- compute ppO2 warnings ------------------------------------------------------------------------------
1370
1371 // compute conditional min/max values
1372 int_ppO2_min = (char_O_main_status & DECO_MODE_LOOP) ? (unsigned int)char_I_ppO2_min_loop : (unsigned int)char_I_ppO2_min;
1373 int_ppO2_max = (char_O_deco_warnings & DECO_FLAG ) ? (unsigned int)char_I_ppO2_max_deco : (unsigned int)char_I_ppO2_max;
1374
1375 // check for safe range of pure oxygen
1376 if ( int_O_O2_ppO2 >= int_ppO2_max ) int_O_O2_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH;
1377
1378 // check for safe range of breathed gas
1379 if ( int_O_breathed_ppO2 <= int_ppO2_min ) int_O_breathed_ppO2 |= INT_FLAG_WARNING + INT_FLAG_LOW;
1380 else if ( int_O_breathed_ppO2 >= int_ppO2_max ) int_O_breathed_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH;
1381 else if ( int_O_breathed_ppO2 >= ppO2_prewarn_threshold ) int_O_breathed_ppO2 |= INT_FLAG_PREWARNING;
1382
1383 // check for safe range of pure diluent
1384 if ( int_O_pure_ppO2 <= (unsigned int)char_I_ppO2_min ) int_O_pure_ppO2 |= INT_FLAG_WARNING + INT_FLAG_LOW;
1385 else if ( int_O_pure_ppO2 >= int_ppO2_max ) int_O_pure_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH;
1386
1387 // check for safe range of calculated pSCR loop gas
1388 if ( int_O_pSCR_ppO2 <= int_ppO2_min ) int_O_pSCR_ppO2 |= INT_FLAG_WARNING + INT_FLAG_LOW;
1389 else if ( int_O_pSCR_ppO2 >= int_ppO2_max ) int_O_pSCR_ppO2 |= INT_FLAG_WARNING + INT_FLAG_HIGH;
1390
1391
1392 //---- toggle between calculation for NDL (bottom time), deco stops and more deco stops (continue) ------
1393
1394 switch( char_O_deco_status & DECO_STATUS_MASK )
1053 { 1395 {
1054 case 3: //---- At surface: start a new dive ------------------------------ 1396 overlay unsigned char i;
1055 clear_deco_table(); 1397
1398 case DECO_STATUS_INIT: //---- At surface: start a new dive ---------------------
1399
1400 clear_deco_table();
1056 copy_deco_table(); 1401 copy_deco_table();
1057 int_O_ascenttime = 0; // Reset DTR. 1402
1058 int_O_extra_ascenttime = 0; 1403
1059 char_O_nullzeit = 0; // Reset bottom time. 1404 char_I_gas_change_time = 1; // TODO: validate proper operation before enabling this options-table parameter
1060 char_O_deco_status = 0; // Calc bottom-time/nullzeit next iteration. 1405
1061 1406 char_I_ascent_speed = 10; // TODO: validate proper operation before enabling this options-table parameter,
1407 // caution: values < 10 may have an impact on the deco calculation run-times!
1408
1409
1410 float_ascent_speed = 1.00 * char_I_ascent_speed;
1411 float_desaturation_multiplier = 0.01 * char_I_desaturation_multiplier;
1412 float_saturation_multiplier = 0.01 * char_I_saturation_multiplier;
1413 float_deco_distance = 0.01 * char_I_deco_distance;
1414
1415 int_O_ascenttime = 0; // reset ascent time in normal plan
1416 int_O_alternate_ascenttime = 0; // reset ascent time in alternative plan
1417 char_O_nullzeit = 0; // reset no decompression limit (NDL) in normal plan
1418 char_O_alternate_nullzeit = 0; // reset no decompression limit (NDL) in alternative plan
1419 char_O_deco_warnings = 0; // reset all deco warning flags
1420 deco_tissue_vector = 0; // reset tissue deco vector
1421 IBCD_tissue_vector = 0; // reset tissue IBCD vector
1422
1423 int_O_desaturation_time = 65535; // tag desaturation time as invalid (it will not be computed during a dive)
1424
1425
1426 for(i=0; i<NUM_GAS; ++i)
1427 {
1428 int_O_gas_volumes[i] = 0;
1429 int_O_tank_pres_need[i] = 0 + INT_FLAG_ZERO; // 0 bar + flag for 0 bar
1430 }
1431
1432 for(i=0; i<NUM_COMP; ++i)
1433 {
1434 split_N2_He[i] = 90; // used for calculation of no-fly time
1435 }
1436
1437
1438 // init CNS counters
1439 CNS_sim_norm_fraction = CNS_sim_alt_fraction = CNS_fraction; // the floats
1440 int_O_normal_CNS_fraction = int_O_alternate_CNS_fraction = int_O_CNS_fraction; // the integers
1441
1442
1062 // Values that should be reset just once for the full real dive. 1443 // Values that should be reset just once for the full real dive.
1063 // This is used to record the lowest stop for the whole dive, 1444 // This is used to record the lowest stop for the whole dive,
1064 // Including ACCROSS all simulated ascent. 1445 // including ACCROSS all simulated ascents.
1065 low_depth = 0.0; 1446 low_depth_norm = low_depth_alt = 0.0;
1066 locked_GF_step = 0.0; 1447 locked_GF_step_norm = locked_GF_step_alt = 0.0;
1067 1448
1068 // Reset gas switch history. 1449
1069 backup_gas_used = sim_gas_last_used = 0; 1450 // continue in state DECO_STATUS_START to calculate the bottom-part of the dive and the NDL
1070 backup_gas_depth = sim_gas_last_depth = 0; 1451 char_O_deco_status &= ~DECO_STATUS_MASK;
1071 sim_dive_mins = 0; 1452
1453 // code execution continues in state DECO_STATUS_START
1454
1455
1456 case DECO_STATUS_START: //---- bottom time -------------------------------------
1457 default:
1458
1459 // reread the GF settings in case there was a switch between GF/aGF
1460 GF_low = char_I_GF_Low_percentage * 0.01;
1461 GF_high = char_I_GF_High_percentage * 0.01;
1462 GF_delta = GF_high - GF_low;
1463
1464 // Lookup current gas and store it also as the first gas used. This gas will be used for the bottom
1465 // segment of the dive and for the period of delayed ascent when calculating fTTS or bailout.
1466 gas_find_current();
1467
1468 // setup the calculation ratio's calc_N2_ratio, calc_He_ratio and calc_O2_ratio
1469 gas_switch_set();
1470
1471 // calculate ppN2 and ppHe from calc_N2_ratio & calc_He_ratio
1472 sim_alveolar_presures();
1473
1474 // clear the internal(!) stops table
1475 clear_deco_table();
1476
1477 // initialize the simulated tissues with the current state of the real tissues
1478 update_startvalues();
1479
1480 // calculate the effect of extended bottom time due to delayed ascent / fTTS on current gas
1481 if( char_O_deco_status & DECO_ASCENT_DELAYED ) sim_extra_time();
1482
1483 // calculate if we are within no decompression limit (NDL)
1484 calc_nullzeit();
1485
1486 // check which plan we are on
1487 if( char_O_deco_status & DECO_PLAN_ALTERNATE )
1488 {
1489 //---- alternate dive plan --------------------------------------------------------------------
1490
1491 // Some NDL time left in alternate plan?
1492 if( char_O_alternate_nullzeit > 0 )
1493 {
1494 // clear tank pressure needs
1495 if( char_O_deco_status & DECO_VOLUME_CALCULATE )
1496 for(i=0; i<NUM_GAS; ++i) int_O_tank_pres_need[i] = 0 + INT_FLAG_ZERO; // 0 bar + flag for 0 bar
1497
1498 // calculate the CNS% at the end of the dive if requested:
1499 // as we are in no stop, CNS at end of dive is more or less the same CNS we have now
1500 if( char_O_deco_status & DECO_CNS_CALCULATE ) int_O_alternate_CNS_fraction = int_O_CNS_fraction;
1501
1502 // clear fTTS ascent time
1503 int_O_alternate_ascenttime = 0;
1504
1505 // YES - computation of alternate plan completed
1506 char_O_deco_status &= ~DECO_STATUS_MASK;
1507 }
1508 else
1509 {
1510 // NO - clear status bits and set status bits for
1511 // calculation of ascent on next invocation
1512 char_O_deco_status &= ~DECO_STATUS_MASK;
1513 char_O_deco_status |= DECO_STATUS_ASCENT;
1514 }
1515 }
1516 else
1517 {
1518 //---- normal dive plan -------------------------------------------------------------------------
1519
1520 // Some NDL time left in normal plan?
1521 if( char_O_nullzeit > 0 )
1522 {
1523 // published (erased) stops table
1524 copy_deco_table();
1525
1526 // ** commented out - char_O_deco_last_stop is not used for anything
1527 //
1528 // // set last stop to 0 (for OSTC menu animation)
1529 // char_O_deco_last_stop = 0;
1530
1531 // clear tank pressure needs
1532 if( char_O_deco_status & DECO_VOLUME_CALCULATE )
1533 for(i=0; i<NUM_GAS; ++i) int_O_tank_pres_need[i] = 0 + INT_FLAG_ZERO; // 0 bar + flag for 0 bar
1534
1535 // calculate the CNS% at the end of the dive if requested:
1536 // as we are in no stop, CNS at end of dive is more or less the same CNS we have now
1537 if( char_O_deco_status & DECO_CNS_CALCULATE ) int_O_normal_CNS_fraction = int_O_CNS_fraction;
1538
1539 // YES - computation of normal plan completed
1540 char_O_deco_status &= ~DECO_STATUS_MASK;
1541 }
1542 else
1543 {
1544 // NO - clear status bits and set status bits for
1545 // calculation of ascent on next invocation
1546 char_O_deco_status &= ~DECO_STATUS_MASK;
1547 char_O_deco_status |= DECO_STATUS_ASCENT;
1548 }
1549 }
1550
1551 break;
1552
1553
1554 case DECO_STATUS_ASCENT: //---- Simulate ascent to first stop -------------------
1555
1556 // initialize depth (in abs.pressure) for ascent and deco simulation, start from current real depth
1557 temp_deco = pres_respiration;
1558
1559 // calculate ascent to first stop
1560 sim_ascent_to_first_stop();
1561
1562 // calculate all further stops next time
1563 char_O_deco_status &= ~DECO_STATUS_MASK; // clear status bits and set status bits
1564 char_O_deco_status |= DECO_STATUS_STOPS; // for calculation of stops on next invocation
1565
1072 break; 1566 break;
1073 1567
1074 case 0: //---- bottom time ----------------------------------------------- 1568
1075 default: 1569 case DECO_STATUS_STOPS: //---- Simulate stops ----------------------------------
1076 gas_switch_find_current(); // Lookup for current gas & time. 1570
1077 gas_switch_set(); // setup calc_ratio's 1571 calc_hauptroutine_calc_deco();
1078 1572
1079 calc_nullzeit(); 1573 // If simulation is finished, do some more computations if requested
1080 if( char_O_nullzeit > 0 ) // Some NDL time left ? 1574 // and restore the GF low reference so that the next ascent simulation
1575 // is done from the current depth:
1576 if( !(char_O_deco_status & DECO_STATUS_MASK) )
1081 { 1577 {
1082 char_O_deco_status = 0; // YES: recalc ndl next time. 1578 // Calculate ascent time, result in int_O_ascenttime or int_O_alternate_ascenttime
1083 clear_deco_table(); // Also clear stops ! 1579 calc_ascenttime();
1084 copy_deco_table(); 1580
1085 char_O_deco_last_stop = 0; // And last stop (OSTC menu anim) 1581 // the current depth is needed by calc_CNS_planning() and gas_volumes()
1086 } 1582 bottom_depth = (unsigned char)((pres_respiration - pres_surface)*BAR_TO_METER);
1087 else 1583
1088 char_O_deco_status = 2; // NO: calc ascent next time. 1584 // if requested, calculate the CNS% at the end of the dive (including the deco stops)
1585 if( char_O_deco_status & DECO_CNS_CALCULATE ) calc_CNS_planning();
1586
1587 // if requested, calculate the required gas volumes and tank pressures at the end of the dive.
1588 if( char_O_deco_status & DECO_VOLUME_CALCULATE ) gas_volumes();
1589
1590 // some more aftermath dependent on the current plan
1591 if( char_O_deco_status & DECO_PLAN_ALTERNATE )
1592 {
1593 //---- alternative plan ----------------------------------------------------
1594
1595 // was CNS at end of dive calculated?
1596 if( char_O_deco_status & DECO_CNS_CALCULATE )
1597 {
1598 // yes - compute CNS value to display
1599 if ( CNS_sim_alt_fraction < 0.01 ) int_O_alternate_CNS_fraction = 0;
1600 else if ( CNS_sim_alt_fraction > 9.985 ) int_O_alternate_CNS_fraction = 999 + INT_FLAG_WARNING;
1601 else
1602 {
1603 // convert float to integer
1604 int_O_alternate_CNS_fraction = (unsigned short)(100 * CNS_sim_alt_fraction + 0.5);
1605
1606 // set warning flag if CNS is >= 100%
1607 if( int_O_alternate_CNS_fraction >= 100 )
1608 int_O_alternate_CNS_fraction |= INT_FLAG_WARNING;
1609
1610 // set invalid flag if there is an overflow in the stops table
1611 if( char_O_deco_warnings & DECO_WARNING_STOPTABLE_OVERFLOW )
1612 int_O_alternate_CNS_fraction |= INT_FLAG_INVALID;
1613 }
1614 }
1615 else
1616 {
1617 // no - invalidate value (value = 0, invalid flag set)
1618 int_O_alternate_CNS_fraction = INT_FLAG_INVALID;
1619 }
1620 }
1621 else
1622 {
1623 //---- normal plan ---------------------------------------------------------
1624
1625 // publish the stops table
1626 copy_deco_table();
1627
1628 // was CNS at end of dive calculated?
1629 if( char_O_deco_status & DECO_CNS_CALCULATE )
1630 {
1631 // yes - compute CNS value to display
1632 if ( CNS_sim_norm_fraction < 0.01 ) int_O_normal_CNS_fraction = 0;
1633 else if ( CNS_sim_norm_fraction >= 9.985 ) int_O_normal_CNS_fraction = 999 + INT_FLAG_WARNING;
1634 else
1635 {
1636 // convert float to integer
1637 int_O_normal_CNS_fraction = (unsigned short)(100 * CNS_sim_norm_fraction + 0.5);
1638
1639 // set warning flag if CNS is >= 100%
1640 if( int_O_normal_CNS_fraction >= 100 )
1641 int_O_normal_CNS_fraction |= INT_FLAG_WARNING;
1642
1643 // set invalid flag if there is an overflow in the stops table
1644 if( char_O_deco_warnings & DECO_WARNING_STOPTABLE_OVERFLOW )
1645 int_O_normal_CNS_fraction |= INT_FLAG_INVALID;
1646 }
1647 }
1648 else
1649 {
1650 // no - invalidate value (value = 0, invalid flag set)
1651 int_O_normal_CNS_fraction = INT_FLAG_INVALID;
1652 }
1653
1654 } // aftermath
1655 } // if
1656
1089 break; 1657 break;
1090 1658
1091 case 2: //---- Simulate ascent to first stop ----------------------------- 1659 } // switch
1092 case 6: // @+5min variation
1093 // Check proposed gas at begin of ascent simulation
1094 sim_dive_mins = int_I_divemins; // Init current time.
1095
1096 gas_switch_find_current(); // Lookup for current gas & time.
1097 gas_switch_set(); // setup calc_ratio's
1098
1099 backup_gas_used = sim_gas_last_used; // And save for later simu steps.
1100 backup_gas_depth = sim_gas_last_depth; // And save for later simu steps.
1101
1102 sim_ascent_to_first_stop();
1103
1104 // Calc stops next time (deco or gas switch).
1105 char_O_deco_status = 1 | ( char_O_deco_status & 4 );
1106 break;
1107
1108 case 1: //---- Simulate stops --------------------------------------------
1109 case 5: // @+5 variation.
1110 calc_hauptroutine_calc_deco();
1111
1112 // If simulation is finished, restore the GF low reference, so that
1113 // next ascent simulation is done from the current depth:
1114 if( (char_O_deco_status & 3) == 0 )
1115 {
1116 sim_gas_last_used = backup_gas_used;
1117 sim_gas_last_depth = backup_gas_depth;
1118 }
1119 break;
1120 }
1121 } 1660 }
1122 1661
1123 ////////////////////////////////////////////////////////////////////////////// 1662 //////////////////////////////////////////////////////////////////////////////
1124 // calc_hauptroutine_data_input 1663 // calc_hauptroutine_data_input
1125 // 1664 //
1126 // Reset all C-code dive parameters from their ASM-code values. 1665 // Reset all C-code dive parameters from their ASM-code values.
1127 // Detect gas change condition. 1666 // Detect gas change condition.
1128 // 1667 //
1129 void calc_hauptroutine_data_input(void) 1668 void calc_hauptroutine_data_input(void)
1130 { 1669 {
1131 overlay short int_temp; 1670 // get the current pressures
1132 overlay unsigned char g; 1671 pres_respiration = 0.001 * int_I_pres_respiration;
1133 1672 pres_surface = 0.001 * int_I_pres_surface;
1134 pres_respiration = int_I_pres_respiration * 0.001; 1673
1135 pres_surface = int_I_pres_surface * 0.001; 1674 // get the currently breathed gas mixture
1136 N2_ratio = char_I_N2_ratio * 0.01; 1675 O2_ratio = 0.01 * char_I_O2_ratio;
1137 He_ratio = char_I_He_ratio * 0.01; 1676 He_ratio = 0.01 * char_I_He_ratio;
1138 float_deco_distance = char_I_deco_distance * 0.01; // Get offset in mbar 1677
1139 1678 // N2 ratios are computed within p2_deco.c from the O2 and He ratios
1140 // ____________________________________________________ 1679 N2_ratio = 1.0 - O2_ratio - He_ratio;
1141 // 1680
1142 // _____________ G A S _ C H A N G E S ________________ 1681 // N2 tissue pressure at surface equilibrium, used for tissue graphics scaling
1143 // ____________________________________________________ 1682 N2_equilibrium = 0.7902 * (pres_surface - ppWater);
1144
1145 // Keep a margin of 150mbar = 1.50m
1146 int_temp = (int_I_pres_respiration - int_I_pres_surface)
1147 + MBAR_REACH_GASCHANGE_AUTO_CHANGE_OFF;
1148
1149 // Gas are selectable if we did not pass the change depth by more than 1.50m:
1150 for(g=0; g < NUM_GAS; ++g)
1151 {
1152 deco_gas_change[g] = 0;
1153 if(char_I_deco_gas_change[g])
1154 if( int_temp > 100 *(short)char_I_deco_gas_change[g] )
1155 deco_gas_change[g] = char_I_deco_gas_change[g];
1156 }
1157
1158 const_ppO2 = char_I_const_ppO2 * 0.01;
1159 float_desaturation_multiplier = char_I_desaturation_multiplier * 0.01;
1160 float_saturation_multiplier = char_I_saturation_multiplier * 0.01;
1161 GF_low = char_I_GF_Low_percentage * 0.01;
1162 GF_high = char_I_GF_High_percentage * 0.01;
1163 GF_delta = GF_high - GF_low;
1164 } 1683 }
1165 1684
1166 ////////////////////////////////////////////////////////////////////////////// 1685 //////////////////////////////////////////////////////////////////////////////
1167 // 1686 //
1168 // 1687 //
1169 void calc_hauptroutine_update_tissues(void) 1688 void calc_hauptroutine_update_tissues(void)
1170 { 1689 {
1690 overlay float pres_diluent = pres_respiration;
1691
1692
1171 assert( 0.00 <= N2_ratio && N2_ratio <= 1.00 ); 1693 assert( 0.00 <= N2_ratio && N2_ratio <= 1.00 );
1172 assert( 0.00 <= He_ratio && He_ratio <= 1.00 ); 1694 assert( 0.00 <= He_ratio && He_ratio <= 1.00 );
1173 assert( (N2_ratio + He_ratio) <= 1.00 ); 1695 assert( (N2_ratio + He_ratio) <= 1.00 );
1174 assert( 0.800 < pres_respiration && pres_respiration < 14.0 ); 1696 assert( 0.800 < pres_respiration && pres_respiration < 14.0 );
1175 1697
1176 pres_diluent = pres_respiration; 1698
1177 if( char_I_const_ppO2 != 0 ) 1699 //---- OC, CCR and Bailout Mode Gas Calculations ------------------------------------------------------------
1700
1701 // calculate ppO2 of pure oxygen
1702 O2_ppO2 = (pres_respiration - ppWater);
1703
1704 // capture failure condition in case pres_respiration is < ppWater (should never happen...)
1705 if( O2_ppO2 < 0.0 ) O2_ppO2 = 0.0;
1706
1707 // calculate ppO2 of the pure gas (diluent)
1708 pure_ppO2 = O2_ppO2 * O2_ratio;
1709
1710
1711 //---- PSCR Mode Gas Calculation-----------------------------------------------------------
1712
1713 // With flags set for PSCR we compute the ppO2 in the loop from the diluent's O2
1714 // ratio and the PSCR parameters. This figure will be used in the pSCR custom view.
1715 // If sensors are used (char_I_const_ppO2 > 0), we will override the calculated ppO2
1716 // with the sensor data. Then we continue with the CCR mode code which calculates
1717 // the increase of ppN2 and ppH2 due to the reduction of the ppO2 in the loop.
1718 // Essentially, diving a pSCR is like diving a CCR with a setpoint set lower than
1719 // the ambient pressure multiplied with the O2 fraction of the diluent...
1720
1721 // calculate pSCR ppO2
1722 //
1723 // pres_respiration is 0.0 ... in bar
1724 // O2_ratio is 0.0 ... 1.0 as factor
1725 // char_I_PSCR_drop is 0 ... 15 as %
1726 // char_I_PSCR_lungratio is 5 ... 20 as %
1727 // pSCRppO2 is 0.0 ... in bar
1728
1729 pSCR_ppO2 = (pres_respiration * O2_ratio) - (1 - O2_ratio) * 0.01 * char_I_PSCR_drop * char_I_PSCR_lungratio;
1730
1731 // capture failure condition if case pSCR_ppO2 becomes negative
1732 if( pSCR_ppO2 < 0.0 ) pSCR_ppO2 = 0.0;
1733
1734
1735 //---- Loop modes : adjust ppN2 and ppHe for change in ppO2 due to setpoint (CCR) or drop (pSCR) ------------
1736 if ( char_O_main_status & DECO_MODE_LOOP )
1178 { 1737 {
1179 overlay float flush_ppO2 = pres_respiration * (1.0 - N2_ratio - He_ratio); 1738 overlay float const_ppO2;
1180 1739
1740 // get the current sensor reading (CCR / pSCR if fitted) or the fixed setpoint (CCR) / a zero (pSCR)
1741 const_ppO2 = 0.01 * char_I_const_ppO2;
1742
1743 // Limit the setpoint to the maximum physically possible ppO2. This prevents for
1744 // example calculating with a setpoint of 1.3 bar in only 2 meters of depth.
1745 // Additionally, if limiting occurs, the ppO2 can be further reduced to account
1746 // for residual inert gases by the user-adjustable setting char_I_cc_max_frac_o2.
1747
1748 if( const_ppO2 > pres_respiration ) // no ppWater subtracted here to give some margin for
1749 { // sensors delivering data a little bit over target
1750
1751 const_ppO2 = 0.01 * char_I_cc_max_frac_o2 * (pres_respiration - ppWater);
1752 }
1753
1754 // check which kind of loop we are on
1755 if( char_O_main_status & DECO_MODE_PSCR )
1756 {
1757 //---- pSCR Mode --------------------------------------------------------------------------
1758
1759 // check if a sensor is fitted
1760 if( char_I_const_ppO2 ) breathed_ppO2 = const_ppO2; // yes - derive ppO2s from (char_I_)const_ppO2
1761 else breathed_ppO2 = pSCR_ppO2; // no - derive ppO2s from calculated ppO2
1762 }
1763 else
1764 {
1765 //---- CCR Mode ---------------------------------------------------------------------------
1766
1767 // derive breathed ppO2 from (char_I_)const_ppO2, which holds sensor reading or fixed setpoint
1768 breathed_ppO2 = const_ppO2;
1769 }
1770
1771 // adjust diluent pressure (ppN2 + ppHe) for change in ppO2 due to setpoint (CCR) or drop (pSCR)
1181 pres_diluent -= const_ppO2; 1772 pres_diluent -= const_ppO2;
1182 pres_diluent /= N2_ratio + He_ratio; // potential DIV/0 issue when O2 is used as diluent! 1773 pres_diluent /= N2_ratio + He_ratio;
1183 if( pres_diluent < 0.0 ) 1774
1184 pres_diluent = 0.0; 1775 // capture all failure conditions, including div/0 in case diluent is pure O2
1185 if(N2_ratio==0&He_ratio==0) pres_diluent = 0.0; // workaround for potential DIV/0 issue 1776 if( (pres_diluent < 0.0) || (char_I_O2_ratio == 100) )
1186 1777 {
1187 char_O_diluent = (unsigned char)(pres_diluent/pres_respiration*100.0 + 0.5); 1778 pres_diluent = 0.0;
1188 1779 breathed_ppO2 = pure_ppO2;
1189 if( flush_ppO2 > 2.545) flush_ppO2 = 2.55; 1780 }
1190 if( flush_ppO2 < 0.0 ) flush_ppO2 = 0.0; 1781
1191 char_O_flush_ppO2 = (unsigned char)(flush_ppO2*100.0 + 0.5); 1782 }
1192 } 1783 else
1193 1784 { //---- OC mode -----------------------------------------------------------------------------------------
1785
1786 // breathed ppO2 is ppO2 of pure gas
1787 breathed_ppO2 = pure_ppO2;
1788 }
1789
1790
1791 // derive char_actual_ppO2 in [cbar], used for calculating CNS%
1792 if ( breathed_ppO2 < 0.01 ) char_actual_ppO2 = 0;
1793 else if ( breathed_ppO2 >= 2.545 ) char_actual_ppO2 = 255;
1794 else char_actual_ppO2 = (unsigned char)(100 * breathed_ppO2 + 0.5);
1795
1796
1797 //---- export ppO2 values in [cbar] for warning generation and display purpose ------------------------------
1798
1799 // pure oxygen ppO2
1800 if ( O2_ppO2 < 0.01 ) int_O_O2_ppO2 = 0;
1801 else if ( O2_ppO2 >= 9.995 ) int_O_O2_ppO2 = 999;
1802 else int_O_O2_ppO2 = (unsigned int)(100 * O2_ppO2 + 0.5);
1803
1804 // pure gas ppO2
1805 if ( pure_ppO2 < 0.01 ) int_O_pure_ppO2 = 0;
1806 else if ( pure_ppO2 >= 9.995 ) int_O_pure_ppO2 = 999;
1807 else int_O_pure_ppO2 = (unsigned int)(100 * pure_ppO2 + 0.5);
1808
1809 // calculated pSCR ppO2
1810 if ( pSCR_ppO2 < 0.01 ) int_O_pSCR_ppO2 = 0;
1811 else if ( pSCR_ppO2 >= 9.995 ) int_O_pSCR_ppO2 = 999;
1812 else int_O_pSCR_ppO2 = (unsigned int)(100 * pSCR_ppO2 + 0.5);
1813
1814 // breathed ppO2
1815 if ( breathed_ppO2 < 0.01 ) int_O_breathed_ppO2 = 0;
1816 else if ( breathed_ppO2 >= 9.995 ) int_O_breathed_ppO2 = 999;
1817 else int_O_breathed_ppO2 = (unsigned int)(100 * breathed_ppO2 + 0.5);
1818
1819
1820 //---- calculate ppN2, ppHe and EAD, END -------------------------------------------------------------------
1821
1194 if( pres_diluent > ppWater ) 1822 if( pres_diluent > ppWater )
1195 { 1823 {
1196 overlay float EAD, END; 1824 overlay float EAD, END;
1197 1825
1198 ppN2 = N2_ratio * (pres_diluent - ppWater); 1826 ppN2 = N2_ratio * (pres_diluent - ppWater);
1199 ppHe = He_ratio * (pres_diluent - ppWater); 1827 ppHe = He_ratio * (pres_diluent - ppWater);
1200 1828
1201 // EAD : Equivalent Air Dive. Equivalent depth for the same N2 level 1829 // EAD : Equivalent Air Depth. Equivalent depth for the same N2 level with plain air.
1202 // with plain air.
1203 // ppN2 = 79% * (P_EAD - ppWater) 1830 // ppN2 = 79% * (P_EAD - ppWater)
1204 // EAD = (P_EAD - Psurface) * 10 1831 // EAD = (P_EAD - Psurface) * 10
1205 // ie: EAD = (ppN2 / 0.7902 + ppWater -Psurface) * 10 1832 // ie: EAD = (ppN2 / 0.7902 + ppWater -Psurface) * 10
1833
1206 EAD = (ppN2 / 0.7902 + ppWater - pres_surface) * BAR_TO_METER; 1834 EAD = (ppN2 / 0.7902 + ppWater - pres_surface) * BAR_TO_METER;
1207 if( EAD < 0.0 || EAD > 245.5 ) EAD = 0.0; 1835
1836 if( (EAD < 0.0) || (EAD > 245.5) ) EAD = 0.0;
1837
1208 char_O_EAD = (unsigned char)(EAD + 0.5); 1838 char_O_EAD = (unsigned char)(EAD + 0.5);
1209 1839
1210 // END : Equivalent Narcotic Dive. 1840
1211 // Here we count O2 as narcotic too. Hence everything but helium (has a narcosis factor of 1841 // END : Equivalent Narcotic Depth.
1212 // 0.23 btw). Hence the formula becomes: 1842 // Here we count O2 as narcotic too. Hence everything but helium (has a narcosis
1843 // factor of 0.23 btw). Hence the formula becomes:
1213 // END * BarPerMeter * (1.0 - 0.0) - ppWater + Psurface == Pambient - ppHe - ppWater 1844 // END * BarPerMeter * (1.0 - 0.0) - ppWater + Psurface == Pambient - ppHe - ppWater
1214 // ie: END = (Pambient - ppHe - Psurface) * BAR_TO_METER 1845 // ie: END = (Pambient - ppHe - Psurface) * BAR_TO_METER
1215 // 1846 //
1216 // Source cited: 1847 // Source cited:
1217 // The Physiology and Medicine of Diving by Peter Bennett and David Elliott, 1848 // The Physiology and Medicine of Diving by Peter Bennett and David Elliott,
1218 // 4th edition, 1993, W.B.Saunders Company Ltd, London. 1849 // 4th edition, 1993, W.B.Saunders Company Ltd, London.
1850
1219 END = (pres_respiration - ppHe - pres_surface) * BAR_TO_METER; 1851 END = (pres_respiration - ppHe - pres_surface) * BAR_TO_METER;
1220 if( END < 0.0 || END > 245.5 ) END = 0.0; 1852
1221 char_O_END = (unsigned char)(END + 0.5); 1853 if( (END < 0.0) || (END > 245.5) ) END = 0.0;
1854
1855 char_O_END = (unsigned char)(END + 0.5);
1222 } 1856 }
1223 else // new in v.101 1857 else
1224 { 1858 {
1225 ppN2 = 0.0; 1859 ppN2 = ppHe = 0.0;
1226 ppHe = 0.0; 1860
1227 char_O_EAD = char_O_END = 0; 1861 char_O_EAD = char_O_END = 0;
1228 } 1862 }
1229 1863
1230 if(!char_I_step_is_1min) 1864
1231 calc_tissue(0); 1865 //---- calculate decompression status ----------------------------------------------------------------------
1866
1867 // Calculate tissues
1868 calc_tissue();
1869
1870 // Calculate limit for surface, ie. GF_high.
1871 calc_limit();
1872
1873
1874 // Fill int_O_ceiling (in mbar) if ceiling is below the surface
1875 if( (calc_lead_tissue_limit - pres_surface) > 0 )
1876 {
1877
1878 // compatibility version
1879 int_O_ceiling = (short)((calc_lead_tissue_limit - pres_surface) * 1000);
1880
1881 // new version
1882 // // Round up to next 10 cm so that the ceiling disappears on the display only when the ceiling
1883 // // limit is really zero. This will coincident then with TTS switching back to NDL time.
1884 // int_O_ceiling = (short)((calc_lead_tissue_limit - pres_surface) * 1000 + 9);
1885
1886
1887 // limit int_O_ceiling to 16000 mbar (150 m)
1888 if( int_O_ceiling > 16000) int_O_ceiling = 16000;
1889 }
1232 else 1890 else
1233 calc_tissue(1); 1891 {
1234
1235 // Calc limit for surface, ie. GF_high.
1236 calc_limit();
1237
1238 // Fill int_O_ceiling if ceiling is below the surface
1239 if ((calc_lead_tissue_limit-pres_surface)>0)
1240 int_O_ceiling = (short)((calc_lead_tissue_limit-pres_surface)*1000);
1241 else
1242 int_O_ceiling = 0; 1892 int_O_ceiling = 0;
1893 }
1243 1894
1244 int_O_gtissue_press = (short)((pres_tissue_N2[char_O_gtissue_no] + pres_tissue_He[char_O_gtissue_no]) * 1000); 1895 int_O_gtissue_press = (short)((pres_tissue_N2[char_O_gtissue_no] + pres_tissue_He[char_O_gtissue_no]) * 1000);
1245 } 1896 }
1246 1897
1247 1898
1248 ////////////////////////////////////////////////////////////////////////////// 1899 //////////////////////////////////////////////////////////////////////////////
1249 // Compute stops. 1900 // Compute stops.
1250 // 1901 //
1251 // Note: because this can be very long, break on 16 iterations, and set state 1902 // Note: because this can be very long, break on 16 iterations, and set state
1252 // to 0 when finished, or to 1 when needing to continue. 1903 // to DECO_STATUS_FINISHED when finished, or to DECO_STATUS_STOPS when
1904 // needing to continue.
1253 // Note: because each iteration might be very long too (~ 66 ms in 1.84beta), 1905 // Note: because each iteration might be very long too (~ 66 ms in 1.84beta),
1254 // break the loop when total time > 512msec. 1906 // break the loop when elapsed time exceeds 512 milliseconds.
1255 // 1907 //
1256 void calc_hauptroutine_calc_deco(void) 1908 void calc_hauptroutine_calc_deco(void)
1257 { 1909 {
1258 overlay unsigned char loop; 1910 overlay unsigned char loop;
1259 1911
1260 for(loop = 0; loop < 16; ++loop) 1912 for(loop = 0; loop < 16; ++loop)
1261 { 1913 {
1262 // Limit loops to 512ms, using timer 5: 1914 // limit loops to 512ms, using timer 5
1263 if( tmr5() & (512*32) ) 1915 if( tmr5() & (512*32) ) break;
1264 break; 1916
1265 1917 // calc_nextdecodepth()
1266 if( calc_nextdecodepth() ) 1918 //
1267 { 1919 // INPUT temp_deco : current depth in absolute pressure
1268 if( temp_depth_limit == 0 ) 1920 // OUTPUT temp_depth_limit : depth of next stop in meters
1269 goto Surface; 1921 // RETURN true if a stop is needed
1270 1922 //
1271 //---- We hit a stop at temp_depth_limit --------------------- 1923 // The function manages gas changes by itself, including priming
1272 temp_deco = temp_depth_limit * METER_TO_BAR // Convert to relative bar, 1924 // the deco stop with the configured gas change time.
1273 + pres_surface; // To absolute. 1925 //
1274 if( !update_deco_table() ) // Adds a one minute stops. 1926 if( calc_nextdecodepth() )
1275 goto Surface; // Deco table full: abort... 1927 {
1276 } 1928 if( temp_depth_limit == 0 ) goto Surface; // this check should not bee needed as in
1277 else 1929 // this case the RETURN value will be false
1278 { 1930
1279 //---- No stop ----------------------------------------------- 1931 //---- stop required at temp_depth_limit -------------------------------------
1280 temp_deco -= (10*METER_TO_BAR); // Ascend 10m, no wait. 1932
1281 1933 // convert stop depth in meters to absolute pressure
1282 //---- Finish computations once surface is reached ----------- 1934 temp_deco = temp_depth_limit * METER_TO_BAR + pres_surface;
1283 if( temp_deco <= pres_surface ) 1935
1284 { 1936 // add one minute to the current stop, or add a new stop,
1937 // or abort deco calculation if the deco table is full.
1938 if( !update_deco_table(1) ) goto Surface;
1939 }
1940 else
1941 {
1942 //---- no stop required --------------------------------------
1943
1944 // ascend by float_ascent_speed for 1 minute
1945 temp_deco -= float_ascent_speed * METER_TO_BAR;
1946
1947 // finish deco calculation if surface is reached
1948 if( temp_deco <= pres_surface )
1949 {
1285 Surface: 1950 Surface:
1286 if( char_O_deco_status == 1 ) // Don't in @+5min variant. 1951 // set deco engine status to done (DECO_STATUS_FINISHED)
1287 copy_deco_table(); 1952 char_O_deco_status &= ~DECO_STATUS_MASK;
1288 1953
1289 calc_ascenttime(); 1954 // ** commented out - char_O_deco_last_stop is not used for anything
1290 char_O_deco_status = 0; // calc nullzeit next time. 1955 //
1291 char_O_deco_last_stop = 0; // Surface reached (to animate menu) 1956 // // surface reached (to animate menu)
1292 return; 1957 // if( !(char_O_deco_status & DECO_PLAN_ALTERNATE)) char_O_deco_last_stop = 0;
1293 } 1958
1294 } 1959 return;
1295 //---- Then update tissue -------------------------------------------- 1960 }
1296 sim_dive_mins++; // Advance simulated time by 1 minute. 1961 }
1297 gas_switch_set(); // Apply any simulated gas change, once validated. 1962
1298 sim_alveolar_presures(); // Updates ppN2 and ppHe. 1963
1299 sim_tissue(1); // Simulate compartiments for 1 minute. 1964 //---- as one minute as passed now, update the tissues ----------------------
1300 } 1965
1301 1966 // program 1 minute interval on simulated tissues (Flagbit 7 = 0)
1302 // Surface not reached, need more stops... for menu animation. 1967 tissue_increment = 1;
1303 char_O_deco_last_stop = temp_depth_limit; // Reached depth. 1968
1304 } 1969 // compute current ppN2 and ppHe
1305 1970 sim_alveolar_presures();
1306 1971
1307 ////////////////////////////////////////////////////////////////////////////// 1972 // update the tissues
1308 // Simulation ascention to first deco stop. 1973 calc_tissue();
1309 // 1974 }
1310 // Note: because we ascent with a constant speed (10m/mn, ie. 1bar/mn), 1975
1311 // there is no need to break on more that 16 iterations 1976 // ** commented out - char_O_deco_last_stop is not used for anything
1312 // (or we are already in deep shit). 1977 //
1313 // 1978 // // surface not reached, need more stops... store reached depth for menu animation.
1314 // Input: pres_respiration 1979 // if( !(char_O_deco_status & DECO_PLAN_ALTERNATE) ) char_O_deco_last_stop = temp_depth_limit;
1315 // Output: temp_deco 1980 }
1316 // 1981
1317 // if char_O_deco_status indicate @+5 variant, add extra time at current depth, 1982
1318 // before ascent. 1983 //////////////////////////////////////////////////////////////////////////////
1984 // Simulate ascent to first deco stop.
1985 //
1986 //
1987 // Modified: temp_deco : current depth in ascent and deco simulation, in bar absolute pressure
1988 //
1319 void sim_ascent_to_first_stop(void) 1989 void sim_ascent_to_first_stop(void)
1320 { 1990 {
1321 overlay unsigned char fast = 1; // 1min or 2sec steps. 1991 overlay unsigned char fast = 1; // 1 = 1 minute steps, 0 = 2 seconds steps
1322 1992 overlay unsigned char gaschange = 0; // 1 = do a gas change, 0 = no better gas available
1323 update_startvalues(); 1993
1324 clear_deco_table(); 1994
1325 1995 //---- Loop until first deco stop or surface is reached ----------
1326 temp_deco = pres_respiration; // Starts from current real depth. 1996 for(;;)
1327 1997 {
1328 // Are we doing the special @+5min variation ? 1998 // depth in absolute pressure we came from
1329 if(char_O_deco_status & 4) 1999 overlay float old_deco = temp_deco;
1330 sim_extra_time(); 2000
1331 2001 // try ascending 1 full minute (fast) or 2 seconds (!fast)
1332 //---- Loop until first stop, gas switch, or surface is reached ---------- 2002 if ( fast ) temp_deco -= float_ascent_speed * METER_TO_BAR; // 1 min at float_ascent_speed ( 5 .. 10 m/min)
1333 for(;;) 2003 else temp_deco -= (float_ascent_speed/30.0) * METER_TO_BAR; // 2 sec at float_ascent_speed (17 .. 33 cm/min)
1334 { 2004
1335 overlay float old_deco = temp_deco; // Pamb backup (bars) 2005 // but don't go over surface
1336 2006 if( temp_deco < pres_surface ) temp_deco = pres_surface;
1337 // Try ascending 1 full minute (fast) or 2sec (!fast): 2007
1338 if( fast ) 2008 // compute sim_lead_tissue_limit
1339 temp_deco -= 10*METER_TO_BAR; // 1 min, at 10m/min. ~ 1bar. 2009 if ( char_I_deco_model != 0 ) sim_limit(GF_low);
1340 else 2010 else sim_limit(1.0);
1341 temp_deco -= (10.0/30.0)*METER_TO_BAR; // 2sec at 10m/min. 2011
1342 2012 // did we overshoot the first deco stop?
1343 if( temp_deco < pres_surface ) // But don't go over surface. 2013 if( temp_deco < sim_lead_tissue_limit )
1344 temp_deco = pres_surface; 2014 {
1345 2015 // YES - back to last depth below first stop
1346 // Recompute sim_lead_tissue_limit at GF_low (deepest stop), because 2016 temp_deco = old_deco;
1347 // one minute passed. 2017
1348 sim_limit(GF_low); 2018 // switch to 2 seconds ascent if not yet in, else done
1349 2019 if( fast )
1350 // Did we reach deepest remaining stop ? 2020 {
1351 if( temp_deco < sim_lead_tissue_limit ) 2021 fast = 0; // retry with 2 seconds ascent steps
1352 { 2022 continue;
1353 temp_deco = old_deco; // Restore last correct depth, 2023 }
1354 2024 else
1355 if( fast ) 2025 {
1356 { 2026 break; // done...
1357 fast = 0; // Retry with 2sec steps. 2027 }
1358 continue; 2028 }
1359 } 2029
1360 else 2030 // If code execution passes along here, we did not overshoot the first stop.
1361 break; // Done... 2031
1362 } 2032 // did we reach the surface? if yes, done!
1363 2033 if( temp_deco == pres_surface ) break;
1364 // Did we reach surface ? 2034
1365 // NOTE: we should round BEFORE checking surface is reached. 2035 // depth in meters where we are now (no round-up)
1366 temp_depth_limit = (unsigned char)(0.5 + (temp_deco - pres_surface) * BAR_TO_METER); 2036 temp_depth_limit = (unsigned char)((temp_deco - pres_surface) * BAR_TO_METER);
1367 if( temp_depth_limit == 0 ) 2037
1368 { 2038 // Check if there is a better gas to switch to, but only in alternative plan mode
1369 temp_deco = pres_surface; // Yes: finished ! 2039 // or if override is set. If yes, introduce a stop for the gas change.
1370 break; 2040 if( ((char_O_deco_status & DECO_PLAN_ALTERNATE) || (char_O_main_status & DECO_GASCHANGE_OVRD))
1371 } 2041 && gas_find_better() )
1372 2042 {
1373 // Check for gas change below new depth ? 2043 // depth in meters we came from
1374 if( gas_switch_deepest() ) 2044 overlay unsigned char old_depth_limit = (unsigned char)((old_deco - pres_surface) * BAR_TO_METER);
1375 { 2045
1376 assert( temp_depth_limit > 0); 2046 // adjust temp_depth_limit to the gas change depth, but not deeper than the depth we came from
1377 2047 temp_depth_limit = (sim_gas_last_depth < old_depth_limit) ? sim_gas_last_depth : old_depth_limit;
1378 temp_deco = temp_depth_limit * METER_TO_BAR + pres_surface; 2048
1379 break; 2049 // create a stop for the gas change
1380 } 2050 update_deco_table(char_I_gas_change_time);
1381 2051
1382 if( fast ) 2052 // set the new calculation values for N2, He and O2
1383 sim_dive_mins++; // Advance simulated time by 1 minute. 2053 gas_switch_set();
1384 sim_alveolar_presures(); // temp_deco --> ppN2/ppHe 2054
1385 sim_tissue(fast); // and update tissues for 1 min. 2055 // signal to create a stop for the gas change and update the tissues
1386 } 2056 gaschange = char_I_gas_change_time;
1387 } 2057
1388 2058 // Adjust the depth for the tissue update to the stop depth. In case of fast mode, this
1389 ////////////////////////////////////////////////////////////////////////////// 2059 // imposes that the ascent from the 'old_deco' depth to this stop took 1 minute although
1390 // Simulation extra time at the current depth. 2060 // we might have only ascended one or two meters...
1391 // 2061 temp_deco = temp_depth_limit * METER_TO_BAR + pres_surface;
1392 // This routine is used for @+5min feature. 2062 }
2063
2064 // Did one minute pass by and/or do we have a gas change?
2065 // Remark: The 2 seconds ascent iterations towards the first deco stop in !fast speed may take
2066 // up to 28 seconds in total - for this rough half of a minute no tissue updates will be computed.
2067 // Well, it could be done by setting tissue_increment = 0 in !fast condition and making calls to
2068 // sim_alveolar_presures() and calc_tissue() - see code commented out below.
2069 if( fast || gaschange )
2070 {
2071 // program interval on simulated tissues (flag bit 7 = 0)
2072 tissue_increment = fast + gaschange;
2073
2074 // clear gas change signal
2075 gaschange = 0;
2076 // }
2077 // else
2078 // {
2079 // // program 2 seconds interval on simulated tissues (flag bit 7 = 0)
2080 // tissue_increment = 0;
2081 // }
2082 // {
2083 // compute ppN2/ppHe for current depth from temp_deco
2084 sim_alveolar_presures();
2085
2086 // update the tissues
2087 calc_tissue();
2088 }
2089 }
2090 }
2091
2092 //////////////////////////////////////////////////////////////////////////////
2093 // Simulate extra time at the current depth.
2094 //
2095 // This routine is used for the futureTTS / delayed ascent feature.
2096 //
1393 void sim_extra_time(void) 2097 void sim_extra_time(void)
1394 { 2098 {
1395 overlay unsigned char extra = char_I_extra_time; 2099 overlay unsigned char backup = tissue_increment; // back-up tissue_increment
1396 do { 2100
1397 sim_dive_mins++; // Advance simulated time by 1 minute. 2101 tissue_increment = char_I_extra_time; // program interval on simulated tissues (Flagbit 7 = 0)
1398 sim_tissue(1); // and update tissues for 1 min. 2102
1399 } while( --extra != 0 ); 2103 calc_tissue(); // update the tissues
2104
2105 tissue_increment = backup; // restore tissue_increment
1400 } 2106 }
1401 2107
1402 ////////////////////////////////////////////////////////////////////////////// 2108 //////////////////////////////////////////////////////////////////////////////
1403 // calc_tissue 2109 // calc_tissue
1404 // 2110 //
1405 // optimized in v.101 2111 // optimized in v.101
1406 // 2112 //
1407 static void calc_tissue(PARAMETER unsigned char period) 2113 // INPUT: ppN2, ppHe, tissue_increment
1408 { 2114 // MODIFIED: pres_tissue_N2[], pres_tissue_He[]
2115 // OUTPUT: char_O_tissue_N2_saturation[], char_O_tissue_He_saturation[]
2116 //
2117 static void calc_tissue()
2118 {
2119 overlay float temp_tissue_N2;
2120 overlay float temp_tissue_He;
2121 overlay unsigned char period;
2122 overlay unsigned char i;
2123
2124
1409 assert( 0.00 <= ppN2 && ppN2 < 11.2 ); // 80% N2 at 130m 2125 assert( 0.00 <= ppN2 && ppN2 < 11.2 ); // 80% N2 at 130m
1410 assert( 0.00 <= ppHe && ppHe < 12.6 ); // 90% He at 130m 2126 assert( 0.00 <= ppHe && ppHe < 12.6 ); // 90% He at 130m
1411 2127
1412 for (ci=0;ci<NUM_COMP;ci++) 2128
2129 for (ci=0;ci<NUM_COMP;ci++) // iterate through all compartments
1413 { 2130 {
1414 read_buhlmann_times(period); // 2 sec or 1 min period. 2131 i = tissue_increment & 127; // extract number of minutes to do (if i > 0)
1415 2132 // or if one 2 second period is to do (if i = 0)
1416 // N2 2133
1417 temp_tissue = (ppN2 - pres_tissue_N2[ci]) * var_N2_e; 2134 if( i == 0 ) // check if we shall do one 2-seconds period
1418 temp_tissue_safety(); 2135 {
1419 pres_tissue_N2[ci] += temp_tissue; 2136 read_Buhlmann_times(0); // YES, program coefficients for a 2 seconds period
1420 2137 period = 1; // set period length (in cycles)
1421 // He 2138 i = 1; // and one cycle to do
1422 temp_tissue = (ppHe - pres_tissue_He[ci]) * var_He_e; 2139 }
1423 temp_tissue_safety(); 2140 else if( i > 9 ) // check if we can start with 10 minutes periods
1424 pres_tissue_He[ci] += temp_tissue; 2141 {
1425 } 2142 read_Buhlmann_times(2); // YES, program coefficients for 10 minutes periods
2143 period = 10; // set period length (in cycles) to ten
2144 }
2145 else // we shall do 1 to 9 minutes
2146 {
2147 read_Buhlmann_times(1); // program coefficients for 1 minute periods
2148 period = 1; // set period length (in cycles) to one
2149 }
2150
2151 do
2152 {
2153 //---- N2 -------------------------------------------------------------------------------
2154
2155 temp_tissue = (tissue_increment & 128) ? pres_tissue_N2[ci] : sim_pres_tissue_N2[ci];
2156
2157 temp_tissue = (ppN2 - temp_tissue) * var_N2_e;
2158
2159 temp_tissue_safety();
2160
2161 if( tissue_increment & 128 )
2162 {
2163 // The temp variable takes on purpose just the tissue increment from the last loop's iteration.
2164 temp_tissue_N2 = temp_tissue;
2165
2166 // Update the real tissues if either we are on the 2 seconds interval,
2167 // or if we shall advance the tissues on a one or several minutes basis.
2168 if( twosectimer || (tissue_increment & 127) ) pres_tissue_N2[ci] += temp_tissue;
2169 }
2170 else
2171 {
2172 // Updates of the sim-tissues always comes on a 1 minutes basis,
2173 // so we do not need to check of the 2 seconds interval.
2174 sim_pres_tissue_N2[ci] += temp_tissue;
2175 }
2176
2177
2178 //---- He -------------------------------------------------------------------------------
2179
2180 temp_tissue = (tissue_increment & 128) ? pres_tissue_He[ci] : sim_pres_tissue_He[ci];
2181
2182 temp_tissue = (ppHe - temp_tissue) * var_He_e;
2183
2184 temp_tissue_safety();
2185
2186 if( tissue_increment & 128 )
2187 {
2188 // The temp variable takes on purpose just the tissue increment from the last loop's iteration.
2189 temp_tissue_He = temp_tissue;
2190
2191 // Update the real tissues if either we are on the 2 seconds interval,
2192 // or if we shall advance the tissues on a one or several minutes basis.
2193 if( twosectimer || (tissue_increment & 127) ) pres_tissue_He[ci] += temp_tissue;
2194
2195 }
2196 else
2197 {
2198 // Updates of the sim-tissues always comes on a 1 minutes basis,
2199 // so we do not need to check of the 2 seconds interval.
2200 sim_pres_tissue_He[ci] += temp_tissue;
2201 }
2202
2203
2204 // decrement loop counter
2205 i -= period;
2206
2207 // check if we need to switch from 10 minute periods to 1 minute periods
2208 if( (i > 0) && (period = 10) && (i < 10) )
2209 {
2210 read_Buhlmann_times(1); // program coefficients for 1 minute periods
2211 period = 1; // set period length (in cycles) to one
2212 }
2213 }
2214 while( i );
2215
2216
2217 // have the computations been done for the "real" tissues?
2218 if( (tissue_increment & 128) && (twosectimer || (tissue_increment & 127)) )
2219 {
2220 // net tissue balance
2221 temp_tissue = temp_tissue_N2 + temp_tissue_He;
2222
2223 // check tissue on-/off-gassing and IBCD with applying a threshold of +/-HYST
2224 //
2225 if ( temp_tissue < -HYST ) // Check if the tissue is off-gassing
2226 {
2227 deco_tissue_vector |= (1 << ci); // tag tissue as being in decompression
2228 IBCD_tissue_vector &= ~(1 << ci); // tag tissue as not experiencing mentionable IBCD
2229 }
2230 else if ( temp_tissue > +HYST ) // check if the tissue in on-gassing
2231 {
2232 deco_tissue_vector &= ~(1 << ci); // tag tissue as not being in decompression
2233
2234 if( ((temp_tissue_N2 > 0.0) && (temp_tissue_He < 0.0)) // check for counter diffusion
2235 || ((temp_tissue_N2 < 0.0) && (temp_tissue_He > 0.0)) )
2236 {
2237 IBCD_tissue_vector |= (1 << ci); // tag tissue as experiencing mentionable IBCD
2238 }
2239 }
2240
2241
2242 // keep the saturating / desaturating flags from last invocation
2243 char_O_tissue_N2_saturation[ci] &= 128;
2244 char_O_tissue_He_saturation[ci] &= 128;
2245
2246 // flip the flags applying a hysteresis of HYST (actual value: see #define of HYST)
2247 if( temp_tissue_N2 > +HYST ) char_O_tissue_N2_saturation[ci] = 128; // set flag for tissue pressure is increasing
2248 else if( temp_tissue_N2 < -HYST ) char_O_tissue_N2_saturation[ci] = 0; // clear flag (-> tissue pressure is decreasing)
2249
2250 if( temp_tissue_He > +HYST ) char_O_tissue_He_saturation[ci] = 128; // set flag for tissue pressure is increasing
2251 else if( temp_tissue_He < -HYST ) char_O_tissue_He_saturation[ci] = 0; // clear flag (-> tissue pressure is decreasing)
2252
2253
2254 // For N2 tissue display purpose:
2255 // Scale tissue press so that saturation in 70m on AIR gives a value of approx. 80.
2256 // The surface steady-state tissue loading of [0.7902 * (pres_respiration - ppWater)] bar
2257 // gives then a 10. If N2 is completely washed out of the tissue, result will be 0.
2258 // This scaling is adapted to the capabilities of the tissue graphics in the custom views.
2259 temp_tissue = (pres_tissue_N2[ci] / N2_equilibrium) * 10;
2260
2261 // limit to 127 to leave space for sat/desat flag
2262 if (temp_tissue > 127) temp_tissue = 127;
2263
2264 // export as integer
2265 char_O_tissue_N2_saturation[ci] += (unsigned char)temp_tissue;
2266
2267
2268 // For H2 tissue display purpose:
2269 // Scale tissue press so that saturation in 120m on TMX 10/70 gives a value of approx. 70.
2270 // With no He in a tissue, result will be 0.
2271 // This scaling is adapted to the capabilities of the tissue graphics in the custom views.
2272 temp_tissue = pres_tissue_He[ci] * 7.7;
2273
2274 // limit to 127 to leave space for sat/desat flag
2275 if (temp_tissue > 127) temp_tissue = 127;
2276
2277 // export as integer
2278 char_O_tissue_He_saturation[ci] += (unsigned char)temp_tissue;
2279 }
2280
2281 }// for
1426 } 2282 }
1427 2283
1428 ////////////////////////////////////////////////////////////////////////////// 2284 //////////////////////////////////////////////////////////////////////////////
1429 // calc_limit 2285 // calc_limit
1430 // 2286 //
1431 // New in v.111 : separated from calc_tissue(), and depends on GF value. 2287 // New in v.111 : separated from calc_tissue(), and depends on GF value.
1432 // 2288 //
1433 static void calc_limit(void) 2289 static void calc_limit(void)
1434 { 2290 {
1435 char_O_gtissue_no = 0; // BUGFIX, changed from 255 to 0 to have a valid leading tissue number defined at any times 2291 char_O_gtissue_no = 0;
1436 calc_lead_tissue_limit = 0.0; 2292 calc_lead_tissue_limit = 0.0;
1437 2293
1438 for(ci=0; ci<NUM_COMP;ci++) 2294 // clear IBCD, microbubbles and outside warning flags (locked warnings will be preserved)
2295 char_O_deco_warnings &= ~(DECO_WARNING_IBCD + DECO_WARNING_MBUBBLES + DECO_WARNING_OUTSIDE);
2296
2297
2298 for(ci=0; ci<NUM_COMP; ci++)
1439 { 2299 {
1440 overlay float N2 = pres_tissue_N2[ci]; 2300 overlay float N2 = pres_tissue_N2[ci];
1441 overlay float He = pres_tissue_He[ci]; 2301 overlay float He = pres_tissue_He[ci];
1442 overlay float p = N2 + He; 2302 overlay float pres_tissue = N2 + He;
1443 2303 overlay float pres_min;
1444 read_buhlmann_coefficients(); 2304 overlay float gf;
1445 var_N2_a = (var_N2_a * N2 + var_He_a * He) / p; 2305 overlay float threshold;
1446 var_N2_b = (var_N2_b * N2 + var_He_b * He) / p; 2306
1447 2307 read_Buhlmann_coefficients();
1448 // Apply the Eric Baker's varying gradient factor correction. 2308 var_N2_a = (var_N2_a * N2 + var_He_a * He) / pres_tissue;
2309 var_N2_b = (var_N2_b * N2 + var_He_b * He) / pres_tissue;
2310
2311 // calculate minimum ambient pressure that the tissue can withstand according to straight Buhlmann
2312 pres_min = (pres_tissue - var_N2_a) * var_N2_b;
2313
2314 // calculate current gf value (1.0 = 100%) of this tissue
2315 gf = (pres_tissue - pres_respiration) / (pres_tissue - pres_min);
2316 if( gf < 0.0 ) gf = 0.0;
2317
2318 // calculate a threshold value for use below
2319 // ToDo: finalize the definition of the threshold
2320 threshold = 0.02 * ci + 0.9;
2321
2322 // check if this tissue is likely to develop microbubbles
2323 // and/or if this tissue is outside the Buhlmann model
2324 if( ci <= 5 )
2325 {
2326 if( gf >= threshold )
2327 {
2328 char_O_deco_warnings |= (DECO_WARNING_MBUBBLES + DECO_WARNING_MBUBBLES_lock);
2329
2330 if( gf >= 1.0 )
2331 {
2332 char_O_deco_warnings |= (DECO_WARNING_OUTSIDE + DECO_WARNING_OUTSIDE_lock);
2333 }
2334 }
2335 }
2336 else
2337 {
2338 if( gf >= 1.0 )
2339 {
2340 char_O_deco_warnings |= (DECO_WARNING_MBUBBLES + DECO_WARNING_MBUBBLES_lock);
2341
2342 if( gf >= threshold )
2343 {
2344 char_O_deco_warnings |= (DECO_WARNING_OUTSIDE + DECO_WARNING_OUTSIDE_lock);
2345 }
2346 }
2347 }
2348
2349
2350 // Apply the Eric Baker's varying gradient factor correction if the GF-Model is selected.
1449 // Note: the correction factor depends both on GF and b, 2351 // Note: the correction factor depends both on GF and b,
1450 // Actual values are in the 1.5 .. 1.0 range (for a GF=30%), 2352 // Actual values are in the 1.5 .. 1.0 range (for a GF=30%),
1451 // so that can change who is the leading gas... 2353 // so that can change who is the leading gas...
1452 // Note: Also depends of the GF. So the calcul is different for 2354 // Note: Also depends of the GF. So the calculus is different for GF_low, current GF, or GF_high...
1453 // GF_low, current GF, or GF_high... 2355 // *BUT* calc_tissue() is used to compute bottom time, hence what would happen at surface,
1454 // *BUT* calc_tissue() is used to compute bottom time,
1455 // hence what would happend at surface,
1456 // hence at GF_high. 2356 // hence at GF_high.
1457 if( char_I_deco_model != 0 ) 2357 if( char_I_deco_model != 0 ) pres_min = ( pres_tissue - var_N2_a * ( GF_high) ) * var_N2_b
1458 p = ( p - var_N2_a * GF_high) * var_N2_b 2358 / ( GF_high + var_N2_b * (1.0 - GF_high) );
1459 / (GF_high + var_N2_b * (1.0 - GF_high)); 2359
1460 else 2360 // check if this tissue requires a higher ambient pressure than was found to be needed up to now
1461 p = (p - var_N2_a) * var_N2_b; 2361 if( pres_min > calc_lead_tissue_limit )
1462 if( p < 0.0 ) p = 0.0;
1463
1464 if( p > calc_lead_tissue_limit )
1465 { 2362 {
1466 char_O_gtissue_no = ci; 2363 char_O_gtissue_no = ci;
1467 calc_lead_tissue_limit = p; 2364 calc_lead_tissue_limit = pres_min;
1468 } 2365 }
1469 } 2366 }
2367
2368 // check IBCD condition
2369 if( !IBCD_tissue_vector )
2370 {
2371 char_O_deco_warnings &= ~DECO_WARNING_IBCD; // no IBCD in any tissue, clear flag
2372 }
2373 else if( (IBCD_tissue_vector & (1 << char_O_gtissue_no))
2374 && ((pres_tissue_N2[char_O_gtissue_no] + pres_tissue_He[char_O_gtissue_no]) > pres_respiration) )
2375 {
2376 // leading tissue is in IBCD condition and in super-saturation, set flags.
2377 char_O_deco_warnings |= (DECO_WARNING_IBCD + DECO_WARNING_IBCD_lock);
2378 }
2379
2380 // check if is any tissue off-gassing
2381 if (deco_tissue_vector) char_O_deco_warnings |= DECO_FLAG; // yes, set deco flag
2382 else char_O_deco_warnings &= ~DECO_FLAG; // no, clear deco flag
2383
1470 2384
1471 assert( char_O_gtissue_no < NUM_COMP ); 2385 assert( char_O_gtissue_no < NUM_COMP );
1472 assert( 0.0 <= calc_lead_tissue_limit && calc_lead_tissue_limit <= 14.0); 2386 assert( 0.0 <= calc_lead_tissue_limit && calc_lead_tissue_limit <= 14.0);
1473 } 2387 }
1474 2388
1480 // NOTE: Erik Baker's closed formula works for Nitroxes. Trimix adds a second 2394 // NOTE: Erik Baker's closed formula works for Nitroxes. Trimix adds a second
1481 // exponential term to the M-value equation, making it impossible to 2395 // exponential term to the M-value equation, making it impossible to
1482 // invert... So we have to make a fast-simu until we find a better way. 2396 // invert... So we have to make a fast-simu until we find a better way.
1483 // 2397 //
1484 // Input: pres_respiration 2398 // Input: pres_respiration
1485 // Output: char_O_nullzeit 2399 // Output: char_O_nullzeit / char_O_alternate_nullzeit
1486 // 2400 //
1487 static void calc_nullzeit(void) 2401 static void calc_nullzeit(void)
1488 { 2402 {
2403 overlay unsigned char nullzeit = 240;
2404
2405
1489 //---- Compute ppN2 and ppHe --------------------------------------------- 2406 //---- Compute ppN2 and ppHe ---------------------------------------------
1490 temp_deco = pres_respiration; 2407 temp_deco = pres_respiration;
1491 sim_alveolar_presures(); 2408 sim_alveolar_presures();
1492 2409
1493 char_O_nullzeit = 240;
1494 for(ci=0; ci<NUM_COMP; ci++) 2410 for(ci=0; ci<NUM_COMP; ci++)
1495 { 2411 {
1496 //---- Read A/B values and loading factor for N2 and He -------------- 2412 //---- Read A/B values and loading factor for N2 and He --------------
1497 overlay float tN2 = pres_tissue_N2[ci]; 2413
1498 overlay float tHe = pres_tissue_He[ci]; 2414 overlay float tN2 = sim_pres_tissue_N2[ci];
2415 overlay float tHe = sim_pres_tissue_He[ci];
2416
1499 overlay float t = tN2 + tHe; 2417 overlay float t = tN2 + tHe;
1500 overlay unsigned char ndl; 2418 overlay unsigned char ndl;
1501 overlay unsigned char period = 10; 2419 overlay unsigned char period = 10;
1502 2420
1503 read_buhlmann_coefficients(); 2421 read_Buhlmann_coefficients();
1504 read_buhlmann_times(2); // Starts with a 10min period. 2422 read_Buhlmann_times(2); // Starts with a 10min period.
1505 2423
1506 //---- Simulate for that tissue -------------------------------------- 2424 //---- Simulate for that tissue --------------------------------------
1507 // NOTE: No need to simulate for longuer than the already found NDL. 2425 // NOTE: No need to simulate for longer than the already found NDL.
1508 for(ndl=0; ndl<char_O_nullzeit;) 2426 for(ndl=0; ndl<nullzeit;)
1509 { 2427 {
1510 //---- Compute updated mix M-value at surface 2428 //---- Compute updated mix M-value at surface
1511 overlay float a = (var_N2_a * tN2 + var_He_a * tHe) / t; 2429 overlay float a = (var_N2_a * tN2 + var_He_a * tHe) / t;
1512 overlay float b = (var_N2_b * tN2 + var_He_b * tHe) / t; 2430 overlay float b = (var_N2_b * tN2 + var_He_b * tHe) / t;
1513 overlay float M0 = (a + pres_surface/b); 2431 overlay float M0 = (a + pres_surface/b);
1514 2432
1515 //---- Add 10min/1min to N2/He tissues 2433 //---- Add 10min/1min to N2/He tissues
1516 overlay float dTN2 = (ppN2 - tN2) * var_N2_e; 2434 overlay float dTN2 = (ppN2 - tN2) * var_N2_e;
1517 overlay float dTHe = (ppHe - tHe) * var_He_e; 2435 overlay float dTHe = (ppHe - tHe) * var_He_e;
1518 2436
1519 //---- Apply security margin for both models 2437 //---- Apply safety margin for both models
1520 // NDL can be computed while ascending... SO we have 2438 // NDL can be computed while ascending... SO we have
1521 // to check wether we are saturating or desaturating. 2439 // to check if we are saturating or desaturating.
1522 if( dTN2 > 0.0 ) dTN2 *= float_saturation_multiplier; 2440 if( dTN2 > 0.0 ) dTN2 *= float_saturation_multiplier;
1523 else dTN2 *= float_desaturation_multiplier; 2441 else dTN2 *= float_desaturation_multiplier;
1524 2442
1525 if( dTHe > 0.0 ) dTHe *= float_saturation_multiplier; 2443 if( dTHe > 0.0 ) dTHe *= float_saturation_multiplier;
1526 else dTHe *= float_saturation_multiplier; 2444 else dTHe *= float_saturation_multiplier;
1527 2445
1528 if (char_I_deco_model != 0 ) 2446 // adopt M0 value when using the GF extension
1529 M0 = GF_high * (M0 - pres_surface) + pres_surface; 2447 if (char_I_deco_model != 0 ) M0 = GF_high * (M0 - pres_surface) + pres_surface;
1530 2448
1531 //---- Simulate off-gasing while going to surface 2449 //---- Simulate off-gassing while going to surface
1532 // TODO ! 2450 // TODO !
1533 // dTN2 -= exp( ... ascent time ... ppN2...) 2451 // dTN2 -= exp( ... ascent time ... ppN2...)
1534 // dTHe -= exp( ... ascent time ... ppHe...) 2452 // dTHe -= exp( ... ascent time ... ppHe...)
1535 2453
1536 //---- Ok now, and still ok to surface after 1 or 10 minutes ? 2454 //---- Ok now, and still ok to surface after 1 or 10 minutes ?
1537 if( (t <= M0) && (t + dTN2 + dTHe <= M0) ) 2455 if( (t <= M0) && (t + dTN2 + dTHe <= M0) )
1538 { 2456 {
1539 tN2 += dTN2; // YES: apply gas loadings, 2457 tN2 += dTN2; // YES: apply gas loadings,
1540 tHe += dTHe; 2458 tHe += dTHe;
1541 t = tN2 + tHe; 2459 t = tN2 + tHe;
1542 ndl += period; // increment NDL, 2460
1543 continue; // and loop. 2461 ndl += period; // increment NDL,
2462
2463 continue; // and loop.
1544 } 2464 }
1545 2465
1546 //---- Should we retry with smaller steps ? 2466 //---- Should we retry with smaller steps ?
1547 if( period == 10 ) 2467 if( period == 10 )
1548 { 2468 {
1549 read_buhlmann_times(1); // 1min coefs. 2469 read_Buhlmann_times(1); // 1min coefs.
1550 period = 1; 2470 period = 1;
2471
1551 continue; 2472 continue;
1552 } 2473 }
1553 2474
1554 //---- ELSE make a linear approx for the last minute 2475 //---- ELSE make a linear approx for the last minute
1555 // Usefull to have a meaningfull rounding of NDL. 2476 // Useful to have a meaningful rounding of NDL.
1556 // But ONLY it positive (negativ casted to unsigned is bad). 2477 // But ONLY if positive (negative casted to unsigned is bad).
1557 if( M0 > t ) 2478 if( M0 > t ) ndl += (unsigned char)(0.5f + (M0-t)/(dTN2+dTHe));
1558 ndl += (unsigned char)(0.5f + (M0-t)/(dTN2+dTHe)); 2479
1559 break; 2480 break;
1560 } 2481 }
1561 2482
1562 // Keep the shortest NDL found 2483 // Keep the shortest NDL found
1563 if( ndl < char_O_nullzeit ) 2484 if ( ndl < nullzeit ) nullzeit = ndl;
1564 char_O_nullzeit = ndl;
1565 } 2485 }
2486
2487 if( char_O_deco_status & DECO_PLAN_ALTERNATE) char_O_alternate_nullzeit = nullzeit;
2488 else char_O_nullzeit = nullzeit;
1566 } 2489 }
1567 2490
1568 ////////////////////////////////////////////////////////////////////////////// 2491 //////////////////////////////////////////////////////////////////////////////
1569 // calc_ascenttime 2492 // calc_ascenttime
1570 // 2493 //
1571 // Summup ascent from bottom to surface, at 1 bar/min, 1min for last 3 meters, 2494 // Sum up ascent from bottom to surface at float_ascent_speed,
1572 // and all stops. 2495 // but 1 minute per meter for the final ascent, and all stops.
1573 // 2496 //
1574 // Result in int_O_ascenttime, or int_O_extra_ascenttime if in @+5min variant. 2497 // Result in int_O_ascenttime,
2498 // or int_O_alternate_ascenttime if doing the alternative plan.
2499 //
1575 static void calc_ascenttime(void) 2500 static void calc_ascenttime(void)
1576 { 2501 {
1577 overlay unsigned char x; 2502 overlay unsigned char x;
1578 overlay unsigned short sum; 2503 overlay unsigned short sum;
1579 2504
1580 // + 0.7 to count 1 minute ascent time from 3 metre to surface 2505 // preset final ascent
1581 overlay float ascent = pres_respiration - pres_surface + 0.7; 2506 overlay float final = (float)char_I_depth_last_deco;
1582 if (ascent < 0.0) 2507
1583 ascent = 0.0; 2508 // calculate depth
1584 sum = (unsigned short)(ascent + 0.99); 2509 overlay float ascent = (pres_respiration - pres_surface) * BAR_TO_METER;
1585 2510
2511 // check if we are already in final ascent
2512 if (ascent <= final)
2513 {
2514 // yes - all ascent is final ascent
2515 final = ascent;
2516 ascent = 0.0;
2517 }
2518 else
2519 {
2520 // no - subtract final ascent part from overall ascent
2521 ascent -= final;
2522
2523 // compute time for ascent part without final ascent
2524 ascent /= float_ascent_speed;
2525 }
2526
2527 // add 1 minute for each meter of final ascent
2528 ascent += final;
2529
2530 // convert to integer
2531 sum = (unsigned short)(ascent + 0.5);
2532
2533 // add all stop times
1586 for(x=0; x<NUM_STOPS && internal_deco_depth[x]; x++) 2534 for(x=0; x<NUM_STOPS && internal_deco_depth[x]; x++)
1587 sum += (unsigned short)internal_deco_time[x]; 2535 sum += (unsigned short)internal_deco_time[x];
1588 2536
1589 if( char_O_deco_status == 1 ) 2537 // limit result to display max.
1590 int_O_ascenttime = sum; 2538 if( sum > 999) sum = 999;
1591 else 2539
1592 int_O_extra_ascenttime = sum; 2540 // tag result as invalid if there is an overflow in the stops table
1593 2541 if( char_O_deco_warnings & DECO_WARNING_STOPTABLE_OVERFLOW ) sum |= INT_FLAG_INVALID;
2542
2543 // route result to output variable
2544 if( char_O_deco_status & DECO_PLAN_ALTERNATE ) int_O_alternate_ascenttime = sum;
2545 else int_O_ascenttime = sum;
1594 } 2546 }
1595 2547
1596 ////////////////////////////////////////////////////////////////////////////// 2548 //////////////////////////////////////////////////////////////////////////////
1597 // update_startvalues 2549 // update_startvalues
1598 // 2550 //
1609 sim_pres_tissue_He[x] = pres_tissue_He[x]; 2561 sim_pres_tissue_He[x] = pres_tissue_He[x];
1610 } 2562 }
1611 2563
1612 // No leading tissue (yet) for this ascent simulation. 2564 // No leading tissue (yet) for this ascent simulation.
1613 sim_lead_tissue_limit = 0.0; 2565 sim_lead_tissue_limit = 0.0;
1614 sim_lead_tissue_no = 255; 2566 sim_lead_tissue_no = 1;
1615 }
1616
1617 //////////////////////////////////////////////////////////////////////////////
1618 // sim_tissue
1619 //
1620 // optimized in v.101
1621 //
1622 // Function very simular to calc_tissue, but:
1623 // + Use a 1min or 10min period.
1624 // + Do it on sim_pres_tissue, instead of pres_tissue.
1625 static void sim_tissue(PARAMETER unsigned char period)
1626 {
1627 assert( 0.00 <= ppN2 && ppN2 < 11.2 ); // 80% N2 at 130m
1628 assert( 0.00 <= ppHe && ppHe < 12.6 ); // 90% He at 130m
1629
1630 for(ci=0; ci<NUM_COMP; ci++)
1631 {
1632 read_buhlmann_times(period); // 1 or 10 minute(s) interval
1633
1634 // N2
1635 temp_tissue = (ppN2 - sim_pres_tissue_N2[ci]) * var_N2_e;
1636 temp_tissue_safety();
1637 sim_pres_tissue_N2[ci] += temp_tissue;
1638
1639 // He
1640 temp_tissue = (ppHe - sim_pres_tissue_He[ci]) * var_He_e;
1641 temp_tissue_safety();
1642 sim_pres_tissue_He[ci] += temp_tissue;
1643 }
1644 } 2567 }
1645 2568
1646 ////////////////////////////////////////////////////////////////////////////// 2569 //////////////////////////////////////////////////////////////////////////////
1647 // sim_limit() 2570 // sim_limit()
1648 // 2571 //
1649 // New in v.111 2572 // New in v.111
1650 // 2573 //
1651 // Function separated from sim_tissue() to allow recomputing limit on 2574 // Function separated from calc_tissue() to allow recomputing limit on
1652 // different depth, because it depends on current gradient factor. 2575 // different depth, because it depends on current gradient factor.
1653 // 2576 //
1654 static void sim_limit(PARAMETER float GF_current) 2577 static void sim_limit(PARAMETER float GF_current)
1655 { 2578 {
1656 assert( 0.0 < GF_current && GF_current <= 1.0f); 2579 assert( 0.0 < GF_current && GF_current <= 1.0 );
1657 2580
1658 sim_lead_tissue_limit = 0.0; 2581 sim_lead_tissue_limit = 0.0;
1659 sim_lead_tissue_no = 0; // If no one is critic, keep first tissue. 2582 sim_lead_tissue_no = 0; // If no one is critic, keep first tissue.
1660 2583
1661 for(ci=0; ci<NUM_COMP; ci++) 2584 for(ci=0; ci<NUM_COMP; ci++)
1662 { 2585 {
1663 overlay float N2 = sim_pres_tissue_N2[ci]; 2586 overlay float N2 = sim_pres_tissue_N2[ci];
1664 overlay float He = sim_pres_tissue_He[ci]; 2587 overlay float He = sim_pres_tissue_He[ci];
1665 overlay float p = N2 + He; 2588 overlay float p = N2 + He;
1666 2589
1667 read_buhlmann_coefficients(); 2590 read_Buhlmann_coefficients();
1668 var_N2_a = (var_N2_a * N2 + var_He_a * He) / p; 2591 var_N2_a = (var_N2_a * N2 + var_He_a * He) / p;
1669 var_N2_b = (var_N2_b * N2 + var_He_b * He) / p; 2592 var_N2_b = (var_N2_b * N2 + var_He_b * He) / p;
1670 2593
1671 // Apply the Eric Baker's varying gradient factor correction. 2594 // Apply the Eric Baker's varying gradient factor correction.
1672 // Note: the correction factor depends both on GF and b, 2595 // Note: the correction factor depends both on GF and b,
1673 // Actual values are in the 1.5 .. 1.0 range (for a GF=30%), 2596 // Actual values are in the 1.5 .. 1.0 range (for a GF=30%),
1674 // so that can change who is the leading gas... 2597 // so that can change who is the leading gas...
1675 // Note: Also depends of the GF_current... 2598 // Note: Also depends of the GF_current...
1676 if( char_I_deco_model != 0 ) 2599 if( char_I_deco_model != 0 ) p = ( p - (var_N2_a * GF_current) )
1677 p = ( p - var_N2_a * GF_current) 2600 / ( 1.0 - GF_current + (GF_current / var_N2_b ) );
1678 / (GF_current / var_N2_b + 1.0 - GF_current); 2601
1679 else 2602 else p = (p - var_N2_a) * var_N2_b;
1680 p = (p - var_N2_a) * var_N2_b; 2603
1681 2604
1682 if( p > sim_lead_tissue_limit ) 2605 if( p > sim_lead_tissue_limit )
1683 { 2606 {
1684 sim_lead_tissue_no = ci; 2607 sim_lead_tissue_no = ci;
1685 sim_lead_tissue_limit = p; 2608 sim_lead_tissue_limit = p;
1686 } 2609 }
1687 } // for ci 2610 } // for ci
1688 2611
1689 assert( sim_lead_tissue_no < NUM_COMP ); 2612 assert( sim_lead_tissue_no < NUM_COMP );
1691 } 2614 }
1692 2615
1693 ////////////////////////////////////////////////////////////////////////////// 2616 //////////////////////////////////////////////////////////////////////////////
1694 // clear_deco_table 2617 // clear_deco_table
1695 // 2618 //
1696 // unchanged in v.101
1697 // 2619 //
1698 static void clear_deco_table(void) 2620 static void clear_deco_table(void)
1699 { 2621 {
1700 overlay unsigned char x; 2622 overlay unsigned char x;
1701 2623
1702 for(x=0; x<NUM_STOPS; ++x) 2624 for(x=0; x<NUM_STOPS; ++x)
1703 { 2625 {
1704 internal_deco_time [x] = 0; 2626 internal_deco_time [x] = 0;
1705 internal_deco_depth[x] = 0; 2627 internal_deco_depth[x] = 0;
1706 } 2628 }
2629
2630 // clear stop table overflow warning
2631 char_O_deco_warnings &= ~DECO_WARNING_STOPTABLE_OVERFLOW;
1707 } 2632 }
1708 2633
1709 ////////////////////////////////////////////////////////////////////////////// 2634 //////////////////////////////////////////////////////////////////////////////
1710 // update_deco_table 2635 // update_deco_table
1711 // 2636 //
1712 // Add 1 min to current stop. 2637 // Add time to a stop at temp_depth_limit
1713 // 2638 //
1714 // Inputs: 2639 // It is possible to create stops with a duration of 0 minutes, e.g. to
1715 // temp_depth_limit = stop's depth, in meters. 2640 // note a gas change "on the fly" while ascending. Therefore the criteria
1716 // In/Out: 2641 // to have reached the end of the list needs always to be depth == 0.
1717 // internal_deco_depth[] : depth (in metres) of each stops. 2642 //
1718 // internal_deco_time [] : time (in minutes) of each stops. 2643 // Input: temp_depth_limit : stop's depth, in meters.
1719 // 2644 // sim_gas_last_used : gas used at stop, as index 1..5 or 0 for gas 6
1720 static unsigned char update_deco_table() 2645 // PARAMETER time_increment : number of minutes to add to the stop
1721 { 2646 //
1722 overlay unsigned char x; 2647 // Updated: internal_deco_depth[] : depth (in meters) of each stop
1723 assert( temp_depth_limit < 128 ); // Can't be negativ (overflown). 2648 // internal_deco_time [] : time (in minutes) of each stop
1724 assert( temp_depth_limit > 0 ); // No stop at surface... 2649 // internal_deco_gas [] : gas used (index 1-5) at each stop
1725 2650 //
1726 for(x=0; x<NUM_STOPS; ++x) 2651 static unsigned char update_deco_table(PARAMETER unsigned char time_increment)
1727 { 2652 {
1728 // Make sure deco-stops are recorded in order: 2653 overlay unsigned char x;
1729 assert( !internal_deco_depth[x] || temp_depth_limit <= internal_deco_depth[x] ); 2654
1730 2655 assert( temp_depth_limit > 0 ); // No stop at surface...
1731 if( internal_deco_depth[x]== temp_depth_limit ) 2656
1732 { 2657 // loop through internal deco table
1733 // Do not overflow (max 255') 2658 for(x=0; x<NUM_STOPS; ++x)
1734 if( internal_deco_time[x] < 255 ) 2659 {
1735 { 2660 // Make sure deco-stops are recorded in order:
1736 internal_deco_time[x]++; 2661 assert( !internal_deco_depth[x] || temp_depth_limit <= internal_deco_depth[x] );
1737 return 1; 2662
1738 } 2663 // Is there already a stop entry for our current depth?
1739 // But store extra in the next stop... 2664 if( internal_deco_depth[x] == temp_depth_limit )
1740 } 2665 {
1741 2666 // Yes - increment stop time if possible
1742 if( internal_deco_depth[x] == 0 ) 2667 // Stop time entries are limited to 99 minutes because of display constraints.
1743 { 2668 // Else a limit of 254 would account because of constrains in calc_CNS_planning().
1744 internal_deco_depth[x] = temp_depth_limit; 2669 if( internal_deco_time[x] < (100 - time_increment) )
1745 2670 {
1746 internal_deco_time[x] = 1; 2671 internal_deco_time[x] += time_increment; // increment stop time
1747 internal_deco_gas[x] = sim_gas_last_used; 2672 return 1; // return with status 'success'
1748 return 1; 2673 }
1749 } 2674 }
1750 } 2675
1751 2676 // If program flow passes here, there is either no stop entry for the current depth yet, or
1752 // Can't store stops at more than 96m. 2677 // the existing entry is saturated with 99 minutes. So we are looking for the next unused
1753 // Or stops at less that 3m too. 2678 // table entry.
1754 // Just do nothing with that... 2679 if( internal_deco_depth[x] == 0 )
1755 return 0; 2680 {
2681 internal_deco_time[x] = time_increment; // initialize entry with first stop's time,
2682 internal_deco_depth[x] = temp_depth_limit; // ... depth, and
2683 internal_deco_gas[x] = sim_gas_last_used; // ... gas
2684 return 1; // return with status 'success'
2685 }
2686 }
2687
2688 // If program flow passes here, all deco table entries are used up.
2689
2690 // set overflow warning
2691 char_O_deco_warnings |= DECO_WARNING_STOPTABLE_OVERFLOW;
2692
2693
2694 // return with status 'failed'.
2695 return 0;
1756 } 2696 }
1757 2697
1758 ////////////////////////////////////////////////////////////////////////////// 2698 //////////////////////////////////////////////////////////////////////////////
1759 // calc_gradient_factor 2699 // calc_gradient_factor
1760 // 2700 //
1768 overlay float He = pres_tissue_He[char_O_gtissue_no]; 2708 overlay float He = pres_tissue_He[char_O_gtissue_no];
1769 2709
1770 assert( char_O_gtissue_no < NUM_COMP ); 2710 assert( char_O_gtissue_no < NUM_COMP );
1771 assert( 0.800 <= pres_respiration && pres_respiration < 14.0 ); 2711 assert( 0.800 <= pres_respiration && pres_respiration < 14.0 );
1772 2712
1773 // tissue > respiration (currently off-gasing) 2713 // tissue > respiration (currently off-gassing)
1774 // GF = 0% when respiration == tissue, ie. bubbles are at equilibrium. 2714 // GF = 0.00 when respiration == tissue, ie. dissolved gases are at equilibrium.
1775 // GF = 100% when respiration == limit. 2715 // GF = 1.00 when respiration == limit.
1776 temp_tissue = N2 + He; 2716 temp_tissue = N2 + He;
1777 if( temp_tissue <= pres_respiration ) 2717 if( temp_tissue <= pres_respiration )
2718 {
1778 gf = 0.0; 2719 gf = 0.0;
2720 int_O_gradient_factor = 0;
2721 }
1779 else 2722 else
1780 { 2723 {
1781 overlay float limit = calc_lead_tissue_limit; 2724 overlay float limit = calc_lead_tissue_limit;
1782 // NOTE: in GF model, calc_lead_tissue_limit include already the 2725 // NOTE: in GF model, calc_lead_tissue_limit include already the
1783 // correction due to gradient factor. To compute the actual 2726 // correction due to gradient factor. To compute the actual
1784 // current GF, we need to (re-)compute the raw ambiant-pressure 2727 // current GF, we need to (re-)compute the raw ambient-pressure
1785 // limit from the Buhlmann model. 2728 // limit from the Buhlmann model.
1786 if( char_I_deco_model != 0 ) 2729 if( char_I_deco_model != 0 )
1787 { 2730 {
1788 ci = char_O_gtissue_no; 2731 ci = char_O_gtissue_no;
1789 read_buhlmann_coefficients(); 2732
2733 read_Buhlmann_coefficients();
2734
1790 var_N2_a = (var_N2_a * N2 + var_He_a * He) / temp_tissue; 2735 var_N2_a = (var_N2_a * N2 + var_He_a * He) / temp_tissue;
1791 var_N2_b = (var_N2_b * N2 + var_He_b * He) / temp_tissue; 2736 var_N2_b = (var_N2_b * N2 + var_He_b * He) / temp_tissue;
1792 limit = (temp_tissue - var_N2_a) * var_N2_b; 2737
2738 limit = (temp_tissue - var_N2_a) * var_N2_b;
1793 } 2739 }
1794 2740
1795 gf = (temp_tissue - pres_respiration) 2741 gf = (temp_tissue - pres_respiration) / (temp_tissue - limit);
1796 / (temp_tissue - limit) 2742
1797 * 100.0; 2743 // limit to 255 because of constraints in ghostwriter code
1798 if( gf > 254.5 ) gf = 255.0; 2744 if ( gf <= 0.0 ) int_O_gradient_factor = 0;
1799 if( gf < 0.0 ) gf = 0.0; 2745 else if( gf > 2.545 ) int_O_gradient_factor = 255 + INT_FLAG_WARNING;
1800 } 2746 else
1801 char_O_gradient_factor = (unsigned char)(gf+0.5f); 2747 {
1802 2748 int_O_gradient_factor = (unsigned int)(100 * gf + 0.5);
1803 } 2749
1804 2750 if ( int_O_gradient_factor >= GF_warning_threshold )
1805 ////////////////////////////////////////////////////////////////////////////// 2751 int_O_gradient_factor |= INT_FLAG_WARNING;
1806 // deco_calc_desaturation_time 2752
1807 // 2753 else if ( int_O_gradient_factor >= char_I_GF_High_percentage )
1808 // FIXED N2_ratio 2754 int_O_gradient_factor |= INT_FLAG_PREWARNING;
1809 // unchanged in v.101 2755 }
2756 }
2757 }
2758
2759 //////////////////////////////////////////////////////////////////////////////
2760 // calc_desaturation_time
2761 //
1810 // Inputs: int_I_pres_surface, ppWater, char_I_desaturation_multiplier 2762 // Inputs: int_I_pres_surface, ppWater, char_I_desaturation_multiplier
1811 // Outputs: int_O_desaturation_time, char_O_tissue_saturation[0..31] 2763 // Outputs: int_O_desaturation_time, int_O_nofly_time
1812 // 2764 //
1813 void deco_calc_desaturation_time(void) 2765 // Helper function
1814 { 2766 //
1815 RESET_C_STACK 2767 void calc_desaturation_time_helper(void)
1816 2768 {
1817 assert( 800 < int_I_pres_surface && int_I_pres_surface < 1100 ); 2769 if( pres_actual > pres_target ) // check if actual pressure is higher then target pressure
1818 assert( 0 < char_I_desaturation_multiplier && char_I_desaturation_multiplier <= 100 ); 2770 { // YES - compute remaining time
1819 2771 overlay float pres_ratio;
1820 N2_ratio = 0.7902; // FIXED sum as stated in buhlmann 2772
1821 pres_surface = int_I_pres_surface * 0.001; 2773 pres_ratio = pres_actual / pres_target;
1822 ppN2 = N2_ratio * (pres_surface - ppWater); 2774
1823 int_O_desaturation_time = 0; 2775 // Compute desaturation time with result rounded up to multiples of 10 minutes.
1824 float_desaturation_multiplier = char_I_desaturation_multiplier * (0.01 * SURFACE_DESAT_FACTOR); 2776 // Main purpose is to avoid confusion, because the times do not clock down in one minute steps any more
1825 2777 // but get constantly re-computed according to current ambient pressure and may therefor make steps of
1826 for(ci=0; ci<NUM_COMP; ci++) 2778 // several minutes forwards and backwards as ambient pressure rises and falls.
2779 short_time = (unsigned short)( (var_ht * log(pres_ratio) / desat_factor) + 0.9 );
2780 }
2781 else
2782 { // NO - desaturation state reached, no remaining time
2783 short_time = 0;
2784 }
2785 }
2786
2787 /////////////////////////////////////////////////////////////////////////////
2788 // Main function
2789 //
2790 void calc_desaturation_time(void)
2791 {
2792 assert( 800 < int_I_pres_surface && int_I_pres_surface < 1100 );
2793 assert( 0 < char_I_desaturation_multiplier && char_I_desaturation_multiplier <= 100 );
2794
2795
2796 N2_ratio = 0.7902; // fraction of N2 in respired air
2797 pres_surface = 0.001 * int_I_pres_surface; // surface pressure in bar
2798 N2_equilibrium = N2_ratio * (pres_surface - ppWater); // partial pressure of N2 in respired air
2799 desat_factor = 0.06931 * char_I_desaturation_multiplier * SURFACE_DESAT_FACTOR; // pre-computed term for later use:
2800 // 10 [Min] * 0.01 [%] * 0.6931 [ln(2)] * ...
2801 int_O_desaturation_time = 0;
2802 int_O_nofly_time = 0;
2803
2804
2805 for(ci=NUM_COMP; ci>0;)
1827 { 2806 {
1828 overlay unsigned short desat_time; // For a particular compartiment, in min. 2807 overlay float pres_tissue_max;
1829 overlay float temp1; 2808 overlay float P_ambient_altitude;
1830 overlay float temp2; 2809 overlay signed char search_direction;
1831 overlay float temp3; 2810 overlay unsigned short nofly_N2 = 0;
1832 overlay float temp4; 2811 overlay unsigned short nofly_He = 0;
1833 2812 overlay unsigned short nofly_last = ~0;
1834 read_buhlmann_ht(); 2813
1835 2814
1836 // saturation_time (for flight) and N2_saturation in multiples of halftime 2815 ci -= 1;
1837 // version v.100: 1.1 = 10 percent distance to totally clean (totally clean is not possible, would take infinite time ) 2816
1838 // new in version v.101: 1.07 = 7 percent distance to totally clean (totally clean is not possible, would take infinite time ) 2817 read_Buhlmann_ht();
1839 // changes in v.101: 1.05 = 5 percent dist to totally clean is new desaturation point for display and NoFly calculations 2818 read_Buhlmann_coefficients();
1840 // N2 2819
1841 temp1 = 1.05 * ppN2 - pres_tissue_N2[ci]; 2820 // get selected target altitude
1842 temp2 = ppN2 - pres_tissue_N2[ci]; 2821 switch( char_I_altitude_wait )
1843 if (temp2 >= 0.0) 2822 {
1844 temp1 = 0.0; 2823 case 1: P_ambient_altitude = P_ambient_1000m; break;
1845 else 2824 case 2: P_ambient_altitude = P_ambient_2000m; break;
1846 temp1 = temp1 / temp2; 2825 case 3: P_ambient_altitude = P_ambient_3000m; break;
1847 2826 default: P_ambient_altitude = P_ambient_fly; break;
1848 if( 0.0 < temp1 && temp1 < 1.0 ) 2827 }
1849 { 2828
1850 // 0.6931 is ln(2), because the math function log() calculates with a base of e not 2 as requested. 2829 // Target pressure for the tissue is the Buhlmann limit. We use the Buhlmann
1851 // minus because log is negative. 2830 // coefficients for N2 also for He because it is easier to calculate and the
1852 temp1 = log(1.0 - temp1) / -0.6931; // temp1 is the multiples of half times necessary. 2831 // N2 coefficients are more conservative than those for He, so we are on the
1853 temp2 = var_N2_ht * temp1 / float_desaturation_multiplier; // time necessary (in minutes ) for complete desaturation (see comment about 5 percent) 2832 // safe side, too.
1854 } 2833 pres_tissue_max = (P_ambient_altitude/var_N2_b + var_N2_a);
1855 else 2834
1856 { 2835 // Adjust target pressure in case the GF model is in use by GF-high
1857 temp1 = 0.0; 2836 if( char_I_deco_model != 0 )
1858 temp2 = 0.0; 2837 {
1859 } 2838 pres_tissue_max = ((pres_tissue_max - P_ambient_altitude) * char_I_GF_High_percentage * 0.01) + P_ambient_altitude;
1860 2839 }
1861 // He 2840
1862 temp3 = 0.1 - pres_tissue_He[ci]; 2841
1863 if (temp3 >= 0.0) 2842 //
1864 temp3 = 0.0; 2843 // Desaturation time
1865 else 2844 //
1866 temp3 = - temp3 / pres_tissue_He[ci]; 2845
1867 2846 // N2: actual amount of tissue pressure above equilibrium.
1868 if( 0.0 < temp3 && temp3 < 1.0 ) 2847 pres_actual = pres_tissue_N2[ci] - N2_equilibrium;
1869 { 2848
1870 temp3 = log(1.0 - temp3) / -0.6931; // temp1 is the multiples of half times necessary. 2849 // N2: half-time of the current tissue
1871 // 0.6931 is ln(2), because the math function log() calculates with a base of e not 2 as requested. 2850 var_ht = var_N2_ht;
1872 // minus because log is negative 2851
1873 temp4 = var_He_ht * temp3 / float_desaturation_multiplier; // time necessary (in minutes ) for "complete" desaturation, new in v.101 float_desaturation_multiplier 2852 // Calculate desaturation time for N2 in tissue.
1874 } 2853 // Desaturated state is defined as residual tissue pressure <= 1.05 x ppN2 respired
1875 else 2854
1876 { 2855 pres_target = 0.05 * N2_equilibrium;
1877 temp3 = 0.0; 2856
1878 temp4 = 0.0; 2857 calc_desaturation_time_helper();
1879 } 2858
1880 2859 if( short_time > int_O_desaturation_time) int_O_desaturation_time = short_time;
1881 // saturation_time (for flight) 2860
1882 if (temp4 > temp2) 2861
1883 desat_time = (unsigned short)temp4; 2862 // He: actual amount of tissue pressure above equilibrium.
1884 else 2863 pres_actual = pres_tissue_He[ci]; // equilibrium for He is 0 bar
1885 desat_time = (unsigned short)temp2; 2864
1886 2865 // He: half-time of the current tissue
1887 if(desat_time > int_O_desaturation_time) 2866 var_ht = var_He_ht;
1888 int_O_desaturation_time = desat_time; 2867
1889 2868 // Calculate desaturation time for He in the tissue.
1890 // N2 saturation in multiples of halftime for display purposes 2869 // Desaturated state is defined as residual tissue pressure <= 0.05 x ppN2 respired
1891 temp2 = temp1 * 20.0; // 0 = 1/8, 120 = 0, 249 = 8 2870
1892 temp2 = temp2 + 80.0; // set center 2871 pres_target = 0.05 * N2_equilibrium;
1893 if (temp2 < 0.0) 2872
1894 temp2 = 0.0; 2873 calc_desaturation_time_helper();
1895 if (temp2 > 255.0) 2874
1896 temp2 = 255.0; 2875 if( short_time > int_O_desaturation_time) int_O_desaturation_time = short_time;
1897 char_O_tissue_N2_saturation[ci] = (char)temp2; 2876
1898 2877
1899 // He saturation in multiples of halftime for display purposes 2878 //
1900 temp4 = temp3 * 20.0; // 0 = 1/8, 120 = 0, 249 = 8 2879 // no-fly time
1901 temp4 = temp4 + 80.0; // set center 2880 //
1902 if (temp4 < 0.0) 2881
1903 temp4 = 0.0; 2882 // initialize search direction
1904 if (temp4 > 255.0) 2883 search_direction = 0;
1905 temp4 = 255.0; 2884
1906 char_O_tissue_He_saturation[ci] = (char)temp4; 2885 for(;;)
1907 } // for 2886 {
2887 // N2: actual amount of tissue pressure above equilibrium.
2888 pres_actual = pres_tissue_N2[ci] - N2_equilibrium;
2889
2890 // N2: half-time of the current tissue
2891 var_ht = var_N2_ht;
2892
2893 // Calculate no-fly time for N2 in the tissue.
2894 // Flying is permitted when the N2 pressure fits into the assigned fraction above equilibrium.
2895
2896 pres_target = (split_N2_He[ci] * 0.01) * (pres_tissue_max - N2_equilibrium);
2897
2898 if( pres_target < 0.0 ) // check if desaturation to fly target is possible
2899 {
2900 int_O_nofly_time = 288; // NO - set no-fly time to 288 * 10 min = 48 h
2901 break; // done for this compartment
2902 }
2903 else
2904 {
2905 calc_desaturation_time_helper();
2906 nofly_N2 = short_time;
2907 }
2908
2909 // He: actual amount of tissue pressure above equilibrium - equilibrium for He is 0 bar.
2910 pres_actual = pres_tissue_He[ci];
2911
2912 // He: half-time of the current tissue
2913 var_ht = var_He_ht;
2914
2915 // Calculate no-fly time for He in the tissue.
2916 // Flying is permitted when the He pressure fits into the assigned fraction.
2917
2918 pres_target = ((100 - split_N2_He[ci]) * 0.01) * (pres_tissue_max - N2_equilibrium);
2919
2920 calc_desaturation_time_helper();
2921 nofly_He = short_time;
2922
2923
2924 // Because the sum of N2 and He tissue pressures needs to fit into the Buhlmann limit for
2925 // no-fly time calculation, each gas gets assigned a fraction of the available total pressure
2926 // limit. The optimum split between the two gases can not be computed by a single formular,
2927 // because this would require the inversion of a function with two exponential terms, which is
2928 // not possible. We do not want to do a computational complex simulation here like it is done
2929 // in the deco calculation code (although we tackle the same base problem here), so we just let
2930 // the computer try out which split will balance the no-fly times induced by the N2 and the He
2931 // at best.
2932
2933 // first of all, skip any optimization in case the current compartment is not the leading one
2934 if( (nofly_N2 <= int_O_nofly_time) && (nofly_He <= int_O_nofly_time) ) break;
2935
2936 // check if the N2 requires more waiting time than the He
2937 if( nofly_N2 >= nofly_He )
2938 {
2939 // check if the search direction has changed, which means we are beyond the
2940 // optimum now, or if we are at the upper stop limit of split_N2_He
2941 if( (search_direction < 0) || (split_N2_He[ci] == 99) )
2942 {
2943 // Either the just completed iteration was more close to the optimum or the one before
2944 // was, so we take the best (i.e. shortest) time of both as the final no-fly time.
2945 int_O_nofly_time = (nofly_N2 < nofly_last) ? nofly_N2 : nofly_last;
2946 break;
2947 }
2948
2949 // store the no-fly time found in this iteration
2950 nofly_last = nofly_N2;
2951
2952 // increase the N2 fraction of the split and set search direction towards more N2
2953 split_N2_He[ci] += 1;
2954 search_direction = +1;
2955 }
2956 else
2957 {
2958 // check if the search direction has changed, which means we are beyond the
2959 // optimum now, or if we are at the lower stop limit of split_N2_He
2960 if( (search_direction > 0) || (split_N2_He[ci] == 1) )
2961 {
2962 // Either the just completed iteration was more close to the optimum or the one before
2963 // was, so we take the best (i.e. shortest) time of both as the final no-fly time.
2964 int_O_nofly_time = (nofly_He < nofly_last) ? nofly_He : nofly_last;
2965 break;
2966 }
2967
2968 // store the no-fly time found in this iteration
2969 nofly_last = nofly_He;
2970
2971 // decrease the N2 fraction of the split and set search direction towards less N2
2972 split_N2_He[ci] -= 1;
2973 search_direction = -1;
2974 }
2975
2976 } // for(;;)
2977
2978 } // for(compartments)
2979
2980
2981 // Rescale int_O_desaturation_time and int_O_nofly_time to full minutes for display purpose
2982 int_O_desaturation_time *= 10;
2983 int_O_nofly_time *= 10;
2984
2985 // Limit int_O_desaturation_time and int_O_nofly_time to 5999 = 99 hours + 59 minutes
2986 // because of display space constraints and rounding done above.
2987 if( int_O_desaturation_time > 5999 ) int_O_desaturation_time = 5999;
2988 if( int_O_nofly_time > 5999 ) int_O_nofly_time = 5999;
2989
2990
2991 // Clear the microbubbles warning when the current gradient factor is < GF_warning_threshold.
2992 // As the locked warning will stay set, this will cause the warning be be displayed in attention
2993 // color instead of warning color.
2994 if( int_O_gradient_factor < GF_warning_threshold ) char_O_deco_warnings &= ~DECO_WARNING_MBUBBLES;
2995
2996 // clear some warnings when the desaturation time has become zero
2997 if( int_O_desaturation_time == 0 ) char_O_deco_warnings &= ~( DECO_WARNING_IBCD + DECO_WARNING_IBCD_lock
2998 + DECO_WARNING_MBUBBLES + DECO_WARNING_MBUBBLES_lock
2999 + DECO_WARNING_OUTSIDE + DECO_WARNING_OUTSIDE_lock );
3000
1908 } 3001 }
1909 3002
1910 ////////////////////////////////////////////////////////////////////////////// 3003 //////////////////////////////////////////////////////////////////////////////
1911 // calc_wo_deco_step_1_min 3004 // calc_wo_deco_step_1_min
1912 // 3005 //
1913 // FIXED N2 Ratio
1914 // optimized in v.101 (...saturation_multiplier) 3006 // optimized in v.101 (...saturation_multiplier)
1915 // desaturation slowed down to 70,42% 3007 // desaturation slowed down to 70,42%
1916 // 3008 //
3009 // Input: int_I_pres_surface [mbar]
3010 //
1917 static void calc_wo_deco_step_1_min(void) 3011 static void calc_wo_deco_step_1_min(void)
1918 { 3012 {
1919 assert( 800 < int_I_pres_surface && int_I_pres_surface < 1100 ); 3013 assert( 800 < int_I_pres_surface && int_I_pres_surface < 1100 );
1920 assert( 800 < int_I_pres_respiration && int_I_pres_respiration < 1100 ); 3014 assert( 100 <= char_I_saturation_multiplier && char_I_saturation_multiplier < 200 );
1921 assert( 100 <= char_I_saturation_multiplier && char_I_saturation_multiplier < 200 ); 3015 assert( 0 < char_I_desaturation_multiplier && char_I_desaturation_multiplier <= 100 );
1922 assert( 0 < char_I_desaturation_multiplier && char_I_desaturation_multiplier <= 100 ); 3016
1923 3017 // setup input data for deco routines
1924 N2_ratio = 0.7902; // FIXED, sum lt. buehlmann
1925 pres_respiration = pres_surface = int_I_pres_surface * 0.001; 3018 pres_respiration = pres_surface = int_I_pres_surface * 0.001;
1926 ppN2 = N2_ratio * (pres_respiration - ppWater); 3019
1927 ppHe = 0.0; 3020 N2_ratio = 0.7902; // according to Buhlmann
1928 float_desaturation_multiplier = char_I_desaturation_multiplier * (0.01 * SURFACE_DESAT_FACTOR); 3021 N2_equilibrium = N2_ratio * (pres_surface - ppWater); // used for N2 tissue graphics scaling
3022 ppN2 = N2_ratio * (pres_respiration - ppWater);
3023 ppHe = 0.0;
3024
3025 float_desaturation_multiplier = char_I_desaturation_multiplier * 0.01 * SURFACE_DESAT_FACTOR;
1929 float_saturation_multiplier = char_I_saturation_multiplier * 0.01; 3026 float_saturation_multiplier = char_I_saturation_multiplier * 0.01;
1930 3027
1931 calc_tissue(1); // update the pressure in the tissues N2/He in accordance with the new ambient pressure 3028
1932 3029 // program what to do: 128 = Flag for "real" tissues, 1 = 1 minute
1933 clear_deco_table(); 3030 tissue_increment = 128 + 1;
1934 char_O_deco_status = 3; // surface new in v.102 : stays in surface state. 3031
1935 char_O_nullzeit = 0; 3032 // update the pressure in the tissues N2/He in accordance with the new ambient pressure
1936 int_O_ascenttime = 0; 3033 calc_tissue();
1937 int_O_extra_ascenttime = 0; 3034
3035 // clock down CNS by a 1 minute step
3036 //CNS_fraction *= 0.992327946; // is done in deco_calc_CNS_decrease_15min
3037
3038 // compute integer copy of CNS value
3039 //compute_CNS_for_display(); // is done in deco_calc_CNS_decrease_15min
3040
3041 // reset deco engine start condition (probably not needed to be done here...)
3042 char_O_deco_status &= ~DECO_STATUS_MASK; // clear bits
3043 char_O_deco_status |= DECO_STATUS_INIT; // set bits
3044
3045 // reset some more data that are not applicable in surface mode
3046 char_O_nullzeit = 0;
3047 int_O_ascenttime = 0;
3048 int_O_alternate_ascenttime = 0;
3049 clear_deco_table();
3050
3051 // calculate gradient factor
1938 calc_gradient_factor(); 3052 calc_gradient_factor();
1939 } 3053 }
1940 3054
1941 ////////////////////////////////////////////////////////////////////////////// 3055 //////////////////////////////////////////////////////////////////////////////
1942 // calc_dive_interval 3056 // calc_dive_interval
1943 // 3057 //
1944 // Prepare tissue for delay before the next dive simulation. 3058 // Prepare tissue for delay before the next dive simulation.
1945 // 3059 //
1946 // Inputs: char_I_dive_interval == delay before dive (in 10' steps). 3060 // Inputs: char_I_dive_interval == delay before dive (in 1 Minute steps).
1947 // Outputs: pres_tissue_N2/He[], CNS_fraction 3061 // Modified: CNS_fraction, int_O_CNS_fraction
3062 // pres_tissue_N2/He[]
1948 // 3063 //
1949 // Should be protected by deco_push_tissues_to_vault(), 3064 // Should be protected by deco_push_tissues_to_vault(),
1950 // deco_pull_tissues_from_vault() 3065 // deco_pull_tissues_from_vault()
1951 // 3066 //
1952 // desaturation slowed down to 70,42%. 3067 // desaturation slowed down to 70,42%.
1954 static void calc_dive_interval(void) 3069 static void calc_dive_interval(void)
1955 { 3070 {
1956 overlay unsigned char t; 3071 overlay unsigned char t;
1957 3072
1958 //---- Initialize simulation parameters ---------------------------------- 3073 //---- Initialize simulation parameters ----------------------------------
1959 N2_ratio = 0.7902; // FIXED, sum lt. buehlmann
1960 pres_respiration = pres_surface = int_I_pres_surface * 0.001; 3074 pres_respiration = pres_surface = int_I_pres_surface * 0.001;
1961 ppN2 = N2_ratio * (pres_respiration - ppWater); 3075
1962 ppHe = 0.0; 3076 N2_ratio = 0.7902; // according to buehlmann
1963 float_desaturation_multiplier = char_I_desaturation_multiplier * (0.01 * SURFACE_DESAT_FACTOR); 3077 N2_equilibrium = N2_ratio * (pres_surface - ppWater); // used for N2 tissue graphics scaling
3078 ppN2 = N2_ratio * (pres_respiration - ppWater);
3079 ppHe = 0.0;
3080
3081 float_desaturation_multiplier = char_I_desaturation_multiplier * 0.01 * SURFACE_DESAT_FACTOR;
1964 float_saturation_multiplier = char_I_saturation_multiplier * 0.01; 3082 float_saturation_multiplier = char_I_saturation_multiplier * 0.01;
1965 3083
1966 //---- Perform simulation ------------------------------------------------ 3084 //---- Perform simulation ------------------------------------------------
1967 for(t=0; t<char_I_dive_interval; ++t) 3085
1968 { 3086 // Calculate tissues:
1969 calc_tissue(2); // period = 10min. 3087 // Because tissue_increment is limited to 127 minutes, we have to do two passes
1970 CNS_fraction = 0.92587471 * CNS_fraction; // Half-time = 90min: (1/2)^(1/9) 3088 // in case char_I_dive_interval is bigger than 127.
1971 } 3089 // Ops: char_I_dive_interval must be limited to 254!
1972 assert( 0.0 <= CNS_fraction && CNS_fraction <= 9.99 ); // 999 % 3090
1973 int_O_CNS_fraction = (unsigned short)(CNS_fraction * 100.0 + 0.5); 3091 t = char_I_dive_interval;
1974 3092
1975 } 3093 if ( t == 255 ) t = 254;
1976 3094
1977 ////////////////////////////////////////////////////////////////////////////// 3095 if ( t > 127 ) // extra pass needed?
1978 // deco_clear_CNS_fraction 3096 {
3097 tissue_increment = 127 // dive interval length in minutes
3098 | 128; // Flag to update the "real" tissues
3099
3100 calc_tissue(); // update tissues
3101
3102 t -= 127; // calculate remaining dive interval length
3103 }
3104
3105 tissue_increment = t // dive interval length in minutes to do
3106 | 128; // Flag to update the "real" tissues
3107 calc_tissue(); // update tissues
3108
3109
3110 // Calculate CNS:
3111 // To speed up things and because on most invocations of this code char_I_dive_interval
3112 // is a multiple of 10 minutes, we loop the loop-counter down using two speeds.
3113
3114 t = char_I_dive_interval;
3115
3116 while ( t )
3117 {
3118 if( t > 9 )
3119 {
3120 CNS_fraction *= 0.925874712; // Half-time = 90min -> 10 min: (1/2)^(1/9)
3121 t -= 10; // fast speed looping
3122 }
3123 else
3124 {
3125 CNS_fraction *= 0.992327946; // Half-time = 90min -> 1 min: (1/2)^(1/90)
3126 t -= 1; // slow speed looping
3127 }
3128 }
3129
3130 // compute integer copy of CNS value
3131 compute_CNS_for_display();
3132 }
3133
3134 //////////////////////////////////////////////////////////////////////////////
3135 // clear_CNS_fraction
1979 // 3136 //
1980 // new in v.101 3137 // new in v.101
1981 // 3138 //
1982 void deco_clear_CNS_fraction(void) 3139 void clear_CNS_fraction(void)
1983 { 3140 {
1984 RESET_C_STACK 3141 CNS_fraction = CNS_sim_norm_fraction = CNS_sim_alt_fraction = 0;
1985 3142 int_O_CNS_fraction = int_O_normal_CNS_fraction = int_O_alternate_CNS_fraction = 0;
1986 CNS_fraction = 0.0; 3143 }
1987 int_O_CNS_fraction = 0; 3144
1988 } 3145 //////////////////////////////////////////////////////////////////////////////
1989 3146 // calc_CNS_fraction
1990 ////////////////////////////////////////////////////////////////////////////// 3147 //
1991 // deco_calc_CNS_fraction 3148 // Input: char_actual_ppO2 : current ppO2 [decibars]
1992 // 3149 // tissue_increment : time increment and tissue selector
1993 // Input: char_I_actual_ppO2 : Current condition (in decibars). 3150 // CNS_fraction : current CNS% as float before period
1994 // char_I_step_is_1min : use 1min or 10min steps instead of 2sec. 3151 //
1995 // CNS_fraction : velue before period. 3152 // Output: CNS_fraction, int_O_CNS_fraction - for the real tissues
1996 // Output: CNS_fraction, int_O_CNS_fraction 3153 // CNS_sim_norm_fraction, int_O_normal_CNS_fraction - in simulation mode, normal plan
1997 // 3154 // CNS_sim_alt_fraction, int_O_alternate_CNS_fraction - in simulation mode, alternative plan
1998 void deco_calc_CNS_fraction(void) 3155 //
1999 { 3156 void calc_CNS_fraction(void)
2000 overlay float time_factor = 1.0f; 3157 {
2001 RESET_C_STACK 3158 overlay float time_factor = 1.0; // default is 2sec
2002 3159 overlay float CNS_fraction_temp = 0.0;
2003 assert( 0.0 <= CNS_fraction && CNS_fraction <= 9.99 ); 3160
2004 assert( char_I_actual_ppO2 > 15 ); 3161 assert( char_actual_ppO2 > 15 );
2005 3162
2006 if( char_I_step_is_1min == 1 ) 3163 // All deco code is now invoked every second. But as the CNS update is based on
2007 time_factor = 30.0f; 3164 // 2 seconds periods, we skip every 2nd seconds-based invocation of this function.
2008 else if( char_I_step_is_1min == 2 ) 3165 // 128 = 128 (flag for "real" CNS) + 0 (2 seconds period)
2009 time_factor = 300.0f; 3166 // To distribute computational load, the CNS% is calculated in "the other second"
3167 // than the tissues.
3168 if( (tissue_increment == 128) && (twosectimer) ) return;
3169
3170 // adjust time factor if minute-based stepping is commanded, mask out flag bit
3171 if( tissue_increment & 127 ) time_factor = 30.0 * (float)(tissue_increment & 127);
3172
3173
2010 //------------------------------------------------------------------------ 3174 //------------------------------------------------------------------------
2011 // Don't increase CNS below 0.5 bar, but keep it steady. 3175 // Don't increase CNS below 0.5 bar, but keep it steady.
2012 if (char_I_actual_ppO2 < 50) 3176 if (char_actual_ppO2 < 50)
2013 ; // no changes 3177 ; // no changes
2014 //------------------------------------------------------------------------ 3178 //------------------------------------------------------------------------
2015 // Below (and including) 1.60 bar 3179 // Below (and including) 1.60 bar
2016 else if (char_I_actual_ppO2 < 61) 3180 else if (char_actual_ppO2 < 61)
2017 CNS_fraction += time_factor/(-533.07 * char_I_actual_ppO2 + 54000.0); 3181 CNS_fraction_temp = time_factor/(-533.07 * char_actual_ppO2 + 54000.0);
2018 else if (char_I_actual_ppO2 < 71) 3182 else if (char_actual_ppO2 < 71)
2019 CNS_fraction += time_factor/(-444.22 * char_I_actual_ppO2 + 48600.0); 3183 CNS_fraction_temp = time_factor/(-444.22 * char_actual_ppO2 + 48600.0);
2020 else if (char_I_actual_ppO2 < 81) 3184 else if (char_actual_ppO2 < 81)
2021 CNS_fraction += time_factor/(-355.38 * char_I_actual_ppO2 + 42300.0); 3185 CNS_fraction_temp = time_factor/(-355.38 * char_actual_ppO2 + 42300.0);
2022 else if (char_I_actual_ppO2 < 91) 3186 else if (char_actual_ppO2 < 91)
2023 CNS_fraction += time_factor/(-266.53 * char_I_actual_ppO2 + 35100.0); 3187 CNS_fraction_temp = time_factor/(-266.53 * char_actual_ppO2 + 35100.0);
2024 else if (char_I_actual_ppO2 < 111) 3188 else if (char_actual_ppO2 < 111)
2025 CNS_fraction += time_factor/(-177.69 * char_I_actual_ppO2 + 27000.0); 3189 CNS_fraction_temp = time_factor/(-177.69 * char_actual_ppO2 + 27000.0);
2026 else if (char_I_actual_ppO2 < 152) 3190 else if (char_actual_ppO2 < 152)
2027 CNS_fraction += time_factor/( -88.84 * char_I_actual_ppO2 + 17100.0); 3191 CNS_fraction_temp = time_factor/( -88.84 * char_actual_ppO2 + 17100.0);
2028 else if (char_I_actual_ppO2 < 167) 3192 else if (char_actual_ppO2 < 167)
2029 CNS_fraction += time_factor/(-222.11 * char_I_actual_ppO2 + 37350.0); 3193 CNS_fraction_temp = time_factor/(-222.11 * char_actual_ppO2 + 37350.0);
2030 //------------------------------------------------------------------------ 3194 //------------------------------------------------------------------------
2031 // Arieli et all.(2002): Modeling pulmonary and CNS O2 toxicity: 3195 // Arieli et all.(2002): Modeling pulmonary and CNS O2 toxicity:
2032 // J Appl Physiol 92: 248--256, 2002, doi:10.1152/japplphysiol.00434.2001 3196 // J Appl Physiol 92: 248--256, 2002, doi:10.1152/japplphysiol.00434.2001
2033 // Formula (A1) based on value for 1.55 and c=20 3197 // Formula (A1) based on value for 1.55 and c=20
2034 // example calculation: Sqrt((1.7/1.55)^20)*0.000404 3198 // example calculation: Sqrt((1.7/1.55)^20)*0.000404
2035 else if (char_I_actual_ppO2 < 172) 3199 else if (char_actual_ppO2 < 172)
2036 CNS_fraction += time_factor*0.00102; 3200 CNS_fraction_temp = time_factor*0.00102;
2037 else if (char_I_actual_ppO2 < 177) 3201 else if (char_actual_ppO2 < 177)
2038 CNS_fraction += time_factor*0.00136; 3202 CNS_fraction_temp = time_factor*0.00136;
2039 else if (char_I_actual_ppO2 < 182) 3203 else if (char_actual_ppO2 < 182)
2040 CNS_fraction += time_factor*0.00180; 3204 CNS_fraction_temp = time_factor*0.00180;
2041 else if (char_I_actual_ppO2 < 187) 3205 else if (char_actual_ppO2 < 187)
2042 CNS_fraction += time_factor*0.00237; 3206 CNS_fraction_temp = time_factor*0.00237;
2043 else if (char_I_actual_ppO2 < 192) 3207 else if (char_actual_ppO2 < 192)
2044 CNS_fraction += time_factor*0.00310; 3208 CNS_fraction_temp = time_factor*0.00310;
2045 else if (char_I_actual_ppO2 < 198) 3209 else if (char_actual_ppO2 < 198)
2046 CNS_fraction += time_factor*0.00401; 3210 CNS_fraction_temp = time_factor*0.00401;
2047 else if (char_I_actual_ppO2 < 203) 3211 else if (char_actual_ppO2 < 203)
2048 CNS_fraction += time_factor*0.00517; 3212 CNS_fraction_temp = time_factor*0.00517;
2049 else if (char_I_actual_ppO2 < 233) 3213 else if (char_actual_ppO2 < 233)
2050 CNS_fraction += time_factor*0.0209; 3214 CNS_fraction_temp = time_factor*0.0209;
2051 else 3215 else
2052 CNS_fraction += time_factor*0.0482; // value for 2.5 3216 CNS_fraction_temp = time_factor*0.0482; // value for 2.5 bar, used for 2.33 bar and above
2053 3217
2054 if( CNS_fraction > 9.99) // Limit display to 999% 3218
2055 CNS_fraction = 9.99; 3219 // Check from where we were called:
2056 if( CNS_fraction < 0.0 ) 3220 // flag (bit 7) is set -> we were called from calc_hauptroutine()
2057 CNS_fraction = 0.0; 3221 // flag (bit 7) not set -> we were called from the deco planning routines
2058 3222 if ( tissue_increment & 128 ) CNS_fraction += CNS_fraction_temp; // real tissues
2059 int_O_CNS_fraction = (unsigned short)(100.0 * CNS_fraction + 0.5); 3223 else if ( char_O_deco_status & DECO_PLAN_ALTERNATE ) CNS_sim_alt_fraction += CNS_fraction_temp; // alternative plan
2060 } 3224 else CNS_sim_norm_fraction += CNS_fraction_temp; // normal plan
2061 3225
2062 ////////////////////////////////////////////////////////////////////////////// 3226 }
2063 // deco_calc_CNS_planning 3227
3228 //////////////////////////////////////////////////////////////////////////////
3229 // calc_CNS_planning
2064 // 3230 //
2065 // Compute CNS during predicted ascent. 3231 // Compute CNS during predicted ascent.
2066 // 3232 //
2067 // Note: Needs a call to deco_push_tissues_to_vault(), 3233 // Note: Needs a call to deco_push_tissues_to_vault(),
2068 // deco_pull_tissues_from_vault() to avoid trashing everything... 3234 // deco_pull_tissues_from_vault() to avoid trashing everything...
2069 // 3235 //
2070 // Input: CNS_fraction, char_O_deco_time[], char_O_deco_depth[] 3236 // Input: CNS_fraction, internal_deco_time[], internal_deco_depth[], internal_deco_gas[]
2071 // Output: CNS_fraction, int_O_CNS_fraction 3237 // Output: CNS_fraction, int_O_normal_CNS_fraction / int_O_alternate_CNS_fraction
2072 // 3238 //
2073 void deco_calc_CNS_planning(void) 3239 void calc_CNS_planning(void)
2074 { 3240 {
2075 overlay unsigned char backup_gas_last_depth; 3241 // start with CNS% we already have
2076 overlay unsigned char backup_gas_last_used; 3242 if( char_O_deco_status & DECO_PLAN_ALTERNATE ) CNS_sim_alt_fraction = CNS_fraction;
2077 overlay unsigned short backup_dive_mins; 3243 else CNS_sim_norm_fraction = CNS_fraction;
2078 overlay unsigned char backup_actual_ppO2; 3244
2079 3245
2080 RESET_C_STACK 3246 //---- CCR mode : do the full TTS at once ---------------------------------
2081 3247
2082 // Backup state machine 3248 if( ((char_O_deco_status & DECO_MODE_MASK) == DECO_MODE_CCR) )
2083 backup_gas_last_depth = sim_gas_last_depth;
2084 backup_gas_last_used = sim_gas_last_used;
2085 backup_dive_mins = sim_dive_mins;
2086 backup_actual_ppO2 = char_I_actual_ppO2;
2087
2088 // Uses 1min CNS period:
2089 char_I_step_is_1min = 1;
2090
2091 //---- Retrieve bottom Gas used, and set variables.
2092 sim_gas_last_used = char_I_first_gas;
2093 sim_gas_last_depth = 0; // Surface gas marker.
2094 gas_switch_set(); // Sets initial calc_N2/He_ratio
2095
2096 //---- CCR mode : do the full TTS at once --------------------------------
2097 if( char_I_const_ppO2 != 0 )
2098 { 3249 {
2099 overlay unsigned short t; // Needs 16bits here ! 3250 overlay unsigned short t; // needs 16 bits here !
2100 char_I_actual_ppO2 = char_I_const_ppO2; 3251
2101 for(t=0; t<int_O_ascenttime; ++t) 3252 // get current ppO2 from sensors or setpoint
2102 deco_calc_CNS_fraction(); 3253 char_actual_ppO2 = char_I_const_ppO2;
3254
3255 // calculate CNS% for the period of additional staying at bottom depth (fTTS / delayed ascent)
3256 if( char_O_deco_status & DECO_ASCENT_DELAYED)
3257 {
3258 tissue_increment = char_I_extra_time; // must be limited to 127, is limited by range of char_I_extra_time
3259 calc_CNS_fraction();
3260 }
3261
3262 // get the ascent time dependent on the current plan
3263 t = (char_O_deco_status & DECO_PLAN_ALTERNATE) ? int_O_alternate_ascenttime : int_O_ascenttime;
3264
3265 // start simulating CNS% in chunks of 127 minutes
3266 tissue_increment = 127;
3267
3268 while( t > 127 )
3269 {
3270 t -= 127; // tissue_increment is limited to 127 minutes because of flag in bit 7
3271 calc_CNS_fraction(); // calculate CNS in chunks of full 127 minutes
3272 }
3273
3274 tissue_increment = (char)t; // get the remaining minutes <= 127
3275 calc_CNS_fraction(); // calculate CNS for the remaining minutes
2103 } 3276 }
2104 else //---- OC mode : have to follow all gas switches... ----------------- 3277 else //---- OC mode and pSCR without sensors: have to follow all gas switches... -----
2105 { 3278 {
2106 overlay unsigned char i = 0; // Decostop loop counter 3279 overlay float float_actual_ppO2;
2107 overlay float actual_ppO2; 3280 overlay float abs_pres;
2108 overlay unsigned char time, t; 3281
2109 3282 overlay unsigned char stop_depth;
2110 //---- Ascent to surface delay 3283 overlay unsigned char last_gas;
2111 // NOTE: count as if time is spent with bottom pressure, 3284 overlay unsigned char i; // stop table index
2112 // AND the bottom gas 3285
2113 actual_ppO2 = (pres_surface + char_I_bottom_depth * METER_TO_BAR) 3286
2114 * (1.0 - calc_N2_ratio - calc_He_ratio); 3287 // retrieve bottom gas: 1-5 for the configured gases or 0 for the manually set gas
2115 if( actual_ppO2 < 0.0 ) actual_ppO2 = 0.0; 3288 last_gas = sim_gas_last_used = sim_gas_first_used;
2116 if( actual_ppO2 > 2.50 ) actual_ppO2 = 2.55; 3289
2117 char_I_actual_ppO2 = (unsigned char)(100.0 * actual_ppO2 + 0.5); 3290 // get the calc_N2/He/O2_ratios of the bottom gas
2118 3291 gas_switch_set();
2119 // Ascent time (rounded up): 3292
2120 time = (unsigned char)(0.1 * char_I_bottom_depth + 0.5); 3293 // calculate absolute pressure
2121 3294 abs_pres = pres_surface + bottom_depth * METER_TO_BAR;
2122 for(t=0; t<time; ++t) 3295
2123 { 3296 // switch on deco mode pSCR / OC
2124 deco_calc_CNS_fraction(); 3297 if( char_O_deco_status & DECO_MODE_PSCR )
2125 sim_dive_mins++; 3298 {
2126 } 3299 //---- pSCR calculated --------------------------------------------
2127 3300
2128 //---- Do all further stops ------------------------------------------ 3301 // abs_pres is 0.0 ... in bar
3302 // calc_O2_ratio is 0.0 ... 1.0 as factor
3303 // char_I_PSCR_drop is 0 ... 15 as %
3304 // char_I_PSCR_lungratio is 5 ... 20 as %
3305 // float_actual_ppO2 is 0.0 ... in cbar (!)
3306
3307 float_actual_ppO2 = (100 * abs_pres * calc_O2_ratio)
3308 - (1.0 - calc_O2_ratio) * char_I_PSCR_drop * char_I_PSCR_lungratio;
3309 }
3310 else
3311 {
3312 //---- OC ---------------------------------------------------------
3313
3314 float_actual_ppO2 = abs_pres * calc_O2_ratio * 100; // in cbar (!)
3315 }
3316
3317 // caution: float_actual_ppO2 is in cbar here!
3318 if ( float_actual_ppO2 < 0.0 ) char_actual_ppO2 = 0;
3319 else if ( float_actual_ppO2 > 254.5 ) char_actual_ppO2 = 255;
3320 else char_actual_ppO2 = (unsigned char)(float_actual_ppO2 + 0.5);
3321
3322
3323 // simulate extended bottom time (fTTS) / delay before ascent (bailout) if configured
3324 if( char_O_deco_status & DECO_ASCENT_DELAYED )
3325 {
3326 tissue_increment = char_I_extra_time; // must be limited to 127, is limited by range of char_I_extra_time
3327 calc_CNS_fraction();
3328 }
3329
3330
3331 // For simplicity reason (non-linearity of the relation between ppO2 and CNS increments), the
3332 // whole ascent is calculated with bottom ppO2. This errs, but it does so to the safe side.
3333
3334 // calculate ascent time (integer division and generous round-up)
3335 tissue_increment = bottom_depth / char_I_ascent_speed + 1;
3336
3337 // ** commented out - not needed when char_I_ascent_speed is limited to a
3338 // ** minimum of 2.something, it is indeed limited to 5.
3339 //
3340 // // limit tissue_increment to 127 minutes
3341 // if( tissue_increment > 127 ) tissue_increment = 127;
3342
3343 // simulate the CNS increase
3344 calc_CNS_fraction();
3345
3346
3347 //---- Stops ---------------------------------------------------------
3348
2129 for(i=0; i<NUM_STOPS; ++i) 3349 for(i=0; i<NUM_STOPS; ++i)
2130 { 3350 {
2131 overlay unsigned char stop_gas; 3351 // get the depth of the stop
2132 3352 stop_depth = internal_deco_depth[i];
2133 //---- Get next stop --------------------------------------------- 3353
3354 // did we reach the last entry (depth = 0)? if yes, done
3355 if (stop_depth == 0) break;
3356
3357 // get the duration of the stop and the gas breathed
3358 tissue_increment = internal_deco_time[i];
3359 sim_gas_last_used = internal_deco_gas[i];
3360
3361 // do we have a gas switch?
3362 if( sim_gas_last_used != last_gas )
2134 { 3363 {
2135 time = char_O_deco_time[(NUM_STOPS-1)-i]; 3364 // yes - get new calc ratios
2136 temp_depth_limit = char_O_deco_depth[(NUM_STOPS-1)-i]; 3365 gas_switch_set();
2137 stop_gas = char_O_deco_gas[(NUM_STOPS-1)-i]; 3366
2138 } 3367 // remember new gas as last gas
2139 if( time == 0 ) continue; 3368 last_gas = sim_gas_last_used;
2140 3369 }
2141 //---- Gas Switch ? ---------------------------------------------- 3370
2142 if( stop_gas != sim_gas_last_used ) 3371 // calculate absolute pressure at stop depth
2143 { 3372 abs_pres = pres_surface + stop_depth * METER_TO_BAR;
2144 sim_gas_last_depth = deco_gas_change[stop_gas-1]; 3373
2145 sim_gas_last_used = stop_gas; 3374 // pSCR mode
2146 gas_switch_set(); 3375 if( char_O_deco_status & DECO_MODE_PSCR )
2147 } 3376 {
2148 3377 // abs_pres is 0.0 ... in bar
2149 //---- Convert Depth and N2_ratio to ppO2 ------------------------ 3378 // calc_O2_ratio is 0.0 ... 1.0 as factor
2150 actual_ppO2 = (pres_surface + temp_depth_limit * METER_TO_BAR) 3379 // char_I_PSCR_drop is 0 ... 15 as %
2151 * (1.0 - calc_N2_ratio - calc_He_ratio); 3380 // char_I_PSCR_lungratio is 5 ... 20 as %
2152 if( actual_ppO2 < 0.0 ) actual_ppO2 = 0.0; 3381 // float_actual_ppO2 is 0.0 ... in cbar (!)
2153 if( actual_ppO2 > 2.50 ) actual_ppO2 = 2.55; 3382
2154 char_I_actual_ppO2 = (unsigned char)(100.0 * actual_ppO2 + 0.5); 3383 float_actual_ppO2 = (100 * abs_pres * calc_O2_ratio)
2155 3384 - (1.0 - calc_O2_ratio) * char_I_PSCR_drop * char_I_PSCR_lungratio;
2156 //---- Apply the stop 3385 }
2157 for(t=0; t<time; ++t) 3386 else // OC mode
2158 { 3387 {
2159 deco_calc_CNS_fraction(); 3388 float_actual_ppO2 = abs_pres * calc_O2_ratio * 100; // in cbar (!)
2160 sim_dive_mins++; 3389 }
2161 } 3390
3391 // caution: float_actual_ppO2 is in cbar here!
3392 if ( float_actual_ppO2 < 0.0 ) char_actual_ppO2 = 0;
3393 else if ( float_actual_ppO2 > 254.5 ) char_actual_ppO2 = 255;
3394 else char_actual_ppO2 = (unsigned char)(float_actual_ppO2 + 0.5);
3395
3396
3397 // ** Currently, stop times per stop entry are limited to 99 minutes in update_deco_table(),
3398 // ** so the following code block is not needed at times.
3399 //
3400 // // tissue_increment is limited to 127 when fed to deco_calc_CNS_fraction(),
3401 // // so if the stop is longer than 127 minutes (but not longer than 254 minutes!)
3402 // // we need to calculate the CNS in two chunks.
3403 // if( tissue_increment > 127)
3404 // {
3405 // tissue_increment -= 127; // subtract full 127 minutes and do the "remaining" minutes first
3406 // calc_CNS_fraction();
3407 // tissue_increment = 127; // catch up with the previously subtracted full 127 minutes
3408 // }
3409
3410 // calculate CNS% for the stop
3411 calc_CNS_fraction();
2162 } 3412 }
2163 } 3413 }
2164 3414 }
2165 //---- Back to normal mode... -------------------------------------------- 3415
2166 char_I_step_is_1min = 0; 3416
2167 sim_gas_last_depth = backup_gas_last_depth; 3417 //////////////////////////////////////////////////////////////////////////////
2168 sim_gas_last_used = backup_gas_last_used; 3418 // gas_volumes
2169 sim_dive_mins = backup_dive_mins; 3419 //
2170 char_I_actual_ppO2 = backup_actual_ppO2; 3420 // calculates volumes and required tank fill pressures for each gas.
2171 } 3421 //
2172 3422 // Input: bottom_depth depth of the bottom segment
2173 ////////////////////////////////////////////////////////////////////////////// 3423 // char_I_bottom_time duration of the bottom segment
2174 // deco_calc_CNS_decrease_15min 3424 // char_I_extra_time extra bottom time for fTTS / delayed ascent
2175 // 3425 // float_ascent_speed ascent speed, in meters/minute
2176 // new in v.101 3426 // sim_gas_first_used the bottom gas (1-5 for configured gases, 0 for the manual gas)
2177 // 3427 // internal_deco_depth[] depth of the stops
2178 // calculates the half time of 90 minutes in 6 steps of 15 min 3428 // internal_deco_time[] duration of the stops
2179 // (Used in sleepmode, for low battery mode). 3429 // internal_deco_gas[] gas breathed at the stops
2180 // 3430 // char_I_bottom_usage gas consumption during bottom part and initial ascent, in liters/minute
2181 // Output: int_O_CNS_fraction 3431 // char_I_deco_usage gas consumption during stops and following ascents, in liters/minute
2182 // Uses and Updates: CNS_fraction 3432 // char_I_tank_size[] size of the tanks for gas 1-5, in liters
2183 // 3433 // char_I_tank_pres_fill[] fill pressure of the tanks
2184 void deco_calc_CNS_decrease_15min(void) 3434 //
2185 { 3435 // Output: int_O_gas_volumes[] amount of gas needed, in liters
3436 // int_O_tank_pres_need[] in bar, + flags for fast evaluation by dive mode warnings:
3437 // 2^15: pres_need >= pres_fill
3438 // 2^14: pres_need >= press_fill * GAS_NEEDS_ATTENTION_THRESHOLD
3439 // 2^11: pres_need == 0
3440 // 2^10: pres_need invalid
3441 //
3442 void gas_volumes_helper(void)
3443 {
3444 // Calculate the gas volume needed at a given depth, time and usage (SAC rate).
3445 // We use 1.0 for the surface pressure to have stable results when used through
3446 // the deco calculator (simulation mode).
3447 volume = (float_depth * METER_TO_BAR + 1.0) * float_time * usage;
3448
3449 return;
3450 }
3451
3452 void gas_volumes(void)
3453 {
3454 overlay float volumes[NUM_GAS];
3455
3456 overlay unsigned char stop_gas;
3457 overlay unsigned char stop_gas_last;
3458 overlay unsigned char stop_time;
3459 overlay unsigned char stop_depth;
3460 overlay unsigned char stop_depth_last;
3461 overlay unsigned char i;
3462
3463
3464 //---- initialization ----------------------------------------------------
3465
3466 // null the volume accumulators
3467 for(i=0; i<NUM_GAS; ++i) volumes[i] = 0.0;
3468
3469 // quit for CCR and pSCR mode
3470 if( char_O_deco_status & DECO_MODE_LOOP ) goto done;
3471
3472
3473 //---- bottom demand -----------------------------------------------------
3474
3475 // sim_gas_first_used : gas used during bottom segment (0, 1-5)
3476 // bottom_depth: depth of the bottom segment
3477
3478 assert(0 <= sim_gas_first_used && sim_gas_first_used <= NUM_GAS);
3479
3480 // get the gas used during bottom segment
3481 stop_gas_last = stop_gas = sim_gas_first_used;
3482
3483 // set the usage (SAC rate) to bottom usage rate for bottom part and initial ascent
3484 usage = char_I_bottom_usage;
3485
3486 // volumes are only calculated for gases 1-5, but not the manually configured one
3487 if( stop_gas )
3488 {
3489 // set the bottom depth
3490 float_depth = (float)bottom_depth;
3491
3492 // calculate either bottom segment or just the fTTS/bailout delayed part
3493 if( char_O_main_status & DECO_BOTTOM_CALCULATE )
3494 {
3495 // duration of bottom segment
3496 float_time = (float)char_I_bottom_time;
3497 }
3498 else
3499 {
3500 // duration of delayed ascent
3501 float_time = (float)char_I_extra_time;
3502 }
3503
3504 // calculate gas demand
3505 gas_volumes_helper();
3506
3507 // take result
3508 volumes[stop_gas-1] = volume;
3509 }
3510
3511
3512 // initialize stop index with first stop
3513 i = 0;
3514
3515
3516 //---- initial ascent demand ---------------------------------------------
3517
3518 // stop_gas : gas from bottom segment
3519 // bottom_depth : depth of the bottom segment in meters
3520 // internal_deco_depth[i=0]: depth of the first stop, may be 0 if no stop exists
3521
3522 // get the data of the first stop
3523 stop_depth = internal_deco_depth[i];
3524 stop_time = internal_deco_time[i];
3525
3526 // volumes are only calculated for gases 1-5, but not the manually configured one
3527 if( stop_gas )
3528 {
3529 // compute distance between bottom and first stop
3530 float_depth = (float)bottom_depth - (float)stop_depth;
3531
3532 // initial ascent exists only if ascent distance is > 0
3533 if( float_depth > 0.0 )
3534 {
3535 // compute ascent time
3536 float_time = float_depth / float_ascent_speed;
3537
3538 // compute average depth between bottom and first stop
3539 float_depth = (float)bottom_depth - float_depth * 0.5;
3540
3541 // calculate gas demand
3542 gas_volumes_helper();
3543
3544 // add result
3545 volumes[stop_gas-1] += volume;
3546 }
3547 }
3548
3549 // switch the usage (SAC rate) to deco usage rate
3550 // for stops, intermediate and final ascent
3551 usage = char_I_deco_usage;
3552
3553 // is there a (first) stop? if yes, goto stops processing
3554 if( stop_depth ) goto stops;
3555
3556 // add demand of a 3 minutes safety stop at 5 meters, at least for contingency...
3557 float_time = 3.0;
3558 float_depth = 5.0;
3559
3560 // calculate gas demand
3561 gas_volumes_helper();
3562
3563 // add result
3564 volumes[stop_gas-1] += volume;
3565
3566 // proceed to volume conversion and pressure calculations
3567 goto done;
3568
3569
3570 //---- intermediate ascent demand ---------------------------------------
3571 inter_ascents:
3572
3573 // store last stop depth and gas
3574 stop_depth_last = stop_depth;
3575 stop_gas_last = stop_gas;
3576
3577 // check if we are at the end of the stops table
3578 if( i < NUM_STOPS-1 )
3579 {
3580 // there are more entries - get the next stop data
3581 i++;
3582
3583 // get the next stop depth
3584 stop_depth = internal_deco_depth[i];
3585
3586 // check if there is indeed another stop,
3587 // if not (depth = 0) treat as end of table
3588 if( stop_depth == 0 ) goto end_of_table;
3589
3590 // get the next stop duration
3591 stop_time = internal_deco_time[i];
3592 }
3593 else
3594 {
3595 end_of_table:
3596
3597 // End of the stops table reached or no more stops: Split the remaining
3598 // ascent into an intermediate ascent and a final ascent by creating a
3599 // dummy stop at the usual last deco stop depth. Stop gas doesn't change.
3600 stop_time = 0;
3601 stop_depth = char_I_depth_last_deco;
3602 }
3603
3604 // volumes are only calculated for gases 1-5, but not the manually configured one
3605 if( stop_gas_last )
3606 {
3607 // compute distance between the two stops:
3608 // last stop will always be deeper than current stop
3609 float_depth = (float)(stop_depth_last - stop_depth);
3610
3611 // compute ascent time
3612 float_time = float_depth / float_ascent_speed;
3613
3614 // compute average depth between the two stops
3615 float_depth = (float)stop_depth_last - float_depth * 0.5;
3616
3617 // calculate gas demand
3618 gas_volumes_helper();
3619
3620 // add result
3621 volumes[stop_gas_last-1] += volume;
3622 }
3623
3624
3625 //---- next stop demand -------------------------------------------------
3626 stops:
3627
3628 // convert depth of the stop
3629 float_depth = (float)stop_depth;
3630
3631 // get the next gas
3632 stop_gas = internal_deco_gas[i];
3633
3634 // do we we have a gas change?
3635 if( stop_gas_last && (stop_gas != stop_gas_last) )
3636 {
3637 // yes - spend an additional char_I_gas_change_time on the old gas
3638 float_time = (float)char_I_gas_change_time;
3639
3640 // calculate gas demand
3641 gas_volumes_helper();
3642
3643 // add result
3644 volumes[stop_gas_last-1] += volume;
3645 }
3646
3647 // calculate and add demand on new gas for the full stop duration
3648 if( stop_gas )
3649 {
3650 // get the duration of the stop
3651 float_time = (float)stop_time;
3652
3653 // calculate gas demand
3654 gas_volumes_helper();
3655
3656 // add result to last gas
3657 volumes[stop_gas-1] += volume;
3658 }
3659
3660 // continue with the next intermediate ascent if this was not the last stop
3661 if( stop_depth > char_I_depth_last_deco ) goto inter_ascents;
3662
3663
3664 //---- final ascent demand -----------------------------------------------
3665 final_ascent:
3666
3667 // float_depth: depth of last stop
3668 // stop_gas : gas from last stop (0 or 1-5)
3669
3670 // volumes are only calculated for gases 1-5, but not the manually configured one
3671 if( stop_gas )
3672 {
3673 // set ascent time according to an ascent speed of 1 meter per minute
3674 float_time = float_depth;
3675
3676 // set half-way depth
3677 float_depth *= 0.5;
3678
3679 // calculate gas demand
3680 gas_volumes_helper();
3681
3682 // add result
3683 volumes[stop_gas-1] += volume;
3684 }
3685
3686
3687 //---- convert results for the assembler interface -----------------------------
3688 done:
3689
3690 for(i=0; i<NUM_GAS; ++i)
3691 {
3692 if( volumes[i] >= 65534.5 )
3693 {
3694 int_O_gas_volumes[i] = 65535;
3695 int_O_tank_pres_need[i] = 999 + INT_FLAG_WARNING; // 999 bar + warning flag for > pres_fill
3696 }
3697 else
3698 {
3699 overlay unsigned short tank_pres_fill = 10.0 * (unsigned short)char_I_tank_pres_fill[i];
3700
3701 // No distinct rounding done here because volumes are not accurate to the single liter anyhow
3702
3703 // convert gas volumes to integers
3704 int_O_gas_volumes[i] = (unsigned short)volumes[i];
3705
3706 // compute how much pressure in the tank will be needed [in bar] (integer-division)
3707 int_O_tank_pres_need[i] = (unsigned short)(int_O_gas_volumes[i] / char_I_tank_size[i]);
3708
3709 // limit to 999 bar because of display constraints
3710 if( int_O_tank_pres_need[i] > 999 ) int_O_tank_pres_need[i] = 999;
3711
3712 // set flags for fast evaluation by divemode check for warnings
3713 if ( int_O_tank_pres_need[i] == 0 )
3714 {
3715 // set flag for 0 bar
3716 int_O_tank_pres_need[i] |= INT_FLAG_ZERO;
3717 }
3718 else if( int_O_tank_pres_need[i] >= tank_pres_fill )
3719 {
3720 // set warning flag
3721 int_O_tank_pres_need[i] |= INT_FLAG_WARNING;
3722
3723 }
3724 else if( int_O_tank_pres_need[i] >= tank_pres_fill * GAS_NEEDS_ATTENTION_THRESHOLD )
3725 {
3726 // set pre-warning flag
3727 int_O_tank_pres_need[i] |= INT_FLAG_PREWARNING;
3728 }
3729
3730 // set invalid flag if there is an overflow in the stops table
3731 if( char_O_deco_warnings & DECO_WARNING_STOPTABLE_OVERFLOW )
3732 int_O_tank_pres_need[i] |= INT_FLAG_INVALID;
3733
3734 } // if( volumes[i] )
3735 } // for
3736 }
3737
3738 //////////////////////////////////////////////////////////////////////////////
3739
3740 void compute_CNS_for_display(void)
3741 {
3742 if ( CNS_fraction < 0.01 ) int_O_CNS_fraction = 0;
3743 else if ( CNS_fraction >= 9.985 ) int_O_CNS_fraction = 999 + INT_FLAG_WARNING;
3744 else
3745 {
3746 // convert float to integer
3747 int_O_CNS_fraction = (unsigned short)(100 * CNS_fraction + 0.5);
3748
3749 // compute warnings
3750 if ( int_O_CNS_fraction >= CNS_warning_threshold )
3751 {
3752 // reset pre-warning and set main warning flag
3753 int_O_CNS_fraction &= ~INT_FLAG_PREWARNING;
3754 int_O_CNS_fraction |= INT_FLAG_WARNING;
3755 }
3756 else if ( int_O_CNS_fraction >= CNS_prewarning_threshold )
3757 {
3758 // reset main warning but set pre-warning flag
3759 int_O_CNS_fraction &= ~INT_FLAG_WARNING;
3760 int_O_CNS_fraction |= INT_FLAG_PREWARNING;
3761 }
3762 else
3763 {
3764 // clear both warnings
3765 int_O_CNS_fraction &= ~(INT_FLAG_WARNING + INT_FLAG_PREWARNING);
3766 }
3767 }
3768 }
3769
3770 //////////////////////////////////////////////////////////////////////////////
3771
3772 void deco_push_tissues_to_vault(void)
3773 {
3774 overlay unsigned char x;
3775
2186 RESET_C_STACK 3776 RESET_C_STACK
2187 assert( 0.0 <= CNS_fraction && CNS_fraction <= 9.99 ); 3777
2188 3778 low_depth_norm_vault = low_depth_norm;
2189 CNS_fraction = 0.890899 * CNS_fraction; 3779 low_depth_alt_vault = low_depth_alt;
2190 int_O_CNS_fraction = (unsigned short)(CNS_fraction * 100.0 + 0.5); 3780 cns_vault_float = CNS_fraction;
2191 } 3781 cns_vault_int = int_O_CNS_fraction;
2192 3782 deco_warnings_vault = char_O_deco_warnings;
2193 //////////////////////////////////////////////////////////////////////////////
2194 // deco_calc_percentage
2195 //
2196 // new in v.101
2197 //
2198 // calculates int_I_temp * char_I_temp / 100
2199 // output is int_I_temp
2200 //
2201 // Used to compute NoFly remaining time.
2202 //
2203 void deco_calc_percentage(void)
2204 {
2205 RESET_C_STACK
2206
2207 assert( 60 <= char_I_temp && char_I_temp <= 100 );
2208 assert( int_I_temp < 5760 ); // Less than 4 days = 96h...
2209
2210 int_I_temp = (unsigned short)(((float)int_I_temp * (float)char_I_temp) * 0.01 );
2211
2212 assert( int_I_temp < 5760 ); // Less than 96h too...
2213 }
2214
2215 //////////////////////////////////////////////////////////////////////////////
2216 // deco_gas_volumes
2217 //
2218 // new in v.111
2219 //
2220 // calculates volumes for each gas.
2221 //
2222 // Input: char_I_bottom_depth, char_I_bottom_time for planned dive.
2223 // Gas list.
2224 // char_I_first_gas is the bottom gas.
2225 // decoplan (char_O_deco_depth, char_O_deco_time).
2226 // char_I_bottom_usage is bottom liters/minutes (5 .. 50) or bar/min.
2227 // char_I_deco_usage is deco liters/minutes (5 .. 50) or bar/min.
2228 // Output: int_O_gas_volumes[0..4] in litters * 0.1
2229 //
2230 void deco_gas_volumes(void)
2231 {
2232 overlay float volumes[NUM_GAS];
2233 overlay float bottom_usage, deco_usage;
2234 overlay unsigned char i;
2235 overlay unsigned char gas, depth;
2236 overlay unsigned char lastGasStop;
2237 RESET_C_STACK
2238
2239 //---- initialize --------------------------------------------------------
2240 for(i=0; i<NUM_GAS; ++i) // Nothing yet...
2241 volumes[i] = 0.0;
2242
2243 bottom_usage = char_I_bottom_usage; // In liter/minutes.
2244 deco_usage = char_I_deco_usage; // In liter/minutes.
2245
2246 // Early return if not defined:
2247 if( deco_usage <= 0.0 || bottom_usage <= 0.0 )
2248 goto done;
2249
2250 //---- Bottom usage -----------------------------------------------------
2251 assert(1 <= char_I_first_gas && char_I_first_gas <= NUM_GAS);
2252 gas = char_I_first_gas - 1;
2253
2254 if( char_I_const_ppO2 == 0 )
2255 volumes[gas]
2256 = (char_I_bottom_depth*0.1 + 1.0) // Use Psurface = 1.0 bar.
2257 * char_I_bottom_time // in minutes.
2258 * bottom_usage; // In liter/minutes.
2259
2260 //---- Ascent usage ------------------------------------------------------
2261 depth = char_I_bottom_depth;
2262 lastGasStop = 255; // Allow deco gas at or below bottom depth
2263
2264 for(i=0; i<NUM_STOPS; ++i)
2265 {
2266 overlay unsigned char newDepth, time;
2267
2268 time = char_O_deco_time [i];
2269 if( time == 0 ) break; // End of table: done.
2270
2271 newDepth = char_O_deco_depth[i];
2272 assert(0 < newDepth && newDepth <= depth);
2273
2274 //---- Any gas switch before this stop -------------------------------
2275 for(;;)
2276 {
2277 overlay unsigned char newGas = 0;
2278 overlay unsigned char newStop = 0; // Mark as NO CHANGE yet
2279 overlay unsigned char j;
2280
2281 for(j=0; j<NUM_GAS; ++j)
2282 {
2283 // Skip gas without changing depth:
2284 if( ! char_I_deco_gas_change[j] )
2285 continue;
2286 // Select gas changed between [newDepth .. lastGasStop[
2287 // Note that <= means changing gas at BEGINNING of this stop.
2288 // Note that < means we cant use the same gas twice
2289 if( newDepth <= char_I_deco_gas_change[j]
2290 && char_I_deco_gas_change[j] < lastGasStop )
2291 {
2292 // Keep the DEEPEST gas in that range:
2293 if( char_I_deco_gas_change[j] >= newStop )
2294 {
2295 newGas = j;
2296 newStop = char_I_deco_gas_change[j];
2297 }
2298 }
2299 }
2300
2301 // Did we find something ?
2302 if( !newStop )
2303 break;
2304
2305 //---- usage BEFORE gas switch (if any), at 10m/min :
2306 if( depth > newStop )
2307 // Plus usage during ascent to the next stop, at 10m/min.
2308 volumes[gas] += ((depth+newStop)*0.05 + 1.0) // average depth --> bar.
2309 * (depth-newStop)*0.1 // metre --> min
2310 * deco_usage;
2311
2312 //---- Do gas switch:
2313 gas = newGas;
2314
2315 lastGasStop = newStop; // Mark last used gas
2316 if( newStop < depth ) // ascent to gas switch,
2317 depth = newStop;
2318 }
2319
2320 // Are we back to gas from the deco list (just in case):
2321 assert(gas == char_O_deco_gas[i]-1);
2322
2323 //---- usage AFTER gas switch (if any), at 10m/min :
2324 if( depth > newDepth )
2325 volumes[gas] += ((depth+newDepth)*0.05 + 1.0) // average depth --> bar.
2326 * (depth-newDepth)*0.1 // metre --> min
2327 * deco_usage;
2328
2329 //---- Do stop:
2330 depth = newDepth;
2331
2332 //---- Usage at stop:
2333 volumes[gas] += (depth*0.1 + 1.0) // depth --> bar.
2334 * time // in minutes.
2335 * deco_usage; // in xxx / min @ 1bar.
2336 }
2337
2338 // From last stop to surface
2339 volumes[gas] += (depth*0.05 + 1.0) // avg depth --> bar.
2340 * depth * 0.1 // time to surface, in minutes.
2341 * deco_usage; // in xxx / min @ 1bar.
2342
2343 //---- convert results for the ASM interface -----------------------------
2344 done:
2345 for(i=0; i<NUM_GAS; ++i)
2346 if( volumes[i] > 65534.0 )
2347 int_O_gas_volumes[i] = 65535;
2348 else
2349 int_O_gas_volumes[i] = (unsigned short)(volumes[i] + 0.5);
2350 }
2351
2352 //////////////////////////////////////////////////////////////////////////////
2353
2354 void deco_push_tissues_to_vault(void)
2355 {
2356 overlay unsigned char x;
2357 RESET_C_STACK
2358
2359 cns_vault = CNS_fraction;
2360 low_depth_vault = low_depth;
2361 3783
2362 for (x=0;x<NUM_COMP;x++) 3784 for (x=0;x<NUM_COMP;x++)
2363 { 3785 {
2364 pres_tissue_N2_vault[x] = pres_tissue_N2[x]; 3786 pres_tissue_N2_vault[x] = pres_tissue_N2[x];
2365 pres_tissue_He_vault[x] = pres_tissue_He[x]; 3787 pres_tissue_He_vault[x] = pres_tissue_He[x];
2367 } 3789 }
2368 3790
2369 void deco_pull_tissues_from_vault(void) 3791 void deco_pull_tissues_from_vault(void)
2370 { 3792 {
2371 overlay unsigned char x; 3793 overlay unsigned char x;
3794
2372 RESET_C_STACK 3795 RESET_C_STACK
2373 3796
3797 low_depth_norm = low_depth_norm_vault;
3798 low_depth_alt = low_depth_alt_vault;
3799 CNS_fraction = cns_vault_float;
3800 int_O_CNS_fraction = cns_vault_int;
3801 char_O_deco_warnings = deco_warnings_vault;
3802
3803 locked_GF_step_norm = GF_delta / low_depth_norm;
3804 locked_GF_step_alt = GF_delta / low_depth_alt;
3805
2374 for (x=0; x<NUM_COMP; x++) 3806 for (x=0; x<NUM_COMP; x++)
2375 { 3807 {
2376 pres_tissue_N2[x] = pres_tissue_N2_vault[x]; 3808 pres_tissue_N2[x] = pres_tissue_N2_vault[x];
2377 pres_tissue_He[x] = pres_tissue_He_vault[x]; 3809 pres_tissue_He[x] = pres_tissue_He_vault[x];
2378 } 3810 }
2379
2380 // Restore both CNS variable, too.
2381 CNS_fraction = cns_vault;
2382 int_O_CNS_fraction = (unsigned short)(CNS_fraction * 100.0 + 0.5);
2383
2384 // GF history too:
2385 low_depth = low_depth_vault;
2386 locked_GF_step = GF_delta / low_depth;
2387 } 3811 }
2388 3812
2389 ////////////////////////////////////////////////////////////////////////////// 3813 //////////////////////////////////////////////////////////////////////////////
2390 // 3814 //
2391 #ifndef CROSS_COMPILE 3815 #ifndef CROSS_COMPILE