Mercurial > public > ostc4
annotate Discovery/Src/simulation.c @ 169:842f57bbaaad cleanup-1
Bugfix: highlight the menu underline correctly on selected state
This fixes an optical bug. In dive and simulation mode, the
underline in the top menu was not highlighted correctly for the
DECO ans SYS submenu's. The check when to highlight simply was not
correct. And, yes, this part of the code is rather obfuscated, and
deserves some attention later on.
Reported-by: Matthias Heinrichs <matthias.heinrichs@heinrichsweikamp.com>
Signed-off-by: Jan Mulder <jlmulder@xs4all.nl>
author | Jan Mulder <jlmulder@xs4all.nl> |
---|---|
date | Sun, 10 Mar 2019 10:09:58 +0100 |
parents | cc9c18075e00 |
children | 1719b9d1094b |
rev | line source |
---|---|
38 | 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 pDiveState->lifeData.ppO2Sensor_bar[0] = stateRealGetPointer()->lifeData.ppO2Sensor_bar[0]; | |
152 pDiveState->lifeData.ppO2Sensor_bar[1] = stateRealGetPointer()->lifeData.ppO2Sensor_bar[1]; | |
153 pDiveState->lifeData.ppO2Sensor_bar[2] = stateRealGetPointer()->lifeData.ppO2Sensor_bar[2]; | |
154 pDiveState->lifeData.sensorVoltage_mV[0] = stateRealGetPointer()->lifeData.sensorVoltage_mV[0]; | |
155 pDiveState->lifeData.sensorVoltage_mV[1] = stateRealGetPointer()->lifeData.sensorVoltage_mV[1]; | |
156 pDiveState->lifeData.sensorVoltage_mV[2] = stateRealGetPointer()->lifeData.sensorVoltage_mV[2]; | |
157 | |
158 pDiveState->lifeData.dive_time_seconds += 1; | |
159 pDiveState->lifeData.pressure_ambient_bar = sim_get_ambiant_pressure(pDiveState); | |
160 | |
161 if(!is_ambient_pressure_close_to_surface(&pDiveState->lifeData) && !(stateSimGetPointer()->lifeData.counterSecondsShallowDepth) ) | |
162 { | |
163 pDiveState->lifeData.dive_time_seconds_without_surface_time += 1; | |
164 } | |
165 | |
166 if(is_ambient_pressure_close_to_surface(&pDiveState->lifeData)) // new hw 170214 | |
167 { | |
168 if(!(stateSimGetPointer()->lifeData.counterSecondsShallowDepth)) | |
169 { | |
170 if(pDiveState->diveSettings.diveMode != DIVEMODE_Apnea) | |
171 pDiveState->lifeData.counterSecondsShallowDepth = settingsGetPointer()->timeoutDiveReachedZeroDepth - 15; | |
172 else | |
173 { | |
174 pDiveState->lifeData.apnea_last_dive_time_seconds = pDiveState->lifeData.dive_time_seconds; | |
175 if(pDiveState->lifeData.apnea_last_dive_time_seconds > pDiveState->lifeData.dive_time_seconds_without_surface_time) | |
176 pDiveState->lifeData.apnea_last_dive_time_seconds = pDiveState->lifeData.dive_time_seconds_without_surface_time; | |
177 pDiveState->lifeData.apnea_last_max_depth_meter = pDiveState->lifeData.max_depth_meter; | |
178 pDiveState->lifeData.counterSecondsShallowDepth = 1; | |
179 } | |
180 } | |
181 } | |
182 else | |
183 { | |
184 pDiveState->lifeData.counterSecondsShallowDepth = 0; | |
185 } | |
186 | |
187 pDiveState->lifeData.depth_meter = (pDiveState->lifeData.pressure_ambient_bar - pDiveState->lifeData.pressure_surface_bar) * 10.0f; | |
188 if(pDiveState->lifeData.max_depth_meter < pDiveState->lifeData.depth_meter) | |
189 pDiveState->lifeData.max_depth_meter = pDiveState->lifeData.depth_meter; | |
190 | |
191 /* apnoe specials | |
192 */ | |
193 if(pDiveState->diveSettings.diveMode == DIVEMODE_Apnea) | |
194 { | |
195 if(pDiveState->lifeData.max_depth_meter > pDiveState->lifeData.apnea_total_max_depth_meter) | |
196 pDiveState->lifeData.apnea_total_max_depth_meter = pDiveState->lifeData.max_depth_meter; | |
197 | |
198 if(pDiveState->lifeData.counterSecondsShallowDepth) | |
199 { | |
200 pDiveState->lifeData.dive_time_seconds = 0; | |
201 pDiveState->lifeData.max_depth_meter = 0; | |
202 pDiveState->lifeData.boolResetAverageDepth = 1; | |
203 pDiveState->lifeData.boolResetStopwatch = 1; | |
204 } | |
205 } | |
206 | |
207 /* average depth | |
208 */ | |
209 float *AvgDepthValue = &pDiveState->lifeData.average_depth_meter; | |
210 float DepthNow = pDiveState->lifeData.depth_meter; | |
211 uint32_t *AvgDepthCount = &pDiveState->lifeData.internal.average_depth_meter_Count; | |
212 uint32_t *AvgDepthTimer = &pDiveState->lifeData.internal.average_depth_last_update_dive_time_seconds_without_surface_time; | |
213 uint32_t AvgSecondsSinceLast; | |
214 uint32_t DiveTime = pDiveState->lifeData.dive_time_seconds_without_surface_time; | |
215 | |
216 if(pDiveState->lifeData.boolResetAverageDepth) | |
217 { | |
218 *AvgDepthValue = DepthNow; | |
219 *AvgDepthCount = 1; | |
220 *AvgDepthTimer = DiveTime; | |
221 pDiveState->lifeData.boolResetAverageDepth = 0; | |
222 } | |
223 else if (DiveTime > *AvgDepthTimer) | |
224 { | |
225 AvgSecondsSinceLast = DiveTime - *AvgDepthTimer; | |
226 for(int i=0;i<AvgSecondsSinceLast;i++) | |
227 { | |
228 *AvgDepthValue = (*AvgDepthValue * *AvgDepthCount + DepthNow) / (*AvgDepthCount + 1); | |
229 *AvgDepthCount += 1; | |
230 } | |
231 *AvgDepthTimer = DiveTime; | |
232 } | |
233 if(*AvgDepthCount == 0) | |
234 *AvgDepthValue = 0; | |
235 | |
236 /* Exposure Tissues | |
237 */ | |
238 decom_tissues_exposure(1, &pDiveState->lifeData); | |
239 /* moved to updateSetpointStateUsed() | |
240 pDiveState->lifeData.ppO2 = decom_calc_ppO2( pDiveState->lifeData.pressure_ambient_bar, &pDiveState->lifeData.actualGas); | |
241 */ | |
242 decom_oxygen_calculate_cns_exposure(1, &pDiveState->lifeData.actualGas, pDiveState->lifeData.pressure_ambient_bar, &pDiveState->lifeData.cns); | |
243 //if((pDiveState->lifeData.depth_meter < 0.1f) || (pDiveState->lifeData.dive_time_seconds > 1*60*60)) | |
244 // if(pDiveState->lifeData.dive_time_seconds > 1*60*60) | |
245 if(pDiveState->lifeData.dive_time_seconds > 5*60*60) // test Dirk Berben | |
246 { | |
247 simulation_exit(); | |
248 } | |
249 if(stateSimGetPointer()->lifeData.counterSecondsShallowDepth) | |
250 { | |
251 stateSimGetPointerWrite()->lifeData.counterSecondsShallowDepth += 1; | |
252 if(stateSimGetPointer()->lifeData.counterSecondsShallowDepth >= settingsGetPointer()->timeoutDiveReachedZeroDepth) | |
253 simulation_exit(); | |
254 } | |
255 vpm_crush(pDiveState); | |
256 } | |
257 | |
258 /** | |
259 ****************************************************************************** | |
260 * @brief adds extra time for fast simulation | |
261 ****************************************************************************** | |
262 *@param minutes | |
263 * @return float : new pressure | |
264 */ | |
265 void simulation_add_time(int minutes) | |
266 { | |
267 for(int i = 0; i < 60 * minutes; i++) | |
268 { | |
269 simulation_UpdateLifeData(0); | |
270 updateMiniLiveLogbook(0); | |
271 timer_UpdateSecond(0); | |
272 } | |
273 } | |
274 | |
275 /** | |
276 ****************************************************************************** | |
277 * @brief get aim_depth | |
278 ****************************************************************************** | |
279 * @return sim_aim_depth_meter; | |
280 */ | |
281 | |
282 uint16_t simulation_get_aim_depth(void) | |
283 { | |
284 return (uint16_t)sim_aim_depth_meter; | |
285 } | |
286 | |
287 /** | |
288 ****************************************************************************** | |
289 * @brief get heed decostops | |
290 ****************************************************************************** | |
291 * @return true if ascend follows decostops; | |
292 */ | |
293 | |
294 _Bool simulation_get_heed_decostops(void) | |
295 { | |
296 return sim_head_decostops; | |
297 } | |
298 | |
299 /** | |
300 ****************************************************************************** | |
301 * @brief sets aim_depth | |
302 ****************************************************************************** | |
303 *@param depth_meter | |
304 * @return float : new pressure | |
305 */ | |
306 void simulation_set_aim_depth(int depth_meter) | |
307 { | |
308 sim_aim_depth_meter = depth_meter; | |
309 } | |
310 | |
311 /** | |
312 ****************************************************************************** | |
313 * @brief simulates ambiant pressure depending on aim depth | |
314 ****************************************************************************** | |
315 * @note if aim_depth != actual depth, the depth change within one second | |
316 * (depending on descent or ascent) rate is calculated | |
317 * @param SDiveState* pDiveState: | |
318 * @return float : new ambiant pressure | |
319 */ | |
320 float sim_get_ambiant_pressure(SDiveState * pDiveState) | |
321 { | |
322 //Calc next depth | |
323 uint8_t actual_deco_stop = decom_get_actual_deco_stop(pDiveState); | |
324 float depth_meter = pDiveState->lifeData.depth_meter; | |
325 float surface_pressure_bar = pDiveState->lifeData.pressure_surface_bar; | |
326 if(depth_meter < sim_aim_depth_meter) | |
327 { | |
328 depth_meter = depth_meter + sim_descent_rate_meter_per_min / 60; | |
329 if(depth_meter > sim_aim_depth_meter) | |
330 depth_meter = sim_aim_depth_meter; | |
331 } | |
332 else if(depth_meter > sim_aim_depth_meter) | |
333 { | |
334 | |
335 depth_meter -= pDiveState->diveSettings.ascentRate_meterperminute / 60; | |
336 if(depth_meter < sim_aim_depth_meter) | |
337 depth_meter = sim_aim_depth_meter; | |
338 | |
339 if(sim_head_decostops && depth_meter < actual_deco_stop) | |
340 { | |
341 if(actual_deco_stop < (depth_meter + pDiveState->diveSettings.ascentRate_meterperminute / 60)) | |
342 depth_meter = actual_deco_stop; | |
343 else | |
344 depth_meter += pDiveState->diveSettings.ascentRate_meterperminute / 60; | |
345 } | |
346 | |
347 } | |
348 | |
349 return surface_pressure_bar + depth_meter / 10; | |
350 } | |
351 | |
352 | |
353 /** | |
354 ****************************************************************************** | |
355 * @brief Reduces deco time of deepest stop by one second | |
356 ****************************************************************************** | |
357 * @note called during fast simulation | |
358 * @param SDiveState* pDiveState: | |
359 * @return void | |
360 */ | |
361 void sim_reduce_deco_time_one_second(SDiveState* pDiveState) | |
362 { | |
363 SDecoinfo* pDecoinfo; | |
364 if(pDiveState->diveSettings.deco_type.ub.standard == GF_MODE) | |
365 pDecoinfo = &pDiveState->decolistBuehlmann; | |
366 else | |
367 pDecoinfo = &pDiveState->decolistVPM; | |
368 | |
369 //Reduce deco time of deepest stop by one second | |
370 for(int i = DECOINFO_STRUCT_MAX_STOPS -1 ;i >= 0; i--) | |
371 { | |
372 if(pDecoinfo->output_stop_length_seconds[i] > 0) | |
373 { | |
374 pDecoinfo->output_stop_length_seconds[i]--; | |
375 break; | |
376 } | |
377 } | |
378 } | |
379 | |
380 SDecoinfo* simulation_decoplaner(uint16_t depth_meter, uint16_t intervall_time_minutes, uint16_t dive_time_minutes, uint8_t *gasChangeListDepthGas20x2) | |
381 { | |
382 uint8_t ptrGasChangeList = 0; // new hw 160704 | |
383 | |
384 SDiveState * pDiveState = &stateSim; | |
385 copyDiveSettingsToSim(); | |
386 vpm_init(&pDiveState->vpm, pDiveState->diveSettings.vpm_conservatism, 0, 0); | |
387 //buehlmann_init(); | |
388 //timer_init(); | |
389 memset(&pDiveState->events,0, sizeof(SEvents)); | |
390 pDiveState->diveSettings.internal__pressure_first_stop_ambient_bar_as_upper_limit_for_gf_low_otherwise_zero = 0; | |
391 //Calc desaturation during intervall (with Air) | |
392 setActualGasAir(&pDiveState->lifeData); | |
393 if(intervall_time_minutes > 0) | |
394 { | |
395 decom_tissues_exposure(intervall_time_minutes * 60, &pDiveState->lifeData); | |
396 decom_oxygen_calculate_cns_degrade(&pDiveState->lifeData.cns, intervall_time_minutes * 60); | |
397 } | |
398 | |
399 //Switch to first Gas | |
400 setActualGasFirst(&pDiveState->lifeData); | |
401 | |
402 // new hw 160704 | |
403 if(gasChangeListDepthGas20x2) | |
404 { | |
405 gasChangeListDepthGas20x2[ptrGasChangeList++] = 0; | |
406 gasChangeListDepthGas20x2[ptrGasChangeList++] = pDiveState->lifeData.actualGas.GasIdInSettings; | |
407 gasChangeListDepthGas20x2[0] =0; // depth zero | |
408 } | |
409 | |
410 //Going down / descent | |
411 simulation_set_aim_depth(depth_meter); | |
412 for(int i = 0; i < 60 * dive_time_minutes; i++) | |
413 { | |
414 simulation_UpdateLifeData(0); | |
415 check_warning2(pDiveState); | |
416 if(pDiveState->warnings.betterGas) | |
417 { | |
418 setActualGas(&pDiveState->lifeData,actualBetterGasId(),pDiveState->lifeData.actualGas.setPoint_cbar); | |
419 if(gasChangeListDepthGas20x2 && (pDiveState->diveSettings.diveMode == DIVEMODE_OC)) | |
420 { | |
421 gasChangeListDepthGas20x2[ptrGasChangeList++] = pDiveState->lifeData.depth_meter; | |
422 gasChangeListDepthGas20x2[ptrGasChangeList++] = actualBetterGasId(); | |
423 } | |
424 } | |
425 } | |
426 | |
427 decom_CreateGasChangeList(&pDiveState->diveSettings, &pDiveState->lifeData); // was there before and needed for buehlmann_calc_deco and vpm_calc | |
428 | |
429 // new hw 160704 | |
430 if(gasChangeListDepthGas20x2 && (pDiveState->diveSettings.diveMode == DIVEMODE_OC)) | |
431 { | |
432 // change direction from better gas to deco gas | |
433 gasChangeListDepthGas20x2[ptrGasChangeList++] = 255; | |
434 gasChangeListDepthGas20x2[ptrGasChangeList++] = 255; | |
435 | |
436 // ascend (deco) gases | |
437 for(int i=1; i<=5;i++) | |
438 { | |
439 if(pDiveState->diveSettings.decogaslist[i].change_during_ascent_depth_meter_otherwise_zero == 0) | |
440 break; | |
441 gasChangeListDepthGas20x2[ptrGasChangeList++] = pDiveState->diveSettings.decogaslist[i].change_during_ascent_depth_meter_otherwise_zero; | |
442 gasChangeListDepthGas20x2[ptrGasChangeList++] = pDiveState->diveSettings.decogaslist[i].GasIdInSettings; | |
443 } | |
444 gasChangeListDepthGas20x2[0] = 0; | |
445 } | |
446 | |
447 // deco and ascend calc | |
448 if(pDiveState->diveSettings.deco_type.ub.standard == GF_MODE) | |
449 { | |
450 /* this does modify the cns now 11.06.2015 */ | |
451 buehlmann_calc_deco(&pDiveState->lifeData,&pDiveState->diveSettings,&pDiveState->decolistBuehlmann); | |
452 pDiveState->lifeData.cns += buehlmann_get_gCNS(); | |
453 return &pDiveState->decolistBuehlmann; | |
454 } | |
455 else | |
456 { | |
457 /* this does modify the cns now 11.06.2015 */ | |
458 vpm_calc(&pDiveState->lifeData,&pDiveState->diveSettings,&pDiveState->vpm,&pDiveState->decolistVPM, DECOSTOPS); | |
459 pDiveState->lifeData.cns += vpm_get_CNS(); | |
460 return &pDiveState->decolistVPM; | |
461 } | |
462 } | |
463 | |
464 float sGChelper_bar(uint16_t depth_meter) | |
465 { | |
466 SDiveState * pDiveState = &stateSim; | |
467 float ambient, surface, density, meter; | |
468 | |
469 surface = pDiveState->lifeData.pressure_surface_bar; | |
470 | |
471 if(!depth_meter) | |
472 return surface; | |
473 | |
474 density = ((float)( 100 + settingsGetPointer()->salinity)) / 100.0f; | |
475 meter = depth_meter * (0.09807f * density); | |
476 ambient = (meter + surface); | |
477 | |
478 return ambient; | |
479 } | |
480 | |
481 | |
482 /** | |
483 ****************************************************************************** | |
484 * @brief simulation_helper_change_points | |
485 ****************************************************************************** | |
486 * @param | |
487 * @return void | |
488 */ | |
489 void simulation_helper_change_points(SSimDataSummary *outputSummary, uint16_t depth_meter, uint16_t dive_time_minutes, SDecoinfo *decoInfoInput, const uint8_t *gasChangeListDepthGas20x2) | |
490 { | |
491 uint8_t ptrDecoInfo = 0; | |
492 uint16_t actualDepthPoint = 0; | |
493 uint16_t nextDepthPoint = 0; | |
494 uint8_t actualConsumGasId = 0; | |
495 uint8_t nextGasChangeMeter = 0; | |
496 uint8_t ptrChangeList = 0; | |
497 | |
498 float timeThis = 0; | |
499 float timeSummary = 0; | |
500 float sim_descent_rate_meter_per_min_local = 10; | |
501 float sim_ascent_rate_meter_per_min_local = 10; | |
502 | |
503 SDiveState * pDiveState = &stateSim; | |
504 | |
505 uint8_t depthDecoNext, depthLast, depthSecond, depthInc; | |
506 | |
507 if(pDiveState->diveSettings.deco_type.ub.standard == GF_MODE) | |
508 { | |
509 sim_descent_rate_meter_per_min_local = sim_descent_rate_meter_per_min; // const float | |
510 sim_ascent_rate_meter_per_min_local = pDiveState->diveSettings.ascentRate_meterperminute; | |
511 } | |
512 else | |
513 { | |
514 sim_descent_rate_meter_per_min_local = sim_descent_rate_meter_per_min; // const float | |
515 sim_ascent_rate_meter_per_min_local = 10;// fix in vpm_calc_deco(); | |
516 } | |
517 | |
518 outputSummary->descentRateMeterPerMinute = sim_descent_rate_meter_per_min_local; | |
519 outputSummary->ascentRateMeterPerMinute = sim_ascent_rate_meter_per_min_local; | |
520 | |
521 // bottom gas ppO2 | |
522 if(gasChangeListDepthGas20x2) | |
523 { | |
524 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++]; | |
525 actualConsumGasId = gasChangeListDepthGas20x2[ptrChangeList++]; | |
526 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++]; | |
527 | |
528 while(actualDepthPoint < depth_meter) | |
529 { | |
530 if(nextGasChangeMeter && (nextGasChangeMeter < depth_meter) && (gasChangeListDepthGas20x2[ptrChangeList] != 255)) // list has 255,255 for turn from travel to deco | |
531 { | |
532 nextDepthPoint = nextGasChangeMeter; | |
533 } | |
534 else | |
535 { | |
536 nextDepthPoint = depth_meter; | |
537 } | |
538 | |
539 if(actualConsumGasId > 5) // safety first | |
540 actualConsumGasId = 0; | |
541 | |
542 actualDepthPoint = nextDepthPoint; | |
543 | |
544 if(actualDepthPoint != depth_meter) | |
545 { | |
546 actualConsumGasId = gasChangeListDepthGas20x2[ptrChangeList++]; | |
547 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++]; | |
548 } | |
549 } | |
550 } | |
551 else | |
552 { | |
553 actualConsumGasId = pDiveState->lifeData.actualGas.GasIdInSettings; | |
554 nextGasChangeMeter = 0; | |
555 } | |
556 outputSummary->ppO2AtBottom = (sGChelper_bar(depth_meter) - WATER_VAPOUR_PRESSURE) * pDiveState->diveSettings.gas[actualConsumGasId].oxygen_percentage / 100.0f; | |
557 | |
558 | |
559 // going down | |
560 actualDepthPoint = 0; | |
561 nextDepthPoint = depth_meter; | |
562 | |
563 timeThis = ((float)(nextDepthPoint - actualDepthPoint)) / sim_descent_rate_meter_per_min_local; | |
564 timeSummary += timeThis; | |
565 outputSummary->timeToBottom = (uint16_t)timeThis; | |
566 | |
567 // bottom time | |
568 timeThis = ((float)dive_time_minutes) - timeSummary; | |
569 timeSummary += timeThis; | |
570 outputSummary->timeAtBottom = (uint16_t)timeSummary; | |
571 | |
572 | |
573 // ascend to first deco stop | |
574 actualDepthPoint = depth_meter; // that is where we are | |
575 timeThis = 0; | |
576 | |
577 if(!decoInfoInput->output_stop_length_seconds[0]) // NDL dive | |
578 { | |
579 depthLast = 0; | |
580 ptrDecoInfo = 0; | |
581 depthDecoNext = 0; | |
582 } | |
583 else | |
584 { | |
585 // prepare deco stop list | |
586 depthLast = (uint8_t)(stateUsed->diveSettings.last_stop_depth_bar * 10); | |
587 depthSecond = (uint8_t)(stateUsed->diveSettings.input_second_to_last_stop_depth_bar * 10); | |
588 depthInc = (uint8_t)(stateUsed->diveSettings.input_next_stop_increment_depth_bar * 10); | |
589 | |
590 for(ptrDecoInfo=DECOINFO_STRUCT_MAX_STOPS-1; ptrDecoInfo>0; ptrDecoInfo--) | |
591 if(decoInfoInput->output_stop_length_seconds[ptrDecoInfo]) break; | |
592 | |
593 if(ptrDecoInfo == 0) | |
594 { | |
595 depthDecoNext = depthLast; | |
596 } | |
597 else | |
598 depthDecoNext = depthSecond + (( ptrDecoInfo - 1 )* depthInc); | |
599 } | |
600 | |
601 nextDepthPoint = depthDecoNext; | |
602 if(actualDepthPoint > nextDepthPoint) | |
603 { | |
604 // flip signs! It's going up | |
605 timeThis = ((float)(actualDepthPoint - nextDepthPoint)) / sim_ascent_rate_meter_per_min_local; | |
606 actualDepthPoint = nextDepthPoint; // that is where we are | |
607 } | |
608 timeSummary += timeThis; | |
609 outputSummary->timeToFirstStop = (uint16_t)timeSummary; | |
610 outputSummary->depthMeterFirstStop = actualDepthPoint; | |
611 | |
612 //ascent | |
613 nextDepthPoint = 0; | |
614 timeThis = 0; | |
615 if(actualDepthPoint > nextDepthPoint) // only if deco | |
616 { | |
617 // ascent time | |
618 timeThis = ((float)(actualDepthPoint - nextDepthPoint)) / sim_ascent_rate_meter_per_min_local; | |
619 actualDepthPoint = actualDepthPoint; // that is where we are | |
620 | |
621 // deco stop time | |
622 for(ptrDecoInfo=0;ptrDecoInfo < DECOINFO_STRUCT_MAX_STOPS; ptrDecoInfo++) | |
623 { | |
624 timeThis += decoInfoInput->output_stop_length_seconds[ptrDecoInfo] / 60; | |
625 if(!decoInfoInput->output_stop_length_seconds[ptrDecoInfo]) break; | |
626 } | |
627 } | |
628 timeSummary += timeThis; | |
629 outputSummary->timeToSurface = (uint16_t)timeSummary; | |
630 | |
631 } | |
632 | |
633 | |
634 /** | |
635 ****************************************************************************** | |
636 * @brief simulation_gas_consumption | |
637 ****************************************************************************** | |
638 * @note called by openEdit_PlanResult() in tMenuEditPlanner.c | |
639 * @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 | |
640 * @param outputConsumptionList list from 1 to 5 for gas 1 to 5 | |
641 * @param depth_meter for descend | |
642 * @param dive_time_minutes for descend and bottom time | |
643 * @param the calculated deco list | |
644 * @param gasConsumTravelInput: how many l/min for all but deco stops | |
645 * @param gasConsumDecoInput: how many l/min for deco stops only | |
646 * @return void | |
647 */ | |
648 | |
649 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) | |
650 { | |
651 uint8_t ptrDecoInfo = 0; | |
652 uint8_t ptrChangeList = 0; | |
653 uint8_t actualConsumGasId = 0; | |
654 uint8_t nextGasChangeMeter = 0; | |
655 uint16_t actualDepthPoint = 0; | |
656 uint16_t nextDepthPoint = 0; | |
657 uint16_t inBetweenDepthPoint = 0; | |
658 float timeThis = 0; | |
659 float consumThis = 0; | |
660 float timeSummary = 0; | |
661 float outputConsumptionTempFloat[6]; | |
662 float sim_descent_rate_meter_per_min_local = 10; | |
663 float sim_ascent_rate_meter_per_min_local = 10; | |
664 | |
665 SDiveState * pDiveState = &stateSim; | |
666 | |
51
8f8ea3a32e82
Resolved warnings pointing to possible invalid memory access
Ideenmodellierer
parents:
38
diff
changeset
|
667 uint8_t depthDecoNext = 0; |
8f8ea3a32e82
Resolved warnings pointing to possible invalid memory access
Ideenmodellierer
parents:
38
diff
changeset
|
668 uint8_t depthLast = 0; |
8f8ea3a32e82
Resolved warnings pointing to possible invalid memory access
Ideenmodellierer
parents:
38
diff
changeset
|
669 uint8_t depthSecond = 0; |
8f8ea3a32e82
Resolved warnings pointing to possible invalid memory access
Ideenmodellierer
parents:
38
diff
changeset
|
670 uint8_t depthInc = 0; |
38 | 671 |
672 for(int i = 1; i < 6; i++) | |
673 outputConsumptionTempFloat[i] = 0; | |
674 | |
675 if(gasChangeListDepthGas20x2) | |
676 { | |
677 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++]; | |
678 actualConsumGasId = gasChangeListDepthGas20x2[ptrChangeList++]; | |
679 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++]; | |
680 } | |
681 else | |
682 { | |
683 actualConsumGasId = pDiveState->lifeData.actualGas.GasIdInSettings; | |
684 nextGasChangeMeter = 0; | |
685 } | |
686 | |
687 if(pDiveState->diveSettings.deco_type.ub.standard == GF_MODE) | |
688 { | |
689 sim_descent_rate_meter_per_min_local = sim_descent_rate_meter_per_min; // const float | |
690 sim_ascent_rate_meter_per_min_local = pDiveState->diveSettings.ascentRate_meterperminute; | |
691 } | |
692 else | |
693 { | |
694 sim_descent_rate_meter_per_min_local = sim_descent_rate_meter_per_min; // const float | |
695 sim_ascent_rate_meter_per_min_local = 10;// fix in vpm_calc_deco(); | |
696 } | |
697 | |
698 // while((nextGasChangeMeter < depth_meter) && (actualDepthPoint < depth_meter)) | |
699 while(actualDepthPoint < depth_meter) | |
700 { | |
701 if(nextGasChangeMeter && (nextGasChangeMeter < depth_meter) && (gasChangeListDepthGas20x2[ptrChangeList] != 255)) // list has 255,255 for turn from travel to deco | |
702 { | |
703 nextDepthPoint = nextGasChangeMeter; | |
704 } | |
705 else | |
706 { | |
707 nextDepthPoint = depth_meter; | |
708 } | |
709 | |
710 if(actualConsumGasId > 5) // safety first | |
711 actualConsumGasId = 0; | |
712 | |
713 timeThis = ((float)(nextDepthPoint - actualDepthPoint)) / sim_descent_rate_meter_per_min_local; | |
714 if(actualDepthPoint) // not if on surface | |
715 { | |
716 consumThis = ((float)gasConsumTravelInput) * sGChelper_bar(actualDepthPoint) * timeThis; | |
717 } | |
718 consumThis += ((float)gasConsumTravelInput) * sGChelper_bar(nextDepthPoint -actualDepthPoint) * timeThis / 2; | |
719 outputConsumptionTempFloat[actualConsumGasId] += consumThis; | |
720 timeSummary += timeThis; | |
721 | |
722 actualDepthPoint = nextDepthPoint; | |
723 | |
724 if(actualDepthPoint != depth_meter) | |
725 { | |
726 actualConsumGasId = gasChangeListDepthGas20x2[ptrChangeList++]; | |
727 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++]; | |
728 } | |
729 } | |
730 | |
731 // bottom Time | |
732 timeThis = ((float)dive_time_minutes) - timeSummary; | |
733 | |
734 if(timeThis > 0) | |
735 { | |
736 consumThis = ((float)gasConsumTravelInput) * sGChelper_bar(depth_meter) * timeThis; | |
737 outputConsumptionTempFloat[actualConsumGasId] += consumThis; | |
738 } | |
739 | |
740 // ascend with deco stops prepare | |
741 if(gasChangeListDepthGas20x2) | |
742 { | |
743 ptrChangeList++;// gasChangeListDepthGas20x2[ptrChangeList++]; // should be the 255 | |
744 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++]; | |
745 } | |
746 else | |
747 { | |
748 nextGasChangeMeter = 0; | |
749 } | |
750 | |
751 | |
752 if(!decoInfoInput->output_stop_length_seconds[0]) // NDL dive | |
753 { | |
754 depthLast = 0; | |
755 ptrDecoInfo = 0; | |
756 } | |
757 else | |
758 { | |
759 // prepare deco stop list | |
760 depthLast = (uint8_t)(stateUsed->diveSettings.last_stop_depth_bar * 10); | |
761 depthSecond = (uint8_t)(stateUsed->diveSettings.input_second_to_last_stop_depth_bar * 10); | |
762 depthInc = (uint8_t)(stateUsed->diveSettings.input_next_stop_increment_depth_bar * 10); | |
763 | |
764 for(ptrDecoInfo=DECOINFO_STRUCT_MAX_STOPS-1; ptrDecoInfo>0; ptrDecoInfo--) | |
765 if(decoInfoInput->output_stop_length_seconds[ptrDecoInfo]) break; | |
766 } | |
767 | |
768 actualDepthPoint = depth_meter; // that is where we are | |
769 | |
770 // ascend with deco stops | |
771 while(actualDepthPoint) | |
772 { | |
773 if(ptrDecoInfo == 0) | |
774 { | |
775 depthDecoNext = depthLast; | |
776 } | |
777 else | |
778 depthDecoNext = depthSecond + (( ptrDecoInfo - 1 )* depthInc); | |
779 | |
780 if(nextGasChangeMeter && (nextGasChangeMeter > depthDecoNext)) | |
781 { | |
782 nextDepthPoint = nextGasChangeMeter; | |
783 } | |
784 else | |
785 { | |
786 nextDepthPoint = depthDecoNext; | |
787 } | |
788 | |
789 if(actualConsumGasId > 5) // safety first | |
790 actualConsumGasId = 0; | |
791 | |
792 if(actualDepthPoint > nextDepthPoint) | |
793 { | |
794 // flip signs! It's going up | |
795 timeThis = ((float)(actualDepthPoint - nextDepthPoint)) / sim_ascent_rate_meter_per_min_local; | |
796 inBetweenDepthPoint = nextDepthPoint + ((actualDepthPoint - nextDepthPoint)/2); | |
797 consumThis = ((float)gasConsumDecoInput) * sGChelper_bar(inBetweenDepthPoint) * timeThis; | |
798 /* | |
799 if(nextDepthPoint) | |
800 { | |
801 consumThis = ((float)gasConsumDecoInput) * sGChelper_bar(nextDepthPoint) * timeThis; | |
802 } | |
803 else | |
804 { | |
805 consumThis = 0; | |
806 } | |
807 consumThis += ((float)gasConsumDecoInput) * sGChelper_bar(actualDepthPoint - nextDepthPoint) * timeThis / 2; | |
808 */ | |
809 outputConsumptionTempFloat[actualConsumGasId] += consumThis; | |
810 } | |
811 | |
812 if(nextGasChangeMeter && (nextDepthPoint == nextGasChangeMeter)) | |
813 { | |
814 actualConsumGasId = gasChangeListDepthGas20x2[ptrChangeList++]; | |
815 nextGasChangeMeter = gasChangeListDepthGas20x2[ptrChangeList++]; | |
816 } | |
817 | |
818 if(actualConsumGasId > 5) // safety first | |
819 actualConsumGasId = 0; | |
820 | |
821 if(nextDepthPoint && (nextDepthPoint == depthDecoNext)) | |
822 { | |
823 if(decoInfoInput->output_stop_length_seconds[ptrDecoInfo]) | |
824 { | |
825 timeThis = ((float)(decoInfoInput->output_stop_length_seconds[ptrDecoInfo])) / 60.0f; | |
826 consumThis = ((float)gasConsumDecoInput) * sGChelper_bar(nextDepthPoint) * timeThis; | |
827 outputConsumptionTempFloat[actualConsumGasId] += consumThis; | |
828 } | |
829 if(ptrDecoInfo != 0) | |
830 { | |
831 ptrDecoInfo--; | |
832 } | |
833 else | |
834 { | |
835 depthLast = 0; | |
836 } | |
837 } | |
838 actualDepthPoint = nextDepthPoint; | |
839 } | |
840 | |
841 // copy and return | |
842 for(int i = 1; i < 6; i++) | |
843 outputConsumptionList[i] = (uint16_t)(outputConsumptionTempFloat[i]); | |
844 } | |
845 | |
846 /** | |
847 ****************************************************************************** | |
848 * @brief Simulator control during simulated dive | |
849 ****************************************************************************** | |
850 * @note called by user via tHomeDiveMenuControl() | |
851 * @param void | |
852 * @return void | |
853 */ | |
854 | |
855 | |
856 void Sim_Descend (void) | |
857 { | |
858 stateSimGetPointerWrite()->lifeData.counterSecondsShallowDepth = 0; | |
859 if(simulation_get_aim_depth() < 200) | |
860 simulation_set_aim_depth(simulation_get_aim_depth() + 1); | |
861 } | |
862 | |
863 | |
864 void Sim_Ascend (void) | |
865 { | |
866 if(simulation_get_aim_depth() > 0) | |
867 simulation_set_aim_depth(simulation_get_aim_depth() - 1); | |
868 } | |
869 | |
870 | |
871 void Sim_Divetime (void) | |
872 { | |
873 simulation_add_time(5); | |
874 } | |
875 | |
876 | |
877 void Sim_Quit (void) | |
878 { | |
879 if(stateSimGetPointer()->lifeData.counterSecondsShallowDepth) | |
880 { | |
881 simulation_exit(); | |
882 return; | |
883 } | |
884 | |
885 if(simulation_get_aim_depth() > 0) | |
886 { | |
887 simulation_set_aim_depth(0); | |
888 } | |
889 else | |
890 { | |
891 stateSimGetPointerWrite()->lifeData.depth_meter = 0; | |
892 if(stateSimGetPointer()->diveSettings.diveMode == DIVEMODE_Apnea) | |
893 { | |
894 stateSimGetPointerWrite()->lifeData.counterSecondsShallowDepth = 1; | |
895 } | |
896 else | |
897 { | |
898 stateSimGetPointerWrite()->lifeData.counterSecondsShallowDepth = settingsGetPointer()->timeoutDiveReachedZeroDepth - 15; | |
899 } | |
900 } | |
901 } |