Hi,

From b36db28344b56c3ccf1c54f46cf006dd7e6e86a0 Mon Sep 17 00:00:00 2001
From: "Robert C. Helling" <hell...@atdotde.de>
Date: Tue, 31 Mar 2015 14:52:37 +0200
Subject: [PATCH] Introduce recreational planner mode

This adopts the planner to the needs of the recreational diver. Rather than 
immediately
starting to ascent doing deco stops this mode, this mode stays at the last 
manually
entered depth for the maximal time before mandantory stops appear (NDL). It 
does not
change gas but keeps using the last used cylinder.

TODO:   * Grey out unused UI elements of the planner in this mode
        * Start ascent before gas runs out (or into reserve)
        * Do a 3min @ 5m safety stop.

Fixes #840

Signed-off-by: Robert C. Helling <hell...@atdotde.de>
---
 planner.c                         | 89 +++++++++++++++++++++++++++++----------
 planner.h                         |  1 +
 pref.h                            |  1 +
 qt-ui/diveplanner.cpp             | 10 +++++
 qt-ui/diveplanner.h               |  1 +
 qt-ui/plannerSettings.ui          | 89 +++++++++++++++++++++------------------
 qt-ui/profile/diveprofileitem.cpp |  2 +-
 subsurfacestartup.c               |  1 +
 8 files changed, 129 insertions(+), 65 deletions(-)

diff --git a/planner.c b/planner.c
index d5852dc..60fc31b 100644
--- a/planner.c
+++ b/planner.c
@@ -800,6 +800,31 @@ int ascend_velocity(int depth, int avg_depth, int 
bottom_time)
        }
 }
 
+bool trial_ascent(int trial_depth, int stoplevel, int avg_depth, int 
bottom_time, double tissue_tolerance, struct gasmix *gasmix, int po2, double 
surface_pressure)
+{
+
+       bool clear_to_ascend = true;
+       char *trial_cache = NULL;
+
+       cache_deco_state(tissue_tolerance, &trial_cache);
+       while (trial_depth > stoplevel) {
+               int deltad = ascend_velocity(trial_depth, avg_depth, 
bottom_time) * TIMESTEP;
+               if (deltad > trial_depth) /* don't test against depth above 
surface */
+                       deltad = trial_depth;
+               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) {
+                       /* We should have stopped */
+                       clear_to_ascend = false;
+                       break;
+               }
+               trial_depth -= deltad;
+       }
+       restore_deco_state(trial_cache);
+       return clear_to_ascend;
+}
+
 int plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool 
show_disclaimer)
 {
        struct sample *sample;
@@ -812,7 +837,6 @@ int plan(struct diveplan *diveplan, char **cached_datap, 
bool is_planner, bool s
        struct gaschanges *gaschanges = NULL;
        int gaschangenr;
        int *stoplevels = NULL;
-       char *trial_cache = NULL;
        bool stopping = false;
        bool clear_to_ascend;
        int clock, previous_point_time;
@@ -880,6 +904,45 @@ int plan(struct diveplan *diveplan, char **cached_datap, 
bool is_planner, bool s
        /* 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) {
+               // How long can we stay at the current depth and still directly 
ascent to the surface?
+               while (trial_ascent(depth, 0, avg_depth, bottom_time, 
tissue_tolerance, &displayed_dive.cylinder[current_cylinder].gasmix,
+                                 po2, diveplan->surface_pressure / 1000.0)) {
+                       tissue_tolerance = add_segment(depth_to_mbar(depth, 
&displayed_dive) / 1000.0,
+                                                      
&displayed_dive.cylinder[current_cylinder].gasmix,
+                                                      DECOTIMESTEP, po2, 
&displayed_dive, prefs.bottomsac);
+                       clock += DECOTIMESTEP;
+               }
+               clock -= DECOTIMESTEP;
+               plan_add_segment(diveplan, clock - previous_point_time, depth, 
gas, po2, false);
+               previous_point_time = clock;
+               do {
+                       /* Ascend to surface */
+                       int deltad = ascend_velocity(depth, avg_depth, 
bottom_time) * TIMESTEP;
+                       if (ascend_velocity(depth, avg_depth, bottom_time) != 
last_ascend_rate) {
+                               plan_add_segment(diveplan, clock - 
previous_point_time, depth, gas, po2, false);
+                               previous_point_time = clock;
+                               last_ascend_rate = ascend_velocity(depth, 
avg_depth, bottom_time);
+                       }
+                       if (depth - deltad < 0)
+                               deltad = depth;
+
+                       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);
+               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);
+               fixup_dc_duration(&displayed_dive.dc);
+
+               free(stoplevels);
+               free(gaschanges);
+
+               return(error);
+       }
 
        if (best_first_ascend_cylinder != current_cylinder) {
                stopping = true;
@@ -935,28 +998,10 @@ int plan(struct diveplan *diveplan, char **cached_datap, 
bool is_planner, bool s
                --stopidx;
 
                /* Save the current state and try to ascend to the next 
stopdepth */
-               int trial_depth = depth;
-               cache_deco_state(tissue_tolerance, &trial_cache);
                while (1) {
                        /* Check if ascending to next stop is clear, go back 
and wait if we hit the ceiling on the way */
-                       clear_to_ascend = true;
-                       while (trial_depth > stoplevels[stopidx]) {
-                               int deltad = ascend_velocity(trial_depth, 
avg_depth, bottom_time) * TIMESTEP;
-                               if (deltad > trial_depth) /* don't test against 
depth above surface */
-                                       deltad = trial_depth;
-                               tissue_tolerance = 
add_segment(depth_to_mbar(trial_depth, &displayed_dive) / 1000.0,
-                                                              
&displayed_dive.cylinder[current_cylinder].gasmix,
-                                                              TIMESTEP, po2, 
&displayed_dive, prefs.decosac);
-                               if (deco_allowed_depth(tissue_tolerance, 
diveplan->surface_pressure / 1000.0, &displayed_dive, 1) > trial_depth - 
deltad) {
-                                       /* We should have stopped */
-                                       clear_to_ascend = false;
-                                       break;
-                               }
-                               trial_depth -= deltad;
-                       }
-                       restore_deco_state(trial_cache);
-
-                       if (clear_to_ascend)
+                       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 */
@@ -970,7 +1015,6 @@ int plan(struct diveplan *diveplan, char **cached_datap, 
bool is_planner, bool s
                        tissue_tolerance = add_segment(depth_to_mbar(depth, 
&displayed_dive) / 1000.0,
                                                       
&displayed_dive.cylinder[current_cylinder].gasmix,
                                                       DECOTIMESTEP, po2, 
&displayed_dive, prefs.decosac);
-                       cache_deco_state(tissue_tolerance, &trial_cache);
                        clock += DECOTIMESTEP;
                        /* Finish infinite deco */
                        if(clock >= 48 * 3600 && depth >= 6000) {
@@ -1002,7 +1046,6 @@ int plan(struct diveplan *diveplan, char **cached_datap, 
bool is_planner, bool s
                                        }
                                }
                        }
-                       trial_depth = depth;
                }
                if (stopping) {
                        /* Next we will ascend again. Add a waypoint if we have 
spend deco time */
diff --git a/planner.h b/planner.h
index b1e37d5..9a2c08b 100644
--- a/planner.h
+++ b/planner.h
@@ -2,6 +2,7 @@
 #define PLANNER_H
 
 #define LONGDECO 1
+#define NOT_RECREATIONAL 2
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/pref.h b/pref.h
index fb01d28..56fc79b 100644
--- a/pref.h
+++ b/pref.h
@@ -78,6 +78,7 @@ struct preferences {
        bool display_runtime;
        bool display_duration;
        bool display_transitions;
+       bool recreational_mode;
        int bottomsac;
        int decosac;
        int o2consumption; // ml per min
diff --git a/qt-ui/diveplanner.cpp b/qt-ui/diveplanner.cpp
index f66d721..2338109 100644
--- a/qt-ui/diveplanner.cpp
+++ b/qt-ui/diveplanner.cpp
@@ -394,6 +394,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.ascrate75 = s.value("ascrate75", prefs.ascrate75).toInt();
        prefs.ascrate50 = s.value("ascrate50", prefs.ascrate50).toInt();
        prefs.ascratestops = s.value("ascratestops", 
prefs.ascratestops).toInt();
@@ -415,6 +416,7 @@ 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.bottompo2->setValue(prefs.bottompo2 / 1000.0);
        ui.decopo2->setValue(prefs.decopo2 / 1000.0);
        ui.backgasBreaks->setChecked(prefs.doo2breaks);
@@ -428,6 +430,7 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget 
*parent, Qt::WindowFlags f)
        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.recreational_mode, SIGNAL(toggled(bool)), plannerModel, 
SLOT(setRecreationalMode(bool)));
        connect(ui.ascRate75, SIGNAL(valueChanged(int)), this, 
SLOT(setAscRate75(int)));
        connect(ui.ascRate75, SIGNAL(valueChanged(int)), plannerModel, 
SLOT(emitDataChanged()));
        connect(ui.ascRate50, SIGNAL(valueChanged(int)), this, 
SLOT(setAscRate50(int)));
@@ -475,6 +478,7 @@ 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("ascrate75", prefs.ascrate75);
        s.setValue("ascrate50", prefs.ascrate50);
        s.setValue("ascratestops", prefs.ascratestops);
@@ -866,6 +870,12 @@ void DivePlannerPointsModel::setDisplayTransitions(bool 
value)
        emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS 
- 1));
 }
 
+void DivePlannerPointsModel::setRecreationalMode(bool value)
+{
+       prefs.recreational_mode = value;
+       emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS 
-1));
+}
+
 void DivePlannerPointsModel::setDropStoneMode(bool value)
 {
        prefs.drop_stone_mode = value;
diff --git a/qt-ui/diveplanner.h b/qt-ui/diveplanner.h
index 196e175..eed481c 100644
--- a/qt-ui/diveplanner.h
+++ b/qt-ui/diveplanner.h
@@ -82,6 +82,7 @@ slots:
        void setDisplayRuntime(bool value);
        void setDisplayDuration(bool value);
        void setDisplayTransitions(bool value);
+       void setRecreationalMode(bool value);
        void savePlan();
        void saveDuplicatePlan();
        void remove(const QModelIndex &index);
diff --git a/qt-ui/plannerSettings.ui b/qt-ui/plannerSettings.ui
index 0951e32..09f019e 100644
--- a/qt-ui/plannerSettings.ui
+++ b/qt-ui/plannerSettings.ui
@@ -262,62 +262,52 @@
           <property name="spacing">
            <number>2</number>
           </property>
-          <item row="0" column="2">
-           <widget class="QSpinBox" name="gflow">
-            <property name="suffix">
-             <string>%</string>
-            </property>
-            <property name="minimum">
-             <number>1</number>
-            </property>
-            <property name="maximum">
-             <number>150</number>
-            </property>
-           </widget>
-          </item>
-          <item row="0" column="1">
-           <widget class="QLabel" name="label_15">
-            <property name="text">
-             <string>GF low</string>
-            </property>
-           </widget>
-          </item>
-          <item row="1" column="1">
+          <item row="2" column="1">
            <widget class="QLabel" name="label_16">
             <property name="text">
              <string>GF high</string>
             </property>
            </widget>
           </item>
-          <item row="4" column="1" colspan="2">
+          <item row="5" column="1" colspan="2">
            <widget class="QCheckBox" name="backgasBreaks">
             <property name="text">
              <string>Plan backgas breaks</string>
             </property>
            </widget>
           </item>
-          <item row="6" column="1">
-           <spacer name="verticalSpacer_2">
-            <property name="orientation">
-             <enum>Qt::Vertical</enum>
+          <item row="2" column="2">
+           <widget class="QSpinBox" name="gfhigh">
+            <property name="suffix">
+             <string>%</string>
             </property>
-            <property name="sizeHint" stdset="0">
-             <size>
-              <width>20</width>
-              <height>40</height>
-             </size>
+            <property name="minimum">
+             <number>1</number>
             </property>
-           </spacer>
+            <property name="maximum">
+             <number>150</number>
+            </property>
+           </widget>
           </item>
-          <item row="3" column="1" colspan="2">
+          <item row="4" column="1" colspan="2">
            <widget class="QCheckBox" name="lastStop">
             <property name="text">
              <string>Last stop at 6m</string>
             </property>
            </widget>
           </item>
+          <item row="6" column="1">
+           <widget class="QComboBox" name="rebreathermode">
+            <property name="currentText">
+             <string/>
+            </property>
+            <property name="maxVisibleItems">
+             <number>6</number>
+            </property>
+           </widget>
+          </item>
           <item row="1" column="2">
-           <widget class="QSpinBox" name="gfhigh">
+           <widget class="QSpinBox" name="gflow">
             <property name="suffix">
              <string>%</string>
             </property>
@@ -329,20 +319,37 @@
             </property>
            </widget>
           </item>
-          <item row="2" column="1" colspan="2">
+          <item row="3" column="1" colspan="2">
            <widget class="QCheckBox" name="drop_stone_mode">
             <property name="text">
              <string>Drop to first depth</string>
             </property>
            </widget>
           </item>
-          <item row="5" column="1">
-           <widget class="QComboBox" name="rebreathermode">
-            <property name="currentText">
-             <string/>
+          <item row="7" column="1">
+           <spacer name="verticalSpacer_2">
+            <property name="orientation">
+             <enum>Qt::Vertical</enum>
             </property>
-            <property name="maxVisibleItems">
-             <number>6</number>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>20</width>
+              <height>40</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+          <item row="1" column="1">
+           <widget class="QLabel" name="label_15">
+            <property name="text">
+             <string>GF low</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="1">
+           <widget class="QCheckBox" name="recreational_mode">
+            <property name="text">
+             <string>Recreational mode</string>
             </property>
            </widget>
           </item>
diff --git a/qt-ui/profile/diveprofileitem.cpp 
b/qt-ui/profile/diveprofileitem.cpp
index 4ad0ba3..7d29d28 100644
--- a/qt-ui/profile/diveprofileitem.cpp
+++ b/qt-ui/profile/diveprofileitem.cpp
@@ -169,7 +169,7 @@ void DiveProfileItem::modelDataChanged(const QModelIndex 
&topLeft, const QModelI
                for (int i = 0; i < dataModel->rowCount(); i++, entry++) {
                        int max = maxCeiling(i);
                        // Don't scream if we violate the ceiling by a few cm
-                       if (entry->depth < max - 100) {
+                       if (entry->depth < max - 100 && entry->sec > 0) {
                                profileColor = QColor(Qt::red);
                                if (!eventAdded) {
                                        add_event(&displayed_dive.dc, 
entry->sec, SAMPLE_EVENT_CEILING, -1, max / 1000, "planned waypoint above 
ceiling");
diff --git a/subsurfacestartup.c b/subsurfacestartup.c
index ce66aee..09858f9 100644
--- a/subsurfacestartup.c
+++ b/subsurfacestartup.c
@@ -50,6 +50,7 @@ struct preferences default_prefs = {
        .display_runtime = true,
        .display_duration = true,
        .display_transitions = true,
+       .recreational_mode = false,
        .bottomsac = 20000,
        .decosac = 17000,
        .o2consumption = 720,
-- 
1.9.5 (Apple Git-50.3)

this patch introduces a recreational mode for the planner: Rather than planning stops it extends the bottom time to the non-decompression limit as a recreational diver avoiding mandatory stops would do.

Best
Robert

-- 
.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oO
Robert C. Helling     Elite Master Course Theoretical and Mathematical Physics
                      Scientific Coordinator
                      Ludwig Maximilians Universitaet Muenchen, Dept. Physik
                      Phone: +49 89 2180-4523  Theresienstr. 39, rm. B339
                      http://www.atdotde.de

Enhance your privacy, use cryptography! My PGP keys have fingerprints
A9D1 A01D 13A5 31FA 6515  BB44 0820 367C 36BC 0C1D    and
DCED 37B6 251C 7861 270D  5613 95C7 9D32 9A8D 9B8F





Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

_______________________________________________
subsurface mailing list
subsurface@subsurface-divelog.org
http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface

Reply via email to