From 9c8fbe494db231b59c2e072b54af85bc217c00d8 Mon Sep 17 00:00:00 2001 From: "Robert C. Helling" Date: Tue, 29 Oct 2019 17:57:34 +0100 Subject: [PATCH] Planner: Add option to treat O2 as narcotic When computing the best mix for a target depth, for helium, one can either require that the partial pressure of N2 is the same as at the target depth or the partial pressure of N2 plus O2. Signed-off-by: Robert C. Helling --- CHANGELOG.md | 1 + Documentation/user-manual.txt | 3 +- core/dive.c | 8 +- core/dive.h | 2 +- core/pref.h | 1 + core/settings/qPrefDivePlanner.cpp | 2 + core/settings/qPrefDivePlanner.h | 5 + core/subsurfacestartup.c | 1 + desktop-widgets/diveplanner.cpp | 9 ++ desktop-widgets/diveplanner.h | 1 + desktop-widgets/plannerSettings.ui | 168 +++++++++++++++-------------- qt-models/cylindermodel.cpp | 6 +- 12 files changed, 121 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be63cb75d..42f8d88dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +Planner: Add checkbox on considering oxygen narcotic Desktop: register changes when clicking "done" on dive-site edit screen Mobile: re-enable GPS location service icon in global drawer Mobile: add support for editing the dive number of a dive diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index d7bf4606d..98fa5622f 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -3545,7 +3545,8 @@ image::images/PlannerWindow1.jpg["FIGURE: Dive planner startup window",align="ce O~2~% according to the depth set. Set to ''*'' to calculate the best O~2~% for the dive maximum depth. ** MND: the gas Maximum Narcotic Depth (MND). Automatically calculated based on the Best Mix END preference (default 30m / 98 ft). Editing this field will modify the He% according to the depth set. - Set to ''*'' to calculate the best He% for the dive maximum depth. + Set to ''*'' to calculate the best He% for the dive maximum depth. Depending on the checkbox, oxygen + is considered narcotic (the END is used) or not (the EAD is used). - The profile of the planned dive can be created in two ways: * Drag the waypoints diff --git a/core/dive.c b/core/dive.c index d845d8077..9788e64c7 100644 --- a/core/dive.c +++ b/core/dive.c @@ -3761,13 +3761,17 @@ fraction_t best_o2(depth_t depth, const struct dive *dive) } //Calculate He in best mix. O2 is considered narcopic -fraction_t best_he(depth_t depth, const struct dive *dive) +fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2) { fraction_t fhe; int pnarcotic, ambient; pnarcotic = depth_to_mbar(prefs.bestmixend.mm, dive); ambient = depth_to_mbar(depth.mm, dive); - fhe.permille = (100 - 100 * pnarcotic / ambient) * 10; //use integer arithmetic to round up to nearest percent + if (o2narcotic) { + fhe.permille = (100 - 100 * pnarcotic / ambient) * 10; //use integer arithmetic to round up to nearest percent + } else { + fhe.permille = 1000 - fo2.permille - N2_IN_AIR * pnarcotic / ambient; + } if (fhe.permille < 0) fhe.permille = 0; return fhe; diff --git a/core/dive.h b/core/dive.h index 87aa3e5fd..6d1bd450d 100644 --- a/core/dive.h +++ b/core/dive.h @@ -226,7 +226,7 @@ extern int explicit_first_cylinder(const struct dive *dive, const struct divecom extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time); extern fraction_t best_o2(depth_t depth, const struct dive *dive); -extern fraction_t best_he(depth_t depth, const struct dive *dive); +extern fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2); extern int get_surface_pressure_in_mbar(const struct dive *dive, bool non_null); extern int calculate_depth_to_mbar(int depth, pressure_t surface_pressure, int salinity); diff --git a/core/pref.h b/core/pref.h index 50e13a117..f7af35415 100644 --- a/core/pref.h +++ b/core/pref.h @@ -160,6 +160,7 @@ struct preferences { bool display_variations; bool doo2breaks; bool dobailout; + bool o2narcotic; bool drop_stone_mode; bool last_stop; // At 6m? int min_switch_duration; // seconds diff --git a/core/settings/qPrefDivePlanner.cpp b/core/settings/qPrefDivePlanner.cpp index f33b390cf..e094bfed0 100644 --- a/core/settings/qPrefDivePlanner.cpp +++ b/core/settings/qPrefDivePlanner.cpp @@ -35,6 +35,7 @@ void qPrefDivePlanner::loadSync(bool doSync) disk_display_variations(doSync); disk_doo2breaks(doSync); disk_dobailout(doSync); + disk_o2narcotic(doSync); disk_drop_stone_mode(doSync); disk_last_stop(doSync); disk_min_switch_duration(doSync); @@ -76,6 +77,7 @@ HANDLE_PREFERENCE_BOOL(DivePlanner, "display_variations", display_variations); HANDLE_PREFERENCE_BOOL(DivePlanner, "doo2breaks", doo2breaks); HANDLE_PREFERENCE_BOOL(DivePlanner, "dobailbout", dobailout); +HANDLE_PREFERENCE_BOOL(DivePlanner, "o2narcotic", o2narcotic); HANDLE_PREFERENCE_BOOL(DivePlanner, "drop_stone_mode", drop_stone_mode); diff --git a/core/settings/qPrefDivePlanner.h b/core/settings/qPrefDivePlanner.h index 09d8fabae..7a75fa4ce 100644 --- a/core/settings/qPrefDivePlanner.h +++ b/core/settings/qPrefDivePlanner.h @@ -23,6 +23,7 @@ class qPrefDivePlanner : public QObject { Q_PROPERTY(bool display_variations READ display_variations WRITE set_display_variations NOTIFY display_variationsChanged); Q_PROPERTY(bool doo2breaks READ doo2breaks WRITE set_doo2breaks NOTIFY doo2breaksChanged); Q_PROPERTY(bool dobailout READ dobailout WRITE set_dobailout NOTIFY dobailoutChanged); + Q_PROPERTY(bool o2narcotic READ o2narcotic WRITE set_o2narcotic NOTIFY o2narcoticChanged) Q_PROPERTY(bool drop_stone_mode READ drop_stone_mode WRITE set_drop_stone_mode NOTIFY drop_stone_modeChanged); Q_PROPERTY(bool last_stop READ last_stop WRITE set_last_stop NOTIFY last_stopChanged); Q_PROPERTY(int min_switch_duration READ min_switch_duration WRITE set_min_switch_duration NOTIFY min_switch_durationChanged); @@ -60,6 +61,7 @@ public: static bool display_variations() { return prefs.display_variations; } static bool doo2breaks() { return prefs.doo2breaks; } static bool dobailout() { return prefs.dobailout; } + static bool o2narcotic() { return prefs.o2narcotic; } static bool drop_stone_mode() { return prefs.drop_stone_mode; } static bool last_stop() { return prefs.last_stop; } static int min_switch_duration() { return prefs.min_switch_duration; } @@ -89,6 +91,7 @@ public slots: static void set_display_variations(bool value); static void set_doo2breaks(bool value); static void set_dobailout(bool value); + static void set_o2narcotic(bool value); static void set_drop_stone_mode(bool value); static void set_last_stop(bool value); static void set_min_switch_duration(int value); @@ -118,6 +121,7 @@ signals: void display_variationsChanged(bool value); void doo2breaksChanged(bool value); void dobailoutChanged(bool value); + void o2narcoticChanged(bool value); void drop_stone_modeChanged(bool value); void last_stopChanged(bool value); void min_switch_durationChanged(int value); @@ -148,6 +152,7 @@ private: static void disk_display_variations(bool doSync); static void disk_doo2breaks(bool doSync); static void disk_dobailout(bool doSync); + static void disk_o2narcotic(bool doSync); static void disk_drop_stone_mode(bool doSync); static void disk_last_stop(bool doSync); static void disk_min_switch_duration(bool doSync); diff --git a/core/subsurfacestartup.c b/core/subsurfacestartup.c index 8b7c14179..920285b14 100644 --- a/core/subsurfacestartup.c +++ b/core/subsurfacestartup.c @@ -73,6 +73,7 @@ struct preferences default_prefs = { .display_duration = true, .display_transitions = true, .display_variations = false, + .o2narcotic = true, .safetystop = true, .bottomsac = 20000, .decosac = 17000, diff --git a/desktop-widgets/diveplanner.cpp b/desktop-widgets/diveplanner.cpp index 33caffd0d..663bb03fb 100644 --- a/desktop-widgets/diveplanner.cpp +++ b/desktop-widgets/diveplanner.cpp @@ -451,6 +451,7 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f) ui.backgasBreaks->setChecked(prefs.doo2breaks); setBailout(false); setBailoutVisibility(false); + ui.o2narcotic->setChecked(prefs.o2narcotic); 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); @@ -496,6 +497,7 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f) connect(ui.vpmb_conservatism, SIGNAL(valueChanged(int)), plannerModel, SLOT(setVpmbConservatism(int))); connect(ui.backgasBreaks, SIGNAL(toggled(bool)), this, SLOT(setBackgasBreaks(bool))); connect(ui.bailout, SIGNAL(toggled(bool)), this, SLOT(setBailout(bool))); + connect(ui.o2narcotic, SIGNAL(toggled(bool)), this, SLOT(setO2narcotic(bool))); connect(ui.switch_at_req_stop, SIGNAL(toggled(bool)), plannerModel, SLOT(setSwitchAtReqStop(bool))); connect(ui.min_switch_duration, SIGNAL(valueChanged(int)), plannerModel, SLOT(setMinSwitchDuration(int))); connect(ui.surface_segment, SIGNAL(valueChanged(int)), plannerModel, SLOT(setSurfaceSegment(int))); @@ -504,6 +506,7 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f) connect(ui.bottompo2, SIGNAL(valueChanged(double)), CylindersModel::instance(), SLOT(updateBestMixes())); connect(ui.bestmixEND, SIGNAL(valueChanged(int)), CylindersModel::instance(), SLOT(updateBestMixes())); + connect(ui.o2narcotic, SIGNAL(toggled(bool)), CylindersModel::instance(), SLOT(updateBestMixes())); connect(modeMapper, SIGNAL(mapped(int)), this, SLOT(disableDecoElements(int))); connect(ui.ascRate75, SIGNAL(valueChanged(int)), this, SLOT(setAscrate75(int))); @@ -679,6 +682,12 @@ void PlannerSettingsWidget::setBailout(bool dobailout) plannerModel->emitDataChanged(); } +void PlannerSettingsWidget::setO2narcotic(bool o2narcotic) +{ + qPrefDivePlanner::instance()->set_o2narcotic(o2narcotic); + plannerModel->emitDataChanged(); +} + void PlannerSettingsWidget::setBailoutVisibility(int mode) { ui.bailout->setDisabled(!(mode == CCR || mode == PSCR)); diff --git a/desktop-widgets/diveplanner.h b/desktop-widgets/diveplanner.h index 6d17c956b..e9e7808b9 100644 --- a/desktop-widgets/diveplanner.h +++ b/desktop-widgets/diveplanner.h @@ -86,6 +86,7 @@ slots: void setBestmixEND(int depth); void setBackgasBreaks(bool dobreaks); void setBailout(bool dobailout); + void setO2narcotic(bool o2narcotic); void disableDecoElements(int mode); void disableBackgasBreaks(bool enabled); void setDiveMode(int mode); diff --git a/desktop-widgets/plannerSettings.ui b/desktop-widgets/plannerSettings.ui index f22d0db45..33e3d2456 100644 --- a/desktop-widgets/plannerSettings.ui +++ b/desktop-widgets/plannerSettings.ui @@ -561,6 +561,83 @@ 2 + + + + Notes + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + Verbatim dive plan + + + + + + + In diveplan, list transitions or treat them as implicit + + + Display transitions in deco + + + + + + + In dive plan, show runtime (absolute time) of stops + + + Display runtime + + + + + + + true + + + In dive plan, show duration (relative time) of stops + + + Display segment duration + + + + + + + true + + + Compute variations of plan (performance cost) + + + Display plan variations + + + + + + @@ -581,7 +658,7 @@ - + Qt::Vertical @@ -594,7 +671,7 @@ - + Qt::Vertical @@ -692,83 +769,6 @@ - - - - Notes - - - - 2 - - - 2 - - - 2 - - - 2 - - - 2 - - - - - Verbatim dive plan - - - - - - - In diveplan, list transitions or treat them as implicit - - - Display transitions in deco - - - - - - - In dive plan, show runtime (absolute time) of stops - - - Display runtime - - - - - - - true - - - In dive plan, show duration (relative time) of stops - - - Display segment duration - - - - - - - true - - - Compute variations of plan (performance cost) - - - Display plan variations - - - - - - @@ -831,6 +831,16 @@ + + + + Treat oxygen as narcotic when computing best mix + + + O₂ narcotic + + + diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 95c6aace2..a05cef23f 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -391,11 +391,11 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (QString::compare(qPrintable(vString), "*") == 0) { cyl->bestmix_he = true; // Calculate fO2 for max. depth - cyl->gasmix.he = best_he(displayed_dive.maxdepth, &displayed_dive); + cyl->gasmix.he = best_he(displayed_dive.maxdepth, &displayed_dive, prefs.o2narcotic, cyl->gasmix.o2); } else { cyl->bestmix_he = false; // Calculate fHe for input depth - cyl->gasmix.he = best_he(string_to_depth(qPrintable(vString)), &displayed_dive); + cyl->gasmix.he = best_he(string_to_depth(qPrintable(vString)), &displayed_dive, prefs.o2narcotic, cyl->gasmix.o2); } changed = true; } @@ -605,7 +605,7 @@ bool CylindersModel::updateBestMixes() gasUpdated = true; } if (cyl->bestmix_he) { - cyl->gasmix.he = best_he(displayed_dive.maxdepth, &displayed_dive); + cyl->gasmix.he = best_he(displayed_dive.maxdepth, &displayed_dive, prefs.o2narcotic, cyl->gasmix.o2); // fO2 + fHe must not be greater than 1 if (get_o2(cyl->gasmix) + get_he(cyl->gasmix) > 1000) cyl->gasmix.o2.permille = 1000 - get_he(cyl->gasmix);