This commit is contained in:
Dirk Hohndel 2015-11-01 13:22:08 -08:00
commit 852c9f4e3b
4 changed files with 147 additions and 72 deletions

4
deco.c
View file

@ -244,7 +244,7 @@ double tissue_tolerance_calc(const struct dive *dive, double pressure)
double lowest_ceiling = 0.0; double lowest_ceiling = 0.0;
double tissue_lowest_ceiling[16]; double tissue_lowest_ceiling[16];
if (prefs.deco_mode != VPMB || !in_planner()) { if (prefs.deco_mode != VPMB) {
for (ci = 0; ci < 16; ci++) { for (ci = 0; ci < 16; ci++) {
tissue_inertgas_saturation[ci] = tissue_n2_sat[ci] + tissue_he_sat[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]; 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_n2_sat[ci] += n2_satmult * pn2_oversat * n2_f;
tissue_he_sat[ci] += he_satmult * phe_oversat * he_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); calc_crushing_pressure(pressure);
return; return;
} }

181
profile.c
View file

@ -28,6 +28,9 @@ unsigned int dc_number = 0;
static struct plot_data *last_pi_entry_new = NULL; static struct plot_data *last_pi_entry_new = NULL;
void populate_pressure_information(struct dive *, struct divecomputer *, struct plot_info *, int); 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 #ifdef DEBUG_PI
/* debugging tool - not normally used */ /* debugging tool - not normally used */
static void dump_pi(struct plot_info *pi) static void dump_pi(struct plot_info *pi)
@ -866,6 +869,8 @@ static void calculate_ndl_tts(struct plot_data *entry, struct dive *dive, double
int ascent_depth = entry->depth; int ascent_depth = entry->depth;
/* at what time should we give up and say that we got enuff NDL? */ /* at what time should we give up and say that we got enuff NDL? */
int cylinderindex = entry->cylinderindex; 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 /* 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 */ * a ndl for lower values than 3m it would take forever */
@ -927,69 +932,133 @@ 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) 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; 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 last_ndl_tts_calc_time = 0;
for (i = 1; i < pi->nr; i++) { int first_ceiling = 0;
struct plot_data *entry = pi->entry + i; bool first_iteration = true;
int j, t0 = (entry - 1)->sec, t1 = entry->sec; int final_tts = 0 , time_clear_ceiling = 0, time_deep_ceiling = 0, deco_time = 0, prev_deco_time = 10000000;
int time_stepsize = 20; 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->ambpressure = depth_to_bar(entry->depth, dive);
entry->gfline = MAX((double)prefs.gflow, (entry->ambpressure - surface_pressure) / (gf_low_pressure_this_dive - surface_pressure) * entry->gfline = MAX((double)prefs.gflow, (entry->ambpressure - surface_pressure) / (gf_low_pressure_this_dive - surface_pressure) *
(prefs.gflow - prefs.gfhigh) + (prefs.gflow - prefs.gfhigh) +
prefs.gfhigh) * prefs.gfhigh) *
(100.0 - AMB_PERCENTAGE) / 100.0 + AMB_PERCENTAGE; (100.0 - AMB_PERCENTAGE) / 100.0 + AMB_PERCENTAGE;
if (t0 > t1) { if (t0 > t1) {
fprintf(stderr, "non-monotonous dive stamps %d %d\n", t0, t1); fprintf(stderr, "non-monotonous dive stamps %d %d\n", t0, t1);
int xchg = t1; int xchg = t1;
t1 = t0; t1 = t0;
t0 = xchg; t0 = xchg;
} }
if (t0 != t1 && t1 - t0 < time_stepsize) if (t0 != t1 && t1 - t0 < time_stepsize)
time_stepsize = t1 - t0; time_stepsize = t1 - t0;
for (j = t0 + time_stepsize; j <= t1; j += time_stepsize) { for (j = t0 + time_stepsize; j <= t1; j += time_stepsize) {
int depth = interpolate(entry[-1].depth, entry[0].depth, j - t0, t1 - t0); int depth = interpolate(entry[-1].depth, entry[0].depth, j - t0, t1 - t0);
add_segment(depth_to_bar(depth, dive), add_segment(depth_to_bar(depth, dive),
&dive->cylinder[entry->cylinderindex].gasmix, time_stepsize, entry->o2pressure.mbar, dive, entry->sac); &dive->cylinder[entry->cylinderindex].gasmix, time_stepsize, entry->o2pressure.mbar, dive, entry->sac);
if ((t1 - j < time_stepsize) && (j < t1)) if ((t1 - j < time_stepsize) && (j < t1))
time_stepsize = t1 - j; time_stepsize = t1 - j;
} }
if (t0 == t1) if (t0 == t1) {
entry->ceiling = (entry - 1)->ceiling; entry->ceiling = (entry - 1)->ceiling;
else } else {
entry->ceiling = deco_allowed_depth(tissue_tolerance_calc(dive, depth_to_bar(entry->depth, dive)), surface_pressure, dive, !prefs.calcceiling3m); /* Keep updating the VPM-B gradients until the start of the ascent phase of the dive. */
for (j = 0; j < 16; j++) { if (prefs.deco_mode == VPMB && !in_planner() && (entry - 1)->ceiling >= first_ceiling && first_iteration == true) {
double m_value = buehlmann_inertgas_a[j] + entry->ambpressure / buehlmann_inertgas_b[j]; nuclear_regeneration(t1);
entry->ceilings[j] = deco_allowed_depth(tolerated_by_tissue[j], surface_pressure, dive, 1); vpmb_start_gradient();
entry->percentages[j] = tissue_inertgas_saturation[j] < entry->ambpressure ? /* For CVA calculations, start by guessing deco time = dive time remaining */
tissue_inertgas_saturation[j] / entry->ambpressure * AMB_PERCENTAGE : deco_time = pi->maxtime - t1;
AMB_PERCENTAGE + (tissue_inertgas_saturation[j] - entry->ambpressure) / (m_value - entry->ambpressure) * (100.0 - AMB_PERCENTAGE); 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);
/* should we do more calculations? /* If using VPM-B outside the planner, take first_ceiling_pressure as the deepest ceiling */
* We don't for print-mode because this info doesn't show up there */ if (prefs.deco_mode == VPMB && !in_planner()) {
if (prefs.calcndltts && !print_mode) { if (entry->ceiling >= first_ceiling) {
/* only calculate ndl/tts on every 30 seconds */ time_deep_ceiling = t1;
if ((entry->sec - last_ndl_tts_calc_time) < 30) { first_ceiling = entry->ceiling;
struct plot_data *prev_entry = (entry - 1); first_ceiling_pressure.mbar = depth_to_mbar(first_ceiling, dive);
entry->stoptime_calc = prev_entry->stoptime_calc; if (first_iteration) {
entry->stopdepth_calc = prev_entry->stopdepth_calc; nuclear_regeneration(t1);
entry->tts_calc = prev_entry->tts_calc; vpmb_start_gradient();
entry->ndl_calc = prev_entry->ndl_calc; /* For CVA calculations, start by guessing deco time = dive time remaining */
continue; 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);
} }
last_ndl_tts_calc_time = entry->sec;
/* We are going to mess up deco state, so store it for later restore */ /* should we do more calculations?
char *cache_data = NULL; * We don't for print-mode because this info doesn't show up there
cache_deco_state(&cache_data); * If the ceiling hasn't cleared by the last data point, we need tts for VPM-B CVA calculation
calculate_ndl_tts(entry, dive, surface_pressure); * It is not necessary to do these calculation on the first VPMB iteration, except for the last data point */
/* Restore "real" deco state for next real time step */ if ((prefs.calcndltts && !print_mode && (prefs.deco_mode != VPMB || in_planner() || !first_iteration)) ||
restore_deco_state(cache_data); (prefs.deco_mode == VPMB && !in_planner() && i == pi->nr - 1)) {
free(cache_data); /* 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 #if DECO_CALC_DEBUG & 1
dump_tissues(); dump_tissues();
#endif #endif

View file

@ -87,7 +87,7 @@ ProfileWidget2::ProfileWidget2(QWidget *parent) : QGraphicsView(parent),
gasPressureItem(new DiveGasPressureItem()), gasPressureItem(new DiveGasPressureItem()),
diveComputerText(new DiveTextItem()), diveComputerText(new DiveTextItem()),
diveCeiling(new DiveCalculatedCeiling()), diveCeiling(new DiveCalculatedCeiling()),
gradientFactor(new DiveTextItem()), decoModelParameters(new DiveTextItem()),
reportedCeiling(new DiveReportedCeiling()), reportedCeiling(new DiveReportedCeiling()),
pn2GasItem(new PartialPressureGasItem()), pn2GasItem(new PartialPressureGasItem()),
pheGasItem(new PartialPressureGasItem()), pheGasItem(new PartialPressureGasItem()),
@ -204,7 +204,7 @@ void ProfileWidget2::addItemsToScene()
diveComputerText->setData(SUBSURFACE_OBJ_DATA, SUBSURFACE_OBJ_DC_TEXT); diveComputerText->setData(SUBSURFACE_OBJ_DATA, SUBSURFACE_OBJ_DC_TEXT);
scene()->addItem(diveComputerText); scene()->addItem(diveComputerText);
scene()->addItem(diveCeiling); scene()->addItem(diveCeiling);
scene()->addItem(gradientFactor); scene()->addItem(decoModelParameters);
scene()->addItem(reportedCeiling); scene()->addItem(reportedCeiling);
scene()->addItem(pn2GasItem); scene()->addItem(pn2GasItem);
scene()->addItem(pheGasItem); scene()->addItem(pheGasItem);
@ -288,11 +288,11 @@ void ProfileWidget2::setupItemOnScene()
rulerItem->setAxis(timeAxis, profileYAxis); rulerItem->setAxis(timeAxis, profileYAxis);
tankItem->setHorizontalAxis(timeAxis); tankItem->setHorizontalAxis(timeAxis);
// show the gradient factor at the top in the center // show the deco model parameters at the top in the center
gradientFactor->setY(0); decoModelParameters->setY(0);
gradientFactor->setX(50); decoModelParameters->setX(50);
gradientFactor->setBrush(getColor(PRESSURE_TEXT)); decoModelParameters->setBrush(getColor(PRESSURE_TEXT));
gradientFactor->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); decoModelParameters->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
setupItem(reportedCeiling, timeAxis, profileYAxis, dataModel, DivePlotDataModel::CEILING, DivePlotDataModel::TIME, 1); setupItem(reportedCeiling, timeAxis, profileYAxis, dataModel, DivePlotDataModel::CEILING, DivePlotDataModel::TIME, 1);
setupItem(diveCeiling, 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 // this copies the dive and makes copies of all the relevant additional data
copy_dive(d, &displayed_dive); 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 { } else {
DivePlannerPointsModel *plannerModel = DivePlannerPointsModel::instance(); DivePlannerPointsModel *plannerModel = DivePlannerPointsModel::instance();
plannerModel->createTemporaryPlan(); plannerModel->createTemporaryPlan();
@ -518,7 +521,10 @@ void ProfileWidget2::plotDive(struct dive *d, bool force)
plannerModel->deleteTemporaryPlan(); plannerModel->deleteTemporaryPlan();
return; 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 // special handling for the first time we display things
@ -926,7 +932,7 @@ void ProfileWidget2::setEmptyState()
toolTipItem->setVisible(false); toolTipItem->setVisible(false);
diveComputerText->setVisible(false); diveComputerText->setVisible(false);
diveCeiling->setVisible(false); diveCeiling->setVisible(false);
gradientFactor->setVisible(false); decoModelParameters->setVisible(false);
reportedCeiling->setVisible(false); reportedCeiling->setVisible(false);
rulerItem->setVisible(false); rulerItem->setVisible(false);
tankItem->setVisible(false); tankItem->setVisible(false);
@ -1053,7 +1059,7 @@ void ProfileWidget2::setProfileState()
diveComputerText->setPos(itemPos.dcLabel.on); diveComputerText->setPos(itemPos.dcLabel.on);
diveCeiling->setVisible(prefs.calcceiling); diveCeiling->setVisible(prefs.calcceiling);
gradientFactor->setVisible(prefs.calcceiling); decoModelParameters->setVisible(prefs.calcceiling);
reportedCeiling->setVisible(prefs.dcceiling); reportedCeiling->setVisible(prefs.dcceiling);
if (prefs.calcalltissues) { if (prefs.calcalltissues) {
@ -1131,7 +1137,7 @@ void ProfileWidget2::setAddState()
/* show the same stuff that the profile shows. */ /* show the same stuff that the profile shows. */
currentState = ADD; /* enable the add state. */ currentState = ADD; /* enable the add state. */
diveCeiling->setVisible(true); diveCeiling->setVisible(true);
gradientFactor->setVisible(true); decoModelParameters->setVisible(true);
setBackgroundBrush(QColor("#A7DCFF")); setBackgroundBrush(QColor("#A7DCFF"));
} }
@ -1165,7 +1171,7 @@ void ProfileWidget2::setPlanState()
/* show the same stuff that the profile shows. */ /* show the same stuff that the profile shows. */
currentState = PLAN; /* enable the add state. */ currentState = PLAN; /* enable the add state. */
diveCeiling->setVisible(true); diveCeiling->setVisible(true);
gradientFactor->setVisible(true); decoModelParameters->setVisible(true);
setBackgroundBrush(QColor("#D7E3EF")); setBackgroundBrush(QColor("#D7E3EF"));
} }

View file

@ -171,7 +171,7 @@ private:
QList<DiveEventItem *> eventItems; QList<DiveEventItem *> eventItems;
DiveTextItem *diveComputerText; DiveTextItem *diveComputerText;
DiveCalculatedCeiling *diveCeiling; DiveCalculatedCeiling *diveCeiling;
DiveTextItem *gradientFactor; DiveTextItem *decoModelParameters;
QList<DiveCalculatedTissue *> allTissues; QList<DiveCalculatedTissue *> allTissues;
DiveReportedCeiling *reportedCeiling; DiveReportedCeiling *reportedCeiling;
PartialPressureGasItem *pn2GasItem; PartialPressureGasItem *pn2GasItem;