Robert,

On 26 October 2015 at 09:06, Robert C. Helling <hell...@atdotde.de> wrote:

> HI,
>
> On 25 Oct 2015, at 10:44, Rick Walsh <rickmwa...@gmail.com> wrote:
>
> I like this concept.  I'm not sure how to turn it into something
> meaningful (other than the single statistic of "was the free gas volume
> more or less than permitted"), but it's worth discussing.
>
>
> as a first step in this direction, I added a number of printf’s to the
> VPM-B part. In particular, this displays the allowable (bottom) gradients
> for all compartments and for each iteration of the CVA how the volume of
> free gas compares to the critical value.
>

Much more efficient than setting breakpoints in gdb, and stepping through
one sample at a time (what I have been doing).

>
>
> This is of course not production code, it should not go into master. But
> for all those who want to understand VPM-B a but better, I pushed it to
> github. You can pull it form
>
> https://github.com/atdotde/subsurface.git analyze_vpm
>
> Have fun.
>
> What I learned so far: There are typically three rounds of the CVA: one
> with the vpmb_start_gradient, one with an updated gradient and then finally
> one with almost the same gradient again that results in the same run time.
> So it converges very fast in essentially one step of updating.
>
>
> 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.

Rick
From b9b57ed61528bba7c2b8d54064334ce976477f24 Mon Sep 17 00:00:00 2001
From: Rick Walsh <rickmwa...@gmail.com>
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 <rickmwa...@gmail.com>
---
 deco.c    |   4 +--
 profile.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 118 insertions(+), 6 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..c7b1e8e 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)
@@ -930,6 +933,13 @@ void calculate_deco_information(struct dive *dive, struct divecomputer *dc, stru
 	int i;
 	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;
+	bool first_ceiling_pressure_set = false;
+	int final_tts = 0 , time_clear_ceiling = 0, time_deep_ceiling = 0, deco_time = 0;
+	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);
 	for (i = 1; i < pi->nr; i++) {
 		struct plot_data *entry = pi->entry + i;
 		int j, t0 = (entry - 1)->sec, t1 = entry->sec;
@@ -955,10 +965,42 @@ void calculate_deco_information(struct dive *dive, struct divecomputer *dc, stru
 			if ((t1 - j < time_stepsize) && (j < t1))
 				time_stepsize = t1 - j;
 		}
-		if (t0 == t1)
+		if (t0 == t1) {
 			entry->ceiling = (entry - 1)->ceiling;
-		else
+		} 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) {
+				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_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);
@@ -968,8 +1010,9 @@ void calculate_deco_information(struct dive *dive, struct divecomputer *dc, stru
 		}
 
 		/* should we do more calculations?
-		 * We don't for print-mode because this info doesn't show up there */
-		if (prefs.calcndltts && !print_mode) {
+		 * 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 && entry->ceiling > 0)) {
 			/* only calculate ndl/tts on every 30 seconds */
 			if ((entry->sec - last_ndl_tts_calc_time) < 30) {
 				struct plot_data *prev_entry = (entry - 1);
@@ -985,11 +1028,80 @@ void calculate_deco_information(struct dive *dive, struct divecomputer *dc, stru
 			char *cache_data = NULL;
 			cache_deco_state(&cache_data);
 			calculate_ndl_tts(entry, dive, surface_pressure);
+			if (prefs.deco_mode == VPMB && !in_planner())
+				final_tts = entry->tts_calc;
 			/* Restore "real" deco state for next real time step */
 			restore_deco_state(cache_data);
 			free(cache_data);
 		}
 	}
+	// Do CVA calculations to adjust the calculated VPM-B ceiling
+	if (prefs.deco_mode == VPMB && !in_planner()) {
+		restore_deco_state(cache_data_initial);
+		free(cache_data_initial);
+		// 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);
+		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;
+
+			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);
+			}
+
+			/* 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;
+				}
+				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 DECO_CALC_DEBUG & 1
 	dump_tissues();
 #endif
-- 
2.4.3

From d29efcb7c369e70eff63ba3ad8e9ffdca28050f1 Mon Sep 17 00:00:00 2001
From: Rick Walsh <rickmwa...@gmail.com>
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 <rickmwa...@gmail.com>
---
 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
subsurface@subsurface-divelog.org
http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface

Reply via email to