/* add period_in_seconds at the given pressure and gas to the deco calculation */ double add_segment(double pressure, struct gasmix *gasmix, int period_in_seconds, double ccpo2, const struct dive *dive) { int ci; int fo2 = gasmix->o2.permille ? gasmix->o2.permille : 209; double ppn2 = (pressure - WV_PRESSURE) * (1000 - fo2 - gasmix->he.permille) / 1000.0; double pphe = (pressure - WV_PRESSURE) * gasmix->he.permille / 1000.0; #if GF_LOW_AT_MAXDEPTH if (pressure > gf_low_pressure_this_dive) gf_low_pressure_this_dive = pressure; #endif if (ccpo2 > 0.0) { /* CC */ double rel_o2_amb, f_dilutent; rel_o2_amb = ccpo2 / pressure; f_dilutent = (1 - rel_o2_amb) / (1 - fo2 / 1000.0); if (f_dilutent < 0) { /* setpoint is higher than ambient pressure -> pure O2 */ ppn2 = 0.0; pphe = 0.0; } else if (f_dilutent < 1.0) { ppn2 *= f_dilutent; pphe *= f_dilutent; } } if (period_in_seconds == 1) { /* one second interval during dive */ for (ci = 0; ci < 16; ci++) { if (ppn2 - tissue_n2_sat[ci] > 0) tissue_n2_sat[ci] += buehlmann_config.satmult * (ppn2 - tissue_n2_sat[ci]) * buehlmann_N2_factor_expositon_one_second[ci]; else tissue_n2_sat[ci] += buehlmann_config.desatmult * (ppn2 - tissue_n2_sat[ci]) * buehlmann_N2_factor_expositon_one_second[ci]; if (pphe - tissue_he_sat[ci] > 0) tissue_he_sat[ci] += buehlmann_config.satmult * (pphe - tissue_he_sat[ci]) * buehlmann_He_factor_expositon_one_second[ci]; else tissue_he_sat[ci] += buehlmann_config.desatmult * (pphe - tissue_he_sat[ci]) * buehlmann_He_factor_expositon_one_second[ci]; } } else { /* all other durations */ for (ci = 0; ci < 16; ci++) { if (ppn2 - tissue_n2_sat[ci] > 0) tissue_n2_sat[ci] += buehlmann_config.satmult * (ppn2 - tissue_n2_sat[ci]) * (1 - pow(2.0,(- period_in_seconds / (buehlmann_N2_t_halflife[ci] * 60)))); else tissue_n2_sat[ci] += buehlmann_config.desatmult * (ppn2 - tissue_n2_sat[ci]) * (1 - pow(2.0,(- period_in_seconds / (buehlmann_N2_t_halflife[ci] * 60)))); if (pphe - tissue_he_sat[ci] > 0) tissue_he_sat[ci] += buehlmann_config.satmult * (pphe - tissue_he_sat[ci]) * (1 - pow(2.0,(- period_in_seconds / (buehlmann_He_t_halflife[ci] * 60)))); else tissue_he_sat[ci] += buehlmann_config.desatmult * (pphe - tissue_he_sat[ci]) * (1 - pow(2.0,(- period_in_seconds / (buehlmann_He_t_halflife[ci] * 60)))); } } return tissue_tolerance_calc(dive); }
/* Let's try to do some deco calculations. */ void calculate_deco_information(struct dive *dive, struct divecomputer *dc, struct plot_info *pi, bool print_mode) { int i, count_iteration = 0; double surface_pressure = (dc->surface_pressure.mbar ? dc->surface_pressure.mbar : get_surface_pressure_in_mbar(dive, true)) / 1000.0; int last_ndl_tts_calc_time = 0; int first_ceiling = 0; bool first_iteration = true; int final_tts = 0 , time_clear_ceiling = 0, time_deep_ceiling = 0, deco_time = 0, prev_deco_time = 10000000; char *cache_data_initial = NULL; /* For VPM-B outside the planner, cache the initial deco state for CVA iterations */ if (prefs.deco_mode == VPMB && !in_planner()) cache_deco_state(&cache_data_initial); /* For VPM-B outside the planner, iterate until deco time converges (usually one or two iterations after the initial) * Set maximum number of iterations to 10 just in case */ while ((abs(prev_deco_time - deco_time) >= 30) && (count_iteration < 10)) { for (i = 1; i < pi->nr; i++) { struct plot_data *entry = pi->entry + i; int j, t0 = (entry - 1)->sec, t1 = entry->sec; int time_stepsize = 20; entry->ambpressure = depth_to_bar(entry->depth, dive); entry->gfline = MAX((double)prefs.gflow, (entry->ambpressure - surface_pressure) / (gf_low_pressure_this_dive - surface_pressure) * (prefs.gflow - prefs.gfhigh) + prefs.gfhigh) * (100.0 - AMB_PERCENTAGE) / 100.0 + AMB_PERCENTAGE; if (t0 > t1) { fprintf(stderr, "non-monotonous dive stamps %d %d\n", t0, t1); int xchg = t1; t1 = t0; t0 = xchg; } if (t0 != t1 && t1 - t0 < time_stepsize) time_stepsize = t1 - t0; for (j = t0 + time_stepsize; j <= t1; j += time_stepsize) { int depth = interpolate(entry[-1].depth, entry[0].depth, j - t0, t1 - t0); add_segment(depth_to_bar(depth, dive), &dive->cylinder[entry->cylinderindex].gasmix, time_stepsize, entry->o2pressure.mbar, dive, entry->sac); if ((t1 - j < time_stepsize) && (j < t1)) time_stepsize = t1 - j; } if (t0 == t1) { entry->ceiling = (entry - 1)->ceiling; } else { /* Keep updating the VPM-B gradients until the start of the ascent phase of the dive. */ if (prefs.deco_mode == VPMB && !in_planner() && (entry - 1)->ceiling >= first_ceiling && first_iteration == true) { nuclear_regeneration(t1); vpmb_start_gradient(); /* For CVA calculations, start by guessing deco time = dive time remaining */ deco_time = pi->maxtime - t1; vpmb_next_gradient(deco_time, surface_pressure / 1000.0); } entry->ceiling = deco_allowed_depth(tissue_tolerance_calc(dive, depth_to_bar(entry->depth, dive)), surface_pressure, dive, !prefs.calcceiling3m); /* If using VPM-B outside the planner, take first_ceiling_pressure as the deepest ceiling */ if (prefs.deco_mode == VPMB && !in_planner()) { if (entry->ceiling >= first_ceiling) { time_deep_ceiling = t1; first_ceiling = entry->ceiling; first_ceiling_pressure.mbar = depth_to_mbar(first_ceiling, dive); if (first_iteration) { nuclear_regeneration(t1); vpmb_start_gradient(); /* For CVA calculations, start by guessing deco time = dive time remaining */ deco_time = pi->maxtime - t1; vpmb_next_gradient(deco_time, surface_pressure / 1000.0); } } // Use the point where the ceiling clears as the end of deco phase for CVA calculations if (entry->ceiling > 0) time_clear_ceiling = 0; else if (time_clear_ceiling == 0) time_clear_ceiling = t1; } } for (j = 0; j < 16; j++) { double m_value = buehlmann_inertgas_a[j] + entry->ambpressure / buehlmann_inertgas_b[j]; entry->ceilings[j] = deco_allowed_depth(tolerated_by_tissue[j], surface_pressure, dive, 1); entry->percentages[j] = tissue_inertgas_saturation[j] < entry->ambpressure ? tissue_inertgas_saturation[j] / entry->ambpressure * AMB_PERCENTAGE : AMB_PERCENTAGE + (tissue_inertgas_saturation[j] - entry->ambpressure) / (m_value - entry->ambpressure) * (100.0 - AMB_PERCENTAGE); } /* should we do more calculations? * We don't for print-mode because this info doesn't show up there * If the ceiling hasn't cleared by the last data point, we need tts for VPM-B CVA calculation * It is not necessary to do these calculation on the first VPMB iteration, except for the last data point */ if ((prefs.calcndltts && !print_mode && (prefs.deco_mode != VPMB || in_planner() || !first_iteration)) || (prefs.deco_mode == VPMB && !in_planner() && i == pi->nr - 1)) { /* only calculate ndl/tts on every 30 seconds */ if ((entry->sec - last_ndl_tts_calc_time) < 30 && i != pi->nr - 1) { struct plot_data *prev_entry = (entry - 1); entry->stoptime_calc = prev_entry->stoptime_calc; entry->stopdepth_calc = prev_entry->stopdepth_calc; entry->tts_calc = prev_entry->tts_calc; entry->ndl_calc = prev_entry->ndl_calc; continue; } last_ndl_tts_calc_time = entry->sec; /* We are going to mess up deco state, so store it for later restore */ char *cache_data = NULL; cache_deco_state(&cache_data); calculate_ndl_tts(entry, dive, surface_pressure); if (prefs.deco_mode == VPMB && !in_planner() && i == pi->nr - 1) final_tts = entry->tts_calc; /* Restore "real" deco state for next real time step */ restore_deco_state(cache_data); free(cache_data); } } if (prefs.deco_mode == VPMB && !in_planner()) { prev_deco_time = deco_time; // Do we need to update deco_time? if (final_tts > 0) deco_time = pi->maxtime + final_tts - time_deep_ceiling; else if (time_clear_ceiling > 0) deco_time = time_clear_ceiling - time_deep_ceiling; vpmb_next_gradient(deco_time, surface_pressure / 1000.0); final_tts = 0; last_ndl_tts_calc_time = 0; first_ceiling = 0; first_iteration = false; count_iteration ++; restore_deco_state(cache_data_initial); } else { // With Buhlmann, or not in planner, iterating isn't needed. This makes the while condition false. prev_deco_time = deco_time = 0; } } free(cache_data_initial); #if DECO_CALC_DEBUG & 1 dump_tissues(); #endif }
/* calculate DECO STOP / TTS / NDL */ static void calculate_ndl_tts(struct plot_data *entry, struct dive *dive, double surface_pressure) { /* FIXME: This should be configurable */ /* ascent speed up to first deco stop */ const int ascent_s_per_step = 1; const int ascent_mm_per_step = 200; /* 12 m/min */ /* ascent speed between deco stops */ const int ascent_s_per_deco_step = 1; const int ascent_mm_per_deco_step = 16; /* 1 m/min */ /* how long time steps in deco calculations? */ const int time_stepsize = 60; const int deco_stepsize = 3000; /* at what depth is the current deco-step? */ int next_stop = ROUND_UP(deco_allowed_depth(tissue_tolerance_calc(dive, depth_to_bar(entry->depth, dive)), surface_pressure, dive, 1), deco_stepsize); int ascent_depth = entry->depth; /* at what time should we give up and say that we got enuff NDL? */ int cylinderindex = entry->cylinderindex; /* If iterating through a dive, entry->tts_calc needs to be reset */ entry->tts_calc = 0; /* If we don't have a ceiling yet, calculate ndl. Don't try to calculate * a ndl for lower values than 3m it would take forever */ if (next_stop == 0) { if (entry->depth < 3000) { entry->ndl = MAX_PROFILE_DECO; return; } /* stop if the ndl is above max_ndl seconds, and call it plenty of time */ while (entry->ndl_calc < MAX_PROFILE_DECO && deco_allowed_depth(tissue_tolerance_calc(dive, depth_to_bar(entry->depth, dive)), surface_pressure, dive, 1) <= 0) { entry->ndl_calc += time_stepsize; add_segment(depth_to_bar(entry->depth, dive), &dive->cylinder[cylinderindex].gasmix, time_stepsize, entry->o2pressure.mbar, dive, prefs.bottomsac); } /* we don't need to calculate anything else */ return; } /* We are in deco */ entry->in_deco_calc = true; /* Add segments for movement to stopdepth */ for (; ascent_depth > next_stop; ascent_depth -= ascent_mm_per_step, entry->tts_calc += ascent_s_per_step) { add_segment(depth_to_bar(ascent_depth, dive), &dive->cylinder[cylinderindex].gasmix, ascent_s_per_step, entry->o2pressure.mbar, dive, prefs.decosac); next_stop = ROUND_UP(deco_allowed_depth(tissue_tolerance_calc(dive, depth_to_bar(ascent_depth, dive)), surface_pressure, dive, 1), deco_stepsize); } ascent_depth = next_stop; /* And how long is the current deco-step? */ entry->stoptime_calc = 0; entry->stopdepth_calc = next_stop; next_stop -= deco_stepsize; /* And how long is the total TTS */ while (next_stop >= 0) { /* save the time for the first stop to show in the graph */ if (ascent_depth == entry->stopdepth_calc) entry->stoptime_calc += time_stepsize; entry->tts_calc += time_stepsize; if (entry->tts_calc > MAX_PROFILE_DECO) break; add_segment(depth_to_bar(ascent_depth, dive), &dive->cylinder[cylinderindex].gasmix, time_stepsize, entry->o2pressure.mbar, dive, prefs.decosac); if (deco_allowed_depth(tissue_tolerance_calc(dive, depth_to_bar(ascent_depth,dive)), surface_pressure, dive, 1) <= next_stop) { /* move to the next stop and add the travel between stops */ for (; ascent_depth > next_stop; ascent_depth -= ascent_mm_per_deco_step, entry->tts_calc += ascent_s_per_deco_step) add_segment(depth_to_bar(ascent_depth, dive), &dive->cylinder[cylinderindex].gasmix, ascent_s_per_deco_step, entry->o2pressure.mbar, dive, prefs.decosac); ascent_depth = next_stop; next_stop -= deco_stepsize; } } }