comparison src/p2_deco-TESTING.c @ 560:b7eb98dbd800

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