comparison Common/Src/decom.c @ 38:5f11787b4f42

include in ostc4 repository
author heinrichsweikamp
date Sat, 28 Apr 2018 11:52:34 +0200
parents
children e941c9e49f73
comparison
equal deleted inserted replaced
37:ccc45c0e1ea2 38:5f11787b4f42
1 ///////////////////////////////////////////////////////////////////////////////
2 /// -*- coding: UTF-8 -*-
3 ///
4 /// \file Common/Src/decom.c
5 /// \brief This code is used to calculate desat, calculated by RTE and send to Firmware
6 /// \author heinrichs weikamp gmbh
7 /// \date 22-Feb-2016
8 ///
9 /// $Id$
10 ///////////////////////////////////////////////////////////////////////////////
11 /// \par Copyright (c) 2014-2018 Heinrichs Weikamp gmbh
12 ///
13 /// This program is free software: you can redistribute it and/or modify
14 /// it under the terms of the GNU General Public License as published by
15 /// the Free Software Foundation, either version 3 of the License, or
16 /// (at your option) any later version.
17 ///
18 /// This program is distributed in the hope that it will be useful,
19 /// but WITHOUT ANY WARRANTY; without even the implied warranty of
20 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 /// GNU General Public License for more details.
22 ///
23 /// You should have received a copy of the GNU General Public License
24 /// along with this program. If not, see <http://www.gnu.org/licenses/>.
25 //////////////////////////////////////////////////////////////////////////////
26 /**
27 @verbatim
28 ==============================================================================
29 ##### Changes #####
30 ==============================================================================
31 V1.0.2 1602220x decom_oxygen_calculate_cns() changed to hwOS version
32
33 @endverbatim
34 ******************************************************************************
35 */
36
37 #include "decom.h"
38
39 #include <math.h>
40 #include "settings.h"
41 #include "calc_crush.h"
42
43 # define FRACTION_N2_AIR 0.7902
44
45 const float helium_time_constant[16] = {
46 3.68695308808482E-001,
47 2.29518933960247E-001,
48 1.46853216220327E-001,
49 9.91626867753856E-002,
50 6.78890480470074E-002,
51 4.78692804254106E-002,
52 3.37626488338989E-002,
53 2.38113081607676E-002,
54 1.68239606932026E-002,
55 1.25592893741610E-002,
56 9.80544886914621E-003,
57 7.67264977374303E-003,
58 6.01220557342307E-003,
59 4.70185307665137E-003,
60 3.68225234041620E-003,
61 2.88775228329769E-003};
62
63 const float nitrogen_time_constant[16] = {
64 1.38629436111989E-001,
65 8.66433975699932E-002,
66 5.54517744447956E-002,
67 3.74674151654024E-002,
68 2.56721177985165E-002,
69 1.80978376125312E-002,
70 1.27651414467762E-002,
71 9.00191143584345E-003,
72 6.35914844550409E-003,
73 4.74758342849278E-003,
74 3.70666941475907E-003,
75 2.90019740820061E-003,
76 2.27261370675392E-003,
77 1.77730046297422E-003,
78 1.39186180835330E-003,
79 1.09157036308653E-003};
80
81
82 const float buehlmann_N2_a[] = {
83 1.1696f,
84 1.0000f,
85 0.8618f,
86 0.7562f,
87 0.6200f,
88 0.5043f,
89 0.4410f,
90 0.4000f,
91 0.3750f,
92 0.3500f,
93 0.3295f,
94 0.3065f,
95 0.2835f,
96 0.2610f,
97 0.2480f,
98 0.2327f};
99
100 const float buehlmann_N2_b[] = {
101 0.5578f,
102 0.6514f,
103 0.7222f,
104 0.7825f,
105 0.8126f,
106 0.8434f,
107 0.8693f,
108 0.8910f,
109 0.9092f,
110 0.9222f,
111 0.9319f,
112 0.9403f,
113 0.9477f,
114 0.9544f,
115 0.9602f,
116 0.9653f};
117
118 const float buehlmann_He_a[] = {
119 1.6189f,
120 1.3830f,
121 1.1919f,
122 1.0458f,
123 0.9220f,
124 0.8205f,
125 0.7305f,
126 0.6502f,
127 0.5950f,
128 0.5545f,
129 0.5333f,
130 0.5189f,
131 0.5181f,
132 0.5176f,
133 0.5172f,
134 0.5119f};
135
136 const float buehlmann_He_b[] = {
137 0.4770f,
138 0.5747f,
139 0.6527f,
140 0.7223f,
141 0.7582f,
142 0.7957f,
143 0.8279f,
144 0.8553f,
145 0.8757f,
146 0.8903f,
147 0.8997f,
148 0.9073f,
149 0.9122f,
150 0.9171f,
151 0.9217f,
152 0.9267f};
153
154 const float buehlmann_N2_t_halflife[] = {
155 5.0f,
156 8.0f,
157 12.5f,
158 18.5f,
159 27.0f,
160 38.3f,
161 54.3f,
162 77.0f,
163 109.0f,
164 146.0f,
165 187.0f,
166 239.0f,
167 305.0f,
168 390.0f,
169 498.0f,
170 635.0f};
171
172 const float buehlmann_He_t_halflife[] = {
173 1.88f,
174 3.02f,
175 4.72f,
176 6.99f,
177 10.21f,
178 14.48f,
179 20.53f,
180 29.11f,
181 41.20f,
182 55.19f,
183 70.69f,
184 90.34f,
185 115.29f,
186 147.42f,
187 188.24f,
188 240.03f};
189
190 const float float_buehlmann_N2_factor_expositon_one_second[] = { 2.30782347297664E-003f, 1.44301447809736E-003f, 9.23769302935806E-004f, 6.24261986779007E-004f, 4.27777107246730E-004f, 3.01585140931371E-004f, 2.12729727268379E-004f, 1.50020603047807E-004f, 1.05980191127841E-004f, 7.91232600646508E-005f, 6.17759153688224E-005f, 4.83354552742732E-005f, 3.78761777920511E-005f, 2.96212356654113E-005f, 2.31974277413727E-005f, 1.81926738960225E-005f};
191 const float float_buehlmann_N2_factor_expositon_003_second[] = { 6.90750456296407E-003f, 4.32279956671600E-003f, 2.76874864793053E-003f, 1.87161709452954E-003f, 1.28278242026003E-003f, 9.04482589432765E-004f, 6.38053429621421E-004f, 4.49994293975742E-004f, 3.17906879170993E-004f, 2.37350999218289E-004f, 1.85316297551252E-004f, 1.44999356986975E-004f, 1.13624229615916E-004f, 8.88610747694640E-005f, 6.95906688746861E-005f, 5.45770287740943E-005f};
192 const float float_buehlmann_N2_factor_expositon_008_second[] = { 1.83141447532454E-002f, 1.14859796471039E-002f, 7.36630472495203E-003f, 4.98319782231915E-003f, 3.41709742823104E-003f, 2.41013596224415E-003f, 1.70057124687550E-003f, 1.19953484034729E-003f, 8.47527105247492E-004f, 6.32810814525819E-004f, 4.94100480767923E-004f, 3.86618231662861E-004f, 3.02969256443353E-004f, 2.36945319086024E-004f, 1.85564355251966E-004f, 1.45532124251058E-004f};
193 const float float_buehlmann_N2_factor_expositon_10_seconds[] = { 2.28400315657541E-002f, 1.43368013598124E-002f, 9.19938673477072E-003f, 6.22511239287027E-003f, 4.69545762670800E-003f, 3.01176178733265E-003f, 2.12526200031782E-003f, 1.49919365737827E-003f, 1.05929662305226E-03f, 7.909509380171760E-004f, 6.17587450108648E-004f, 4.83249432061905E-004f, 3.78697227222391E-004f, 2.61728759809380E-004f, 2.31950063482533E-004f, 1.81911845881011E-004f};
194 const float float_buehlmann_N2_factor_expositon_18_seconds[] = { 4.07358806747357E-002f, 2.56581087982929E-002f, 1.64979259737517E-002f, 1.11772892486697E-002f, 7.67205373705648E-003f, 5.41463899418337E-003f, 3.82221908774349E-003f, 2.69693016270112E-003f, 1.90592594569927E-003f, 1.42326123023573E-003f, 1.11138278062062E-003f, 8.69680830683950E-004f, 6.81551750048359E-004f, 5.33048018290350E-004f, 4.17471377070378E-004f, 3.27417496114757E-004f};
195 const float float_buehlmann_N2_factor_expositon_20_seconds[] = { 4.51583960895835E-002f, 2.84680588463941E-002f, 1.83141447532454E-002f, 1.24114727614367E-002f, 8.52086250432193E-003f, 6.01445286560154E-003f, 4.24600726206570E-003f, 2.99613973313428E-003f, 2.11747113676897E-003f, 1.58127627264804E-003f, 1.23479348595879E-003f, 9.66265334110261E-004f, 7.57251042854845E-004f, 5.92258033589421E-004f, 4.63846326133055E-004f, 3.63790599842373E-004f};
196 const float float_buehlmann_N2_factor_expositon_one_minute[] = { 1.29449436703876E-001f, 8.29959567953288E-002f, 5.39423532744041E-002f, 3.67741962370398E-002f, 2.53453908775689E-002f, 1.79350552316596E-002f, 1.26840126026602E-002f, 8.96151553540825E-003f, 6.33897185233323E-003f, 4.73633146787078E-003f, 3.69980819572546E-003f, 2.89599589841472E-003f, 2.27003327536857E-003f, 1.77572199977927E-003f, 1.39089361795441E-003f, 1.09097481687104E-003f};
197 const float float_buehlmann_N2_factor_expositon_100_second[] = { 2.06299474015900E-001f, 1.34463438993857E-001f, 8.82775114417832E-002f, 6.05359181023788E-002f, 4.18844218114071E-002f, 2.97126970072147E-002f, 2.10505144045823E-002f, 1.48911986890571E-002f, 1.05426136839346E-002f, 7.88141652426455E-003f, 6.15873909572406E-003f, 4.82199900095137E-003f, 3.78052526350936E-003f, 2.95778454900952E-003f, 2.31708109427220E-003f, 1.81763004457269E-003f};
198 const float float_buehlmann_N2_factor_expositon_five_minutes[]= { 5.00000000000000E-001f, 3.51580222674495E-001f, 2.42141716744801E-001f, 1.70835801932547E-001f, 1.20463829104624E-001f, 8.65157896183918E-002f, 6.18314987350977E-002f, 4.40116547625051E-002f, 3.12955727186929E-002f, 2.34583889613009E-002f, 1.83626606868127E-002f, 1.43963540993090E-002f, 1.12987527093947E-002f, 8.84713405486026E-003f, 6.93514912851934E-003f, 5.44298480182925E-003f};
199 const float float_buehlmann_N2_factor_expositon_800_second[] = { 8.42509868763141E-001f, 6.85019737526282E-001f, 5.22579198044792E-001f, 3.93205767018569E-001f, 2.89861248917861E-001f, 2.14397627137602E-001f, 1.56505490290652E-001f, 1.13102166881646E-001f, 8.12935637814599E-002f, 6.13392112527207E-002f, 4.82208523469105E-002f, 3.79311861210304E-002f, 2.98470272862601E-002f, 2.34187624071612E-002f, 1.83870151711824E-002f, 1.44488700649190E-002f};
200 const float float_buehlmann_N2_factor_expositon_one_hour[]= { 9.99755859375000E-001f, 9.94475728271980E-001f, 9.64103176406343E-001f, 8.94394508891055E-001f, 7.85689004286732E-001f, 6.62392147498621E-001f, 5.35088626789486E-001f, 4.17318576947576E-001f, 3.17197008420226E-001f, 2.47876700002107E-001f, 1.99405069752929E-001f, 1.59713055172538E-001f, 1.27468761759271E-001f, 1.01149026804458E-001f, 8.01196838116008E-002f, 6.33955413542552E-002f};
201
202 const float float_buehlmann_He_factor_expositon_one_second[] = { 6.12608039419837E-003f, 3.81800836683133E-003f, 2.44456078654209E-003f, 1.65134647076792E-003f, 1.13084424730725E-003f, 7.97503165599123E-004f, 5.62552521860549E-004f, 3.96776399429366E-004f, 2.80360036664540E-004f, 2.09299583354805E-004f, 1.63410794820518E-004f, 1.27869320250551E-004f, 1.00198406028040E-004f, 7.83611475491108E-005f, 6.13689891868496E-005f, 4.81280465299827E-005f};
203 const float float_buehlmann_He_factor_expositon_003_second[] = { 1.82658845044263E-002f, 1.14103491926518E-002f, 7.31576933570466E-003f, 4.94586307993539E-003f, 3.38869776192019E-003f, 2.39060197012086E-003f, 1.68670834759044E-003f, 1.18985696621965E-003f, 8.40844326779777E-004f, 6.27767340286467E-004f, 4.90152279561396E-004f, 3.83558911153159E-004f, 3.00565099928485E-004f, 2.35065021719993E-004f, 1.84095669333084E-004f, 1.44377190774980E-004f}; // 3 He
204 const float float_buehlmann_He_factor_expositon_008_second[] = { 4.79706116082057E-002f, 3.01390075707096E-002f, 1.93899772993034E-002f, 1.31346689569831E-002f, 9.01102820363486E-003f, 6.36224538449637E-003f, 4.49156910795023E-003f, 3.16980660943422E-003f, 2.24068067793926E-003f, 1.67317060331207E-003f, 1.30653891641375E-003f, 1.02249686330114E-003f, 8.01306192375617E-004f, 6.26717274191169E-004f, 4.90846474157092E-004f, 3.84959521834594E-004f}; // 8 He
205 const float float_buehlmann_He_factor_expositon_10_seconds[] = { 5.95993001714799E-002f, 3.75307444923134E-002f, 2.41784389107607E-002f, 1.63912909924208E-002f, 1.25106927410620E-002f, 7.94647192918641E-003f, 5.61130562069978E-003f, 3.96068706690245E-003f, 2.80006593100546E-003f, 2.09102564918129E-003f, 1.63290683272987E-003f, 1.27795767799976E-003f, 1.00153239354972E-003f, 7.33352120986130E-004f, 6.13520442722559E-004f, 4.81176244777948E-004f};
206 const float float_buehlmann_He_factor_expositon_18_seconds[] = { 1.04710896899039E-001f, 6.65386126706349E-002f, 4.30995968284519E-002f, 2.93106657684409E-002f, 2.01607137751910E-002f, 1.42581599093282E-002f, 1.00776711616688E-002f, 7.11793906429403E-003f, 5.03447255531631E-003f, 3.76069760984632E-003f, 2.93731229281968E-003f, 2.29914783358365E-003f, 1.80203605181650E-003f, 1.40956155658090E-003f, 1.10406577253352E-003f, 8.65950533235460E-004f};
207 const float float_buehlmann_He_factor_expositon_20_seconds[] = { 1.15646523762030E-001f, 7.36529322024796E-002f, 4.77722809133601E-002f, 3.25139075644434E-002f, 2.23755519884017E-002f, 1.58297974422514E-002f, 1.11911244906306E-002f, 7.90568709176287E-003f, 5.59229149279306E-003f, 4.17767891009702E-003f, 3.26314728073529E-003f, 2.55428218017273E-003f, 2.00206171996409E-003f, 1.56605681014277E-003f, 1.22666447811148E-003f, 9.62120958977297E-004f};
208 const float float_buehlmann_He_factor_expositon_one_minute[] = { 3.08363886219441E-001f, 2.05084082411030E-001f, 1.36579295730211E-001f, 9.44046323514587E-002f, 6.56358626478964E-002f, 4.67416115355790E-002f, 3.31990512604121E-002f, 2.35300557146709E-002f, 1.66832281977395E-002f, 1.24807506400979E-002f, 9.75753219809561E-003f, 7.64329013320042E-003f, 5.99416843126677E-003f, 4.69081666943783E-003f, 3.67548116287808E-003f, 2.88358673732592E-003f};
209 const float float_buehlmann_He_factor_expositon_100_second[] = { 4.59084487437744E-001f, 3.17867635141657E-001f, 2.17103957783539E-001f, 1.52336166567559E-001f, 1.06981885584572E-001f, 7.66825160768219E-002f, 5.47171474343117E-002f, 3.89083581201959E-002f, 2.76504642556165E-002f, 2.07145921483078E-002f, 1.62096019995457E-002f, 1.27063337640768E-002f, 9.97030625587825E-003f, 7.80579708939710E-003f, 6.11829377951190E-003f, 4.80135692933603E-003f}; // 100 He
210 const float float_buehlmann_He_factor_expositon_five_minutes[]= { 8.41733751018722E-001f, 6.82600697933713E-001f, 5.20142493735619E-001f, 3.90924736715930E-001f, 2.87834706153524E-001f, 2.12857832580192E-001f, 1.55333364924147E-001f, 1.12242395185686E-001f, 8.06788883581406E-002f, 6.08653819753062E-002f, 4.78448115000141E-002f, 3.76366999883051E-002f, 2.96136888654287E-002f, 2.32350754744602E-002f, 1.82428098114835E-002f, 1.43350223887367E-002f}; // thre
211 const float float_buehlmann_He_factor_expositon_800_second[] = { 9.92671155759686E-001f, 9.53124140216102E-001f, 8.58865632718416E-001f, 7.33443528431762E-001f, 5.95533881446524E-001f, 4.71787742036413E-001f, 3.62479376011699E-001f, 2.72021750877104E-001f, 2.00940186773410E-001f, 1.54187175639359E-001f, 1.22553521140786E-001f, 9.72431193565182E-002f, 7.70338702477497E-002f, 6.07666995543268E-002f, 4.79109397391700E-002f, 3.77715319879068E-002f}; // 800 He
212 const float float_buehlmann_He_factor_expositon_one_hour[]= { 9.99999999753021E-001f, 9.99998954626205E-001f, 9.99850944669188E-001f, 9.97393537149572E-001f, 9.82979603888650E-001f, 9.43423231328217E-001f, 8.68106292901111E-001f, 7.60374619482322E-001f, 6.35576141220644E-001f, 5.29310840978539E-001f, 4.44744511849213E-001f, 3.68942936079581E-001f, 3.02834419265355E-001f, 2.45810174422126E-001f, 1.98231319020275E-001f, 1.59085372294989E-001f};
213
214 void decom_get_inert_gases(const float ambient_pressure_bar,const SGas* pGas, float* fraction_nitrogen, float* fraction_helium )
215 {
216 float fraction_all_inertgases;
217 float ppo2_fraction_setpoint;
218 float diluent_divisor;
219
220
221 *fraction_nitrogen = ((float)pGas->nitrogen_percentage) / 100.0f;
222 *fraction_helium = ((float)pGas->helium_percentage) / 100.0f;
223
224 if(!pGas->setPoint_cbar)
225 return;
226
227 // continue with CCR
228 fraction_all_inertgases = *fraction_nitrogen + *fraction_helium;
229
230 ppo2_fraction_setpoint = (float)pGas->setPoint_cbar/ (100 * ambient_pressure_bar);
231
232 diluent_divisor = (1.0f - ppo2_fraction_setpoint) / fraction_all_inertgases;
233 if(diluent_divisor < 0)
234 diluent_divisor = 0;
235
236 *fraction_nitrogen *= diluent_divisor;
237 *fraction_helium *= diluent_divisor;
238 }
239
240
241 void decom_tissues_exposure(int period_in_seconds, SLifeData * pLifeData)
242 {
243 decom_tissues_exposure2(period_in_seconds, &pLifeData->actualGas, pLifeData->pressure_ambient_bar, pLifeData->tissue_nitrogen_bar, pLifeData->tissue_helium_bar);
244 }
245
246
247 void decom_tissues_exposure2(int period_in_seconds, SGas* pActualGas, float ambiant_pressure_bar, float *tissue_N2_selected_stage, float *tissue_He_selected_stage)
248 {
249 int ci;
250 float percent_N2;
251 float percent_He;
252 float partial_pressure_N2;
253 float partial_pressure_He;
254
255
256
257 int period_in_seconds_left;
258
259 if(period_in_seconds <= 0)
260 return;
261
262
263 decom_get_inert_gases(ambiant_pressure_bar, pActualGas, &percent_N2, &percent_He);
264
265 partial_pressure_N2 = (ambiant_pressure_bar - WATER_VAPOUR_PRESSURE) * percent_N2;
266 partial_pressure_He = (ambiant_pressure_bar - WATER_VAPOUR_PRESSURE) * percent_He;
267 period_in_seconds_left = period_in_seconds;
268
269 while(period_in_seconds_left)
270 {
271 if(period_in_seconds_left >= 3600)
272 period_in_seconds = 3600;
273 else
274 if(period_in_seconds_left >= 800)
275 period_in_seconds = 800;
276 else
277 if(period_in_seconds_left >= 300)
278 period_in_seconds = 300;
279 else
280 if(period_in_seconds_left >= 100)
281 period_in_seconds = 100;
282 else
283 if(period_in_seconds_left >= 60)
284 period_in_seconds = 60;
285 else
286 if(period_in_seconds_left == 36)
287 period_in_seconds = 18;
288 else
289 if(period_in_seconds_left >= 20)
290 period_in_seconds = 20;
291 else
292 if(period_in_seconds_left >= 18)
293 period_in_seconds = 18;
294 else
295 if(period_in_seconds_left >= 10)
296 period_in_seconds = 10;
297 else
298 if(period_in_seconds_left >= 8)
299 period_in_seconds = 8;
300 else
301 if(period_in_seconds_left >= 3)
302 period_in_seconds = 3;
303 else
304 period_in_seconds = 1;
305
306 period_in_seconds_left -= period_in_seconds;
307
308 switch (period_in_seconds)
309 {
310 case 1:
311 for (ci=0;ci<16;ci++)
312 {
313 tissue_N2_selected_stage[ci] += (partial_pressure_N2 - tissue_N2_selected_stage[ci]) * float_buehlmann_N2_factor_expositon_one_second[ci];
314 tissue_He_selected_stage[ci] += (partial_pressure_He - tissue_He_selected_stage[ci]) * float_buehlmann_He_factor_expositon_one_second[ci];
315 }
316 break;
317 case 3:
318 for (ci=0;ci<16;ci++)
319 {
320 tissue_N2_selected_stage[ci] += (partial_pressure_N2 - tissue_N2_selected_stage[ci]) * float_buehlmann_N2_factor_expositon_003_second[ci];
321 tissue_He_selected_stage[ci] += (partial_pressure_He - tissue_He_selected_stage[ci]) * float_buehlmann_He_factor_expositon_003_second[ci];
322 }
323 break;
324 case 8:
325 for (ci=0;ci<16;ci++)
326 {
327 tissue_N2_selected_stage[ci] += (partial_pressure_N2 - tissue_N2_selected_stage[ci]) * float_buehlmann_N2_factor_expositon_008_second[ci];
328 tissue_He_selected_stage[ci] += (partial_pressure_He - tissue_He_selected_stage[ci]) * float_buehlmann_He_factor_expositon_008_second[ci];
329 }
330 break;
331 case 10:
332 for (ci=0;ci<16;ci++)
333 {
334 tissue_N2_selected_stage[ci] += (partial_pressure_N2 - tissue_N2_selected_stage[ci]) * float_buehlmann_N2_factor_expositon_10_seconds[ci];
335 tissue_He_selected_stage[ci] += (partial_pressure_He - tissue_He_selected_stage[ci]) * float_buehlmann_He_factor_expositon_10_seconds[ci];
336 }
337 break;
338 case 18:
339 for (ci=0;ci<16;ci++)
340 {
341 tissue_N2_selected_stage[ci] += (partial_pressure_N2 - tissue_N2_selected_stage[ci]) * float_buehlmann_N2_factor_expositon_18_seconds[ci];
342 tissue_He_selected_stage[ci] += (partial_pressure_He - tissue_He_selected_stage[ci]) * float_buehlmann_He_factor_expositon_18_seconds[ci];
343 }
344 break;
345 case 20:
346 for (ci=0;ci<16;ci++)
347 {
348 tissue_N2_selected_stage[ci] += (partial_pressure_N2 - tissue_N2_selected_stage[ci]) * float_buehlmann_N2_factor_expositon_20_seconds[ci];
349 tissue_He_selected_stage[ci] += (partial_pressure_He - tissue_He_selected_stage[ci]) * float_buehlmann_He_factor_expositon_20_seconds[ci];
350 }
351 break;
352 case 60:
353 for (ci=0;ci<16;ci++)
354 {
355 tissue_N2_selected_stage[ci] += (partial_pressure_N2 - tissue_N2_selected_stage[ci]) * float_buehlmann_N2_factor_expositon_one_minute[ci];
356 tissue_He_selected_stage[ci] += (partial_pressure_He - tissue_He_selected_stage[ci]) * float_buehlmann_He_factor_expositon_one_minute[ci];
357 }
358 break;
359 case 100:
360 for (ci=0;ci<16;ci++)
361 {
362 tissue_N2_selected_stage[ci] += (partial_pressure_N2 - tissue_N2_selected_stage[ci]) * float_buehlmann_N2_factor_expositon_100_second[ci];
363 tissue_He_selected_stage[ci] += (partial_pressure_He - tissue_He_selected_stage[ci]) * float_buehlmann_He_factor_expositon_100_second[ci];
364 }
365 break;
366 case 300:
367 for (ci=0;ci<16;ci++)
368 {
369 tissue_N2_selected_stage[ci] += (partial_pressure_N2 - tissue_N2_selected_stage[ci]) * float_buehlmann_N2_factor_expositon_five_minutes[ci];
370 tissue_He_selected_stage[ci] += (partial_pressure_He - tissue_He_selected_stage[ci]) * float_buehlmann_He_factor_expositon_five_minutes[ci];
371 }
372 break;
373 case 800:
374 for (ci=0;ci<16;ci++)
375 {
376 tissue_N2_selected_stage[ci] += (partial_pressure_N2 - tissue_N2_selected_stage[ci]) * float_buehlmann_N2_factor_expositon_800_second[ci];
377 tissue_He_selected_stage[ci] += (partial_pressure_He - tissue_He_selected_stage[ci]) * float_buehlmann_He_factor_expositon_800_second[ci];
378 }
379 break;
380 case 3600:
381 for (ci=0;ci<16;ci++)
382 {
383 tissue_N2_selected_stage[ci] += (partial_pressure_N2 - tissue_N2_selected_stage[ci]) * float_buehlmann_N2_factor_expositon_one_hour[ci];
384 tissue_He_selected_stage[ci] += (partial_pressure_He - tissue_He_selected_stage[ci]) * float_buehlmann_He_factor_expositon_one_hour[ci];
385 }
386 break;
387 }
388 }
389 }
390
391 void decom_reset_with_1000mbar(SLifeData * pLifeData)
392 {
393 double saturation = 1.0;
394
395 saturation -= WATER_VAPOUR_PRESSURE;
396 saturation *= FRACTION_N2_AIR;
397
398 for(int i=0;i<16;i++)
399 {
400 pLifeData->tissue_nitrogen_bar[i] = saturation;
401 pLifeData->tissue_helium_bar[i] = 0;
402 }
403 pLifeData->otu = 0;
404 pLifeData->cns = 0;
405 pLifeData->desaturation_time_minutes = 0;
406 pLifeData->no_fly_time_minutes = 0;
407 }
408
409
410 /* =============================================================================== */
411 /* NOTE ABOUT PRESSURE UNITS USED IN CALCULATIONS: */
412 /* It is the convention in decompression calculations to compute all gas */
413 /* loadings, absolute pressures, partial pressures, etc., in the units of */
414 /* depth pressure that you are diving - either feet of seawater (fsw) or */
415 /* meters of seawater (msw). This program follows that convention with the */
416 /* the exception that all VPM calculations are performed in SI units (by */
417 /* necessity). Accordingly, there are several conversions back and forth */
418 /* between the diving pressure units and the SI units. */
419 /* =============================================================================== */
420 /* =============================================================================== */
421 /* FUNCTION SUBPROGRAM FOR GAS LOADING CALCULATIONS - ASCENT AND DESCENT */
422 /* =============================================================================== */
423
424
425 float decom_schreiner_equation(float *initial_inspired_gas_pressure,
426 float *rate_change_insp_gas_pressure,
427 float *interval_time_minutes,
428 const float *gas_time_constant,
429 float *initial_gas_pressure)
430 {
431 /* System generated locals */
432 float ret_val;
433 float time_null_pressure = 0.0f;
434 float time_rest = 0.0f;
435 float time = *interval_time_minutes;
436 /* =============================================================================== */
437 /* Note: The Schreiner equation is applied when calculating the uptake or */
438 /* elimination of compartment gases during linear ascents or descents at a */
439 /* constant rate. For ascents, a negative number for rate must be used. */
440 /* =============================================================================== */
441 if( *rate_change_insp_gas_pressure < 0.0f)
442 {
443 time_null_pressure = -1.0f * *initial_inspired_gas_pressure / *rate_change_insp_gas_pressure;
444 if(time > time_null_pressure )
445 {
446 time_rest = time - time_null_pressure;
447 time = time_null_pressure;
448 }
449 }
450 ret_val =
451 *initial_inspired_gas_pressure +
452 *rate_change_insp_gas_pressure *
453 (time - 1.f / *gas_time_constant) -
454 (*initial_inspired_gas_pressure -
455 *initial_gas_pressure -
456 *rate_change_insp_gas_pressure / *gas_time_constant) *
457 expf(-(*gas_time_constant) * time);
458
459 if(time_rest > 0.0f)
460 {
461 ret_val = ret_val * expf(-(*gas_time_constant) * time_rest);
462 }
463
464
465 return ret_val;
466 }; /* schreiner_equation__2 */
467
468 void decom_tissues_exposure_stage_schreiner(int period_in_seconds, SGas* pGas, float starting_ambient_pressure_bar, float ending_ambient_pressure_bar,
469 float* pTissue_nitrogen_bar, float* pTissue_helium_bar)
470 {
471
472 float initial_pressure_N2;
473 float initial_pressure_He;
474
475 float ending_pressure_N2;
476 float ending_pressure_He;
477
478 float fraction_N2_begin;
479 float fraction_N2_end;
480 float fraction_He_begin;
481 float fraction_He_end;
482
483 float rate_N2;
484 float rate_He;
485
486 float period_in_minutes;
487
488 int ci;
489
490 if(period_in_seconds <= 0)
491 return;
492
493 decom_get_inert_gases(starting_ambient_pressure_bar, pGas, &fraction_N2_begin, &fraction_He_begin );
494 decom_get_inert_gases(ending_ambient_pressure_bar, pGas, &fraction_N2_end, &fraction_He_end );
495
496 initial_pressure_N2 = (starting_ambient_pressure_bar - WATER_VAPOUR_PRESSURE) * fraction_N2_begin;
497 initial_pressure_He = (starting_ambient_pressure_bar - WATER_VAPOUR_PRESSURE) * fraction_He_begin;
498
499 ending_pressure_N2 = (ending_ambient_pressure_bar - WATER_VAPOUR_PRESSURE) * fraction_N2_end;
500 ending_pressure_He = (ending_ambient_pressure_bar - WATER_VAPOUR_PRESSURE) * fraction_He_end;
501
502 rate_N2 = (ending_pressure_N2 - initial_pressure_N2) / period_in_seconds;
503 rate_He = (ending_pressure_He - initial_pressure_He) / period_in_seconds;
504
505 period_in_minutes = ((float)period_in_seconds) / 60.0f;
506
507 for (ci=0;ci<16;ci++)
508 {
509 pTissue_nitrogen_bar[ci] =
510 decom_schreiner_equation(
511 &initial_pressure_N2,
512 &rate_N2,
513 &period_in_minutes,
514 &nitrogen_time_constant[ci],
515 &pTissue_nitrogen_bar[ci]);
516
517 pTissue_helium_bar[ci] =
518 decom_schreiner_equation(
519 &initial_pressure_He,
520 &rate_He,
521 &period_in_minutes,
522 &helium_time_constant[ci],
523 &pTissue_helium_bar[ci]);
524 }
525 }
526
527 _Bool nextSetpointChange(SDiveSettings* pDiveSettings, uint8_t depth_meter, uint8_t* change_depth_meter, char* setpoint)
528 {
529 uint8_t new_depth = 0;
530 char new_setpoint = 0;
531 for(int i = 1; i <= 5; i++)
532 {
533 if(pDiveSettings->setpoint[i].setpoint_cbar > 0 && pDiveSettings->setpoint[i].depth_meter > 0 )
534 {
535 if( pDiveSettings->setpoint[i].depth_meter > new_depth && pDiveSettings->setpoint[i].depth_meter < depth_meter)
536 {
537 new_depth = pDiveSettings->setpoint[i].depth_meter;
538 new_setpoint = pDiveSettings->setpoint[i].setpoint_cbar;
539 }
540 }
541 }
542 if(new_depth)
543 {
544 * change_depth_meter = new_depth;
545 * setpoint = new_setpoint;
546 return 1;
547 }
548 return 0;
549 }
550
551
552
553 void decom_CreateGasChangeList(SDiveSettings* pInput, const SLifeData* pLifeData)
554 {
555 int i=0, j = 0;
556 int count = 0;
557 for(i=0;i< 5;i++)
558 {
559 //FirstGas
560
561 pInput->decogaslist[i].change_during_ascent_depth_meter_otherwise_zero = 0;
562 pInput->decogaslist[i].GasIdInSettings = 255;
563 pInput->decogaslist[i].setPoint_cbar = 0;
564 pInput->decogaslist[i].helium_percentage = 0;
565 pInput->decogaslist[i].nitrogen_percentage = 0;
566 }
567 //pInput->liveData.dive_time_seconds = 0;
568
569 /* FirstGas
570 * 0 = special gas, 1 to 5 ist OC gas, 6 to 10 is diluent
571 */
572
573
574
575 pInput->decogaslist[0] = pLifeData->actualGas;
576
577 /* Add Deco Gases
578 * special (gasId == 0) is never a deco/travel gas but actual gas only
579 */
580 if(pInput->diveMode == DIVEMODE_OC)
581 {
582
583 for(i=1;i<= 5;i++)
584 {
585 if(pInput->gas[i].note.ub.active && pInput->gas[i].depth_meter
586 && (pLifeData->actualGas.GasIdInSettings != i)
587 &&(pInput->gas[i].depth_meter < pLifeData->depth_meter ) )
588 {
589 count = 1;
590 for(j=1;j<= 5;j++)
591 {
592 if( (pInput->gas[j].note.ub.active && pInput->gas[j].depth_meter > 0)
593 && (pLifeData->actualGas.GasIdInSettings != j) // new hw 160905
594 && (pInput->gas[j].depth_meter > pInput->gas[i].depth_meter))
595 count++;
596 }
597 pInput->decogaslist[count].change_during_ascent_depth_meter_otherwise_zero = pInput->gas[i].depth_meter;
598 pInput->decogaslist[count].nitrogen_percentage = 100;
599 pInput->decogaslist[count].nitrogen_percentage -= pInput->gas[i].oxygen_percentage;
600 pInput->decogaslist[count].nitrogen_percentage -= pInput->gas[i].helium_percentage;
601 pInput->decogaslist[count].helium_percentage = pInput->gas[i].helium_percentage;
602 pInput->decogaslist[count].GasIdInSettings = i;
603
604 }
605 }
606 }
607 else
608 {
609 //divmode CCR
610 for(i=6; i <= 10; i++)
611 {
612 if(pInput->gas[i].note.ub.active && pInput->gas[i].depth_meter
613 && (pLifeData->actualGas.GasIdInSettings != i)
614 &&(pInput->gas[i].depth_meter < pLifeData->depth_meter ) )
615 {
616 count = 1;
617 for(j=6;j<= 10;j++)
618 {
619 // if(pInput->gas[j].note.ub.active && pInput->gas[j].depth_meter > 0 &&pInput->gas[j].depth_meter > pInput->gas[i].depth_meter)
620 if( (pInput->gas[j].note.ub.active && pInput->gas[j].depth_meter > 0)
621 && (pLifeData->actualGas.GasIdInSettings != j) // new hw 160905
622 && (pInput->gas[j].depth_meter > pInput->gas[i].depth_meter))
623 count++;
624 }
625 pInput->decogaslist[count].change_during_ascent_depth_meter_otherwise_zero = pInput->gas[i].depth_meter;
626 pInput->decogaslist[count].nitrogen_percentage = 100;
627 pInput->decogaslist[count].nitrogen_percentage -= pInput->gas[i].oxygen_percentage;
628 pInput->decogaslist[count].nitrogen_percentage -= pInput->gas[i].helium_percentage;
629 pInput->decogaslist[count].helium_percentage = pInput->gas[i].helium_percentage;
630 pInput->decogaslist[count].GasIdInSettings = i;
631
632 }
633 }
634 /* Include Setpoint Changes */
635 for(j=0; j <= count; j++)
636 {
637 uint8_t depth = 0;
638 uint8_t changedepth = 0;
639 char newSetpoint;
640 if(j == 0)
641 {
642 depth = pLifeData->depth_meter;
643 }
644 else
645 {
646 //no setpointchange ?
647 pInput->decogaslist[j].setPoint_cbar = pInput->decogaslist[j - 1].setPoint_cbar;
648 depth = pInput->decogaslist[j].change_during_ascent_depth_meter_otherwise_zero + 0.1f;
649 }
650 /* Setpoint change at the same depth as gas changes */
651 if(nextSetpointChange(pInput,depth + 1, &changedepth,&newSetpoint) && changedepth == depth)
652 {
653 pInput->decogaslist[j].setPoint_cbar = newSetpoint;
654 }
655 /* Setpoint changes inbetween gas changes */
656 while(nextSetpointChange(pInput, depth, &changedepth,&newSetpoint)
657 && (
658 ( (j < count) && (changedepth > pInput->decogaslist[j + 1].change_during_ascent_depth_meter_otherwise_zero))
659 || ((j == count) && (changedepth > 0))
660 ))
661 {
662 //Include new entry with setpoint change in decogaslist
663 for(int k = count; k > j; k--)
664 {
665 pInput->decogaslist[k+1] = pInput->decogaslist[k];
666 }
667 pInput->decogaslist[j + 1] = pInput->decogaslist[j];
668 pInput->decogaslist[j + 1].setPoint_cbar = newSetpoint;
669 j++;
670 count++;
671 depth = changedepth;
672 }
673
674 }
675
676 }
677 }
678 void test_decom_CreateGasChangeList(void)
679 {
680 SDiveSettings diveSetting;
681 SLifeData lifeData;
682 lifeData.depth_meter = 100;
683 lifeData.actualGas.helium_percentage = 30;
684 lifeData.actualGas.nitrogen_percentage = 60;
685 lifeData.actualGas.setPoint_cbar = 18;
686 lifeData.actualGas.GasIdInSettings = 0;
687 lifeData.actualGas.change_during_ascent_depth_meter_otherwise_zero = 0;
688 diveSetting.diveMode = DIVEMODE_CCR;
689 diveSetting.gas[6].depth_meter = 0;
690 diveSetting.gas[6].helium_percentage = 30;
691 diveSetting.gas[6].oxygen_percentage = 10;
692 diveSetting.gas[6].note.ub.active = 1;
693
694 diveSetting.gas[7].depth_meter = 60;
695 diveSetting.gas[7].helium_percentage = 0;
696 diveSetting.gas[7].oxygen_percentage = 10;
697 diveSetting.gas[7].note.ub.active = 1;
698 diveSetting.gas[8].note.ub.active = 0;
699 diveSetting.gas[9].note.ub.active = 0;
700 diveSetting.gas[10].note.ub.active = 0;
701
702 diveSetting.setpoint[0].depth_meter = 0;
703 diveSetting.setpoint[1].depth_meter = 80;
704 diveSetting.setpoint[1].setpoint_cbar = 20;
705 diveSetting.setpoint[2].depth_meter = 60;
706 diveSetting.setpoint[2].setpoint_cbar = 25;
707 diveSetting.setpoint[3].depth_meter = 0;
708 diveSetting.setpoint[4].depth_meter = 0;
709 diveSetting.setpoint[5].depth_meter = 0;
710
711
712 decom_CreateGasChangeList(&diveSetting, &lifeData);
713 }
714
715 uint8_t decom_tissue_test_tolerance(float* Tissue_nitrogen_bar, float* Tissue_helium_bar, float GF_value, float depth_in_bar_absolute)
716 {
717 float tissue_inertgas_saturation;
718 float inertgas_a;
719 float inertgas_b;
720 float inertgas_tolerance;
721 float gf_minus_1;
722
723 gf_minus_1 = GF_value - 1.0f;
724
725 for (int ci = 0; ci < 16; ci++)
726 {
727 if(Tissue_helium_bar[ci] == 0)
728 {
729 tissue_inertgas_saturation = Tissue_nitrogen_bar[ci];
730 //
731 inertgas_a = buehlmann_N2_a[ci];
732 inertgas_b = buehlmann_N2_b[ci];
733 }
734 else
735 {
736 tissue_inertgas_saturation = Tissue_nitrogen_bar[ci] + Tissue_helium_bar[ci];
737 //
738 inertgas_a = ( ( buehlmann_N2_a[ci] * Tissue_nitrogen_bar[ci]) + ( buehlmann_He_a[ci] * Tissue_helium_bar[ci]) ) / tissue_inertgas_saturation;
739 inertgas_b = ( ( buehlmann_N2_b[ci] * Tissue_nitrogen_bar[ci]) + ( buehlmann_He_b[ci] * Tissue_helium_bar[ci]) ) / tissue_inertgas_saturation;
740 }
741 //
742 inertgas_tolerance = ( (GF_value / inertgas_b - gf_minus_1) * depth_in_bar_absolute ) + ( GF_value * inertgas_a );
743 //
744 if(inertgas_tolerance < tissue_inertgas_saturation)
745 return 0;
746 }
747 return 1;
748 }
749
750
751 void decom_tissues_desaturation_time(const SLifeData* pLifeData, SLifeData2* pOutput)
752 {
753 float pressure_in_gas_for_complete;
754 float pressure_in_gas_for_desat;
755 float diff_to_complete;
756 float diff_to_desatpoint;
757 float necessary_halftimes;
758 float desattime;
759
760 pressure_in_gas_for_complete = 0.7902f * ( pLifeData->pressure_surface_bar - 0.0627f);
761 pressure_in_gas_for_desat = 1.05f * pressure_in_gas_for_complete;
762 for(int i=0; i<16; i++)
763 {
764 diff_to_complete = pressure_in_gas_for_complete - pLifeData->tissue_nitrogen_bar[i];
765 diff_to_desatpoint = pressure_in_gas_for_desat - pLifeData->tissue_nitrogen_bar[i];
766
767 if((diff_to_desatpoint >= 0) || (diff_to_complete >= 0))
768 pOutput->tissue_nitrogen_desaturation_time_minutes[i] = 0;
769 else
770 {
771 necessary_halftimes = (logf(1.0f - (diff_to_desatpoint/diff_to_complete)) / -0.6931f);
772 desattime = buehlmann_N2_t_halflife[i] * necessary_halftimes;
773 if(desattime <= (float)0xFFFF)
774 pOutput->tissue_nitrogen_desaturation_time_minutes[i] = desattime;
775 else
776 pOutput->tissue_nitrogen_desaturation_time_minutes[i] = 0xFFFF;
777 }
778 }
779
780 for(int i=0; i<16; i++)
781 {
782 diff_to_desatpoint = 0.05f - pLifeData->tissue_helium_bar[i];
783 diff_to_complete = -1.0f * pLifeData->tissue_helium_bar[i];
784
785 if((diff_to_desatpoint >= 0) || (diff_to_complete >= 0))
786 pOutput->tissue_helium_desaturation_time_minutes[i] = 0;
787 else
788 {
789 necessary_halftimes = (logf(1.0f - (diff_to_desatpoint/diff_to_complete)) / -0.6931f);
790 desattime = buehlmann_He_t_halflife[i] * necessary_halftimes;
791 if(desattime <= (float)0xFFFF)
792 pOutput->tissue_helium_desaturation_time_minutes[i] = desattime;
793 else
794 pOutput->tissue_helium_desaturation_time_minutes[i] = 0xFFFF;
795 }
796 }
797 }
798
799 #define MAX_DEGRADE_OTU_TIME_MINUTES (1440)
800 //CNS&OTU:
801 #define OXY_TEN_MINUTES_IN_SECONDS (600)
802 #define OXY_HALF_LIVE_OF_TEN_MINUTES__INVERSE_NINTH_ROOT_OF_TWO (0.92587471f)
803 #define OXY_NINE_DAYS_IN_TEN_MINUTES (1296)
804 #define OXY_ONE_SIXTIETH_PART (0.0166667f)
805 #define OXY_NEGATIVE_FIVE_SIXTH_PARTS (-0.8333333f)
806 void decom_oxygen_calculate_otu(float* oxygen_otu, float pressure_oxygen_real)
807 {
808 if(pressure_oxygen_real <= 0.5f)
809 return;
810 *oxygen_otu += (pow((double)(0.5f / (pressure_oxygen_real - 0.5f)),OXY_NEGATIVE_FIVE_SIXTH_PARTS)) * OXY_ONE_SIXTIETH_PART;
811 }
812
813 void decom_oxygen_calculate_otu_degrade(float* oxygen_otu, long seconds_since_last_dive)
814 {
815 static long otu_time_ticker = 0;
816 static double otu_degrade_every_10_minutes = 999.9;
817 long cycles_since_last_call;
818
819 if((*oxygen_otu <= 0) || (seconds_since_last_dive == 0))
820 *oxygen_otu = 0;
821 else if(seconds_since_last_dive < OXY_TEN_MINUTES_IN_SECONDS)
822 {
823 otu_time_ticker = 1;
824 otu_degrade_every_10_minutes = *oxygen_otu / (MAX_DEGRADE_OTU_TIME_MINUTES / 10);
825 }
826 else
827 {
828 cycles_since_last_call = seconds_since_last_dive / (otu_time_ticker * OXY_TEN_MINUTES_IN_SECONDS);
829 *oxygen_otu -= ((double)cycles_since_last_call) * otu_degrade_every_10_minutes;
830 otu_time_ticker += cycles_since_last_call;
831 if((*oxygen_otu < 0) || (otu_time_ticker > (MAX_DEGRADE_OTU_TIME_MINUTES / 10)))
832 *oxygen_otu = 0;
833 }
834 }
835
836
837
838 void decom_oxygen_calculate_cns_degrade(float* oxygen_cns, long seconds_since_last_dive)
839 {
840 static long cns_time_ticker = 0;
841 int cns_max_cycles;
842
843 if((*oxygen_cns <= 0.5f) || (seconds_since_last_dive == 0))
844 *oxygen_cns = 0;
845 else if(seconds_since_last_dive < OXY_TEN_MINUTES_IN_SECONDS)
846 cns_time_ticker = 1;
847 else
848 {
849 cns_max_cycles = OXY_NINE_DAYS_IN_TEN_MINUTES;
850 while((*oxygen_cns >= 0.5f) && ((cns_time_ticker * OXY_TEN_MINUTES_IN_SECONDS) < seconds_since_last_dive) && cns_max_cycles)
851 {
852 cns_time_ticker++;
853 cns_max_cycles--;
854 *oxygen_cns *= OXY_HALF_LIVE_OF_TEN_MINUTES__INVERSE_NINTH_ROOT_OF_TWO;
855 }
856 }
857 }
858
859
860 // new hwOS style
861 void decom_oxygen_calculate_cns(float* oxygen_cns, float pressure_oxygen_real)
862 {
863 uint8_t char_I_actual_ppO2;
864 float CNS_fraction = 0;
865 const float time_factor = 3000.0f;
866
867 if(pressure_oxygen_real < 0.15f)
868 char_I_actual_ppO2 = 15;
869 else
870 if(pressure_oxygen_real >= 2.5f)
871 char_I_actual_ppO2 = 255;
872 else
873 char_I_actual_ppO2 = (uint8_t)(pressure_oxygen_real * 100);
874
875 if (char_I_actual_ppO2 < 50)
876 (void)0; // no changes
877 //------------------------------------------------------------------------
878 // Below (and including) 1.60 bar
879 else if (char_I_actual_ppO2 < 61)
880 CNS_fraction += time_factor/(-533.07f * char_I_actual_ppO2 + 54000.0f);
881 else if (char_I_actual_ppO2 < 71)
882 CNS_fraction += time_factor/(-444.22f * char_I_actual_ppO2 + 48600.0f);
883 else if (char_I_actual_ppO2 < 81)
884 CNS_fraction += time_factor/(-355.38f * char_I_actual_ppO2 + 42300.0f);
885 else if (char_I_actual_ppO2 < 91)
886 CNS_fraction += time_factor/(-266.53f * char_I_actual_ppO2 + 35100.0f);
887 else if (char_I_actual_ppO2 < 111)
888 CNS_fraction += time_factor/(-177.69f * char_I_actual_ppO2 + 27000.0f);
889 else if (char_I_actual_ppO2 < 152)
890 CNS_fraction += time_factor/( -88.84f * char_I_actual_ppO2 + 17100.0f);
891 else if (char_I_actual_ppO2 < 167)
892 CNS_fraction += time_factor/(-222.11f * char_I_actual_ppO2 + 37350.0f);
893 //------------------------------------------------------------------------
894 // Arieli et all.(2002): Modeling pulmonary and CNS O2 toxicity:
895 // J Appl Physiol 92: 248--256, 2002, doi:10.1152/japplphysiol.00434.2001
896 // Formula (A1) based on value for 1.55 and c=20
897 // example calculation: Sqrt((1.7/1.55)^20)*0.000404
898 else if (char_I_actual_ppO2 < 172)
899 CNS_fraction += time_factor*0.00102f;
900 else if (char_I_actual_ppO2 < 177)
901 CNS_fraction += time_factor*0.00136f;
902 else if (char_I_actual_ppO2 < 182)
903 CNS_fraction += time_factor*0.00180f;
904 else if (char_I_actual_ppO2 < 187)
905 CNS_fraction += time_factor*0.00237f;
906 else if (char_I_actual_ppO2 < 192)
907 CNS_fraction += time_factor*0.00310f;
908 else if (char_I_actual_ppO2 < 198)
909 CNS_fraction += time_factor*0.00401f;
910 else if (char_I_actual_ppO2 < 203)
911 CNS_fraction += time_factor*0.00517f;
912 else if (char_I_actual_ppO2 < 233)
913 CNS_fraction += time_factor*0.0209f;
914 else
915 CNS_fraction += time_factor*0.0482f; // value for 2.5
916
917 if( CNS_fraction > 999.0f) // Limit display to 999%
918 CNS_fraction = 999.0f;
919 if( CNS_fraction < 0.0f )
920 CNS_fraction = 0.0f;
921
922 //calculate cns for the actual ppo2 for 1 second
923 *oxygen_cns += OXY_ONE_SIXTIETH_PART * CNS_fraction;
924
925 if( *oxygen_cns > 999.0f) // Limit display to 999%
926 *oxygen_cns = 999.0f;
927 if( *oxygen_cns < 0.0f )
928 *oxygen_cns = 0.0f;
929 }
930
931 /* old DR5 style
932 void decom_oxygen_calculate_cns(float* oxygen_cns, float pressure_oxygen_real)
933 {
934 int cns_no_range = 0;
935 _Bool not_found = 1;
936 //for the cns calculation
937 const float cns_ppo2_ranges[60][2] = {
938 {0.50f, 0.00f}, {0.60f, 0.14f}, {0.64f, 0.15f}, {0.66f, 0.16f}, {0.68f, 0.17f}, {0.70f, 0.18f},
939 {0.74f, 0.19f}, {0.76f, 0.20f}, {0.78f, 0.21f}, {0.80f, 0.22f}, {0.82f, 0.23f}, {0.84f, 0.24f},
940 {0.86f, 0.25f}, {0.88f, 0.26f}, {0.90f, 0.28f}, {0.92f, 0.29f}, {0.94f, 0.30f}, {0.96f, 0.31f},
941 {0.98f, 0.32f}, {1.00f, 0.33f}, {1.02f, 0.35f}, {1.04f, 0.36f}, {1.06f, 0.38f}, {1.08f, 0.40f},
942 {1.10f, 0.42f}, {1.12f, 0.43f}, {1.14f, 0.43f}, {1.16f, 0.44f}, {1.18f, 0.46f}, {1.20f, 0.47f},
943 {1.22f, 0.48f}, {1.24f, 0.51f}, {1.26f, 0.52f}, {1.28f, 0.54f}, {1.30f, 0.56f}, {1.32f, 0.57f},
944 {1.34f, 0.60f}, {1.36f, 0.62f}, {1.38f, 0.63f}, {1.40f, 0.65f}, {1.42f, 0.68f}, {1.44f, 0.71f},
945 {1.46f, 0.74f}, {1.48f, 0.78f}, {1.50f, 0.83f}, {1.52f, 0.93f}, {1.54f, 1.04f}, {1.56f, 1.19f},
946 {1.58f, 1.47f}, {1.60f, 2.22f}, {1.62f, 5.00f}, {1.65f, 6.25f}, {1.67f, 7.69f}, {1.70f, 10.0f},
947 {1.72f,12.50f}, {1.74f,20.00f}, {1.77f,25.00f}, {1.79f,31.25f}, {1.80f,50.00f}, {1.82f,100.0f}};
948 //find the correct cns range for the corresponding ppo2
949 cns_no_range = 58;
950 while (cns_no_range && not_found)
951 {
952 if (pressure_oxygen_real > cns_ppo2_ranges[cns_no_range][0])
953 {
954 cns_no_range++;
955 not_found = 0;
956 }
957 else
958 cns_no_range--;
959 }
960
961 //calculate cns for the actual ppo2 for 1 second
962 *oxygen_cns += OXY_ONE_SIXTIETH_PART * cns_ppo2_ranges[cns_no_range][1];
963 }
964 */
965
966 void decom_oxygen_calculate_cns_exposure(int period_in_seconds, SGas* pActualGas, float pressure_ambient_bar, float* oxygen_cns)
967 {
968 float pressure_oxygen_real;
969 float one_second_cns;
970
971 pressure_oxygen_real = decom_calc_ppO2(pressure_ambient_bar, pActualGas);
972 one_second_cns = 0;
973 decom_oxygen_calculate_cns(&one_second_cns, pressure_oxygen_real);
974 *oxygen_cns += one_second_cns * period_in_seconds;
975 }
976
977
978 void decom_oxygen_calculate_cns_stage_SchreinerStyle(int period_in_seconds, SGas* pGas, float starting_ambient_pressure_bar, float ending_ambient_pressure_bar, float* oxygen_cns)
979 {
980 if(ending_ambient_pressure_bar == starting_ambient_pressure_bar)
981 {
982 decom_oxygen_calculate_cns_exposure(period_in_seconds, pGas, starting_ambient_pressure_bar, oxygen_cns);
983 return;
984 }
985
986 float pressure_oxygen_real;
987 float initial_pressure_oxygen;
988 float ending_pressure_oxygen;
989 float rate_oxygen;
990
991 initial_pressure_oxygen = decom_calc_ppO2(starting_ambient_pressure_bar, pGas);
992 ending_pressure_oxygen = decom_calc_ppO2(ending_ambient_pressure_bar, pGas);
993
994 rate_oxygen = (ending_pressure_oxygen - initial_pressure_oxygen) / period_in_seconds;
995
996 pressure_oxygen_real = initial_pressure_oxygen;
997 for(int i = 0; i < period_in_seconds; i++)
998 {
999 decom_oxygen_calculate_cns(oxygen_cns, pressure_oxygen_real);
1000 pressure_oxygen_real += rate_oxygen;
1001 }
1002 }
1003
1004
1005 float decom_calc_ppO2(const float ambiant_pressure_bar, const SGas* pGas)
1006 {
1007 float percent_N2 = 0;
1008 float percent_He = 0;
1009 float percent_O2 = 0;
1010 decom_get_inert_gases(ambiant_pressure_bar, pGas, &percent_N2, &percent_He);
1011 percent_O2 = 1 - percent_N2 - percent_He;
1012
1013 return (ambiant_pressure_bar - WATER_VAPOUR_PRESSURE) * percent_O2;
1014 }
1015
1016
1017 uint8_t decom_get_actual_deco_stop(SDiveState* pDiveState)
1018 {
1019 SDecoinfo* pDecoinfo;
1020 uint8_t depthNext, depthLast, depthSecond, depthInc;
1021 if(pDiveState->diveSettings.deco_type.ub.standard == GF_MODE)
1022 pDecoinfo = &pDiveState->decolistBuehlmann;
1023 else
1024 pDecoinfo = &pDiveState->decolistVPM;
1025
1026 depthLast = (uint8_t)(pDiveState->diveSettings.last_stop_depth_bar * 10);
1027 depthSecond = (uint8_t)(pDiveState->diveSettings.input_second_to_last_stop_depth_bar * 10);
1028 depthInc = (uint8_t)(pDiveState->diveSettings.input_next_stop_increment_depth_bar * 10);
1029 if(pDecoinfo->output_stop_length_seconds[0] > 0)
1030 {
1031 depthNext = depthLast;
1032 }
1033 else
1034 return 0;
1035 for(int i = DECOINFO_STRUCT_MAX_STOPS -1 ;i > 0; i--)
1036 {
1037 if(pDecoinfo->output_stop_length_seconds[i] > 0)
1038 {
1039 depthNext = depthSecond + ( (i - 1) * depthInc);
1040 break;
1041 }
1042 }
1043 return depthNext;
1044 }
1045
1046
1047 // ===============================================================================
1048 // decom_calc_desaturation_time
1049 /// @brief This code is used to calculate desat, calculated by RTE and send to Firmware
1050 /// similar but more technical in code than decom_tissues_desaturation_time()
1051 /// the later has 0.05 for helium in contrast to this one.
1052 /// This one goes down to 70%, the oterh
1053 ///
1054 /// output is desat time in minutes
1055 // ===============================================================================
1056 int decom_calc_desaturation_time(float* Tissue_nitrogen_bar, float* Tissue_helium_bar, float surface_pressure_bar)
1057 {
1058 const float N2_ratio = 0.7902; // FIXED sum as stated in b"uhlmann
1059
1060 float pres_surface;
1061 float temp_atem;
1062 float float_desaturation_multiplier;
1063 float temp1,temp2,temp3,temp4;
1064 int ci;
1065 int int_temp;
1066 int int_O_desaturation_time;
1067 pres_surface = ((float)surface_pressure_bar);
1068 temp_atem = N2_ratio * (pres_surface - 0.0627f);
1069
1070 int_O_desaturation_time = 0;
1071 float_desaturation_multiplier = 100 / 142.0f; // new in v.101 (70,42%/100.=142)
1072
1073 for (ci=0;ci<16;ci++)
1074 {
1075 // saturation_time (for flight) and N2_saturation in multiples of halftime
1076 // version v.100: 1.1 = 10 percent distance to totally clean (totally clean is not possible, would take infinite time )
1077 // new in version v.101: 1.07 = 7 percent distance to totally clean (totally clean is not possible, would take infinite time )
1078 // changes in v.101: 1.05 = 5 percent dist to totally clean is new desaturation point for display and noFly calculations
1079
1080 // N2
1081 temp1 = 1.05f * temp_atem;
1082 temp1 = temp1 - (float)Tissue_nitrogen_bar[ci];
1083 temp2 = temp_atem - (float)Tissue_nitrogen_bar[ci];
1084 if (temp2 >= 0)
1085 {
1086 temp1 = 0;
1087 temp2 = 0;
1088 }
1089 else
1090 temp1 = temp1 / temp2;
1091
1092 if (temp1 > 0)
1093 {
1094 temp1 = logf(1.0f - temp1);
1095 temp1 = temp1 / -0.6931f; // temp1 is the multiples of half times necessary.
1096 // 0.6931 is ln(2), because the math function log() calculates with a base of e not 2 as requested.
1097 // minus because log is negative
1098 temp2 = buehlmann_N2_t_halflife[ci] * temp1 / float_desaturation_multiplier; // time necessary (in minutes ) for complete desaturation (see comment about 10 percent) , new in v.101: float_desaturation_multiplier
1099 }
1100 else
1101 {
1102 temp1 = 0;
1103 temp2 = 0;
1104 }
1105
1106 // He
1107 temp3 = 0.1f - (float)Tissue_helium_bar[ci];
1108 if (temp3 >= 0)
1109 {
1110 temp3 = 0;
1111 temp4 = 0;
1112 }
1113 else
1114 temp3 = -1.0f * temp3 / (float)Tissue_helium_bar[ci];
1115 if (temp3 > 0)
1116 {
1117 temp3 = logf(1.0f - temp3);
1118 temp3 = temp3 / -0.6931f; // temp1 is the multiples of half times necessary.
1119 // 0.6931 is ln(2), because the math function log() calculates with a base of e not 2 as requested.
1120 // minus because log is negative
1121 temp4 = buehlmann_He_t_halflife[ci] * temp3 / float_desaturation_multiplier; // time necessary (in minutes ) for "complete" desaturation, new in v.101 float_desaturation_multiplier
1122 }
1123 else
1124 {
1125 temp3 = 0;
1126 temp4 = 0;
1127 }
1128
1129 // saturation_time (for flight)
1130 if (temp4 > temp2)
1131 int_temp = (int)temp4;
1132 else
1133 int_temp = (int)temp2;
1134 if(int_temp > int_O_desaturation_time)
1135 int_O_desaturation_time = int_temp;
1136
1137 /*// N2 saturation in multiples of halftime for display purposes
1138 temp2 = temp1 * 20.0; // 0 = 1/8, 120 = 0, 249 = 8
1139 temp2 = temp2 + 80.0; // set center
1140 if (temp2 < 0.0)
1141 temp2 = 0.0;
1142 if (temp2 > 255.0)
1143 temp2 = 255.0;
1144 U8_tissue_N2_saturation[ci] = (U8)temp2;
1145 // He saturation in multiples of halftime for display purposes
1146 temp4 = temp3 * 20.0; // 0 = 1/8, 120 = 0, 249 = 8
1147 temp4 = temp4 + 80.0; // set center
1148 if (temp4 < 0.0)
1149 temp4 = 0.0;
1150 if (temp4 > 255.0)
1151 temp4 = 255.0;
1152 U8_tissue_He_saturation[ci] = (char)temp4;*/
1153 }
1154
1155 return int_O_desaturation_time;
1156 }