Robert, On 27 October 2015 at 08:09, Rick Walsh <[email protected]> wrote:
> Thanks Roberts, > (sorry for adding an s to your name) > On 27 Oct 2015 06:08, "Robert C. Helling" <[email protected]> wrote: > > > > Hi Rick, > > > >> On 26 Oct 2015, at 10:25, Rick Walsh <[email protected]> wrote: > >> > >> So, using your observation that it converges fast, I realized we don't > actually need to calculate tts for every sample point. We just need to > calculate tts for the final sample, iif the ceiling has been broken. We > can then take deco_time = time from max ceiling depth until the ceiling > clears, or would have cleared. > >> > >> See patches attached. More testing is needed, and feedback welcome. > > > > > > I think this makes much more sense. Just a few comments/questions for > patch 1: > > > > 1) I think deco_time is defined, at least in the prose of the VPM-B > documents as the time at which at least one tissue is off-gassing. Given > that we have to go through all samples, can’t we just detect off-gassing in > add_segment by checking if at least one oversat is negative, i.e. start with > > > > bool off_gassing = false > > > > and then in the loop > > > > off_gassing ||= (pn2_oversat < 0) || (phe_oversat <0); > > > > and finally change add_segment from void to bool by returning > off_gassing. Then we can simply add all the time for all segments the we > added which return true. > > This would work, but will achieve almost the same outcome. Taking deco > time as time from max ceiling until time of clear ceiling implies that at > least one tissues, the leading tissue, is off-gassing. With a very deep > (setting deepest ceiling) to shallow (shallower ceiling) to deep (all > tissues on-gas and ceiling deepens but not to max), there could be a small > difference in calculated deco time. > A problem with this approach is that with a deep -> shallow -> deeper (setting deepest ceiling) profile is that it would start counting deco time before we are in the real ascent phase and actual offgassing. I don't believe we should be distributing the extra bubble volume allowed by the CVA over this section. I have kept it as is, but happy to discuss further. > > > > 2) I don’t really understand what you are doing > with first_ceiling_pressure_set. It seems to me you want to pick up the > first step after the maximum of the ceiling. So you look for a segment > where the ceiling goes up but in the previous step it didn’t. Why don’t you > simply memorise the last step where the ceiling gets deeper (it should be > the previous), by getting rid of first_ceiling_pressure_set and simply doing > > > > if (entry->ceiling > first_ceiling) { > > first_ceiling = entry->ceiling; > > first_ceiling_pressure.mbar = depth_to_mbar(first_ceiling, dive); > > } > > > > This would give you the value for the maximum ceiling. > > Yes, that's much simpler way to achieve the same thing. I'll do that. > I tried doing that. But it messes up the setting of the gradients. Until we reach the deepest ceiling, we want to use first_gradient, after setting the deepest ceiling, we want to use next_gradient (set once, when first_ceiling_pressure is set) with the approximated deco_time. To distinguish, I have used the first_ceiling_pressure_set boolean variable. > > > > 3) I am a bit worried that the last hunk copies quite a bit of > non-trivial code. Wouldn’t it be possible to factor this out, i.e. do it > with a loop or as a separate function that the logic is in the source only > once? Otherwise, we are almost guaranteed that when changing the code in > the future we will end up with two inconsistent copies. > > > Yes, at first glance I thought only a little bit of code needed to be > repeated so I thought copying it would be better. But it turns out I > needed to copy most of it. I loop is better. > I have reworked to loop through the code until it deco_time converges, or a max of 3 iterations. > > But all this in the end is cosmetic, I like what the code is doing. > > > Thanks. I'll resubmit addressing your second and third points at least. > I need to think about the effect (or not) of calculating deco time as you > suggested. > > All I have really changed is that the code now loops, rather than copying code. Please let me know if you can think of a better way to create the VPM-B ceiling. Note that if you cut short a planned deco profile, skipping the final gas change, then the final_tts will be over-estimated because it doesn't consider using the final gas. This alters the CVA calculation, so the calculated ceiling does change a little bit from that of the planned profile if it isn't cut short. We could change this by altering the tts calculation to use all gasses in the cylinder list, but I think that would be worse. For a real dive, we should only consider the gasses that were used in that dive. Cheers, Rick
From 8403bcf3f9615baaa7acbf09f9af1a8cb93dc7a6 Mon Sep 17 00:00:00 2001 From: Rick Walsh <[email protected]> Date: Sun, 25 Oct 2015 11:26:15 +1100 Subject: [PATCH 1/2] Calculate VPM-B ceiling outside of planner Doing VPM-B calculations for dives outside of the planner has not been possible because real dive data does not record either: - reference pressure for the Boyle's law compensation (i.e. first_stop_pressure), or - deco_time for the vpmb_next_gradient function used to do the CVA calculations However, we can infer these values to be: - first_stop_pressure is the deepest ceiling in the dive - deco_time is dive time from the deepest ceiling until the ceiling clears (or would have cleared if the diver finished their deco obligations) These assumptions avoid having to do more than one extra CVA iterations to calculate the optimum deco profile. Signed-off-by: Rick Walsh <[email protected]> --- deco.c | 4 +- profile.c | 179 ++++++++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 124 insertions(+), 59 deletions(-) diff --git a/deco.c b/deco.c index 86acc03..506d8b5 100644 --- a/deco.c +++ b/deco.c @@ -244,7 +244,7 @@ double tissue_tolerance_calc(const struct dive *dive, double pressure) double lowest_ceiling = 0.0; double tissue_lowest_ceiling[16]; - if (prefs.deco_mode != VPMB || !in_planner()) { + if (prefs.deco_mode != VPMB) { for (ci = 0; ci < 16; ci++) { tissue_inertgas_saturation[ci] = tissue_n2_sat[ci] + tissue_he_sat[ci]; buehlmann_inertgas_a[ci] = ((buehlmann_N2_a[ci] * tissue_n2_sat[ci]) + (buehlmann_He_a[ci] * tissue_he_sat[ci])) / tissue_inertgas_saturation[ci]; @@ -509,7 +509,7 @@ void add_segment(double pressure, const struct gasmix *gasmix, int period_in_sec tissue_n2_sat[ci] += n2_satmult * pn2_oversat * n2_f; tissue_he_sat[ci] += he_satmult * phe_oversat * he_f; } - if(prefs.deco_mode == VPMB && in_planner()) + if(prefs.deco_mode == VPMB) calc_crushing_pressure(pressure); return; } diff --git a/profile.c b/profile.c index d39133c..1f6c7fa 100644 --- a/profile.c +++ b/profile.c @@ -28,6 +28,9 @@ unsigned int dc_number = 0; static struct plot_data *last_pi_entry_new = NULL; void populate_pressure_information(struct dive *, struct divecomputer *, struct plot_info *, int); +extern bool in_planner(); +extern pressure_t first_ceiling_pressure; + #ifdef DEBUG_PI /* debugging tool - not normally used */ static void dump_pi(struct plot_info *pi) @@ -927,69 +930,131 @@ static void calculate_ndl_tts(struct plot_data *entry, struct dive *dive, double */ void calculate_deco_information(struct dive *dive, struct divecomputer *dc, struct plot_info *pi, bool print_mode) { - int i; + 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; - 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 - entry->ceiling = deco_allowed_depth(tissue_tolerance_calc(dive, depth_to_bar(entry->depth, dive)), surface_pressure, dive, !prefs.calcceiling3m); - 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); - } + int first_ceiling; + bool first_ceiling_pressure_set = false, 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, we do two iterations, cache the initial deco state for later */ + if (prefs.deco_mode == VPMB && !in_planner()) + cache_deco_state(&cache_data_initial); + while ((abs(prev_deco_time - deco_time) >= 30) && (count_iteration < 4)) { + 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 we start the ascent phase of the dive. */ + if (prefs.deco_mode == VPMB && !in_planner() && !first_ceiling_pressure_set && first_iteration == true) { + nuclear_regeneration(t0); + vpmb_start_gradient(); + /* For CVA calculations, start by guessing deco time = dive time remaining */ + deco_time = pi->maxtime - t0; + 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 (first_iteration == true) { + /* if first_ceiling_pressure hasn't been set yet, and this entry's ceiling is shallower + * than the previous entry's, then locally the deeperst ceiling is previous entry's ceiling */ + if (first_ceiling_pressure_set == false) { + if (entry->ceiling < (entry - 1)->ceiling) { + time_deep_ceiling = t0; + first_ceiling = (entry - 1)->ceiling; + first_ceiling_pressure.mbar = depth_to_mbar(first_ceiling, dive); + first_ceiling_pressure_set = true; + } + } else { + /* if this entry's ceiling is deeper than the currently set first_ceiling_pressure + * then we unset first_ceiling_pressure so that the deepest ceiling with be found */ + if (entry->ceiling > first_ceiling) + first_ceiling_pressure_set = false; + } + } + // We will 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 (prefs.calcndltts && !print_mode) { - /* only calculate ndl/tts on every 30 seconds */ - if ((entry->sec - last_ndl_tts_calc_time) < 30) { - 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; + /* 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 */ + if ((prefs.calcndltts && !print_mode) || (prefs.deco_mode == VPMB && !in_planner() && i == pi->nr - 1 && entry->ceiling > 0)) { + /* 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); } - 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); - /* Restore "real" deco state for next real time step */ - restore_deco_state(cache_data); - free(cache_data); + } + if (prefs.deco_mode == VPMB && !in_planner()) { + restore_deco_state(cache_data_initial); + prev_deco_time = deco_time; + last_ndl_tts_calc_time = 0; + // 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); + first_iteration = false; + count_iteration ++; + } else { + prev_deco_time = deco_time = 0; } } + free(cache_data_initial); #if DECO_CALC_DEBUG & 1 dump_tissues(); #endif -- 2.4.3
From 55491917e93e54e132630ac5e2196409fd877b28 Mon Sep 17 00:00:00 2001 From: Rick Walsh <[email protected]> Date: Sat, 24 Oct 2015 20:03:46 +1100 Subject: [PATCH 2/2] Profile: Display VPM-B rather than GF when in VPM-B mode If we are planning a dive using VPM-B with +x conservatism, we want to print "VPM-B +x" at the top of the profile, instead of "GF xx/yy". Accordingly, the variable gradientFactor in profilewidget2.cpp is renamed decoModelParameters to reflect what it represents. Signed-off-by: Rick Walsh <[email protected]> --- qt-ui/profile/profilewidget2.cpp | 32 +++++++++++++++++++------------- qt-ui/profile/profilewidget2.h | 2 +- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/qt-ui/profile/profilewidget2.cpp b/qt-ui/profile/profilewidget2.cpp index 3ccd1bb..d474670 100644 --- a/qt-ui/profile/profilewidget2.cpp +++ b/qt-ui/profile/profilewidget2.cpp @@ -87,7 +87,7 @@ ProfileWidget2::ProfileWidget2(QWidget *parent) : QGraphicsView(parent), gasPressureItem(new DiveGasPressureItem()), diveComputerText(new DiveTextItem()), diveCeiling(new DiveCalculatedCeiling()), - gradientFactor(new DiveTextItem()), + decoModelParameters(new DiveTextItem()), reportedCeiling(new DiveReportedCeiling()), pn2GasItem(new PartialPressureGasItem()), pheGasItem(new PartialPressureGasItem()), @@ -204,7 +204,7 @@ void ProfileWidget2::addItemsToScene() diveComputerText->setData(SUBSURFACE_OBJ_DATA, SUBSURFACE_OBJ_DC_TEXT); scene()->addItem(diveComputerText); scene()->addItem(diveCeiling); - scene()->addItem(gradientFactor); + scene()->addItem(decoModelParameters); scene()->addItem(reportedCeiling); scene()->addItem(pn2GasItem); scene()->addItem(pheGasItem); @@ -288,11 +288,11 @@ void ProfileWidget2::setupItemOnScene() rulerItem->setAxis(timeAxis, profileYAxis); tankItem->setHorizontalAxis(timeAxis); - // show the gradient factor at the top in the center - gradientFactor->setY(0); - gradientFactor->setX(50); - gradientFactor->setBrush(getColor(PRESSURE_TEXT)); - gradientFactor->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + // show the deco model parameters at the top in the center + decoModelParameters->setY(0); + decoModelParameters->setX(50); + decoModelParameters->setBrush(getColor(PRESSURE_TEXT)); + decoModelParameters->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); setupItem(reportedCeiling, timeAxis, profileYAxis, dataModel, DivePlotDataModel::CEILING, DivePlotDataModel::TIME, 1); setupItem(diveCeiling, timeAxis, profileYAxis, dataModel, DivePlotDataModel::CEILING, DivePlotDataModel::TIME, 1); @@ -509,7 +509,10 @@ void ProfileWidget2::plotDive(struct dive *d, bool force) // this copies the dive and makes copies of all the relevant additional data copy_dive(d, &displayed_dive); - gradientFactor->setText(QString("GF %1/%2").arg(prefs.gflow).arg(prefs.gfhigh)); + if (prefs.deco_mode == VPMB) + decoModelParameters->setText(QString("VPM-B +%1").arg(prefs.conservatism_level)); + else + decoModelParameters->setText(QString("GF %1/%2").arg(prefs.gflow).arg(prefs.gfhigh)); } else { DivePlannerPointsModel *plannerModel = DivePlannerPointsModel::instance(); plannerModel->createTemporaryPlan(); @@ -518,7 +521,10 @@ void ProfileWidget2::plotDive(struct dive *d, bool force) plannerModel->deleteTemporaryPlan(); return; } - gradientFactor->setText(QString("GF %1/%2").arg(diveplan.gflow).arg(diveplan.gfhigh)); + if (prefs.deco_mode == VPMB) + decoModelParameters->setText(QString("VPM-B +%1").arg(prefs.conservatism_level)); + else + decoModelParameters->setText(QString("GF %1/%2").arg(prefs.gflow).arg(prefs.gfhigh)); } // special handling for the first time we display things @@ -926,7 +932,7 @@ void ProfileWidget2::setEmptyState() toolTipItem->setVisible(false); diveComputerText->setVisible(false); diveCeiling->setVisible(false); - gradientFactor->setVisible(false); + decoModelParameters->setVisible(false); reportedCeiling->setVisible(false); rulerItem->setVisible(false); tankItem->setVisible(false); @@ -1053,7 +1059,7 @@ void ProfileWidget2::setProfileState() diveComputerText->setPos(itemPos.dcLabel.on); diveCeiling->setVisible(prefs.calcceiling); - gradientFactor->setVisible(prefs.calcceiling); + decoModelParameters->setVisible(prefs.calcceiling); reportedCeiling->setVisible(prefs.dcceiling); if (prefs.calcalltissues) { @@ -1131,7 +1137,7 @@ void ProfileWidget2::setAddState() /* show the same stuff that the profile shows. */ currentState = ADD; /* enable the add state. */ diveCeiling->setVisible(true); - gradientFactor->setVisible(true); + decoModelParameters->setVisible(true); setBackgroundBrush(QColor("#A7DCFF")); } @@ -1165,7 +1171,7 @@ void ProfileWidget2::setPlanState() /* show the same stuff that the profile shows. */ currentState = PLAN; /* enable the add state. */ diveCeiling->setVisible(true); - gradientFactor->setVisible(true); + decoModelParameters->setVisible(true); setBackgroundBrush(QColor("#D7E3EF")); } diff --git a/qt-ui/profile/profilewidget2.h b/qt-ui/profile/profilewidget2.h index 2d1a7bf..23e9398 100644 --- a/qt-ui/profile/profilewidget2.h +++ b/qt-ui/profile/profilewidget2.h @@ -171,7 +171,7 @@ private: QList<DiveEventItem *> eventItems; DiveTextItem *diveComputerText; DiveCalculatedCeiling *diveCeiling; - DiveTextItem *gradientFactor; + DiveTextItem *decoModelParameters; QList<DiveCalculatedTissue *> allTissues; DiveReportedCeiling *reportedCeiling; PartialPressureGasItem *pn2GasItem; -- 2.4.3
_______________________________________________ subsurface mailing list [email protected] http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface
