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