From 11471009302532054afd0a12e2c09abdde990c6a Mon Sep 17 00:00:00 2001 From: Jan Darowski Date: Fri, 3 Jul 2015 21:30:53 +0200 Subject: VPM-B: add basic algorithm settings. Created vpmb_config structure based on buehlmann_config. Set it's default values to ones taken from the existing C implementation. Signed-off-by: Jan Darowski --- deco.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/deco.c b/deco.c index 9d0ec9d53..1d604c92c 100644 --- a/deco.c +++ b/deco.c @@ -32,6 +32,19 @@ struct buehlmann_config { }; struct buehlmann_config buehlmann_config = { 1.0, 1.01, 0, 0.75, 0.35, 1.0, false }; +//! Option structure for VPM-B decompression. +struct vpmb_config { + double crit_radius_N2; //! Critical radius of N2 nucleon (microns). + double crit_radius_He; //! Critical radius of He nucleon (microns). + double crit_volume_lambda; //! Constant corresponding to critical gas volume. + double gradient_of_imperm; //! Gradient after which bubbles become impermeable. + double surface_tension_gamma; //! Nucleons surface tension constant. + double skin_compression_gammaC; //! + double regeneration_time; //! Time needed for the bubble to regenerate to the start radius. + double other_gases_pressure; //! Always present pressure of other gasses in tissues. +}; +struct vpmb_config vpmb_config = { 0.6, 0.5, 250.0, 8.2, 0.179, 2.57, 20160, 0.1359888 }; + const double buehlmann_N2_a[] = { 1.1696, 1.0, 0.8618, 0.7562, 0.62, 0.5043, 0.441, 0.4, 0.375, 0.35, 0.3295, 0.3065, -- cgit v1.2.3-70-g09d2 From 94f3fc854295be3457331727e1d20105e3595ba6 Mon Sep 17 00:00:00 2001 From: Jan Darowski Date: Fri, 3 Jul 2015 21:50:39 +0200 Subject: VPM-B: add calculating nucleons inner pressure. This function calculates the pressure inside the nucleon during the impermeable phase. In the original code, Newton's method is used, for simplicity, we use binary search for finding cubic equations root. Signed-off-by: Jan Darowski --- deco.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/deco.c b/deco.c index 1d604c92c..c4b3f53af 100644 --- a/deco.c +++ b/deco.c @@ -197,6 +197,43 @@ double he_factor(int period_in_seconds, int ci) return cache[ci].last_factor; } +// Calculates the nucleons inner pressure during the impermeable period +double calc_inner_pressure(double crit_radius, double onset_tension, double current_ambient_pressure) +{ + double onset_radius; + double current_radius; + double A, B, C, low_bound, high_bound, root; + double valH, valL; + int ci; + const int max_iters = 10; + + // const, depends only on config. + onset_radius = 1.0 / (vpmb_config.gradient_of_imperm / (2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma)) + 1.0 / crit_radius); + + // A*r^3 + B*r^2 + C = 0 + A = current_ambient_pressure - vpmb_config.gradient_of_imperm + (2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma)) / onset_radius; + B = 2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma); + C = onset_tension * pow(onset_radius, 3); + + // According to the algorithm's authors... + low_bound = B / A; + high_bound = onset_radius; + + valH = high_bound * high_bound * (A * high_bound - B) - C; + valL = low_bound * low_bound * (A * low_bound - B) - C; + + // Binary search for equations root. + for (ci = 0; ci < max_iters; ++ci) { + current_radius = (high_bound + low_bound) *0.5; + root = (current_radius * current_radius * (A * current_radius - B)) - C; + if (root >= 0.0) + high_bound = current_radius; + else + low_bound = current_radius; + } + return onset_tension * (pow(onset_radius, 3) / pow(current_radius, 3)); +} + /* add period_in_seconds at the given pressure and gas to the deco calculation */ double add_segment(double pressure, const struct gasmix *gasmix, int period_in_seconds, int ccpo2, const struct dive *dive, int sac) { -- cgit v1.2.3-70-g09d2 From bed3b9eea3d0ba27433d8df55cd0e0eadeca5dcf Mon Sep 17 00:00:00 2001 From: Jan Darowski Date: Fri, 3 Jul 2015 22:10:12 +0200 Subject: VPM-B: add crushin pressure calculation. Add new structures holding vpm-b state. Add function calculating current crushing pressure. Call it from add_segment() on every ambient pressure change. It determines what pressure acts on nuclei during the descent and thus their size at the beggining of the deco. Signed-off-by: Jan Darowski --- deco.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/deco.c b/deco.c index c4b3f53af..f29a82600 100644 --- a/deco.c +++ b/deco.c @@ -102,6 +102,15 @@ double tolerated_by_tissue[16]; double tissue_inertgas_saturation[16]; double buehlmann_inertgas_a[16], buehlmann_inertgas_b[16]; +double max_n2_crushing_pressure[16]; +double max_he_crushing_pressure[16]; + +double crushing_onset_tension[16]; // total inert gas tension in the t* moment +double n2_regen_radius[16]; // rs +double he_regen_radius[16]; +double max_ambient_pressure; // last moment we were descending + + static double tissue_tolerance_calc(const struct dive *dive) { int ci = -1; @@ -234,6 +243,39 @@ double calc_inner_pressure(double crit_radius, double onset_tension, double curr return onset_tension * (pow(onset_radius, 3) / pow(current_radius, 3)); } +// Calculates the crushing pressure in the given moment. Updates crushing_onset_tension and critical radius if needed +void calc_crushing_pressure(double pressure) +{ + int ci; + double gradient; + double gas_tension; + double n2_crushing_pressure, he_crushing_pressure; + double n2_inner_pressure, he_inner_pressure; + + for (ci = 0; ci < 16; ++ci) { + gas_tension = tissue_n2_sat[ci] + tissue_he_sat[ci] + vpmb_config.other_gases_pressure; + gradient = pressure - gas_tension; + + if (gradient <= vpmb_config.gradient_of_imperm) { // permeable situation + n2_crushing_pressure = he_crushing_pressure = gradient; + crushing_onset_tension[ci] = gas_tension; + } + else { // impermeable + if (max_ambient_pressure >= pressure) + return; + + n2_inner_pressure = calc_inner_pressure(vpmb_config.crit_radius_N2, crushing_onset_tension[ci], pressure); + he_inner_pressure = calc_inner_pressure(vpmb_config.crit_radius_He, crushing_onset_tension[ci], pressure); + + n2_crushing_pressure = pressure - n2_inner_pressure; + he_crushing_pressure = pressure - he_inner_pressure; + } + max_n2_crushing_pressure[ci] = MAX(max_n2_crushing_pressure[ci], n2_crushing_pressure); + max_he_crushing_pressure[ci] = MAX(max_he_crushing_pressure[ci], he_crushing_pressure); + } + max_ambient_pressure = MAX(pressure, max_ambient_pressure); +} + /* add period_in_seconds at the given pressure and gas to the deco calculation */ double add_segment(double pressure, const struct gasmix *gasmix, int period_in_seconds, int ccpo2, const struct dive *dive, int sac) { @@ -256,6 +298,7 @@ double add_segment(double pressure, const struct gasmix *gasmix, int period_in_s tissue_n2_sat[ci] += n2_satmult * pn2_oversat * n2_f; tissue_he_sat[ci] += he_satmult * phe_oversat * he_f; } + calc_crushing_pressure(pressure); return tissue_tolerance_calc(dive); } -- cgit v1.2.3-70-g09d2 From ecd0e3e170e5a8ec96be8d19d095e7c75c63c825 Mon Sep 17 00:00:00 2001 From: Jan Darowski Date: Fri, 3 Jul 2015 22:18:41 +0200 Subject: VPM-B: add initial values for crushing pressure variables. Signed-off-by: Jan Darowski --- deco.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deco.c b/deco.c index f29a82600..b8aae27a0 100644 --- a/deco.c +++ b/deco.c @@ -322,10 +322,13 @@ void clear_deco(double surface_pressure) for (ci = 0; ci < 16; ci++) { tissue_n2_sat[ci] = (surface_pressure - WV_PRESSURE) * N2_IN_AIR / 1000; tissue_he_sat[ci] = 0.0; + max_n2_crushing_pressure[ci] = 0.0; + max_he_crushing_pressure[ci] = 0.0; } gf_low_pressure_this_dive = surface_pressure; if (!buehlmann_config.gf_low_at_maxdepth) gf_low_pressure_this_dive += buehlmann_config.gf_low_position_min; + max_ambient_pressure = 0.0; } void cache_deco_state(double tissue_tolerance, char **cached_datap) -- cgit v1.2.3-70-g09d2 From 0180d2eb1e8101eb362efb00edaae9eb9bcb24aa Mon Sep 17 00:00:00 2001 From: "Robert C. Helling" Date: Wed, 1 Jul 2015 12:27:42 +0200 Subject: VPM-B: use an analytic solution for nucleon inner pressure instead of binary root search According to mathematica In[4]:= f[x_] := x^3 - b x^2 - c In[18]:= Solve[f[x] == 0, x] Out[18]= {{x -> 1/3 (b + ( 2^(1/3) b^2)/(2 b^3 + 27 c + 3 Sqrt[3] Sqrt[4 b^3 c + 27 c^2])^( 1/3) + (2 b^3 + 27 c + 3 Sqrt[3] Sqrt[4 b^3 c + 27 c^2])^(1/3)/ 2^(1/3))}, {x -> b/3 - ((1 + I Sqrt[3]) b^2)/( 3 2^(2/3) (2 b^3 + 27 c + 3 Sqrt[3] Sqrt[4 b^3 c + 27 c^2])^( 1/3)) - ((1 - I Sqrt[3]) (2 b^3 + 27 c + 3 Sqrt[3] Sqrt[4 b^3 c + 27 c^2])^(1/3))/(6 2^(1/3))}, {x -> b/3 - ((1 - I Sqrt[3]) b^2)/( 3 2^(2/3) (2 b^3 + 27 c + 3 Sqrt[3] Sqrt[4 b^3 c + 27 c^2])^( 1/3)) - ((1 + I Sqrt[3]) (2 b^3 + 27 c + 3 Sqrt[3] Sqrt[4 b^3 c + 27 c^2])^(1/3))/(6 2^(1/3))}} For the values of b and c encounterd in the algorithm, the first solution is in fact the only real one that we are after. So we can use this solution instead of doing a binary search for the root of the cubic. Signed-off-by: Robert C. Helling Signed-off-by: Jan Darowski --- deco.c | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/deco.c b/deco.c index b8aae27a0..70430bb66 100644 --- a/deco.c +++ b/deco.c @@ -209,38 +209,30 @@ double he_factor(int period_in_seconds, int ci) // Calculates the nucleons inner pressure during the impermeable period double calc_inner_pressure(double crit_radius, double onset_tension, double current_ambient_pressure) { - double onset_radius; - double current_radius; - double A, B, C, low_bound, high_bound, root; - double valH, valL; - int ci; - const int max_iters = 10; + double onset_radius = 1.0 / (vpmb_config.gradient_of_imperm / (2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma)) + 1.0 / crit_radius); - // const, depends only on config. - onset_radius = 1.0 / (vpmb_config.gradient_of_imperm / (2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma)) + 1.0 / crit_radius); + // A*r^3 + B*r^2 + C == 0 + // Solved with the help of mathematica - // A*r^3 + B*r^2 + C = 0 - A = current_ambient_pressure - vpmb_config.gradient_of_imperm + (2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma)) / onset_radius; - B = 2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma); - C = onset_tension * pow(onset_radius, 3); + double A = current_ambient_pressure - vpmb_config.gradient_of_imperm + (2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma)) / onset_radius; + double B = 2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma); + double C = onset_tension * pow(onset_radius, 3); - // According to the algorithm's authors... - low_bound = B / A; - high_bound = onset_radius; + double BA = B/A; + double CA = C/A; - valH = high_bound * high_bound * (A * high_bound - B) - C; - valL = low_bound * low_bound * (A * low_bound - B) - C; + double discriminant = CA * (4 * BA * BA * BA + 27 * CA); - // Binary search for equations root. - for (ci = 0; ci < max_iters; ++ci) { - current_radius = (high_bound + low_bound) *0.5; - root = (current_radius * current_radius * (A * current_radius - B)) - C; - if (root >= 0.0) - high_bound = current_radius; - else - low_bound = current_radius; + // Let's make sure we have a real solution: + if (discriminant < 0.0) { + // This should better not happen + report_error("Complex solution for inner pressure encountered!\n A=%f\tB=%f\tC=%f\n", A, B, C); + return 0.0; } - return onset_tension * (pow(onset_radius, 3) / pow(current_radius, 3)); + double denominator = pow(BA * BA * BA + 1.5 * (9 * CA + sqrt(3.0) * sqrt(discriminant)), 1/3.0); + double current_radius = (BA + BA * BA / denominator + denominator) / 3.0; + + return onset_tension * onset_radius * onset_radius * onset_radius / (current_radius * current_radius * current_radius); } // Calculates the crushing pressure in the given moment. Updates crushing_onset_tension and critical radius if needed -- cgit v1.2.3-70-g09d2 From 2435d79c0e6e4e30ace3b23765005c90181ddae5 Mon Sep 17 00:00:00 2001 From: Jan Darowski Date: Fri, 3 Jul 2015 22:45:29 +0200 Subject: VPM-B: add nuclei size calculation and nuclear regeneration. This function calculates the size of nuclei at the end of deco, then simulates their regeneration, to the moment before the deco. This is redundant as nuclear regeneration is a very slow process. Function should be called with time in seconds, just before the ascent. Signed-off-by: Jan Darowski --- deco.c | 15 +++++++++++++++ dive.h | 1 + 2 files changed, 16 insertions(+) diff --git a/deco.c b/deco.c index 70430bb66..0b221c225 100644 --- a/deco.c +++ b/deco.c @@ -206,6 +206,21 @@ double he_factor(int period_in_seconds, int ci) return cache[ci].last_factor; } +void nuclear_regeneration(double time) +{ + time /= 60.0; + int ci; + double crushing_radius_N2, crushing_radius_He; + for (ci = 0; ci < 16; ++ci) { + //rm + crushing_radius_N2 = 1.0 / (max_n2_crushing_pressure[ci] / (2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma)) + 1.0 / vpmb_config.crit_radius_N2); + crushing_radius_He = 1.0 / (max_he_crushing_pressure[ci] / (2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma)) + 1.0 / vpmb_config.crit_radius_He); + //rs + n2_regen_radius[ci] = crushing_radius_N2 + (vpmb_config.crit_radius_N2 - crushing_radius_N2) * (1.0 - exp (-time / vpmb_config.regeneration_time)); + he_regen_radius[ci] = crushing_radius_He + (vpmb_config.crit_radius_He - crushing_radius_He) * (1.0 - exp (-time / vpmb_config.regeneration_time)); + } +} + // Calculates the nucleons inner pressure during the impermeable period double calc_inner_pressure(double crit_radius, double onset_tension, double current_ambient_pressure) { diff --git a/dive.h b/dive.h index f7044fba2..8f0f84832 100644 --- a/dive.h +++ b/dive.h @@ -783,6 +783,7 @@ extern unsigned int deco_allowed_depth(double tissues_tolerance, double surface_ extern void set_gf(short gflow, short gfhigh, bool gf_low_at_maxdepth); extern void cache_deco_state(double, char **datap); extern double restore_deco_state(char *data); +extern void nuclear_regeneration(double time); /* this should be converted to use our types */ struct divedatapoint { -- cgit v1.2.3-70-g09d2 From 500fbe499411bfa96192626c8a6213916190aaae Mon Sep 17 00:00:00 2001 From: Jan Darowski Date: Fri, 3 Jul 2015 23:07:58 +0200 Subject: VPM-B: add deco choice to the ui. Removed recreational mode from ui and pref and replaced it with new deco_mode enum. Added radio button ui selection. Set default deco_mode to Buehlmann algorithm. Signed-off-by: Jan Darowski --- planner.c | 3 ++- pref.h | 8 +++++++- qt-models/diveplannermodel.cpp | 6 +++--- qt-models/diveplannermodel.h | 2 +- qt-ui/diveplanner.cpp | 20 ++++++++++++++++---- qt-ui/diveplanner.h | 2 ++ qt-ui/plannerSettings.ui | 23 ++++++++++++++++++++--- subsurfacestartup.c | 4 ++-- 8 files changed, 53 insertions(+), 15 deletions(-) diff --git a/planner.c b/planner.c index 35158fe12..d376acc7c 100644 --- a/planner.c +++ b/planner.c @@ -955,7 +955,8 @@ bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool /* Keep time during the ascend */ bottom_time = clock = previous_point_time = displayed_dive.dc.sample[displayed_dive.dc.samples - 1].time.seconds; gi = gaschangenr - 1; - if(prefs.recreational_mode) { + + if(prefs.deco_mode == RECREATIONAL) { bool safety_stop = prefs.safetystop && max_depth >= 10000; track_ascent_gas(depth, &displayed_dive.cylinder[current_cylinder], avg_depth, bottom_time, safety_stop); // How long can we stay at the current depth and still directly ascent to the surface? diff --git a/pref.h b/pref.h index 0470d0e3b..fcb051bd9 100644 --- a/pref.h +++ b/pref.h @@ -32,6 +32,12 @@ typedef struct { enum taxonomy_category category[3]; } geocoding_prefs_t; +enum deco_mode { + BUEHLMANN, + RECREATIONAL, + VPMB +}; + struct preferences { const char *divelist_font; const char *default_filename; @@ -89,7 +95,6 @@ struct preferences { bool display_runtime; bool display_duration; bool display_transitions; - bool recreational_mode; bool safetystop; bool switch_at_req_stop; int reserve_gas; @@ -110,6 +115,7 @@ struct preferences { short cloud_verification_status; bool cloud_background_sync; geocoding_prefs_t geocoding; + enum deco_mode deco_mode; }; enum unit_system_values { METRIC, diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index adbcdba9a..e28b4d146 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -449,10 +449,10 @@ void DivePlannerPointsModel::setDisplayTransitions(bool value) emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1)); } -void DivePlannerPointsModel::setRecreationalMode(bool value) +void DivePlannerPointsModel::setDecoMode(int mode) { - prefs.recreational_mode = value; - emit recreationChanged(value); + prefs.deco_mode = deco_mode(mode); + emit recreationChanged(mode == int(RECREATIONAL)); emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS -1)); } diff --git a/qt-models/diveplannermodel.h b/qt-models/diveplannermodel.h index 422499084..a8524393a 100644 --- a/qt-models/diveplannermodel.h +++ b/qt-models/diveplannermodel.h @@ -78,7 +78,7 @@ slots: void setDisplayRuntime(bool value); void setDisplayDuration(bool value); void setDisplayTransitions(bool value); - void setRecreationalMode(bool value); + void setDecoMode(int mode); void setSafetyStop(bool value); void savePlan(); void saveDuplicatePlan(); diff --git a/qt-ui/diveplanner.cpp b/qt-ui/diveplanner.cpp index 8b0e93cd8..684ef44aa 100644 --- a/qt-ui/diveplanner.cpp +++ b/qt-ui/diveplanner.cpp @@ -243,7 +243,7 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f) prefs.display_duration = s.value("display_duration", prefs.display_duration).toBool(); prefs.display_runtime = s.value("display_runtime", prefs.display_runtime).toBool(); prefs.display_transitions = s.value("display_transitions", prefs.display_transitions).toBool(); - prefs.recreational_mode = s.value("recreational_mode", prefs.recreational_mode).toBool(); + prefs.deco_mode = deco_mode(s.value("deco_mode", prefs.deco_mode).toInt()); prefs.safetystop = s.value("safetystop", prefs.safetystop).toBool(); prefs.reserve_gas = s.value("reserve_gas", prefs.reserve_gas).toInt(); prefs.ascrate75 = s.value("ascrate75", prefs.ascrate75).toInt(); @@ -269,7 +269,6 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f) ui.display_duration->setChecked(prefs.display_duration); ui.display_runtime->setChecked(prefs.display_runtime); ui.display_transitions->setChecked(prefs.display_transitions); - ui.recreational_mode->setChecked(prefs.recreational_mode); ui.safetystop->setChecked(prefs.safetystop); ui.reserve_gas->setValue(prefs.reserve_gas / 1000); ui.bottompo2->setValue(prefs.bottompo2 / 1000.0); @@ -278,17 +277,30 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f) ui.drop_stone_mode->setChecked(prefs.drop_stone_mode); ui.switch_at_req_stop->setChecked(prefs.switch_at_req_stop); ui.min_switch_duration->setValue(prefs.min_switch_duration / 60); + ui.recreational_deco->setChecked(prefs.deco_mode == RECREATIONAL); + ui.buehlmann_deco->setChecked(prefs.deco_mode == BUEHLMANN); + ui.vpmb_deco->setChecked(prefs.deco_mode == VPMB); + // should be the same order as in dive_comp_type! rebreater_modes << tr("Open circuit") << tr("CCR") << tr("pSCR"); ui.rebreathermode->insertItems(0, rebreater_modes); + modeMapper = new QSignalMapper(this); + connect(modeMapper, SIGNAL(mapped(int)) , plannerModel, SLOT(setDecoMode(int))); + modeMapper->setMapping(ui.recreational_deco, int(RECREATIONAL)); + modeMapper->setMapping(ui.buehlmann_deco, int(BUEHLMANN)); + modeMapper->setMapping(ui.vpmb_deco, int(VPMB)); + + connect(ui.recreational_deco, SIGNAL(clicked()), modeMapper, SLOT(map())); + connect(ui.buehlmann_deco, SIGNAL(clicked()), modeMapper, SLOT(map())); + connect(ui.vpmb_deco, SIGNAL(clicked()), modeMapper, SLOT(map())); + connect(ui.lastStop, SIGNAL(toggled(bool)), plannerModel, SLOT(setLastStop6m(bool))); connect(ui.verbatim_plan, SIGNAL(toggled(bool)), plannerModel, SLOT(setVerbatim(bool))); connect(ui.display_duration, SIGNAL(toggled(bool)), plannerModel, SLOT(setDisplayDuration(bool))); connect(ui.display_runtime, SIGNAL(toggled(bool)), plannerModel, SLOT(setDisplayRuntime(bool))); connect(ui.display_transitions, SIGNAL(toggled(bool)), plannerModel, SLOT(setDisplayTransitions(bool))); connect(ui.safetystop, SIGNAL(toggled(bool)), plannerModel, SLOT(setSafetyStop(bool))); - connect(ui.recreational_mode, SIGNAL(toggled(bool)), plannerModel, SLOT(setRecreationalMode(bool))); connect(ui.reserve_gas, SIGNAL(valueChanged(int)), plannerModel, SLOT(setReserveGas(int))); connect(ui.ascRate75, SIGNAL(valueChanged(int)), this, SLOT(setAscRate75(int))); connect(ui.ascRate75, SIGNAL(valueChanged(int)), plannerModel, SLOT(emitDataChanged())); @@ -341,7 +353,6 @@ PlannerSettingsWidget::~PlannerSettingsWidget() s.setValue("display_duration", prefs.display_duration); s.setValue("display_runtime", prefs.display_runtime); s.setValue("display_transitions", prefs.display_transitions); - s.setValue("recreational_mode", prefs.recreational_mode); s.setValue("safetystop", prefs.safetystop); s.setValue("reserve_gas", prefs.reserve_gas); s.setValue("ascrate75", prefs.ascrate75); @@ -357,6 +368,7 @@ PlannerSettingsWidget::~PlannerSettingsWidget() s.setValue("min_switch_duration", prefs.min_switch_duration); s.setValue("bottomsac", prefs.bottomsac); s.setValue("decosac", prefs.decosac); + s.setValue("deco_mode", int(prefs.deco_mode)); s.endGroup(); } diff --git a/qt-ui/diveplanner.h b/qt-ui/diveplanner.h index 8c7ff9c46..c4210aadf 100644 --- a/qt-ui/diveplanner.h +++ b/qt-ui/diveplanner.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "dive.h" @@ -84,6 +85,7 @@ slots: private: Ui::plannerSettingsWidget ui; void updateUnitsUI(); + QSignalMapper *modeMapper; }; #include "ui_plannerDetails.h" diff --git a/qt-ui/plannerSettings.ui b/qt-ui/plannerSettings.ui index 55ca83a9b..5916b2777 100644 --- a/qt-ui/plannerSettings.ui +++ b/qt-ui/plannerSettings.ui @@ -359,13 +359,30 @@ - + Recreational mode + + + Buehlmann deco + + + true + + + + + + + VPM-B deco + + + + Safety stop @@ -375,14 +392,14 @@ - + Reserve gas - + bar diff --git a/subsurfacestartup.c b/subsurfacestartup.c index 3005e7e04..17bd43e45 100644 --- a/subsurfacestartup.c +++ b/subsurfacestartup.c @@ -54,7 +54,6 @@ struct preferences default_prefs = { .display_runtime = true, .display_duration = true, .display_transitions = true, - .recreational_mode = false, .safetystop = true, .bottomsac = 20000, .decosac = 17000, @@ -75,7 +74,8 @@ struct preferences default_prefs = { .parse_dive_without_gps = false, .tag_existing_dives = false, .category = { 0 } - } + }, + .deco_mode = BUEHLMANN }; int run_survey; -- cgit v1.2.3-70-g09d2 From fad592e43a26343fdef9cd3e0703be10d9069b20 Mon Sep 17 00:00:00 2001 From: Jan Darowski Date: Fri, 3 Jul 2015 23:19:57 +0200 Subject: VPM-B: add initial gradient calculation. Calculate the max difference between tissue saturation and ambient pressure that can be accepted during the ascent. Partial results are kept for later improving in next CVA iterations Signed-off-by: Jan Darowski --- deco.c | 17 +++++++++++++++++ dive.h | 1 + 2 files changed, 18 insertions(+) diff --git a/deco.c b/deco.c index 0b221c225..ada13fbb0 100644 --- a/deco.c +++ b/deco.c @@ -110,6 +110,10 @@ double n2_regen_radius[16]; // rs double he_regen_radius[16]; double max_ambient_pressure; // last moment we were descending +double allowable_n2_gradient[16]; +double allowable_he_gradient[16]; +double total_gradient[16]; + static double tissue_tolerance_calc(const struct dive *dive) { @@ -206,6 +210,19 @@ double he_factor(int period_in_seconds, int ci) return cache[ci].last_factor; } +void vpmb_start_gradient() +{ + int ci; + double gradient_n2, gradient_he; + + for (ci = 0; ci < 16; ++ci) { + allowable_n2_gradient[ci] = 2.0 * (vpmb_config.surface_tension_gamma / vpmb_config.skin_compression_gammaC) * ((vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma) / n2_regen_radius[ci]); + allowable_he_gradient[ci] = 2.0 * (vpmb_config.surface_tension_gamma / vpmb_config.skin_compression_gammaC) * ((vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma) / he_regen_radius[ci]); + + total_gradient[ci] = ((allowable_n2_gradient[ci] * tissue_n2_sat[ci]) + (allowable_he_gradient[ci] * tissue_he_sat[ci])) / (tissue_n2_sat[ci] + tissue_he_sat[ci]); + } +} + void nuclear_regeneration(double time) { time /= 60.0; diff --git a/dive.h b/dive.h index 8f0f84832..d9885184e 100644 --- a/dive.h +++ b/dive.h @@ -784,6 +784,7 @@ extern void set_gf(short gflow, short gfhigh, bool gf_low_at_maxdepth); extern void cache_deco_state(double, char **datap); extern double restore_deco_state(char *data); extern void nuclear_regeneration(double time); +extern void vpmb_start_gradient(); /* this should be converted to use our types */ struct divedatapoint { -- cgit v1.2.3-70-g09d2 From e7aa686f1658e689d215397d4493859829965646 Mon Sep 17 00:00:00 2001 From: Jan Darowski Date: Fri, 3 Jul 2015 23:24:20 +0200 Subject: VPM-B: add vpm-b based deco checking. Check during the trial_ascent() if existing pressure gradient is smaller than previously calculated max gradient. If not, ascent is impossible from the vpm-b's point of view. Signed-off-by: Jan Darowski --- deco.c | 15 +++++++++++++++ dive.h | 1 + planner.c | 6 +++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/deco.c b/deco.c index ada13fbb0..47b752ee8 100644 --- a/deco.c +++ b/deco.c @@ -210,6 +210,21 @@ double he_factor(int period_in_seconds, int ci) return cache[ci].last_factor; } +bool is_vpmb_ok(double pressure) +{ + int ci; + double gradient; + double gas_tension; + + for (ci = 0; ci < 16; ++ci) { + gas_tension = tissue_n2_sat[ci] + tissue_he_sat[ci] + vpmb_config.other_gases_pressure; + gradient = gas_tension - pressure; + if (gradient > total_gradient[ci]) + return false; + } + return true; +} + void vpmb_start_gradient() { int ci; diff --git a/dive.h b/dive.h index d9885184e..9b44700e7 100644 --- a/dive.h +++ b/dive.h @@ -785,6 +785,7 @@ extern void cache_deco_state(double, char **datap); extern double restore_deco_state(char *data); extern void nuclear_regeneration(double time); extern void vpmb_start_gradient(); +extern bool is_vpmb_ok(double pressure); /* this should be converted to use our types */ struct divedatapoint { diff --git a/planner.c b/planner.c index d376acc7c..0307ea89c 100644 --- a/planner.c +++ b/planner.c @@ -836,11 +836,15 @@ bool trial_ascent(int trial_depth, int stoplevel, int avg_depth, int bottom_time tissue_tolerance = add_segment(depth_to_mbar(trial_depth, &displayed_dive) / 1000.0, gasmix, TIMESTEP, po2, &displayed_dive, prefs.decosac); - if (deco_allowed_depth(tissue_tolerance, surface_pressure, &displayed_dive, 1) > trial_depth - deltad) { + if (prefs.deco_mode != VPMB && deco_allowed_depth(tissue_tolerance, surface_pressure, &displayed_dive, 1) > trial_depth - deltad) { /* We should have stopped */ clear_to_ascend = false; break; } + if (prefs.deco_mode == VPMB && (!is_vpmb_ok(depth_to_mbar(trial_depth, &displayed_dive) / 1000.0))){ + clear_to_ascend = false; + break; + } trial_depth -= deltad; } restore_deco_state(trial_cache); -- cgit v1.2.3-70-g09d2 From 02f13399177d301c9a1242ce0d342b7c91dd28fd Mon Sep 17 00:00:00 2001 From: Jan Darowski Date: Fri, 3 Jul 2015 23:54:57 +0200 Subject: VPM-B: vpm without CVA working. Add call of initial calculation of critical radius and start gradient, so the VPM could work. Currently without CVA, so the gradient isn't improved. Only one iteration is run. Signed-off-by: Jan Darowski --- planner.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/planner.c b/planner.c index 0307ea89c..700985052 100644 --- a/planner.c +++ b/planner.c @@ -1023,6 +1023,11 @@ bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool (get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[best_first_ascend_cylinder].depth / 1000.0); #endif } + + // VPM-B or Buehlmann Deco + nuclear_regeneration(clock); + vpmb_start_gradient(); + while (1) { /* We will break out when we hit the surface */ do { -- cgit v1.2.3-70-g09d2 From bfb9f1908050e4a42c2e626e00acde01c6c7d4da Mon Sep 17 00:00:00 2001 From: Jan Darowski Date: Fri, 3 Jul 2015 23:59:22 +0200 Subject: VPM-B: add next gradient calculation. It improves (increases) gradients for all the compartments, so more free gas can be created in the divers body. Next gradients will converge, so the volume won't exceed the safe limit, indicated by the crit_volume_lambda parameter. Function takes time of the last deco in seconds. Requires vpmb_start_gradient() to be run before. Signed-off-by: Jan Darowski --- deco.c | 24 ++++++++++++++++++++++++ dive.h | 1 + 2 files changed, 25 insertions(+) diff --git a/deco.c b/deco.c index 47b752ee8..f1bc8039f 100644 --- a/deco.c +++ b/deco.c @@ -238,6 +238,30 @@ void vpmb_start_gradient() } } +void vpmb_next_gradient(double deco_time) +{ + int ci; + double gradient_n2, gradient_he; + double n2_b, n2_c; + double he_b, he_c; + deco_time /= 60.0 ; + + for (ci = 0; ci < 16; ++ci) { + n2_b = allowable_n2_gradient[ci] + ((vpmb_config.crit_volume_lambda * vpmb_config.surface_tension_gamma) / (vpmb_config.skin_compression_gammaC * (deco_time + buehlmann_N2_t_halflife[ci] * 60.0 / log(2.0)))); + he_b = allowable_he_gradient[ci] + ((vpmb_config.crit_volume_lambda * vpmb_config.surface_tension_gamma) / (vpmb_config.skin_compression_gammaC * (deco_time + buehlmann_He_t_halflife[ci] * 60.0 / log(2.0)))); + + n2_c = vpmb_config.surface_tension_gamma * vpmb_config.surface_tension_gamma * vpmb_config.crit_volume_lambda * max_n2_crushing_pressure[ci]; + n2_c = n2_c / (vpmb_config.skin_compression_gammaC * vpmb_config.skin_compression_gammaC * (deco_time + buehlmann_N2_t_halflife[ci] * 60.0 / log(2.0))); + he_c = vpmb_config.surface_tension_gamma * vpmb_config.surface_tension_gamma * vpmb_config.crit_volume_lambda * max_he_crushing_pressure[ci]; + he_c = he_c / (vpmb_config.skin_compression_gammaC * vpmb_config.skin_compression_gammaC * (deco_time + buehlmann_He_t_halflife[ci] * 60.0 / log(2.0))); + + allowable_n2_gradient[ci] = 0.5 * ( n2_b + sqrt(n2_b * n2_b - 4.0 * n2_c)); + allowable_he_gradient[ci] = 0.5 * ( he_b + sqrt(he_b * he_b - 4.0 * he_c)); + + total_gradient[ci] = ((allowable_n2_gradient[ci] * tissue_n2_sat[ci]) + (allowable_he_gradient[ci] * tissue_he_sat[ci])) / (tissue_n2_sat[ci] + tissue_he_sat[ci]); + } +} + void nuclear_regeneration(double time) { time /= 60.0; diff --git a/dive.h b/dive.h index 9b44700e7..042878c1b 100644 --- a/dive.h +++ b/dive.h @@ -785,6 +785,7 @@ extern void cache_deco_state(double, char **datap); extern double restore_deco_state(char *data); extern void nuclear_regeneration(double time); extern void vpmb_start_gradient(); +extern void vpmb_next_gradient(double deco_time); extern bool is_vpmb_ok(double pressure); /* this should be converted to use our types */ -- cgit v1.2.3-70-g09d2 From ddfd046c8d0efe3bebc5c14b3d53e6d39eabe217 Mon Sep 17 00:00:00 2001 From: Jan Darowski Date: Sat, 4 Jul 2015 00:13:34 +0200 Subject: VPM-B: add CVA to the deco planner. Added keeping bottom dive state and every deco's time, so we can run multiple deco simulations with different gradients until they converge to some optimal value. Some improvements on the deco time calculation may be needed. Signed-off-by: Jan Darowski --- planner.c | 282 +++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 161 insertions(+), 121 deletions(-) diff --git a/planner.c b/planner.c index 700985052..83e475758 100644 --- a/planner.c +++ b/planner.c @@ -869,6 +869,14 @@ bool enough_gas(int current_cylinder) bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool show_disclaimer) { + int bottom_depth; + int bottom_gi; + int bottom_stopidx; + + bool is_final_plan = true; + int deco_time; + int previous_deco_time; + char *bottom_cache = NULL; struct sample *sample; int po2; int transitiontime, gi; @@ -886,7 +894,7 @@ bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool int avg_depth, max_depth, bottom_time = 0; int last_ascend_rate; int best_first_ascend_cylinder; - struct gasmix gas; + struct gasmix gas, bottom_gas; int o2time = 0; int breaktime = -1; int breakcylinder = 0; @@ -1008,7 +1016,6 @@ bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool free(stoplevels); free(gaschanges); - return(false); } @@ -1027,148 +1034,181 @@ bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool // VPM-B or Buehlmann Deco nuclear_regeneration(clock); vpmb_start_gradient(); + previous_deco_time = 100000000; + deco_time = 10000000; + cache_deco_state(tissue_tolerance, &bottom_cache); // Lets us make several iterations + bottom_depth = depth; + bottom_gi = gi; + bottom_gas = gas; + bottom_stopidx = stopidx; + //CVA + do { + is_final_plan = (prefs.deco_mode == BUEHLMANN) || (previous_deco_time - deco_time < 10); // CVA time converges + previous_deco_time = deco_time; + restore_deco_state(bottom_cache); + + depth = bottom_depth; + gi = bottom_gi; + clock = previous_point_time = bottom_time; + gas = bottom_gas; + stopping = false; + decodive = false; + stopidx = bottom_stopidx; + breaktime = -1; + breakcylinder = 0; + o2time = 0; + last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); + if ((current_cylinder = get_gasidx(&displayed_dive, &gas)) == -1) { + report_error(translate("gettextFromC", "Can't find gas %s"), gasname(&gas)); + current_cylinder = 0; + } + vpmb_next_gradient(deco_time); - while (1) { - /* We will break out when we hit the surface */ - do { - /* Ascend to next stop depth */ - int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP; - if (ascent_velocity(depth, avg_depth, bottom_time) != last_ascend_rate) { - plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); + while (1) { + /* We will break out when we hit the surface */ + do { + /* Ascend to next stop depth */ + int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP; + if (ascent_velocity(depth, avg_depth, bottom_time) != last_ascend_rate) { + if (is_final_plan) + plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); + previous_point_time = clock; + stopping = false; + last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); + } + if (depth - deltad < stoplevels[stopidx]) + deltad = depth - stoplevels[stopidx]; + + tissue_tolerance = add_segment(depth_to_mbar(depth, &displayed_dive) / 1000.0, + &displayed_dive.cylinder[current_cylinder].gasmix, + TIMESTEP, po2, &displayed_dive, prefs.decosac); + clock += TIMESTEP; + depth -= deltad; + } while (depth > 0 && depth > stoplevels[stopidx]); + + if (depth <= 0) + break; /* We are at the surface */ + + if (gi >= 0 && stoplevels[stopidx] <= gaschanges[gi].depth) { + /* We have reached a gas change. + * Record this in the dive plan */ + if (is_final_plan) + plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); previous_point_time = clock; - stopping = false; - last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); - } - if (depth - deltad < stoplevels[stopidx]) - deltad = depth - stoplevels[stopidx]; + stopping = true; - tissue_tolerance = add_segment(depth_to_mbar(depth, &displayed_dive) / 1000.0, - &displayed_dive.cylinder[current_cylinder].gasmix, - TIMESTEP, po2, &displayed_dive, prefs.decosac); - clock += TIMESTEP; - depth -= deltad; - } while (depth > 0 && depth > stoplevels[stopidx]); - - if (depth <= 0) - break; /* We are at the surface */ - - if (gi >= 0 && stoplevels[stopidx] <= gaschanges[gi].depth) { - /* We have reached a gas change. - * Record this in the dive plan */ - plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); - previous_point_time = clock; - stopping = true; - - /* Check we need to change cylinder. - * We might not if the cylinder was chosen by the user - * or user has selected only to switch only at required stops. - * If current gas is hypoxic, we want to switch asap */ - if (current_cylinder != gaschanges[gi].gasidx) { - if (!prefs.switch_at_req_stop || - !trial_ascent(depth, stoplevels[stopidx - 1], avg_depth, bottom_time, tissue_tolerance, - &displayed_dive.cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0) || get_o2(&displayed_dive.cylinder[current_cylinder].gasmix) < 160) { - current_cylinder = gaschanges[gi].gasidx; + /* Check we need to change cylinder. + * We might not if the cylinder was chosen by the user + * or user has selected only to switch only at required stops. + * If current gas is hypoxic, we want to switch asap */ + if (current_cylinder != gaschanges[gi].gasidx) { + if (!prefs.switch_at_req_stop || + !trial_ascent(depth, stoplevels[stopidx - 1], avg_depth, bottom_time, tissue_tolerance, + &displayed_dive.cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0) || get_o2(&displayed_dive.cylinder[current_cylinder].gasmix) < 160) { + current_cylinder = gaschanges[gi].gasidx; + gas = displayed_dive.cylinder[current_cylinder].gasmix; +#if DEBUG_PLAN & 16 + printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi].gasidx, + (get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[gi].depth / 1000.0); +#endif + /* Stop for the minimum duration to switch gas */ + tissue_tolerance = add_segment(depth_to_mbar(depth, &displayed_dive) / 1000.0, + &displayed_dive.cylinder[current_cylinder].gasmix, + prefs.min_switch_duration, po2, &displayed_dive, prefs.decosac); + clock += prefs.min_switch_duration; + } else { + pendinggaschange = true; + } + gi--; + } + } + --stopidx; + + /* Save the current state and try to ascend to the next stopdepth */ + while (1) { + /* Check if ascending to next stop is clear, go back and wait if we hit the ceiling on the way */ + if (trial_ascent(depth, stoplevels[stopidx], avg_depth, bottom_time, tissue_tolerance, + &displayed_dive.cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0)) + break; /* We did not hit the ceiling */ + + /* Add a minute of deco time and then try again */ + decodive = true; + if (!stopping) { + /* The last segment was an ascend segment. + * Add a waypoint for start of this deco stop */ + if (is_final_plan) + plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); + previous_point_time = clock; + stopping = true; + } + if (pendinggaschange) { + current_cylinder = gaschanges[gi + 1].gasidx; gas = displayed_dive.cylinder[current_cylinder].gasmix; #if DEBUG_PLAN & 16 - printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi].gasidx, - (get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[gi].depth / 1000.0); + printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi + 1].gasidx, + (get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[gi + 1].depth / 1000.0); #endif /* Stop for the minimum duration to switch gas */ tissue_tolerance = add_segment(depth_to_mbar(depth, &displayed_dive) / 1000.0, &displayed_dive.cylinder[current_cylinder].gasmix, prefs.min_switch_duration, po2, &displayed_dive, prefs.decosac); clock += prefs.min_switch_duration; - } else { - pendinggaschange = true; + pendinggaschange = false; } - gi--; - } - } - --stopidx; - - /* Save the current state and try to ascend to the next stopdepth */ - while (1) { - /* Check if ascending to next stop is clear, go back and wait if we hit the ceiling on the way */ - if (trial_ascent(depth, stoplevels[stopidx], avg_depth, bottom_time, tissue_tolerance, - &displayed_dive.cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0)) - break; /* We did not hit the ceiling */ - - /* Add a minute of deco time and then try again */ - decodive = true; - if (!stopping) { - /* The last segment was an ascend segment. - * Add a waypoint for start of this deco stop */ - plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); - previous_point_time = clock; - stopping = true; - } + /* Deco stop should end when runtime is at a whole minute */ + int this_decotimestep; + this_decotimestep = DECOTIMESTEP - clock % DECOTIMESTEP; - if (pendinggaschange) { - current_cylinder = gaschanges[gi + 1].gasidx; - gas = displayed_dive.cylinder[current_cylinder].gasmix; -#if DEBUG_PLAN & 16 - printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi + 1].gasidx, - (get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[gi + 1].depth / 1000.0); -#endif - /* Stop for the minimum duration to switch gas */ tissue_tolerance = add_segment(depth_to_mbar(depth, &displayed_dive) / 1000.0, - &displayed_dive.cylinder[current_cylinder].gasmix, - prefs.min_switch_duration, po2, &displayed_dive, prefs.decosac); - clock += prefs.min_switch_duration; - pendinggaschange = false; - } - - /* Deco stop should end when runtime is at a whole minute */ - int this_decotimestep; - this_decotimestep = DECOTIMESTEP - clock % DECOTIMESTEP; - - tissue_tolerance = add_segment(depth_to_mbar(depth, &displayed_dive) / 1000.0, - &displayed_dive.cylinder[current_cylinder].gasmix, - this_decotimestep, po2, &displayed_dive, prefs.decosac); - clock += this_decotimestep; - /* Finish infinite deco */ - if(clock >= 48 * 3600 && depth >= 6000) { - error = LONGDECO; - break; - } - if (prefs.doo2breaks) { - if (get_o2(&displayed_dive.cylinder[current_cylinder].gasmix) == 1000) { - o2time += DECOTIMESTEP; - if (o2time >= 12 * 60) { - breaktime = 0; - breakcylinder = current_cylinder; - plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); - previous_point_time = clock; - current_cylinder = 0; - gas = displayed_dive.cylinder[current_cylinder].gasmix; - } - } else { - if (breaktime >= 0) { - breaktime += DECOTIMESTEP; - if (breaktime >= 6 * 60) { - o2time = 0; - plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); + &displayed_dive.cylinder[current_cylinder].gasmix, + this_decotimestep, po2, &displayed_dive, prefs.decosac); + clock += this_decotimestep; + /* Finish infinite deco */ + if(clock >= 48 * 3600 && depth >= 6000) { + error = LONGDECO; + break; + } + if (prefs.doo2breaks) { + if (get_o2(&displayed_dive.cylinder[current_cylinder].gasmix) == 1000) { + o2time += DECOTIMESTEP; + if (o2time >= 12 * 60) { + breaktime = 0; + breakcylinder = current_cylinder; + if (is_final_plan) + plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); previous_point_time = clock; - current_cylinder = breakcylinder; + current_cylinder = 0; gas = displayed_dive.cylinder[current_cylinder].gasmix; - breaktime = -1; + } + } else { + if (breaktime >= 0) { + breaktime += DECOTIMESTEP; + if (breaktime >= 6 * 60) { + o2time = 0; + if (is_final_plan) + plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); + previous_point_time = clock; + current_cylinder = breakcylinder; + gas = displayed_dive.cylinder[current_cylinder].gasmix; + breaktime = -1; + } } } } } + if (stopping) { + /* Next we will ascend again. Add a waypoint if we have spend deco time */ + if (is_final_plan) + plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); + previous_point_time = clock; + stopping = false; + } } - if (stopping) { - /* Next we will ascend again. Add a waypoint if we have spend deco time */ - plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false); - previous_point_time = clock; - stopping = false; - } - } - /* We made it to the surface - * Create the final dive, add the plan to the notes and fixup some internal - * data that we need to be there when plotting the dive */ + deco_time = clock - bottom_time; + } while (!is_final_plan); + plan_add_segment(diveplan, clock - previous_point_time, 0, gas, po2, false); create_dive_from_plan(diveplan, is_planner); add_plan_to_notes(diveplan, &displayed_dive, show_disclaimer, error); -- cgit v1.2.3-70-g09d2