comparison Discovery/Src/simulation.c @ 38:5f11787b4f42

include in ostc4 repository
author heinrichsweikamp
date Sat, 28 Apr 2018 11:52:34 +0200
parents
children 8f8ea3a32e82
comparison
equal deleted inserted replaced
37:ccc45c0e1ea2 38:5f11787b4f42
1 ///////////////////////////////////////////////////////////////////////////////
2 /// -*- coding: UTF-8 -*-
3 ///
4 /// \file Discovery/Src/simulation.c
5 /// \brief Contains dive simulation functionality
6 /// \author Heinrichs Weikamp gmbh
7 /// \date 13-Oct-2014
8 ///
9 /// \details
10 /// The simulation uses "extern SDiveState stateSim" defined in dataCentral.h"
11 ///
12 /// simulation_start(void) sets stateUsed to stateSim and initializes simulation
13 /// simulation_UpdateLifeData should be called at least once per second
14 /// simulation_end() sets stateUsed back to stateReal
15 ///
16 /// $Id$
17 ///////////////////////////////////////////////////////////////////////////////
18 /// \par Copyright (c) 2014-2018 Heinrichs Weikamp gmbh
19 ///
20 /// This program is free software: you can redistribute it and/or modify
21 /// it under the terms of the GNU General Public License as published by
22 /// the Free Software Foundation, either version 3 of the License, or
23 /// (at your option) any later version.
24 ///
25 /// This program is distributed in the hope that it will be useful,
26 /// but WITHOUT ANY WARRANTY; without even the implied warranty of
27 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 /// GNU General Public License for more details.
29 ///
30 /// You should have received a copy of the GNU General Public License
31 /// along with this program. If not, see <http://www.gnu.org/licenses/>.
32 //////////////////////////////////////////////////////////////////////////////
33
34 #include <string.h>
35 #include "simulation.h"
36
37 #include "decom.h"
38 #include "calc_crush.h"
39 #include "data_exchange.h"
40 #include "timer.h"
41 #include "check_warning.h"
42 #include "vpm.h"
43 #include "buehlmann.h"
44 #include "logbook_miniLive.h"
45
46 //Private state variables
47 float sim_aim_depth_meter;
48 _Bool sim_head_decostops = 1;
49
50 const float sim_descent_rate_meter_per_min = 20;
51
52
53 //Private functions
54 float sim_get_ambiant_pressure(SDiveState * pDiveState);
55 void sim_reduce_deco_time_one_second(SDiveState* pDiveState);
56
57 /**
58 ******************************************************************************
59 * @brief sets heed_decostops_while_ascending
60 ******************************************************************************
61 * @param heed_decostops_while_ascending : true -> deco_stops are considered while ascending
62 * @return void
63 */
64 void simulation_set_heed_decostops(_Bool heed_decostops_while_ascending)
65 {
66 sim_head_decostops = heed_decostops_while_ascending;
67 }
68
69 /**
70 ******************************************************************************
71 * @brief start of simulation
72 ******************************************************************************
73 * @return void
74 */
75 void simulation_start(int aim_depth)
76 {
77 copyDiveSettingsToSim();
78 copyVpmRepetetiveDataToSim();
79 //vpm_init(&stateSimGetPointerWrite()->vpm, stateSimGetPointerWrite()->diveSettings.vpm_conservatism, 0, 0);
80 stateSimGetPointerWrite()->lifeData.counterSecondsShallowDepth = 0;
81 stateSimGetPointerWrite()->mode = MODE_DIVE;
82 if(aim_depth <= 0)
83 aim_depth = 20;
84 simulation_set_aim_depth(aim_depth);
85 timer_init();
86 stateUsed = &stateSim;
87 stateSim.lifeData.boolResetAverageDepth = 1;
88 decoLock = DECO_CALC_init_as_is_start_of_dive;
89
90 stateSim.lifeData.apnea_total_max_depth_meter = 0;
91 }
92
93 /**
94 ******************************************************************************
95 * @brief end of simulation
96 ******************************************************************************
97 *
98 * @return void
99 */
100 void simulation_exit(void)
101 {
102 timer_Stopwatch_Stop();
103 set_stateUsedToReal();
104 }
105
106 /**
107 ******************************************************************************
108 * @brief simulates change of Lifedata (saturation, depth change, etc.) within one second
109 ******************************************************************************
110 *
111 * @param checkOncePerSecond : true -> simulation in real time (function is evaluated only once per second)
112 * and copy of parts of LifeData from SmallCPU with each call from HAL_TIM_PeriodElapsedCallback()
113 * : false -> fast simulation (many simulation cycles per second are possible)
114 * @return void
115 */
116 void simulation_UpdateLifeData( _Bool checkOncePerSecond)
117 {
118 SDiveState * pDiveState = &stateSim;
119
120 static int last_second = -1;
121 static _Bool two_second = 0;
122 static float lastPressure_bar = 0;
123
124 if(checkOncePerSecond)
125 {
126 pDiveState->lifeData.temperature_celsius = stateRealGetPointer()->lifeData.temperature_celsius;
127 pDiveState->lifeData.compass_heading = stateRealGetPointer()->lifeData.compass_heading;
128 pDiveState->lifeData.battery_charge = stateRealGetPointer()->lifeData.battery_charge;
129
130 int now = current_second();
131 if( last_second == now)
132 return;
133 last_second = now;
134
135 if(!two_second)
136 two_second = 1;
137 else
138 {
139 two_second = 0;
140 if(lastPressure_bar >= 0)
141 {
142 //2 seconds * 30 == 1 minute, bar * 10 = meter
143 pDiveState->lifeData.ascent_rate_meter_per_min = (lastPressure_bar - pDiveState->lifeData.pressure_ambient_bar) * 30 * 10;
144 }
145 lastPressure_bar = pDiveState->lifeData.pressure_ambient_bar;
146 }
147 }
148 else if(pDiveState->lifeData.depth_meter <= (float)(decom_get_actual_deco_stop(pDiveState) + 0.001))
149 sim_reduce_deco_time_one_second(pDiveState);
150
151 if(getLicence() == LICENCEBONEX)
152 {
153 pDiveState->lifeData.scooterType = stateRealGetPointer()->lifeData.scooterType;
154 pDiveState->lifeData.scooterTemperature = stateRealGetPointer()->lifeData.scooterTemperature;
155 pDiveState->lifeData.scooterAgeInMilliSeconds = stateRealGetPointer()->lifeData.scooterAgeInMilliSeconds;
156 pDiveState->lifeData.scooterDrehzahl = stateRealGetPointer()->lifeData.scooterDrehzahl;
157 pDiveState->lifeData.scooterRestkapazitaet = stateRealGetPointer()->lifeData.scooterRestkapazitaet;
158 pDiveState->lifeData.scooterWattstunden = stateRealGetPointer()->lifeData.scooterWattstunden;
159 pDiveState->lifeData.scooterAmpere = stateRealGetPointer()->lifeData.scooterAmpere;
160 pDiveState->lifeData.scooterSpannung = stateRealGetPointer()->lifeData.scooterSpannung;
161 pDiveState->lifeData.scooterSpeed = stateRealGetPointer()->lifeData.scooterSpeed;
162 pDiveState->lifeData.scooterRestkapazitaetWhBased = stateRealGetPointer()->lifeData.scooterRestkapazitaetWhBased;
163 pDiveState->lifeData.scooterRestkapazitaetVoltageBased = stateRealGetPointer()->lifeData.scooterRestkapazitaetVoltageBased;
164 }
165
166 pDiveState->lifeData.ppO2Sensor_bar[0] = stateRealGetPointer()->lifeData.ppO2Sensor_bar[0];
167 pDiveState->lifeData.ppO2Sensor_bar[1] = stateRealGetPointer()->lifeData.ppO2Sensor_bar[1];
168 pDiveState->lifeData.ppO2Sensor_bar[2] = stateRealGetPointer()->lifeData.ppO2Sensor_bar[2];
169 pDiveState->lifeData.sensorVoltage_mV[0] = stateRealGetPointer()->lifeData.sensorVoltage_mV[0];
170 pDiveState->lifeData.sensorVoltage_mV[1] = stateRealGetPointer()->lifeData.sensorVoltage_mV[1];
171 pDiveState->lifeData.sensorVoltage_mV[2] = stateRealGetPointer()->lifeData.sensorVoltage_mV[2];
172
173 pDiveState->lifeData.dive_time_seconds += 1;
174 pDiveState->lifeData.pressure_ambient_bar = sim_get_ambiant_pressure(pDiveState);
175
176 if(!is_ambient_pressure_close_to_surface(&pDiveState->lifeData) && !(stateSimGetPointer()->lifeData.counterSecondsShallowDepth) )
177 {
178 pDiveState->lifeData.dive_time_seconds_without_surface_time += 1;
179 }
180
181 if(is_ambient_pressure_close_to_surface(&pDiveState->lifeData)) // new hw 170214
182 {
183 if(!(stateSimGetPointer()->lifeData.counterSecondsShallowDepth))
184 {
185 if(pDiveState->diveSettings.diveMode != DIVEMODE_Apnea)
186 pDiveState->lifeData.counterSecondsShallowDepth = settingsGetPointer()->timeoutDiveReachedZeroDepth - 15;
187 else
188 {
189 pDiveState->lifeData.apnea_last_dive_time_seconds = pDiveState->lifeData.dive_time_seconds;
190 if(pDiveState->lifeData.apnea_last_dive_time_seconds > pDiveState->lifeData.dive_time_seconds_without_surface_time)
191 pDiveState->lifeData.apnea_last_dive_time_seconds = pDiveState->lifeData.dive_time_seconds_without_surface_time;
192 pDiveState->lifeData.apnea_last_max_depth_meter = pDiveState->lifeData.max_depth_meter;
193 pDiveState->lifeData.counterSecondsShallowDepth = 1;
194 }
195 }
196 }
197 else
198 {
199 pDiveState->lifeData.counterSecondsShallowDepth = 0;
200 }
201
202 pDiveState->lifeData.depth_meter = (pDiveState->lifeData.pressure_ambient_bar - pDiveState->lifeData.pressure_surface_bar) * 10.0f;
203 if(pDiveState->lifeData.max_depth_meter < pDiveState->lifeData.depth_meter)
204 pDiveState->lifeData.max_depth_meter = pDiveState->lifeData.depth_meter;
205
206 /* apnoe specials
207 */
208 if(pDiveState->diveSettings.diveMode == DIVEMODE_Apnea)
209 {
210 if(pDiveState->lifeData.max_depth_meter > pDiveState->lifeData.apnea_total_max_depth_meter)
211 pDiveState->lifeData.apnea_total_max_depth_meter = pDiveState->lifeData.max_depth_meter;
212
213 if(pDiveState->lifeData.counterSecondsShallowDepth)
214 {
215 pDiveState->lifeData.dive_time_seconds = 0;
216 pDiveState->lifeData.max_depth_meter = 0;
217 pDiveState->lifeData.boolResetAverageDepth = 1;
218 pDiveState->lifeData.boolResetStopwatch = 1;
219 }
220 }
221
222 /* average depth
223 */
224 float *AvgDepthValue = &pDiveState->lifeData.average_depth_meter;
225 float DepthNow = pDiveState->lifeData.depth_meter;
226 uint32_t *AvgDepthCount = &pDiveState->lifeData.internal.average_depth_meter_Count;
227 uint32_t *AvgDepthTimer = &pDiveState->lifeData.internal.average_depth_last_update_dive_time_seconds_without_surface_time;
228 uint32_t AvgSecondsSinceLast;
229 uint32_t DiveTime = pDiveState->lifeData.dive_time_seconds_without_surface_time;
230
231 if(pDiveState->lifeData.boolResetAverageDepth)
232 {
233 *AvgDepthValue = DepthNow;
234 *AvgDepthCount = 1;
235 *AvgDepthTimer = DiveTime;
236 pDiveState->lifeData.boolResetAverageDepth = 0;
237 }
238 else if (DiveTime > *AvgDepthTimer)
239 {
240 AvgSecondsSinceLast = DiveTime - *AvgDepthTimer;
241 for(int i=0;i<AvgSecondsSinceLast;i++)
242 {
243 *AvgDepthValue = (*AvgDepthValue * *AvgDepthCount + DepthNow) / (*AvgDepthCount + 1);
244 *AvgDepthCount += 1;
245 }
246 *AvgDepthTimer = DiveTime;
247 }
248 if(*AvgDepthCount == 0)
249 *AvgDepthValue = 0;
250
251 /* Exposure Tissues
252 */
253 decom_tissues_exposure(1, &pDiveState->lifeData);
254 /* moved to updateSetpointStateUsed()
255 pDiveState->lifeData.ppO2 = decom_calc_ppO2( pDiveState->lifeData.pressure_ambient_bar, &pDiveState->lifeData.actualGas);
256 */
257 decom_oxygen_calculate_cns_exposure(1, &pDiveState->lifeData.actualGas, pDiveState->lifeData.pressure_ambient_bar, &pDiveState->lifeData.cns);
258 //if((pDiveState->lifeData.depth_meter < 0.1f) || (pDiveState->lifeData.dive_time_seconds > 1*60*60))
259 // if(pDiveState->lifeData.dive_time_seconds > 1*60*60)
260 if(pDiveState->lifeData.dive_time_seconds > 5*60*60) // test Dirk Berben
261 {
262 simulation_exit();
263 }
264 if(stateSimGetPointer()->lifeData.counterSecondsShallowDepth)
265 {
266 stateSimGetPointerWrite()->lifeData.counterSecondsShallowDepth += 1;
267 if(stateSimGetPointer()->lifeData.counterSecondsShallowDepth >= settingsGetPointer()->timeoutDiveReachedZeroDepth)
268 simulation_exit();
269 }
270 vpm_crush(pDiveState);
271 }
272
273 /**
274 ******************************************************************************
275 * @brief adds extra time for fast simulation
276 ******************************************************************************
277 *@param minutes
278 * @return float : new pressure
279 */
280 void simulation_add_time(int minutes)
281 {
282 for(int i = 0; i < 60 * minutes; i++)
283 {
284 simulation_UpdateLifeData(0);
285 updateMiniLiveLogbook(0);
286 timer_UpdateSecond(0);
287 }
288 }
289
290 /**
291 ******************************************************************************
292 * @brief get aim_depth
293 ******************************************************************************
294 * @return sim_aim_depth_meter;
295 */
296
297 uint16_t simulation_get_aim_depth(void)
298 {
299 return (uint16_t)sim_aim_depth_meter;
300 }
301
302 /**
303 ******************************************************************************
304 * @brief get heed decostops
305 ******************************************************************************
306 * @return true if ascend follows decostops;
307 */
308
309 _Bool simulation_get_heed_decostops(void)
310 {
311 return sim_head_decostops;
312 }
313
314 /**
315 ******************************************************************************
316 * @brief sets aim_depth
317 ******************************************************************************
318 *@param depth_meter
319 * @return float : new pressure
320 */
321 void simulation_set_aim_depth(int depth_meter)
322 {
323 sim_aim_depth_meter = depth_meter;
324 }
325
326 /**
327 ******************************************************************************
328 * @brief simulates ambiant pressure depending on aim depth
329 ******************************************************************************
330 * @note if aim_depth != actual depth, the depth change within one second
331 * (depending on descent or ascent) rate is calculated
332 * @param SDiveState* pDiveState:
333 * @return float : new ambiant pressure
334 */
335 float sim_get_ambiant_pressure(SDiveState * pDiveState)
336 {
337 //Calc next depth
338 uint8_t actual_deco_stop = decom_get_actual_deco_stop(pDiveState);
339 float depth_meter = pDiveState->lifeData.depth_meter;
340 float surface_pressure_bar = pDiveState->lifeData.pressure_surface_bar;
341 if(depth_meter < sim_aim_depth_meter)
342 {
343 depth_meter = depth_meter + sim_descent_rate_meter_per_min / 60;
344 if(depth_meter > sim_aim_depth_meter)
345 depth_meter = sim_aim_depth_meter;
346 }
347 else if(depth_meter > sim_aim_depth_meter)
348 {
349
350 depth_meter -= pDiveState->diveSettings.ascentRate_meterperminute / 60;
351 if(depth_meter < sim_aim_depth_meter)
352 depth_meter = sim_aim_depth_meter;
353
354 if(sim_head_decostops && depth_meter < actual_deco_stop)
355 {
356 if(actual_deco_stop < (depth_meter + pDiveState->diveSettings.ascentRate_meterperminute / 60))
357 depth_meter = actual_deco_stop;
358 else
359 depth_meter += pDiveState->diveSettings.ascentRate_meterperminute / 60;
360 }
361
362 }
363
364 return surface_pressure_bar + depth_meter / 10;
365 }
366
367
368 /**
369 ******************************************************************************
370 * @brief Reduces deco time of deepest stop by one second
371 ******************************************************************************
372 * @note called during fast simulation
373 * @param SDiveState* pDiveState:
374 * @return void
375 */
376 void sim_reduce_deco_time_one_second(SDiveState* pDiveState)
377 {
378 SDecoinfo* pDecoinfo;
379 if(pDiveState->diveSettings.deco_type.ub.standard == GF_MODE)
380 pDecoinfo = &pDiveState->decolistBuehlmann;
381 else
382 pDecoinfo = &pDiveState->decolistVPM;
383
384 //Reduce deco time of deepest stop by one second
385 for(int i = DECOINFO_STRUCT_MAX_STOPS -1 ;i >= 0; i--)
386 {
387 if(pDecoinfo->output_stop_length_seconds[i] > 0)
388 {
389 pDecoinfo->output_stop_length_seconds[i]--;
390 break;
391 }
392 }
393 }
394
395 SDecoinfo* simulation_decoplaner(uint16_t depth_meter, uint16_t intervall_time_minutes, uint16_t dive_time_minutes, uint8_t *gasChangeListDepthGas20x2)
396 {
397 uint8_t ptrGasChangeList = 0; // new hw 160704
398
399 SDiveState * pDiveState = &stateSim;
400 copyDiveSettingsToSim();
401 vpm_init(&pDiveState->vpm, pDiveState->diveSettings.vpm_conservatism, 0, 0);
402 //buehlmann_init();
403 //timer_init();
404 memset(&pDiveState->events,0, sizeof(SEvents));
405 pDiveState->diveSettings.internal__pressure_first_stop_ambient_bar_as_upper_limit_for_gf_low_otherwise_zero = 0;
406 //Calc desaturation during intervall (with Air)
407 setActualGasAir(&pDiveState->lifeData);
408 if(intervall_time_minutes > 0)
409 {
410 decom_tissues_exposure(intervall_time_minutes * 60, &pDiveState->lifeData);
411 decom_oxygen_calculate_cns_degrade(&pDiveState->lifeData.cns, intervall_time_minutes * 60);
412 }
413
414 //Switch to first Gas
415 setActualGasFirst(&pDiveState->lifeData);
416
417 // new hw 160704
418 if(gasChangeListDepthGas20x2)
419 {
420 gasChangeListDepthGas20x2[ptrGasChangeList++] = 0;
421 gasChangeListDepthGas20x2[ptrGasChangeList++] = pDiveState->lifeData.actualGas.GasIdInSettings;
422 gasChangeListDepthGas20x2[0] =0; // depth zero
423 }
424
425 //Going down / descent
426 simulation_set_aim_depth(depth_meter);
427 for(int i = 0; i < 60 * dive_time_minutes; i++)
428 {
429 simulation_UpdateLifeData(0);
430 check_warning2(pDiveState);
431 if(pDiveState->warnings.betterGas)
432 {
433 setActualGas(&pDiveState->lifeData,actualBetterGasId(),pDiveState->lifeData.actualGas.setPoint_cbar);
434 if(gasChangeListDepthGas20x2 && (pDiveState->diveSettings.diveMode == DIVEMODE_OC))
435 {
436 gasChangeListDepthGas20x2[ptrGasChangeList++] = pDiveState->lifeData.depth_meter;
437 gasChangeListDepthGas20x2[ptrGasChangeList++] = actualBetterGasId();
438 }
439 }
440 }
441
442 decom_CreateGasChangeList(&pDiveState->diveSettings, &pDiveState->lifeData); // was there before and needed for buehlmann_calc_deco and vpm_calc
443
444 // new hw 160704
445 if(gasChangeListDepthGas20x2 && (pDiveState->diveSettings.diveMode == DIVEMODE_OC))
446 {
447 // change direction from better gas to deco gas
448 gasChangeListDepthGas20x2[ptrGasChangeList++] = 255;
449 gasChangeListDepthGas20x2[ptrGasChangeList++] = 255;
450
451 // ascend (deco) gases
452 for(int i=1; i<=5;i++)
453 {
454 if(pDiveState->diveSettings.decogaslist[i].change_during_ascent_depth_meter_otherwise_zero == 0)
455 break;
456 gasChangeListDepthGas20x2[ptrGasChangeList++] = pDiveState->diveSettings.decogaslist[i].change_during_ascent_depth_meter_otherwise_zero;
457 gasChangeListDepthGas20x2[ptrGasChangeList++] = pDiveState->diveSettings.decogaslist[i].GasIdInSettings;
458 }
459 gasChangeListDepthGas20x2[0] = 0;
460 }
461
462 // deco and ascend calc
463 if(pDiveState->diveSettings.deco_type.ub.standard == GF_MODE)
464 {
465 /* this does modify the cns now 11.06.2015 */
466 buehlmann_calc_deco(&pDiveState->lifeData,&pDiveState->diveSettings,&pDiveState->decolistBuehlmann);
467 pDiveState->lifeData.cns += buehlmann_get_gCNS();
468 return &pDiveState->decolistBuehlmann;
469 }
470 else
471 {
472 /* this does modify the cns now 11.06.2015 */
473 vpm_calc(&pDiveState->lifeData,&pDiveState->diveSettings,&pDiveState->vpm,&pDiveState->decolistVPM, DECOSTOPS);
474 pDiveState->lifeData.cns += vpm_get_CNS();
475 return &pDiveState->decolistVPM;
476 }
477 }
478
479 float sGChelper_bar(uint16_t depth_meter)
480 {
481 SDiveState * pDiveState = &stateSim;
482 float ambient, surface, density, meter;
483
484 surface = pDiveState->lifeData.pressure_surface_bar;
485
486 if(!depth_meter)
487 return surface;
488
489 density = ((float)( 100 + settingsGetPointer()->salinity)) / 100.0f;
490 meter = depth_meter * (0.09807f * density);
491 ambient = (meter + surface);
492
493 return ambient;
494 }
495
496
497 /**
498 ******************************************************************************
499 * @brief simulation_helper_change_points
500 ******************************************************************************
501 * @param
502 * @return void
503 */
504 void simulation_helper_change_points(SSimDataSummary *outputSummary, uint16_t depth_meter, uint16_t dive_time_minutes, SDecoinfo *decoInfoInput, const uint8_t *gasChangeListDepthGas20x2)
505 {
506 uint8_t ptrDecoInfo = 0;
507 uint16_t actualDepthPoint = 0;
508 uint16_t nextDepthPoint = 0;
509 uint8_t actualConsumGasId = 0;
510 uint8_t nextGasChangeMeter = 0;
511 uint8_t ptrChangeList = 0;
512
513 float timeThis = 0;
514 float timeSummary = 0;
515 float sim_descent_rate_meter_per_min_local = 10;
516 float sim_ascent_rate_meter_per_min_local = 10;
517
518 SDiveState * pDiveState = &stateSim;
519
520 uint8_t depthDecoNext, depthLast, depthSecond, depthInc;
521
522 if(pDiveState->diveSettings.deco_type.ub.standard == GF_MODE)
523 {
524 sim_descent_rate_meter_per_min_local = sim_descent_rate_meter_per_min; // const float
525 sim_ascent_rate_meter_per_min_local = pDiveState->diveSettings.ascentRate_meterperminute;
526 }
527 else
528 {
529 sim_descent_rate_meter_per_min_local = sim_descent_rate_meter_per_min; // const float
530 sim_ascent_rate_meter_per_min_local = 10;// fix in vpm_calc_deco();
531 }
532
533 outputSummary->descentRateMeterPerMinute = sim_descent_rate_meter_per_min_local;
534 outputSummary->ascentRateMeterPerMinute = sim_ascent_rate_meter_per_min_local;
535
536 // bottom gas ppO2
537 if(gasChangeListDepthGas20x2)
538 {
539 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++];
540 actualConsumGasId = gasChangeListDepthGas20x2[ptrChangeList++];
541 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++];
542
543 while(actualDepthPoint < depth_meter)
544 {
545 if(nextGasChangeMeter && (nextGasChangeMeter < depth_meter) && (gasChangeListDepthGas20x2[ptrChangeList] != 255)) // list has 255,255 for turn from travel to deco
546 {
547 nextDepthPoint = nextGasChangeMeter;
548 }
549 else
550 {
551 nextDepthPoint = depth_meter;
552 }
553
554 if(actualConsumGasId > 5) // safety first
555 actualConsumGasId = 0;
556
557 actualDepthPoint = nextDepthPoint;
558
559 if(actualDepthPoint != depth_meter)
560 {
561 actualConsumGasId = gasChangeListDepthGas20x2[ptrChangeList++];
562 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++];
563 }
564 }
565 }
566 else
567 {
568 actualConsumGasId = pDiveState->lifeData.actualGas.GasIdInSettings;
569 nextGasChangeMeter = 0;
570 }
571 outputSummary->ppO2AtBottom = (sGChelper_bar(depth_meter) - WATER_VAPOUR_PRESSURE) * pDiveState->diveSettings.gas[actualConsumGasId].oxygen_percentage / 100.0f;
572
573
574 // going down
575 actualDepthPoint = 0;
576 nextDepthPoint = depth_meter;
577
578 timeThis = ((float)(nextDepthPoint - actualDepthPoint)) / sim_descent_rate_meter_per_min_local;
579 timeSummary += timeThis;
580 outputSummary->timeToBottom = (uint16_t)timeThis;
581
582 // bottom time
583 timeThis = ((float)dive_time_minutes) - timeSummary;
584 timeSummary += timeThis;
585 outputSummary->timeAtBottom = (uint16_t)timeSummary;
586
587
588 // ascend to first deco stop
589 actualDepthPoint = depth_meter; // that is where we are
590 timeThis = 0;
591
592 if(!decoInfoInput->output_stop_length_seconds[0]) // NDL dive
593 {
594 depthLast = 0;
595 ptrDecoInfo = 0;
596 depthDecoNext = 0;
597 }
598 else
599 {
600 // prepare deco stop list
601 depthLast = (uint8_t)(stateUsed->diveSettings.last_stop_depth_bar * 10);
602 depthSecond = (uint8_t)(stateUsed->diveSettings.input_second_to_last_stop_depth_bar * 10);
603 depthInc = (uint8_t)(stateUsed->diveSettings.input_next_stop_increment_depth_bar * 10);
604
605 for(ptrDecoInfo=DECOINFO_STRUCT_MAX_STOPS-1; ptrDecoInfo>0; ptrDecoInfo--)
606 if(decoInfoInput->output_stop_length_seconds[ptrDecoInfo]) break;
607
608 if(ptrDecoInfo == 0)
609 {
610 depthDecoNext = depthLast;
611 }
612 else
613 depthDecoNext = depthSecond + (( ptrDecoInfo - 1 )* depthInc);
614 }
615
616 nextDepthPoint = depthDecoNext;
617 if(actualDepthPoint > nextDepthPoint)
618 {
619 // flip signs! It's going up
620 timeThis = ((float)(actualDepthPoint - nextDepthPoint)) / sim_ascent_rate_meter_per_min_local;
621 actualDepthPoint = nextDepthPoint; // that is where we are
622 }
623 timeSummary += timeThis;
624 outputSummary->timeToFirstStop = (uint16_t)timeSummary;
625 outputSummary->depthMeterFirstStop = actualDepthPoint;
626
627 //ascent
628 nextDepthPoint = 0;
629 timeThis = 0;
630 if(actualDepthPoint > nextDepthPoint) // only if deco
631 {
632 // ascent time
633 timeThis = ((float)(actualDepthPoint - nextDepthPoint)) / sim_ascent_rate_meter_per_min_local;
634 actualDepthPoint = actualDepthPoint; // that is where we are
635
636 // deco stop time
637 for(ptrDecoInfo=0;ptrDecoInfo < DECOINFO_STRUCT_MAX_STOPS; ptrDecoInfo++)
638 {
639 timeThis += decoInfoInput->output_stop_length_seconds[ptrDecoInfo] / 60;
640 if(!decoInfoInput->output_stop_length_seconds[ptrDecoInfo]) break;
641 }
642 }
643 timeSummary += timeThis;
644 outputSummary->timeToSurface = (uint16_t)timeSummary;
645
646 }
647
648
649 /**
650 ******************************************************************************
651 * @brief simulation_gas_consumption
652 ******************************************************************************
653 * @note called by openEdit_PlanResult() in tMenuEditPlanner.c
654 * @note the ascend and descend time is taken from pDiveState->lifeData.ascent_rate_meter_per_min and const float sim_descent_rate_meter_per_min
655 * @param outputConsumptionList list from 1 to 5 for gas 1 to 5
656 * @param depth_meter for descend
657 * @param dive_time_minutes for descend and bottom time
658 * @param the calculated deco list
659 * @param gasConsumTravelInput: how many l/min for all but deco stops
660 * @param gasConsumDecoInput: how many l/min for deco stops only
661 * @return void
662 */
663
664 void simulation_gas_consumption(uint16_t *outputConsumptionList, uint16_t depth_meter, uint16_t dive_time_minutes, SDecoinfo *decoInfoInput, uint8_t gasConsumTravelInput, uint8_t gasConsumDecoInput, const uint8_t *gasChangeListDepthGas20x2)
665 {
666 uint8_t ptrDecoInfo = 0;
667 uint8_t ptrChangeList = 0;
668 uint8_t actualConsumGasId = 0;
669 uint8_t nextGasChangeMeter = 0;
670 uint16_t actualDepthPoint = 0;
671 uint16_t nextDepthPoint = 0;
672 uint16_t inBetweenDepthPoint = 0;
673 float timeThis = 0;
674 float consumThis = 0;
675 float timeSummary = 0;
676 float outputConsumptionTempFloat[6];
677 float sim_descent_rate_meter_per_min_local = 10;
678 float sim_ascent_rate_meter_per_min_local = 10;
679
680 SDiveState * pDiveState = &stateSim;
681
682 uint8_t depthDecoNext, depthLast, depthSecond, depthInc;
683
684 for(int i = 1; i < 6; i++)
685 outputConsumptionTempFloat[i] = 0;
686
687 if(gasChangeListDepthGas20x2)
688 {
689 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++];
690 actualConsumGasId = gasChangeListDepthGas20x2[ptrChangeList++];
691 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++];
692 }
693 else
694 {
695 actualConsumGasId = pDiveState->lifeData.actualGas.GasIdInSettings;
696 nextGasChangeMeter = 0;
697 }
698
699 if(pDiveState->diveSettings.deco_type.ub.standard == GF_MODE)
700 {
701 sim_descent_rate_meter_per_min_local = sim_descent_rate_meter_per_min; // const float
702 sim_ascent_rate_meter_per_min_local = pDiveState->diveSettings.ascentRate_meterperminute;
703 }
704 else
705 {
706 sim_descent_rate_meter_per_min_local = sim_descent_rate_meter_per_min; // const float
707 sim_ascent_rate_meter_per_min_local = 10;// fix in vpm_calc_deco();
708 }
709
710 // while((nextGasChangeMeter < depth_meter) && (actualDepthPoint < depth_meter))
711 while(actualDepthPoint < depth_meter)
712 {
713 if(nextGasChangeMeter && (nextGasChangeMeter < depth_meter) && (gasChangeListDepthGas20x2[ptrChangeList] != 255)) // list has 255,255 for turn from travel to deco
714 {
715 nextDepthPoint = nextGasChangeMeter;
716 }
717 else
718 {
719 nextDepthPoint = depth_meter;
720 }
721
722 if(actualConsumGasId > 5) // safety first
723 actualConsumGasId = 0;
724
725 timeThis = ((float)(nextDepthPoint - actualDepthPoint)) / sim_descent_rate_meter_per_min_local;
726 if(actualDepthPoint) // not if on surface
727 {
728 consumThis = ((float)gasConsumTravelInput) * sGChelper_bar(actualDepthPoint) * timeThis;
729 }
730 consumThis += ((float)gasConsumTravelInput) * sGChelper_bar(nextDepthPoint -actualDepthPoint) * timeThis / 2;
731 outputConsumptionTempFloat[actualConsumGasId] += consumThis;
732 timeSummary += timeThis;
733
734 actualDepthPoint = nextDepthPoint;
735
736 if(actualDepthPoint != depth_meter)
737 {
738 actualConsumGasId = gasChangeListDepthGas20x2[ptrChangeList++];
739 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++];
740 }
741 }
742
743 // bottom Time
744 timeThis = ((float)dive_time_minutes) - timeSummary;
745
746 if(timeThis > 0)
747 {
748 consumThis = ((float)gasConsumTravelInput) * sGChelper_bar(depth_meter) * timeThis;
749 outputConsumptionTempFloat[actualConsumGasId] += consumThis;
750 }
751
752 // ascend with deco stops prepare
753 if(gasChangeListDepthGas20x2)
754 {
755 ptrChangeList++;// gasChangeListDepthGas20x2[ptrChangeList++]; // should be the 255
756 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++];
757 }
758 else
759 {
760 nextGasChangeMeter = 0;
761 }
762
763
764 if(!decoInfoInput->output_stop_length_seconds[0]) // NDL dive
765 {
766 depthLast = 0;
767 ptrDecoInfo = 0;
768 }
769 else
770 {
771 // prepare deco stop list
772 depthLast = (uint8_t)(stateUsed->diveSettings.last_stop_depth_bar * 10);
773 depthSecond = (uint8_t)(stateUsed->diveSettings.input_second_to_last_stop_depth_bar * 10);
774 depthInc = (uint8_t)(stateUsed->diveSettings.input_next_stop_increment_depth_bar * 10);
775
776 for(ptrDecoInfo=DECOINFO_STRUCT_MAX_STOPS-1; ptrDecoInfo>0; ptrDecoInfo--)
777 if(decoInfoInput->output_stop_length_seconds[ptrDecoInfo]) break;
778 }
779
780 actualDepthPoint = depth_meter; // that is where we are
781
782 // ascend with deco stops
783 while(actualDepthPoint)
784 {
785 if(ptrDecoInfo == 0)
786 {
787 depthDecoNext = depthLast;
788 }
789 else
790 depthDecoNext = depthSecond + (( ptrDecoInfo - 1 )* depthInc);
791
792 if(nextGasChangeMeter && (nextGasChangeMeter > depthDecoNext))
793 {
794 nextDepthPoint = nextGasChangeMeter;
795 }
796 else
797 {
798 nextDepthPoint = depthDecoNext;
799 }
800
801 if(actualConsumGasId > 5) // safety first
802 actualConsumGasId = 0;
803
804 if(actualDepthPoint > nextDepthPoint)
805 {
806 // flip signs! It's going up
807 timeThis = ((float)(actualDepthPoint - nextDepthPoint)) / sim_ascent_rate_meter_per_min_local;
808 inBetweenDepthPoint = nextDepthPoint + ((actualDepthPoint - nextDepthPoint)/2);
809 consumThis = ((float)gasConsumDecoInput) * sGChelper_bar(inBetweenDepthPoint) * timeThis;
810 /*
811 if(nextDepthPoint)
812 {
813 consumThis = ((float)gasConsumDecoInput) * sGChelper_bar(nextDepthPoint) * timeThis;
814 }
815 else
816 {
817 consumThis = 0;
818 }
819 consumThis += ((float)gasConsumDecoInput) * sGChelper_bar(actualDepthPoint - nextDepthPoint) * timeThis / 2;
820 */
821 outputConsumptionTempFloat[actualConsumGasId] += consumThis;
822 }
823
824 if(nextGasChangeMeter && (nextDepthPoint == nextGasChangeMeter))
825 {
826 actualConsumGasId = gasChangeListDepthGas20x2[ptrChangeList++];
827 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++];
828 }
829
830 if(actualConsumGasId > 5) // safety first
831 actualConsumGasId = 0;
832
833 if(nextDepthPoint && (nextDepthPoint == depthDecoNext))
834 {
835 if(decoInfoInput->output_stop_length_seconds[ptrDecoInfo])
836 {
837 timeThis = ((float)(decoInfoInput->output_stop_length_seconds[ptrDecoInfo])) / 60.0f;
838 consumThis = ((float)gasConsumDecoInput) * sGChelper_bar(nextDepthPoint) * timeThis;
839 outputConsumptionTempFloat[actualConsumGasId] += consumThis;
840 }
841 if(ptrDecoInfo != 0)
842 {
843 ptrDecoInfo--;
844 }
845 else
846 {
847 depthLast = 0;
848 }
849 }
850 actualDepthPoint = nextDepthPoint;
851 }
852
853 // copy and return
854 for(int i = 1; i < 6; i++)
855 outputConsumptionList[i] = (uint16_t)(outputConsumptionTempFloat[i]);
856 }
857
858 /**
859 ******************************************************************************
860 * @brief Simulator control during simulated dive
861 ******************************************************************************
862 * @note called by user via tHomeDiveMenuControl()
863 * @param void
864 * @return void
865 */
866
867
868 void Sim_Descend (void)
869 {
870 stateSimGetPointerWrite()->lifeData.counterSecondsShallowDepth = 0;
871 if(simulation_get_aim_depth() < 200)
872 simulation_set_aim_depth(simulation_get_aim_depth() + 1);
873 }
874
875
876 void Sim_Ascend (void)
877 {
878 if(simulation_get_aim_depth() > 0)
879 simulation_set_aim_depth(simulation_get_aim_depth() - 1);
880 }
881
882
883 void Sim_Divetime (void)
884 {
885 simulation_add_time(5);
886 }
887
888
889 void Sim_Quit (void)
890 {
891 if(stateSimGetPointer()->lifeData.counterSecondsShallowDepth)
892 {
893 simulation_exit();
894 return;
895 }
896
897 if(simulation_get_aim_depth() > 0)
898 {
899 simulation_set_aim_depth(0);
900 }
901 else
902 {
903 stateSimGetPointerWrite()->lifeData.depth_meter = 0;
904 if(stateSimGetPointer()->diveSettings.diveMode == DIVEMODE_Apnea)
905 {
906 stateSimGetPointerWrite()->lifeData.counterSecondsShallowDepth = 1;
907 }
908 else
909 {
910 stateSimGetPointerWrite()->lifeData.counterSecondsShallowDepth = settingsGetPointer()->timeoutDiveReachedZeroDepth - 15;
911 }
912 }
913 }